Skip to content

Commit

Permalink
added reorientation
Browse files Browse the repository at this point in the history
  • Loading branch information
niksirbi committed May 23, 2024
1 parent 0507534 commit 9ef6011
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 14 deletions.
18 changes: 13 additions & 5 deletions brainglobe_template_builder/napari/_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@

from brainglobe_template_builder.napari.align_widget import AlignMidplane
from brainglobe_template_builder.napari.mask_widget import CreateMask
from brainglobe_template_builder.napari.save_widget import SaveWidget
from brainglobe_template_builder.napari.reorient_widget import Reorient
from brainglobe_template_builder.napari.save_widget import SaveFiles


class PreprocWidgets(CollapsibleWidgetContainer):
def __init__(self, napari_viewer: Viewer, parent=None):
super().__init__()

self.add_widget(
Reorient(napari_viewer, parent=self),
collapsible=True,
widget_title="Reorient to standard space",
)

self.add_widget(
CreateMask(napari_viewer, parent=self),
collapsible=True,
Expand All @@ -23,18 +30,19 @@ def __init__(self, napari_viewer: Viewer, parent=None):
)

self.add_widget(
SaveWidget(napari_viewer, parent=self),
SaveFiles(napari_viewer, parent=self),
collapsible=True,
widget_title="Save",
widget_title="Save files",
)

(
self.reorient_widget,
self.mask_widget,
self.midplane_widget,
self.save_widget,
) = self.collapsible_widgets
# expand mask widget by default
self.mask_widget.expand()
# expand first widget by default
self.reorient_widget.expand()
# refresh dropdowns when midline widget is toggled
self.midplane_widget.toggled.connect(
self.midplane_widget.content().refresh_dropdowns
Expand Down
19 changes: 13 additions & 6 deletions brainglobe_template_builder/napari/align_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ def _on_estimate_button_click(self):
axis = self.select_axis_dropdown.currentText()
estimator = MidplaneEstimator(mask.data, symmetry_axis=axis)
points = estimator.get_points()
prefix = mask_name.split("_label-")[0]
points_name = f"{prefix}_points-midplane"

# Point layer attributes
point_attrs = {
Expand All @@ -134,15 +136,15 @@ def _on_estimate_button_click(self):
"opacity": 0.6,
"size": 6,
"ndim": mask.ndim,
"name": "midplane points",
"name": points_name,
}

self.viewer.add_points(points, **point_attrs)
self.refresh_dropdowns()
# Move viewer to show z-plane of first point
self.viewer.dims.set_point(0, points[0][0])
# Enable "Select points" mode
self.viewer.layers["midplane_points"].mode = "select"
self.viewer.layers[points_name].mode = "select"
show_info("Please move all 9 estimated points exactly to the midplane")

def _on_align_button_click(self):
Expand All @@ -161,19 +163,24 @@ def _on_align_button_click(self):
symmetry_axis=axis,
)
aligned_image = aligner.transform_image(image_data)
self.viewer.add_image(aligned_image, name="aligned_image")
aligned_image_name = f"{image_name}_aligned"
self.viewer.add_image(aligned_image, name=aligned_image_name)
aligned_mask = aligner.transform_image(mask_data)
self.viewer.add_labels(aligned_mask, name="aligned_mask", opacity=0.5)
aligned_mask_name = f"{mask_name}_aligned"
self.viewer.add_labels(
aligned_mask, name=aligned_mask_name, opacity=0.5
)
aligned_halves = aligner.label_halves(aligned_image)
aligned_halves_name = aligned_mask_name.replace("-brain", "-halves")
self.viewer.add_labels(
aligned_halves, name="aligned_halves", opacity=0.5
aligned_halves, name=aligned_halves_name, opacity=0.5
)
# Hide original image, mask, and points layers
self.viewer.layers[image_name].visible = False
self.viewer.layers[mask_name].visible = False
self.viewer.layers[points_name].visible = False
# Hide aligned mask layer
self.viewer.layers["aligned_mask"].visible = False
self.viewer.layers[aligned_mask_name].visible = False
# Make aligner object accessible to other methods
self.aligner = aligner

Expand Down
3 changes: 2 additions & 1 deletion brainglobe_template_builder/napari/mask_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,5 @@ def _on_button_click(self):
closing_size=self.closing_size.value(),
)

self.viewer.add_labels(mask_data, name="mask", opacity=0.5)
mask_name = f"{image.name}_label-brain"
self.viewer.add_labels(mask_data, name=mask_name, opacity=0.5)
128 changes: 128 additions & 0 deletions brainglobe_template_builder/napari/reorient_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
from brainglobe_space import AnatomicalSpace
from napari.layers import Image, Labels, Points
from napari.utils.notifications import show_info
from napari.viewer import Viewer
from qtpy.QtWidgets import (
QComboBox,
QFormLayout,
QGroupBox,
QHBoxLayout,
QPushButton,
QWidget,
)


