@@ -26,11 +26,13 @@ def __init__(self):
2626 self .viewer = napari .current_viewer ()
2727 layout = QVBoxLayout ()
2828
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"
3132 # # Create the image selection dropdown.
3233 self .image_selector_widget = self ._create_layer_selector (self .image_selector_name , layer_type = "Shapes" )
3334 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" )
3436
3537 # Create new layer name
3638 self .new_layer_name_param , new_layer_name_layout = self ._add_string_param (
@@ -54,12 +56,13 @@ def __init__(self):
5456 self .settings = self ._create_settings_widget ()
5557
5658 # Create and connect buttons.
57- self .measure_button1 = QPushButton ("Measure Vesicle Morphology " )
59+ self .measure_button1 = QPushButton ("Create Vesicle Pool " )
5860 self .measure_button1 .clicked .connect (self .on_pool_vesicles )
5961
6062
6163 # Add the widgets to the layout.
6264 layout .addWidget (self .image_selector_widget )
65+ layout .addWidget (self .image_selector_widget2 )
6366 layout .addWidget (self .segmentation1_selector_widget )
6467 layout .addLayout (query_layout )
6568 layout .addLayout (new_layer_name_layout )
@@ -70,14 +73,14 @@ def __init__(self):
7073
7174 self .setLayout (layout )
7275
73- def _create_shapes_layer (self , name , pooling ):
74- print (name , pooling )
75- return
76-
7776 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
7979 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 )
8184
8285 if segmentation is None :
8386 show_info ("INFO: Please choose a segmentation." )
@@ -116,11 +119,122 @@ def on_pool_vesicles(self):
116119 # resolution = segmentation.ndim * [self.voxel_size_param.value()]
117120
118121 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
124238
125239 def _create_settings_widget (self ):
126240 setting_values = QWidget ()
0 commit comments