@@ -26,11 +26,13 @@ def __init__(self):
26
26
self .viewer = napari .current_viewer ()
27
27
layout = QVBoxLayout ()
28
28
29
- self .image_selector_name = "Distances"
30
- self .image_selector_name1 = "Segmentation"
29
+ self .image_selector_name = "Distances to Structure"
30
+ self .image_selector_name1 = "Vesicles Segmentation"
31
+ self .image_selector_name2 = "Vesicles Morphology"
31
32
# # Create the image selection dropdown.
32
33
self .image_selector_widget = self ._create_layer_selector (self .image_selector_name , layer_type = "Shapes" )
33
34
self .segmentation1_selector_widget = self ._create_layer_selector (self .image_selector_name1 , layer_type = "Labels" )
35
+ self .image_selector_widget2 = self ._create_layer_selector (self .image_selector_name2 , layer_type = "Shapes" )
34
36
35
37
# Create new layer name
36
38
self .new_layer_name_param , new_layer_name_layout = self ._add_string_param (
@@ -54,12 +56,13 @@ def __init__(self):
54
56
self .settings = self ._create_settings_widget ()
55
57
56
58
# Create and connect buttons.
57
- self .measure_button1 = QPushButton ("Measure Vesicle Morphology " )
59
+ self .measure_button1 = QPushButton ("Create Vesicle Pool " )
58
60
self .measure_button1 .clicked .connect (self .on_pool_vesicles )
59
61
60
62
61
63
# Add the widgets to the layout.
62
64
layout .addWidget (self .image_selector_widget )
65
+ layout .addWidget (self .image_selector_widget2 )
63
66
layout .addWidget (self .segmentation1_selector_widget )
64
67
layout .addLayout (query_layout )
65
68
layout .addLayout (new_layer_name_layout )
@@ -70,14 +73,14 @@ def __init__(self):
70
73
71
74
self .setLayout (layout )
72
75
73
- def _create_shapes_layer (self , name , pooling ):
74
- print (name , pooling )
75
- return
76
-
77
76
def on_pool_vesicles (self ):
78
- distances = self ._get_layer_selector_data (self .image_selector_name , return_metadata = True )
77
+ distances_layer = self ._get_layer_selector_layer (self .image_selector_name )
78
+ distances = distances_layer .properties
79
79
segmentation = self ._get_layer_selector_data (self .image_selector_name1 )
80
- morphology = self ._get_layer_selector_data (self .image_selector_name1 , return_metadata = True )
80
+ morphology_layer = self ._get_layer_selector_layer (self .image_selector_name2 )
81
+ morphology = morphology_layer .properties
82
+ print ("distances" , distances )
83
+ print ("morphology" , morphology )
81
84
82
85
if segmentation is None :
83
86
show_info ("INFO: Please choose a segmentation." )
@@ -116,11 +119,122 @@ def on_pool_vesicles(self):
116
119
# resolution = segmentation.ndim * [self.voxel_size_param.value()]
117
120
118
121
def _compute_vesicle_pool (self , segmentation , distances , morphology , new_layer_name , pooled_group_name , query ):
119
- print (segmentation , distances , morphology , new_layer_name , pooled_group_name , query )
120
- vesicle_pool = {
121
- "segmentation" : segmentation ,
122
- }
123
- return vesicle_pool
122
+ """
123
+ Compute a vesicle pool based on the provided query parameters.
124
+
125
+ Args:
126
+ segmentation (array): Segmentation data (e.g., labeled regions).
127
+ distances (dict): Properties from the distances layer.
128
+ morphology (dict): Properties from the morphology layer.
129
+ new_layer_name (str): Name for the new layer to be created.
130
+ pooled_group_name (str): Name for the pooled group to be assigned.
131
+ query (dict): Query parameters with keys "min_radius" and "max_distance".
132
+
133
+ Returns:
134
+ dict: Updated properties for the new vesicle pool.
135
+ """
136
+
137
+ distances_ids = distances .get ("id" , [])
138
+ morphology_ids = morphology .get ("label_id" , [])
139
+
140
+ # Check if IDs are identical
141
+ if set (distances_ids ) != set (morphology_ids ):
142
+ show_info ("ERROR: The IDs in distances and morphology are not identical." )
143
+ return
144
+
145
+ distances = pd .DataFrame (distances )
146
+ morphology = pd .DataFrame (morphology )
147
+
148
+ # Merge dataframes on the 'id' column
149
+ merged_df = morphology .merge (distances , left_on = "label_id" , right_on = "id" , suffixes = ("_morph" , "_dist" ))
150
+ # Filter rows based on query parameters
151
+ # Apply the query string to filter the data
152
+ filtered_df = self ._parse_query (query , merged_df )
153
+
154
+ # Extract valid vesicle IDs
155
+ valid_vesicle_ids = filtered_df ["label_id" ].tolist ()
156
+
157
+ # Debugging output
158
+ for _ , row in filtered_df .iterrows ():
159
+ print (f"Vesicle { row ['label_id' ]} : Passed (Radius: { row ['radius' ]} , Distance: { row ['distance' ]} )" )
160
+
161
+ # Update segmentation layer with valid vesicles
162
+ new_layer_data = np .zeros (segmentation .shape , dtype = np .uint8 )
163
+ for vesicle_id in valid_vesicle_ids :
164
+ new_layer_data [segmentation == vesicle_id ] = 1 # Highlight pooled vesicles
165
+
166
+ # Create a new layer in the viewer
167
+ self .viewer .add_labels (
168
+ new_layer_data ,
169
+ name = new_layer_name ,
170
+ properties = {
171
+ "id" : valid_vesicle_ids ,
172
+ "radius" : filtered_df ["radius" ].tolist (),
173
+ "distance" : filtered_df ["distance" ].tolist (),
174
+ },
175
+ )
176
+ show_info (f"Vesicle pool created with { len (valid_vesicle_ids )} vesicles." )
177
+ return {
178
+ "id" : valid_vesicle_ids ,
179
+ "radius" : filtered_df ["radius" ].tolist (),
180
+ "distance" : filtered_df ["distance" ].tolist (),
181
+ }
182
+
183
+ # # Filter vesicles based on the query
184
+ # valid_vesicles = []
185
+ # for i in distances_ids:
186
+ # distance = distances.get("distance", [])[i]
187
+ # radius = morphology.get("radius", [])[i]
188
+ # if radius >= min_radius and distance <= max_distance:
189
+ # print(f"Vesicle {i}: Passed (Radius: {radius}, Distance: {distance})")
190
+ # valid_vesicles.append(i)
191
+ # else:
192
+ # print(f"Vesicle {i}: Failed (Radius: {radius}, Distance: {distance})")
193
+
194
+ # Create pooled properties
195
+ # pooled_properties = {
196
+ # "id": [i for i in valid_vesicles],
197
+ # "radius": [morphology["radius"][i] for i in valid_vesicles],
198
+ # "distance": [distances["distance"][i] for i in valid_vesicles],
199
+ # }
200
+ # print("pooled_properties", pooled_properties)
201
+ # print("np.unique(segmenation)", np.unique(segmentation))
202
+
203
+ # # Create a new layer in the viewer
204
+ # new_layer_data = np.zeros(segmentation.shape, dtype=np.uint8)
205
+ # for vesicle_id in valid_vesicles:
206
+ # new_layer_data[segmentation == vesicle_id] = 1
207
+
208
+ # self.viewer.add_labels(
209
+ # new_layer_data,
210
+ # name=new_layer_name,
211
+ # properties=pooled_properties,
212
+ # )
213
+
214
+ # show_info(f"INFO: Created pooled vesicle layer '{new_layer_name}' with {len(valid_vesicles)} vesicles.")
215
+ # return pooled_properties
216
+
217
+ def _parse_query (self , query , data ):
218
+ """
219
+ Parse and apply a query string to filter data.
220
+
221
+ Args:
222
+ query (str): Comma-separated query string (e.g., "radius > 15, distance > 250").
223
+ data (pd.DataFrame): DataFrame containing the data to filter.
224
+
225
+ Returns:
226
+ pd.DataFrame: Filtered DataFrame.
227
+ """
228
+ filters = query .split ("," ) # Split the query into individual conditions
229
+ filters = [f .strip () for f in filters ] # Remove extra spaces
230
+ for condition in filters :
231
+ try :
232
+ # Apply each condition to filter the DataFrame
233
+ data = data .query (condition )
234
+ except Exception as e :
235
+ print (f"Failed to apply condition '{ condition } ': { e } " )
236
+ continue
237
+ return data
124
238
125
239
def _create_settings_widget (self ):
126
240
setting_values = QWidget ()
0 commit comments