class Reorient(QWidget):
"""Widget to reorient images to a standard anatomical space."""

def __init__(self, napari_viewer: Viewer, parent=None):
super().__init__(parent=parent)
self.viewer = napari_viewer
self.setLayout(QFormLayout())

self._create_reorient_group()

def _create_reorient_group(self):
self.reorient_groupbox = QGroupBox("Reorient to standard space")
self.reorient_groupbox.setLayout(QFormLayout())
self.layout().addRow(self.reorient_groupbox)

avail_labels = AnatomicalSpace.lims_labels

# Enter labels for source space
source_origin_row = QHBoxLayout()
self.source_origin1 = QComboBox(parent=self.reorient_groupbox)
self.source_origin1.addItems(avail_labels)
self.source_origin1.setCurrentIndex(0)
source_origin_row.addWidget(self.source_origin1)
self.source_origin2 = QComboBox(parent=self.reorient_groupbox)
self.source_origin2.addItems(avail_labels)
self.source_origin2.setCurrentIndex(2)
source_origin_row.addWidget(self.source_origin2)
self.source_origin3 = QComboBox(parent=self.reorient_groupbox)
self.source_origin3.addItems(avail_labels)
self.source_origin3.setCurrentIndex(5)
source_origin_row.addWidget(self.source_origin3)
self.reorient_groupbox.layout().addRow(
"Source origin:", source_origin_row
)

# Enter labels for target space
target_origin_row = QHBoxLayout()
self.target_origin1 = QComboBox(parent=self.reorient_groupbox)
self.target_origin1.addItems(avail_labels)
self.target_origin1.setCurrentIndex(1)
target_origin_row.addWidget(self.target_origin1)
self.target_origin2 = QComboBox(parent=self.reorient_groupbox)
self.target_origin2.addItems(avail_labels)
self.target_origin2.setCurrentIndex(2)
target_origin_row.addWidget(self.target_origin2)
self.target_origin3 = QComboBox(parent=self.reorient_groupbox)
self.target_origin3.addItems(avail_labels)
self.target_origin3.setCurrentIndex(5)
target_origin_row.addWidget(self.target_origin3)
self.reorient_groupbox.layout().addRow(
"Target origin:", target_origin_row
)

self.reorient_button = QPushButton(
"Reorient selected layers", parent=self.reorient_groupbox
)
self.reorient_button.clicked.connect(self.reorient_layers)
self.reorient_groupbox.layout().addRow(self.reorient_button)

def reorient_layers(self):
"""Reorient selected layers to the target space."""
selected_layers = self.viewer.layers.selection
selected_stacks = [
layer
for layer in selected_layers
if isinstance(layer, Image) or isinstance(layer, Labels)
]
selected_points = [
layer for layer in selected_layers if isinstance(layer, Points)
]

if not selected_layers:
show_info("No layers selected. Select layers to reorient.")
return

source_origin = [
self.source_origin1.currentText(),
self.source_origin2.currentText(),
self.source_origin3.currentText(),
]
target_origin = [
self.target_origin1.currentText(),
self.target_origin2.currentText(),
self.target_origin3.currentText(),
]
source_origin = "".join(source_origin)
target_origin = "".join(target_origin)

for layer in selected_points:
if not selected_stacks:
show_info(
"Cannot reorient Points layer alone. Please select "
"at least one Image or Labels layer along with the "
"Points layer."
)
return
source_stack_shape = selected_stacks[0].data.shape
source_space = AnatomicalSpace(
source_origin, shape=source_stack_shape
)
layer.data = source_space.map_points_to(target_origin, layer.data)
layer.name = f"{layer.name}_space-{target_origin}"

for layer in selected_stacks:
interp_order = 0 if isinstance(layer, Labels) else 3
source_space = AnatomicalSpace(
source_origin, shape=layer.data.shape
)
layer.data = source_space.map_stack_to(
target_origin, layer.data, interp_order=interp_order
)
layer.name = f"{layer.name}_orig-{target_origin}"

show_info("Layers reoriented to target space.")
2 changes: 1 addition & 1 deletion brainglobe_template_builder/napari/save_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from brainglobe_template_builder.io import save_3d_points_to_csv, save_nii


class SaveWidget(QWidget):
class SaveFiles(QWidget):
def __init__(self, napari_viewer: Viewer, parent=None):
super().__init__(parent=parent)
self.viewer = napari_viewer
Expand Down
2 changes: 1 addition & 1 deletion brainglobe_template_builder/preproc/alignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,4 @@ def label_halves(self, image: np.ndarray) -> np.ndarray:
labelled_halves[tuple(slicer_half1)] = 2
labelled_halves[tuple(slicer_half2)] = 3

return labelled_halves
return labelled_halves

0 comments on commit 9ef6011

Please sign in to comment.