synapse_net.tools.vesicle_pool_widget

  1from typing import Dict
  2
  3import napari
  4import napari.layers
  5import napari.viewer
  6import numpy as np
  7import pandas as pd
  8
  9from napari.utils.notifications import show_info
 10from qtpy.QtWidgets import QWidget, QVBoxLayout, QPushButton
 11
 12from .base_widget import BaseWidget
 13
 14# This will fail if we have more than 8 pools.
 15COLORMAP = ["red", "blue", "yellow", "cyan", "purple", "magenta", "orange", "green"]
 16
 17
 18class VesiclePoolWidget(BaseWidget):
 19    def __init__(self):
 20        super().__init__()
 21
 22        self.viewer = napari.current_viewer()
 23        layout = QVBoxLayout()
 24
 25        # Create the selectors for the layers:
 26        # 1. Selector for the labels layer with vesicles.
 27        self.vesicle_selector_name = "Vesicle Segmentation"
 28        self.vesicle_selector_widget = self._create_layer_selector(self.vesicle_selector_name, layer_type="Labels")
 29        # 2. Selector for a distance layer.
 30        self.dist_selector_name1 = "Distances to Structure"
 31        self.dist_selector_widget1 = self._create_layer_selector(self.dist_selector_name1, layer_type="Shapes")
 32        # 3. Selector for a second distance layer (optional).
 33        self.dist_selector_name2 = "Distances to Structure 2"
 34        self.dist_selector_widget2 = self._create_layer_selector(self.dist_selector_name2, layer_type="Shapes")
 35
 36        # Add the selector widgets to the layout.
 37        layout.addWidget(self.vesicle_selector_widget)
 38        layout.addWidget(self.dist_selector_widget1)
 39        layout.addWidget(self.dist_selector_widget2)
 40
 41        # Create the UI elements for defining the vesicle pools:
 42        # The name of the output name, the name of the vesicle pool, and the criterion for the pool.
 43        self.pool_layer_name_param, pool_layer_name_layout = self._add_string_param(name="Layer Name", value="")
 44        self.pool_name_param, pool_name_layout = self._add_string_param(name="Vesicle Pool", value="")
 45        self.query_param, query_layout = self._add_string_param(
 46            name="Criterion", value="",
 47            tooltip="Enter a comma separated criterion (e.g., 'radius > 15, distance > 250') "
 48            "Possible filters: radius, distance, area, intensity_max, intensity_mean, intensity_min, intensity_std"
 49        )
 50        layout.addLayout(pool_layer_name_layout)
 51        layout.addLayout(pool_name_layout)
 52        layout.addLayout(query_layout)
 53
 54        # Create the UI elements for advanced settings and the run button.
 55        self.settings = self._create_settings_widget()
 56        self.measure_button = QPushButton("Create Vesicle Pool")
 57        self.measure_button.clicked.connect(self.on_pool_vesicles)
 58        layout.addWidget(self.settings)
 59        layout.addWidget(self.measure_button)
 60
 61        self.setLayout(layout)
 62
 63        # The colormap for displaying the vesicle pools.
 64        self.pool_colors = {}
 65
 66    def on_pool_vesicles(self):
 67        segmentation = self._get_layer_selector_data(self.vesicle_selector_name)
 68        morphology = self._get_layer_selector_layer(self.vesicle_selector_name).properties
 69        if not morphology:
 70            morphology = None
 71
 72        distance_layer = self._get_layer_selector_layer(self.dist_selector_name1)
 73        distances = None if distance_layer is None else distance_layer.properties
 74        distance_layer2 = self._get_layer_selector_layer(self.dist_selector_name2)
 75        # Check if the second distance is the same as the first.
 76        if distance_layer2.name == distance_layer.name:
 77            distance_layer2 = None
 78        distances2 = None if distance_layer2 is None else distance_layer2.properties
 79
 80        if segmentation is None:
 81            show_info("INFO: Please choose a segmentation.")
 82            return
 83        if self.query_param.text() == "":
 84            show_info("INFO: Please enter a query string.")
 85            return
 86        query = self.query_param.text()
 87
 88        if self.pool_layer_name_param.text() == "":
 89            show_info("INFO: Please enter a name for the pool layer.")
 90            return
 91        pool_layer_name = self.pool_layer_name_param.text()
 92        if self.pool_name_param.text() == "":
 93            show_info("INFO: Please enter a name for the vesicle pool.")
 94            return
 95        pool_name = self.pool_name_param.text()
 96
 97        pool_color = self.pool_color_param.text()
 98        self._compute_vesicle_pool(
 99            segmentation, distances, morphology, pool_layer_name, pool_name, query, pool_color, distances2
100            )
101
102    def _update_pool_colors(self, pool_name, pool_color):
103        if pool_color == "":
104            next_color_id = len(self.pool_colors)
105            next_color = COLORMAP[next_color_id]
106        else:
107            # We could check here that this is a valid color.
108            next_color = pool_color
109        self.pool_colors[pool_name] = next_color
110
111    def _compute_vesicle_pool(
112        self,
113        segmentation: np.ndarray,
114        distances: Dict,
115        morphology: Dict,
116        pool_layer_name: str,
117        pool_name: str,
118        query: str,
119        pool_color: str,
120        distances2: Dict = None
121    ):
122        """Compute a vesicle pool based on the provided query parameters.
123
124        Args:
125            segmentation: Segmentation data (e.g., labeled regions).
126            distances: Properties from the distances layer.
127            morphology: Properties from the morphology layer.
128            pool_layer_name: Name for the new layer to be created.
129            pool_name: Name for the pooled group to be assigned.
130            query: Query parameters.
131            pool_color: Optional color for the vesicle pool.
132            distances2: Properties from the second distances layer (optional).
133        """
134        # Check which of the properties are present and construct the combined properties based on this.
135        if distances is None and morphology is None:  # No properties were given -> we can't do anything.
136            show_info("ERROR: Neither distances nor vesicle morphology were found.")
137            return
138        elif distances is None and morphology is not None:  # Only morphology props were found.
139            merged_df = pd.DataFrame(morphology).drop(columns=["index"])
140        elif distances is not None and morphology is None:  # Only distances were found.
141            merged_df = pd.DataFrame(distances).drop(columns=["index"])
142        else:  # Both were found.
143            distance_ids = distances.get("label", [])
144            morphology_ids = morphology.get("label", [])
145
146            # Ensure that IDs are identical.
147            if set(distance_ids) != set(morphology_ids):
148                show_info("ERROR: The IDs in distances and morphology are not identical.")
149                return
150
151            # Create a merged dataframe from the dataframes which are relevant for the criterion.
152            distances = pd.DataFrame(distances).drop(columns=["index"])
153            morphology = pd.DataFrame(morphology).drop(columns=["index"])
154            merged_df = morphology.merge(distances, left_on="label", right_on="label", suffixes=("_morph", "_dist"))
155        # Add distances2 if present.
156        if distances2 is not None:
157            distance_ids = distances2.get("label", [])
158            if set(distance_ids) != set(merged_df.label):
159                show_info("ERROR: The IDs in distances2 and morphology are not identical.")
160                return
161            distances2 = pd.DataFrame(distances2).drop(columns=["index"])
162            merged_df = merged_df.merge(distances2, left_on="label", right_on="label", suffixes=("", "2"))
163        # Assign the vesicles to the current pool by filtering the mergeddataframe based on the query.
164        filtered_df = self._parse_query(query, merged_df)
165        if len(filtered_df) == 0:
166            show_info("No vesicles were found matching the condition.")
167            return
168        pool_vesicle_ids = filtered_df.label.values.tolist()
169        vesicles_in_pool = len(pool_vesicle_ids)
170
171        # Check if this layer was already created in a previous pool assignment.
172        if pool_layer_name in self.viewer.layers:
173            # If yes then load the previous pool assignments and merge them with the new pool assignments
174            pool_layer = self.viewer.layers[pool_layer_name]
175            pool_properties = pd.DataFrame.from_dict(pool_layer.properties)
176
177            pool_names = pd.unique(pool_properties.pool)
178            if pool_name in pool_names:
179                show_info(f"Updating pool '{pool_name}' with {vesicles_in_pool} vesicles.")
180                # This pool has already been assigned and we changed the criterion.
181                # Its old assignment has to be over-written, remove the rows for this pool.
182                pool_properties = pool_properties[pool_properties.pool != pool_name]
183            else:
184                show_info(f"Creating pool '{pool_name}' with {vesicles_in_pool} vesicles.")
185
186            # Combine the vesicle ids corresponding to the previous assignment with the
187            # assignment for the new / current pool.
188            old_pool_ids = pool_properties.label.values.tolist()
189
190            # Overwrite the intersection of the two pool assignments with the new pool.
191            pool_intersections = np.intersect1d(pool_vesicle_ids, old_pool_ids)
192            old_pool_ids = [item for item in old_pool_ids if item not in pool_intersections]
193            pool_properties = pool_properties[~pool_properties['label'].isin(pool_intersections)]
194
195            pool_assignments = sorted(pool_vesicle_ids + old_pool_ids)
196
197            # Get a map for each vesicle id to its pool.
198            id_to_pool_name = {ves_id: pool_name for ves_id in pool_vesicle_ids}
199            id_to_pool_name.update({k: v for k, v in zip(old_pool_ids, pool_properties.pool.values)})
200
201            # Get the pool values.
202            # This is the list of pool names, corresponding to the selected ids in pool_assignments.
203            pool_values = [id_to_pool_name[ves_id] for ves_id in pool_assignments]
204
205        else:
206            show_info(f"Creating pool '{pool_name}' with {vesicles_in_pool} vesicles.")
207            # Otherwise, this is the first pool assignment.
208            pool_assignments = pool_vesicle_ids
209            pool_values = [pool_name] * len(pool_assignments)
210
211        # Create the filtered segmentation.
212        vesicle_pools = segmentation.copy()
213        vesicle_pools[~np.isin(vesicle_pools, pool_assignments)] = 0
214
215        # Create the pool properties.
216        pool_properties = merged_df[merged_df.label.isin(pool_assignments)]
217        # Remove columns that are not relevant for measurements.
218        keep_columns = [
219            col for col in pool_properties.columns
220            if col not in ("x", "y", "z", "begin-x", "begin-y", "begin-z", "end-x", "end-y", "end-z")
221        ]
222        pool_properties = pool_properties[keep_columns]
223        # Add a colun for the pool.
224        pool_properties.insert(1, "pool", pool_values)
225
226        # Update the colormap to display the pools.
227        self._update_pool_colors(pool_name, pool_color)
228
229        # Assign the vesicle ids to their pool color.
230        vesicle_colors = {
231            label_id: self.pool_colors[pname] for label_id, pname in zip(
232                pool_properties.label.values, pool_properties.pool.values
233            )
234        }
235        vesicle_colors[None] = "gray"
236
237        # Add or replace the pool layer and properties.
238        if pool_layer_name in self.viewer.layers:
239            pool_layer = self.viewer.layers[pool_layer_name]
240            pool_layer.data = vesicle_pools
241            pool_layer.colormap = vesicle_colors
242        else:
243            pool_layer = self.viewer.add_labels(vesicle_pools, name=pool_layer_name, colormap=vesicle_colors)
244
245        self._add_properties_and_table(pool_layer, pool_properties, save_path=self.save_path.text())
246        pool_layer.refresh()
247
248    def _parse_query(self, query: str, data: pd.DataFrame) -> pd.DataFrame:
249        """Parse and apply a query string to filter data.
250
251        Args:
252            query: Comma-separated query string (e.g., "radius > 15, distance > 250").
253            data: DataFrame containing the data to filter.
254
255        Returns:
256            Filtered DataFrame.
257        """
258        filters = query.split(",")  # Split the query into individual conditions
259        filters = [f.strip() for f in filters]  # Remove extra spaces
260        for condition in filters:
261            try:
262                # Apply each condition to filter the DataFrame
263                data = data.query(condition)
264            except Exception as e:
265                print(f"Failed to apply condition '{condition}': {e}")
266                continue
267        return data
268
269    def _create_settings_widget(self):
270        setting_values = QWidget()
271        setting_values.setLayout(QVBoxLayout())
272
273        self.save_path, layout = self._add_path_param(name="Save Table", select_type="file", value="")
274        setting_values.layout().addLayout(layout)
275
276        self.pool_color_param, layout = self._add_string_param(name="Pool Color", value="")
277        setting_values.layout().addLayout(layout)
278
279        settings = self._make_collapsible(widget=setting_values, title="Advanced Settings")
280        return settings
COLORMAP = ['red', 'blue', 'yellow', 'cyan', 'purple', 'magenta', 'orange', 'green']
class VesiclePoolWidget(synapse_net.tools.base_widget.BaseWidget):
 19class VesiclePoolWidget(BaseWidget):
 20    def __init__(self):
 21        super().__init__()
 22
 23        self.viewer = napari.current_viewer()
 24        layout = QVBoxLayout()
 25
 26        # Create the selectors for the layers:
 27        # 1. Selector for the labels layer with vesicles.
 28        self.vesicle_selector_name = "Vesicle Segmentation"
 29        self.vesicle_selector_widget = self._create_layer_selector(self.vesicle_selector_name, layer_type="Labels")
 30        # 2. Selector for a distance layer.
 31        self.dist_selector_name1 = "Distances to Structure"
 32        self.dist_selector_widget1 = self._create_layer_selector(self.dist_selector_name1, layer_type="Shapes")
 33        # 3. Selector for a second distance layer (optional).
 34        self.dist_selector_name2 = "Distances to Structure 2"
 35        self.dist_selector_widget2 = self._create_layer_selector(self.dist_selector_name2, layer_type="Shapes")
 36
 37        # Add the selector widgets to the layout.
 38        layout.addWidget(self.vesicle_selector_widget)
 39        layout.addWidget(self.dist_selector_widget1)
 40        layout.addWidget(self.dist_selector_widget2)
 41
 42        # Create the UI elements for defining the vesicle pools:
 43        # The name of the output name, the name of the vesicle pool, and the criterion for the pool.
 44        self.pool_layer_name_param, pool_layer_name_layout = self._add_string_param(name="Layer Name", value="")
 45        self.pool_name_param, pool_name_layout = self._add_string_param(name="Vesicle Pool", value="")
 46        self.query_param, query_layout = self._add_string_param(
 47            name="Criterion", value="",
 48            tooltip="Enter a comma separated criterion (e.g., 'radius > 15, distance > 250') "
 49            "Possible filters: radius, distance, area, intensity_max, intensity_mean, intensity_min, intensity_std"
 50        )
 51        layout.addLayout(pool_layer_name_layout)
 52        layout.addLayout(pool_name_layout)
 53        layout.addLayout(query_layout)
 54
 55        # Create the UI elements for advanced settings and the run button.
 56        self.settings = self._create_settings_widget()
 57        self.measure_button = QPushButton("Create Vesicle Pool")
 58        self.measure_button.clicked.connect(self.on_pool_vesicles)
 59        layout.addWidget(self.settings)
 60        layout.addWidget(self.measure_button)
 61
 62        self.setLayout(layout)
 63
 64        # The colormap for displaying the vesicle pools.
 65        self.pool_colors = {}
 66
 67    def on_pool_vesicles(self):
 68        segmentation = self._get_layer_selector_data(self.vesicle_selector_name)
 69        morphology = self._get_layer_selector_layer(self.vesicle_selector_name).properties
 70        if not morphology:
 71            morphology = None
 72
 73        distance_layer = self._get_layer_selector_layer(self.dist_selector_name1)
 74        distances = None if distance_layer is None else distance_layer.properties
 75        distance_layer2 = self._get_layer_selector_layer(self.dist_selector_name2)
 76        # Check if the second distance is the same as the first.
 77        if distance_layer2.name == distance_layer.name:
 78            distance_layer2 = None
 79        distances2 = None if distance_layer2 is None else distance_layer2.properties
 80
 81        if segmentation is None:
 82            show_info("INFO: Please choose a segmentation.")
 83            return
 84        if self.query_param.text() == "":
 85            show_info("INFO: Please enter a query string.")
 86            return
 87        query = self.query_param.text()
 88
 89        if self.pool_layer_name_param.text() == "":
 90            show_info("INFO: Please enter a name for the pool layer.")
 91            return
 92        pool_layer_name = self.pool_layer_name_param.text()
 93        if self.pool_name_param.text() == "":
 94            show_info("INFO: Please enter a name for the vesicle pool.")
 95            return
 96        pool_name = self.pool_name_param.text()
 97
 98        pool_color = self.pool_color_param.text()
 99        self._compute_vesicle_pool(
100            segmentation, distances, morphology, pool_layer_name, pool_name, query, pool_color, distances2
101            )
102
103    def _update_pool_colors(self, pool_name, pool_color):
104        if pool_color == "":
105            next_color_id = len(self.pool_colors)
106            next_color = COLORMAP[next_color_id]
107        else:
108            # We could check here that this is a valid color.
109            next_color = pool_color
110        self.pool_colors[pool_name] = next_color
111
112    def _compute_vesicle_pool(
113        self,
114        segmentation: np.ndarray,
115        distances: Dict,
116        morphology: Dict,
117        pool_layer_name: str,
118        pool_name: str,
119        query: str,
120        pool_color: str,
121        distances2: Dict = None
122    ):
123        """Compute a vesicle pool based on the provided query parameters.
124
125        Args:
126            segmentation: Segmentation data (e.g., labeled regions).
127            distances: Properties from the distances layer.
128            morphology: Properties from the morphology layer.
129            pool_layer_name: Name for the new layer to be created.
130            pool_name: Name for the pooled group to be assigned.
131            query: Query parameters.
132            pool_color: Optional color for the vesicle pool.
133            distances2: Properties from the second distances layer (optional).
134        """
135        # Check which of the properties are present and construct the combined properties based on this.
136        if distances is None and morphology is None:  # No properties were given -> we can't do anything.
137            show_info("ERROR: Neither distances nor vesicle morphology were found.")
138            return
139        elif distances is None and morphology is not None:  # Only morphology props were found.
140            merged_df = pd.DataFrame(morphology).drop(columns=["index"])
141        elif distances is not None and morphology is None:  # Only distances were found.
142            merged_df = pd.DataFrame(distances).drop(columns=["index"])
143        else:  # Both were found.
144            distance_ids = distances.get("label", [])
145            morphology_ids = morphology.get("label", [])
146
147            # Ensure that IDs are identical.
148            if set(distance_ids) != set(morphology_ids):
149                show_info("ERROR: The IDs in distances and morphology are not identical.")
150                return
151
152            # Create a merged dataframe from the dataframes which are relevant for the criterion.
153            distances = pd.DataFrame(distances).drop(columns=["index"])
154            morphology = pd.DataFrame(morphology).drop(columns=["index"])
155            merged_df = morphology.merge(distances, left_on="label", right_on="label", suffixes=("_morph", "_dist"))
156        # Add distances2 if present.
157        if distances2 is not None:
158            distance_ids = distances2.get("label", [])
159            if set(distance_ids) != set(merged_df.label):
160                show_info("ERROR: The IDs in distances2 and morphology are not identical.")
161                return
162            distances2 = pd.DataFrame(distances2).drop(columns=["index"])
163            merged_df = merged_df.merge(distances2, left_on="label", right_on="label", suffixes=("", "2"))
164        # Assign the vesicles to the current pool by filtering the mergeddataframe based on the query.
165        filtered_df = self._parse_query(query, merged_df)
166        if len(filtered_df) == 0:
167            show_info("No vesicles were found matching the condition.")
168            return
169        pool_vesicle_ids = filtered_df.label.values.tolist()
170        vesicles_in_pool = len(pool_vesicle_ids)
171
172        # Check if this layer was already created in a previous pool assignment.
173        if pool_layer_name in self.viewer.layers:
174            # If yes then load the previous pool assignments and merge them with the new pool assignments
175            pool_layer = self.viewer.layers[pool_layer_name]
176            pool_properties = pd.DataFrame.from_dict(pool_layer.properties)
177
178            pool_names = pd.unique(pool_properties.pool)
179            if pool_name in pool_names:
180                show_info(f"Updating pool '{pool_name}' with {vesicles_in_pool} vesicles.")
181                # This pool has already been assigned and we changed the criterion.
182                # Its old assignment has to be over-written, remove the rows for this pool.
183                pool_properties = pool_properties[pool_properties.pool != pool_name]
184            else:
185                show_info(f"Creating pool '{pool_name}' with {vesicles_in_pool} vesicles.")
186
187            # Combine the vesicle ids corresponding to the previous assignment with the
188            # assignment for the new / current pool.
189            old_pool_ids = pool_properties.label.values.tolist()
190
191            # Overwrite the intersection of the two pool assignments with the new pool.
192            pool_intersections = np.intersect1d(pool_vesicle_ids, old_pool_ids)
193            old_pool_ids = [item for item in old_pool_ids if item not in pool_intersections]
194            pool_properties = pool_properties[~pool_properties['label'].isin(pool_intersections)]
195
196            pool_assignments = sorted(pool_vesicle_ids + old_pool_ids)
197
198            # Get a map for each vesicle id to its pool.
199            id_to_pool_name = {ves_id: pool_name for ves_id in pool_vesicle_ids}
200            id_to_pool_name.update({k: v for k, v in zip(old_pool_ids, pool_properties.pool.values)})
201
202            # Get the pool values.
203            # This is the list of pool names, corresponding to the selected ids in pool_assignments.
204            pool_values = [id_to_pool_name[ves_id] for ves_id in pool_assignments]
205
206        else:
207            show_info(f"Creating pool '{pool_name}' with {vesicles_in_pool} vesicles.")
208            # Otherwise, this is the first pool assignment.
209            pool_assignments = pool_vesicle_ids
210            pool_values = [pool_name] * len(pool_assignments)
211
212        # Create the filtered segmentation.
213        vesicle_pools = segmentation.copy()
214        vesicle_pools[~np.isin(vesicle_pools, pool_assignments)] = 0
215
216        # Create the pool properties.
217        pool_properties = merged_df[merged_df.label.isin(pool_assignments)]
218        # Remove columns that are not relevant for measurements.
219        keep_columns = [
220            col for col in pool_properties.columns
221            if col not in ("x", "y", "z", "begin-x", "begin-y", "begin-z", "end-x", "end-y", "end-z")
222        ]
223        pool_properties = pool_properties[keep_columns]
224        # Add a colun for the pool.
225        pool_properties.insert(1, "pool", pool_values)
226
227        # Update the colormap to display the pools.
228        self._update_pool_colors(pool_name, pool_color)
229
230        # Assign the vesicle ids to their pool color.
231        vesicle_colors = {
232            label_id: self.pool_colors[pname] for label_id, pname in zip(
233                pool_properties.label.values, pool_properties.pool.values
234            )
235        }
236        vesicle_colors[None] = "gray"
237
238        # Add or replace the pool layer and properties.
239        if pool_layer_name in self.viewer.layers:
240            pool_layer = self.viewer.layers[pool_layer_name]
241            pool_layer.data = vesicle_pools
242            pool_layer.colormap = vesicle_colors
243        else:
244            pool_layer = self.viewer.add_labels(vesicle_pools, name=pool_layer_name, colormap=vesicle_colors)
245
246        self._add_properties_and_table(pool_layer, pool_properties, save_path=self.save_path.text())
247        pool_layer.refresh()
248
249    def _parse_query(self, query: str, data: pd.DataFrame) -> pd.DataFrame:
250        """Parse and apply a query string to filter data.
251
252        Args:
253            query: Comma-separated query string (e.g., "radius > 15, distance > 250").
254            data: DataFrame containing the data to filter.
255
256        Returns:
257            Filtered DataFrame.
258        """
259        filters = query.split(",")  # Split the query into individual conditions
260        filters = [f.strip() for f in filters]  # Remove extra spaces
261        for condition in filters:
262            try:
263                # Apply each condition to filter the DataFrame
264                data = data.query(condition)
265            except Exception as e:
266                print(f"Failed to apply condition '{condition}': {e}")
267                continue
268        return data
269
270    def _create_settings_widget(self):
271        setting_values = QWidget()
272        setting_values.setLayout(QVBoxLayout())
273
274        self.save_path, layout = self._add_path_param(name="Save Table", select_type="file", value="")
275        setting_values.layout().addLayout(layout)
276
277        self.pool_color_param, layout = self._add_string_param(name="Pool Color", value="")
278        setting_values.layout().addLayout(layout)
279
280        settings = self._make_collapsible(widget=setting_values, title="Advanced Settings")
281        return settings

