diff --git a/brainglobe_registration/registration_widget.py b/brainglobe_registration/registration_widget.py index edaffc9..05adfab 100644 --- a/brainglobe_registration/registration_widget.py +++ b/brainglobe_registration/registration_widget.py @@ -24,7 +24,7 @@ from napari.utils.notifications import show_error from napari.viewer import Viewer from pytransform3d.rotations import active_matrix_from_angle -from qtpy.QtWidgets import QPushButton, QTabWidget +from qtpy.QtWidgets import QCheckBox, QPushButton, QTabWidget from skimage.segmentation import find_boundaries from skimage.transform import rescale @@ -33,6 +33,7 @@ adjust_napari_image_layer, calculate_rotated_bounding_box, check_atlas_installed, + filter_plane, find_layer_index, get_image_layer_names, open_parameter_file, @@ -141,6 +142,9 @@ def __init__(self, napari_viewer: Viewer): self._on_default_file_selection_change ) + self.filter_checkbox = QCheckBox("Filter Images") + self.filter_checkbox.setChecked(False) + self.run_button = QPushButton("Run") self.run_button.clicked.connect(self._on_run_button_click) self.run_button.setEnabled(False) @@ -176,6 +180,9 @@ def __init__(self, napari_viewer: Viewer): self.add_widget( self.parameters_tab, widget_title="Advanced Settings (optional)" ) + + self.add_widget(self.filter_checkbox, collapsible=False) + self.add_widget(self.run_button, collapsible=False) self.layout().itemAt(1).widget().collapse(animate=False) @@ -276,9 +283,22 @@ def _on_run_button_click(self): current_atlas_slice = self._viewer.dims.current_step[0] + if self.filter_checkbox.isChecked(): + atlas_layer = filter_plane( + self._atlas_data_layer.data[ + current_atlas_slice, :, : + ].compute() + ) + moving_image_layer = filter_plane(self._moving_image.data) + else: + atlas_layer = self._atlas_data_layer.data[ + current_atlas_slice, :, : + ] + moving_image_layer = self._moving_image.data + result, parameters, registered_annotation_image = run_registration( - self._atlas_data_layer.data[current_atlas_slice, :, :], - self._moving_image.data, + atlas_layer, + moving_image_layer, self._atlas_annotations_layer.data[current_atlas_slice, :, :], self.transform_selections, ) diff --git a/brainglobe_registration/utils/utils.py b/brainglobe_registration/utils/utils.py index e94faa7..858ebab 100644 --- a/brainglobe_registration/utils/utils.py +++ b/brainglobe_registration/utils/utils.py @@ -8,6 +8,8 @@ from brainglobe_utils.qtpy.dialog import display_info from pytransform3d.rotations import active_matrix_from_angle from qtpy.QtWidgets import QWidget +from scipy.ndimage import gaussian_filter +from skimage import morphology def adjust_napari_image_layer( @@ -175,3 +177,80 @@ def check_atlas_installed(parent_widget: QWidget): "brainglobe-atlasapi or brainrender-napari", ) + + +def filter_plane(img_plane): + """ + Apply a set of filter to the plane (typically to avoid overfitting details + in the image during registration) + The filter is composed of a despeckle filter using opening and a pseudo + flatfield filter + + Originally from: [https://github.com/brainglobe/brainreg/blob/main + /brainreg/core/utils/preprocess.py] + + Parameters + ---------- + img_plane : np.array + A 2D array to filter + + Returns + ------- + np.array + Filtered image + """ + + img_plane = despeckle_by_opening(img_plane) + img_plane = pseudo_flatfield(img_plane) + return img_plane + + +def despeckle_by_opening(img_plane, radius=2): + """ + Despeckle the image plane using a grayscale opening operation + + Originally from: [https://github.com/brainglobe/brainreg/blob/main + /brainreg/core/utils/preprocess.py] + + Parameters + ---------- + img_plane : np.array + The image to filter + + radius: int + The radius of the opening kernel + + Returns + ------- + np.array + The despeckled image + """ + kernel = morphology.disk(radius) + morphology.opening(img_plane, out=img_plane, footprint=kernel) + return img_plane + + +def pseudo_flatfield(img_plane, sigma=5): + """ + Pseudo flat field filter implementation using a de-trending by a + heavily gaussian filtered copy of the image. + + Originally from: [https://github.com/brainglobe/brainreg/blob/main + /brainreg/core/utils/preprocess.py] + + Parameters + ---------- + img_plane : np.array + The image to filter + + sigma : int + The sigma of the gaussian filter applied to the + image used for de-trending + + Returns + ------- + np.array + The pseudo flat field filtered image + """ + filtered_img = gaussian_filter(img_plane, sigma) + return img_plane / (filtered_img + 1)