synapse_net.tools.distance_measure_widget
1import napari 2import napari.layers 3import numpy as np 4import pandas as pd 5 6from napari.utils.notifications import show_info 7from qtpy.QtWidgets import QWidget, QVBoxLayout, QPushButton 8 9from .base_widget import BaseWidget 10from .. import distance_measurements 11 12 13class DistanceMeasureWidget(BaseWidget): 14 def __init__(self): 15 super().__init__() 16 17 self.viewer = napari.current_viewer() 18 layout = QVBoxLayout() 19 20 self.image_selector_name1 = "Segmentation" 21 self.image_selector_name2 = "Object" 22 # Create the image selection dropdown. 23 self.segmentation1_selector_widget = self._create_layer_selector(self.image_selector_name1, layer_type="Labels") 24 self.segmentation2_selector_widget = self._create_layer_selector(self.image_selector_name2, layer_type="Labels") 25 26 # Create advanced settings. 27 self.settings = self._create_settings_widget() 28 29 # Create and connect buttons. 30 self.measure_button1 = QPushButton("Measure Distances") 31 self.measure_button1.clicked.connect(self.on_measure_seg_to_object) 32 33 self.measure_button2 = QPushButton("Measure Pairwise Distances") 34 self.measure_button2.clicked.connect(self.on_measure_pairwise) 35 36 # Add the widgets to the layout. 37 layout.addWidget(self.segmentation1_selector_widget) 38 layout.addWidget(self.segmentation2_selector_widget) 39 layout.addWidget(self.settings) 40 layout.addWidget(self.measure_button1) 41 layout.addWidget(self.measure_button2) 42 43 self.setLayout(layout) 44 45 def _to_table_data(self, distances, seg_ids, endpoints1=None, endpoints2=None): 46 assert len(distances) == len(seg_ids), f"{distances.shape}, {seg_ids.shape}" 47 if seg_ids.ndim == 2: 48 table_data = {"label1": seg_ids[:, 0], "label2": seg_ids[:, 1], "distance": distances} 49 else: 50 table_data = {"label": seg_ids, "distance": distances} 51 if endpoints1 is not None: 52 axis_names = "zyx" if endpoints1.shape[1] == 3 else "yx" 53 table_data.update({f"begin-{ax}": endpoints1[:, i] for i, ax in enumerate(axis_names)}) 54 table_data.update({f"end-{ax}": endpoints2[:, i] for i, ax in enumerate(axis_names)}) 55 return pd.DataFrame(table_data) 56 57 def _add_lines_and_table(self, lines, table_data, name): 58 line_layer = self.viewer.add_shapes( 59 lines, 60 name=name, 61 shape_type="line", 62 edge_width=2, 63 edge_color="red", 64 blending="additive", 65 ) 66 self._add_properties_and_table(line_layer, table_data, self.save_path.text()) 67 68 def on_measure_seg_to_object(self): 69 segmentation = self._get_layer_selector_data(self.image_selector_name1) 70 object_data = self._get_layer_selector_data(self.image_selector_name2) 71 72 # Get the resolution / voxel size. 73 metadata = self._get_layer_selector_data(self.image_selector_name1, return_metadata=True) 74 resolution = self._handle_resolution(metadata, self.voxel_size_param, segmentation.ndim) 75 76 (distances, 77 endpoints1, 78 endpoints2, 79 seg_ids) = distance_measurements.measure_segmentation_to_object_distances( 80 segmentation=segmentation, segmented_object=object_data, distance_type="boundary", 81 resolution=resolution 82 ) 83 lines, _ = distance_measurements.create_object_distance_lines( 84 distances=distances, 85 endpoints1=endpoints1, 86 endpoints2=endpoints2, 87 seg_ids=seg_ids, 88 ) 89 table_data = self._to_table_data(distances, seg_ids, endpoints1, endpoints2) 90 structure_layer_name = self._get_layer_selector_layer(self.image_selector_name2).name 91 self._add_lines_and_table(lines, table_data, name="distances-to-" + structure_layer_name) 92 93 def on_measure_pairwise(self): 94 segmentation = self._get_layer_selector_data(self.image_selector_name1) 95 if segmentation is None: 96 show_info("Please choose a segmentation.") 97 return 98 99 metadata = self._get_layer_selector_data(self.image_selector_name1, return_metadata=True) 100 resolution = self._handle_resolution(metadata, self.voxel_size_param, segmentation.ndim) 101 102 (distances, 103 endpoints1, 104 endpoints2, 105 seg_ids) = distance_measurements.measure_pairwise_object_distances( 106 segmentation=segmentation, distance_type="boundary", resolution=resolution 107 ) 108 lines, properties = distance_measurements.create_pairwise_distance_lines( 109 distances=distances, endpoints1=endpoints1, endpoints2=endpoints2, seg_ids=seg_ids.tolist(), 110 ) 111 table_data = self._to_table_data( 112 distances=properties["distance"], 113 seg_ids=np.concatenate([properties["id_a"][:, None], properties["id_b"][:, None]], axis=1) 114 ) 115 self._add_lines_and_table(lines, table_data, name="pairwise-distances") 116 117 def _create_settings_widget(self): 118 setting_values = QWidget() 119 setting_values.setLayout(QVBoxLayout()) 120 121 self.save_path, layout = self._add_path_param(name="Save Table", select_type="file", value="") 122 setting_values.layout().addLayout(layout) 123 124 self.voxel_size_param, layout = self._add_float_param( 125 "voxel_size", 0.0, min_val=0.0, max_val=100.0, 126 ) 127 setting_values.layout().addLayout(layout) 128 129 settings = self._make_collapsible(widget=setting_values, title="Advanced Settings") 130 return settings
14class DistanceMeasureWidget(BaseWidget): 15 def __init__(self): 16 super().__init__() 17 18 self.viewer = napari.current_viewer() 19 layout = QVBoxLayout() 20 21 self.image_selector_name1 = "Segmentation" 22 self.image_selector_name2 = "Object" 23 # Create the image selection dropdown. 24 self.segmentation1_selector_widget = self._create_layer_selector(self.image_selector_name1, layer_type="Labels") 25 self.segmentation2_selector_widget = self._create_layer_selector(self.image_selector_name2, layer_type="Labels") 26 27 # Create advanced settings. 28 self.settings = self._create_settings_widget() 29 30 # Create and connect buttons. 31 self.measure_button1 = QPushButton("Measure Distances") 32 self.measure_button1.clicked.connect(self.on_measure_seg_to_object) 33 34 self.measure_button2 = QPushButton("Measure Pairwise Distances") 35 self.measure_button2.clicked.connect(self.on_measure_pairwise) 36 37 # Add the widgets to the layout. 38 layout.addWidget(self.segmentation1_selector_widget) 39 layout.addWidget(self.segmentation2_selector_widget) 40 layout.addWidget(self.settings) 41 layout.addWidget(self.measure_button1) 42 layout.addWidget(self.measure_button2) 43 44 self.setLayout(layout) 45 46 def _to_table_data(self, distances, seg_ids, endpoints1=None, endpoints2=None): 47 assert len(distances) == len(seg_ids), f"{distances.shape}, {seg_ids.shape}" 48 if seg_ids.ndim == 2: 49 table_data = {"label1": seg_ids[:, 0], "label2": seg_ids[:, 1], "distance": distances} 50 else: 51 table_data = {"label": seg_ids, "distance": distances} 52 if endpoints1 is not None: 53 axis_names = "zyx" if endpoints1.shape[1] == 3 else "yx" 54 table_data.update({f"begin-{ax}": endpoints1[:, i] for i, ax in enumerate(axis_names)}) 55 table_data.update({f"end-{ax}": endpoints2[:, i] for i, ax in enumerate(axis_names)}) 56 return pd.DataFrame(table_data) 57 58 def _add_lines_and_table(self, lines, table_data, name): 59 line_layer = self.viewer.add_shapes( 60 lines, 61 name=name, 62 shape_type="line", 63 edge_width=2, 64 edge_color="red", 65 blending="additive", 66 ) 67 self._add_properties_and_table(line_layer, table_data, self.save_path.text()) 68 69 def on_measure_seg_to_object(self): 70 segmentation = self._get_layer_selector_data(self.image_selector_name1) 71 object_data = self._get_layer_selector_data(self.image_selector_name2) 72 73 # Get the resolution / voxel size. 74 metadata = self._get_layer_selector_data(self.image_selector_name1, return_metadata=True) 75 resolution = self._handle_resolution(metadata, self.voxel_size_param, segmentation.ndim) 76 77 (distances, 78 endpoints1, 79 endpoints2, 80 seg_ids) = distance_measurements.measure_segmentation_to_object_distances( 81 segmentation=segmentation, segmented_object=object_data, distance_type="boundary", 82 resolution=resolution 83 ) 84 lines, _ = distance_measurements.create_object_distance_lines( 85 distances=distances, 86 endpoints1=endpoints1, 87 endpoints2=endpoints2, 88 seg_ids=seg_ids, 89 ) 90 table_data = self._to_table_data(distances, seg_ids, endpoints1, endpoints2) 91 structure_layer_name = self._get_layer_selector_layer(self.image_selector_name2).name 92 self._add_lines_and_table(lines, table_data, name="distances-to-" + structure_layer_name) 93 94 def on_measure_pairwise(self): 95 segmentation = self._get_layer_selector_data(self.image_selector_name1) 96 if segmentation is None: 97 show_info("Please choose a segmentation.") 98 return 99 100 metadata = self._get_layer_selector_data(self.image_selector_name1, return_metadata=True) 101 resolution = self._handle_resolution(metadata, self.voxel_size_param, segmentation.ndim) 102 103 (distances, 104 endpoints1, 105 endpoints2, 106 seg_ids) = distance_measurements.measure_pairwise_object_distances( 107 segmentation=segmentation, distance_type="boundary", resolution=resolution 108 ) 109 lines, properties = distance_measurements.create_pairwise_distance_lines( 110 distances=distances, endpoints1=endpoints1, endpoints2=endpoints2, seg_ids=seg_ids.tolist(), 111 ) 112 table_data = self._to_table_data( 113 distances=properties["distance"], 114 seg_ids=np.concatenate([properties["id_a"][:, None], properties["id_b"][:, None]], axis=1) 115 ) 116 self._add_lines_and_table(lines, table_data, name="pairwise-distances") 117 118 def _create_settings_widget(self): 119 setting_values = QWidget() 120 setting_values.setLayout(QVBoxLayout()) 121 122 self.save_path, layout = self._add_path_param(name="Save Table", select_type="file", value="") 123 setting_values.layout().addLayout(layout) 124 125 self.voxel_size_param, layout = self._add_float_param( 126 "voxel_size", 0.0, min_val=0.0, max_val=100.0, 127 ) 128 setting_values.layout().addLayout(layout) 129 130 settings = self._make_collapsible(widget=setting_values, title="Advanced Settings") 131 return settings
QWidget(parent: typing.Optional[QWidget] = None, flags: Union[Qt.WindowFlags, Qt.WindowType] = Qt.WindowFlags())
def
on_measure_seg_to_object(self):
69 def on_measure_seg_to_object(self): 70 segmentation = self._get_layer_selector_data(self.image_selector_name1) 71 object_data = self._get_layer_selector_data(self.image_selector_name2) 72 73 # Get the resolution / voxel size. 74 metadata = self._get_layer_selector_data(self.image_selector_name1, return_metadata=True) 75 resolution = self._handle_resolution(metadata, self.voxel_size_param, segmentation.ndim) 76 77 (distances, 78 endpoints1, 79 endpoints2, 80 seg_ids) = distance_measurements.measure_segmentation_to_object_distances( 81 segmentation=segmentation, segmented_object=object_data, distance_type="boundary", 82 resolution=resolution 83 ) 84 lines, _ = distance_measurements.create_object_distance_lines( 85 distances=distances, 86 endpoints1=endpoints1, 87 endpoints2=endpoints2, 88 seg_ids=seg_ids, 89 ) 90 table_data = self._to_table_data(distances, seg_ids, endpoints1, endpoints2) 91 structure_layer_name = self._get_layer_selector_layer(self.image_selector_name2).name 92 self._add_lines_and_table(lines, table_data, name="distances-to-" + structure_layer_name)
def
on_measure_pairwise(self):
94 def on_measure_pairwise(self): 95 segmentation = self._get_layer_selector_data(self.image_selector_name1) 96 if segmentation is None: 97 show_info("Please choose a segmentation.") 98 return 99 100 metadata = self._get_layer_selector_data(self.image_selector_name1, return_metadata=True) 101 resolution = self._handle_resolution(metadata, self.voxel_size_param, segmentation.ndim) 102 103 (distances, 104 endpoints1, 105 endpoints2, 106 seg_ids) = distance_measurements.measure_pairwise_object_distances( 107 segmentation=segmentation, distance_type="boundary", resolution=resolution 108 ) 109 lines, properties = distance_measurements.create_pairwise_distance_lines( 110 distances=distances, endpoints1=endpoints1, endpoints2=endpoints2, seg_ids=seg_ids.tolist(), 111 ) 112 table_data = self._to_table_data( 113 distances=properties["distance"], 114 seg_ids=np.concatenate([properties["id_a"][:, None], properties["id_b"][:, None]], axis=1) 115 ) 116 self._add_lines_and_table(lines, table_data, name="pairwise-distances")