QWidget(parent: typing.Optional[QWidget] = None, flags: Union[Qt.WindowFlags, Qt.WindowType] = Qt.WindowFlags())

viewer
vesicle_selector_name
vesicle_selector_widget
dist_selector_name1
dist_selector_widget1
dist_selector_name2
dist_selector_widget2
settings
measure_button
pool_colors
def on_pool_vesicles(self):
 67    def on_pool_vesicles(self):
 68        segmentation = self._get_layer_selector_data(self.vesicle_selector_name)
 69        morphology = self._get_layer_selector_layer(self.vesicle_selector_name).properties
 70        if not morphology:
 71            morphology = None
 72
 73        distance_layer = self._get_layer_selector_layer(self.dist_selector_name1)
 74        distances = None if distance_layer is None else distance_layer.properties
 75        distance_layer2 = self._get_layer_selector_layer(self.dist_selector_name2)
 76        # Check if the second distance is the same as the first.
 77        if distance_layer2.name == distance_layer.name:
 78            distance_layer2 = None
 79        distances2 = None if distance_layer2 is None else distance_layer2.properties
 80
 81        if segmentation is None:
 82            show_info("INFO: Please choose a segmentation.")
 83            return
 84        if self.query_param.text() == "":
 85            show_info("INFO: Please enter a query string.")
 86            return
 87        query = self.query_param.text()
 88
 89        if self.pool_layer_name_param.text() == "":
 90            show_info("INFO: Please enter a name for the pool layer.")
 91            return
 92        pool_layer_name = self.pool_layer_name_param.text()
 93        if self.pool_name_param.text() == "":
 94            show_info("INFO: Please enter a name for the vesicle pool.")
 95            return
 96        pool_name = self.pool_name_param.text()
 97
 98        pool_color = self.pool_color_param.text()
 99        self._compute_vesicle_pool(
100            segmentation, distances, morphology, pool_layer_name, pool_name, query, pool_color, distances2
101            )