LiangLabUMB commited on
Commit
8b3506c
·
verified ·
1 Parent(s): ec76a58

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +113 -24
app.py CHANGED
@@ -128,31 +128,64 @@ def measure_confluency(masks, image_np):
128
  cell_pixels = np.count_nonzero(masks)
129
  confluency = cell_pixels / tot_pixels * 100
130
  return confluency
131
-
132
- def filter_mask_by_size(masks,minimum_pixels):
133
- filtered_masks=masks.copy()
134
  cell_ids = np.unique(masks)
135
  cell_ids = cell_ids[cell_ids > 0]
136
 
137
  removed_count = 0
138
-
139
  for cell_id in cell_ids:
140
  cell_mask = (masks == cell_id)
141
  cell_pixels = np.count_nonzero(cell_mask)
142
-
143
  if cell_pixels < minimum_pixels:
144
  filtered_masks[cell_mask] = 0
145
- removed_count +=1
146
 
147
  unique_ids = np.unique(filtered_masks)
148
  unique_ids = unique_ids[unique_ids > 0]
149
-
150
  renumbered_masks = np.zeros_like(filtered_masks)
151
  for new_id, old_id in enumerate(unique_ids, start=1):
152
  renumbered_masks[filtered_masks == old_id] = new_id
153
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  return renumbered_masks, removed_count
155
 
 
 
 
 
 
 
 
 
 
 
 
156
  @spaces.GPU
157
  def run_segmentation(image, model_choice,min_cell_size):
158
  image_np = np.array(image)
@@ -182,13 +215,41 @@ def run_segmentation(image, model_choice,min_cell_size):
182
  # Run Cellpose segmentation
183
  masks, flows, styles = model.eval(processed_image_np, diameter=None, channels=[0, 0])
184
 
185
- if min_cell_size > 0:
186
- masks, removed_count = filter_mask_by_size(masks, min_cell_size)
187
- filter_msg = f"Removed {removed_count} small objects (< {min_cell_size} pixels).\n"
188
- else:
189
- removed_count = 0
190
- filter_msg=""
191
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  cell_count = len(np.unique(masks)) - 1
193
  confluency = measure_confluency(masks, processed_image_np)
194
 
@@ -197,17 +258,24 @@ def run_segmentation(image, model_choice,min_cell_size):
197
  if masks.max() > 0:
198
  np.random.seed(42) # For consistent random colors
199
  colors = np.random.randint(0, 255, size=(masks.max() + 1, 3))
200
- colors[0] = [0, 0, 0] # Background color
201
  colored_mask = colors[masks]
202
  alpha = 0.4
203
  segmentation_overlay = (1 - alpha) * segmentation_overlay + alpha * colored_mask
204
  segmentation_overlay = np.clip(segmentation_overlay, 0, 255).astype(np.uint8)
205
 
206
- info_msg = f"Segmentation complete! Found {cell_count} cells.\n"
 
 
 
207
  info_msg += f"Confluency: {confluency:.1f}%\n"
208
- info_msg += f"Now adjust the Blue Threshold for viability assessment."
 
 
 
 
 
209
 
210
- # Return initial segmentation display and state variables (packed for Gradio State)
211
  return (
212
  cell_count,
213
  Image.fromarray(segmentation_overlay),
@@ -216,10 +284,22 @@ def run_segmentation(image, model_choice,min_cell_size):
216
  pack_array(masks),
217
  pack_array(processed_image_np),
218
  confluency,
 
219
  )
220
 
221
  except Exception as e:
222
- return 0, None, f"Error during segmentation: {str(e)}", gr.update(visible=False), None, None, 0.0
 
 
 
 
 
 
 
 
 
 
 
223
 
224
 
225
 
@@ -294,11 +374,20 @@ with gr.Blocks(
294
  min_size_slider1 = gr.Slider(
295
  minimum=0,
296
  maximum=500,
297
- value=50,
298
  step=10,
299
- label="Minimum Cell Size (pixels)",
300
 
301
  )
 
 
 
 
 
 
 
 
 
302
  segment_btn1 = gr.Button("🔬 Run Segmentation", variant="primary", size="lg")
303
 
304
  with gr.Column():
@@ -334,8 +423,8 @@ with gr.Blocks(
334
  # Event handlers
335
  segment_btn1.click(
336
  fn=run_segmentation,
337
- inputs=[img_input, model_dropdown1,min_size_slider1],
338
- outputs=[cell_count_output1, overlay_output1, info_output1, viability_section1, masks_state, image_state, confluency_output1]
339
  ).then( # Chain the initial viability assessment after segmentation
340
  fn=update_viability_realtime,
341
  inputs=[blue_threshold1, masks_state, image_state], # Pass stored state as inputs
 
128
  cell_pixels = np.count_nonzero(masks)
129
  confluency = cell_pixels / tot_pixels * 100
130
  return confluency
131
+
132
+ def filter_mask_by_size(masks, minimum_pixels):
133
+ filtered_masks = masks.copy()
134
  cell_ids = np.unique(masks)
135
  cell_ids = cell_ids[cell_ids > 0]
136
 
137
  removed_count = 0
138
+
139
  for cell_id in cell_ids:
140
  cell_mask = (masks == cell_id)
141
  cell_pixels = np.count_nonzero(cell_mask)
 
142
  if cell_pixels < minimum_pixels:
143
  filtered_masks[cell_mask] = 0
144
+ removed_count += 1
145
 
146
  unique_ids = np.unique(filtered_masks)
147
  unique_ids = unique_ids[unique_ids > 0]
148
+
149
  renumbered_masks = np.zeros_like(filtered_masks)
150
  for new_id, old_id in enumerate(unique_ids, start=1):
151
  renumbered_masks[filtered_masks == old_id] = new_id
152
+
153
+ return renumbered_masks, removed_count
154
+
155
+
156
+ def filter_mask_by_maxsize(masks, maximum_pixels):
157
+ filtered_masks = masks.copy()
158
+ cell_ids = np.unique(masks)
159
+ cell_ids = cell_ids[cell_ids > 0]
160
+
161
+ removed_count = 0
162
+ for cell_id in cell_ids:
163
+ cell_mask = (masks == cell_id)
164
+ cell_pixels = np.count_nonzero(cell_mask)
165
+ if cell_pixels > maximum_pixels:
166
+ filtered_masks[cell_mask] = 0
167
+ removed_count += 1
168
+
169
+ unique_ids = np.unique(filtered_masks)
170
+ unique_ids = unique_ids[unique_ids > 0]
171
+
172
+ renumbered_masks = np.zeros_like(filtered_masks)
173
+ for new_id, old_id in enumerate(unique_ids, start=1):
174
+ renumbered_masks[filtered_masks == old_id] = new_id
175
+
176
  return renumbered_masks, removed_count
177
 
178
+
179
+ def rec_min_size(masks, q=25):
180
+ ids = np.unique(masks)
181
+ ids = ids[ids > 0]
182
+ if len(ids) == 0:
183
+ return 0
184
+ sizes = np.array([np.count_nonzero(masks == cid) for cid in ids])
185
+ return int(round(np.percentile(sizes, q)))
186
+
187
+
188
+
189
  @spaces.GPU
190
  def run_segmentation(image, model_choice,min_cell_size):
191
  image_np = np.array(image)
 
215
  # Run Cellpose segmentation
216
  masks, flows, styles = model.eval(processed_image_np, diameter=None, channels=[0, 0])
217
 
218
+ ids = np.unique(masks_raw)
219
+ ids = ids[ids > 0]
220
+
221
+ sizes = np.array([np.count_nonzero(masks_raw == cid) for cid in ids])
222
+
223
+ print("num_cells:", len(ids))
224
+ print("mean:", sizes.mean())
225
+ print("median:", np.median(sizes))
226
+ print("p90:", np.percentile(sizes, 90))
227
+ print("max:", sizes.max())
228
+ # Compute recommendation from RAW masks
229
+ recommend_min = rec_min_size(masks_raw)
230
+
231
+
232
+ # If user sets slider to 0, use the recommendation
233
+ min_used = recommend_min if (min_cell_size == 0) else int(min_cell_size)
234
+
235
+ # Apply filters
236
+ masks = masks_raw
237
+ removed_small = 0
238
+ removed_large = 0
239
+
240
+ if min_used > 0:
241
+ masks, removed_small = filter_mask_by_size(masks, min_used)
242
+
243
+ if max_cell_size > 0:
244
+ masks, removed_large = filter_mask_by_maxsize(masks, int(max_cell_size))
245
+
246
+
247
+ filter_msg = ""
248
+ if removed_small:
249
+ filter_msg += f"Removed {removed_small} small objects (< {min_used} pixels).\n"
250
+ if removed_large:
251
+ filter_msg += f"Removed {removed_large} large objects (> {int(max_cell_size)} pixels).\n"
252
+
253
  cell_count = len(np.unique(masks)) - 1
254
  confluency = measure_confluency(masks, processed_image_np)
255
 
 
258
  if masks.max() > 0:
259
  np.random.seed(42) # For consistent random colors
260
  colors = np.random.randint(0, 255, size=(masks.max() + 1, 3))
261
+ colors[0] = [0, 0, 0]
262
  colored_mask = colors[masks]
263
  alpha = 0.4
264
  segmentation_overlay = (1 - alpha) * segmentation_overlay + alpha * colored_mask
265
  segmentation_overlay = np.clip(segmentation_overlay, 0, 255).astype(np.uint8)
266
 
267
+ info_msg = ""
268
+ if filter_msg:
269
+ info_msg += filter_msg
270
+ info_msg += f"Segmentation complete! Found {cell_count} cells.\n"
271
  info_msg += f"Confluency: {confluency:.1f}%\n"
272
+ if region_coords:
273
+ info_msg += (
274
+ f"Processed region: {region_coords[0]},{region_coords[1]} "
275
+ f"to {region_coords[2]},{region_coords[3]}\n"
276
+ )
277
+ info_msg += "Now adjust the Blue Threshold for viability assessment."
278
 
 
279
  return (
280
  cell_count,
281
  Image.fromarray(segmentation_overlay),
 
284
  pack_array(masks),
285
  pack_array(processed_image_np),
286
  confluency,
287
+ gr.update(value=recommend_min), # update slider display to recommended
288
  )
289
 
290
  except Exception as e:
291
+ return (
292
+ 0,
293
+ None,
294
+ f"Error during segmentation: {str(e)}",
295
+ gr.update(visible=False),
296
+ None,
297
+ None,
298
+ 0.0,
299
+ gr.update(),
300
+ )
301
+
302
+
303
 
304
 
305
 
 
374
  min_size_slider1 = gr.Slider(
375
  minimum=0,
376
  maximum=500,
377
+ value=0,
378
  step=10,
379
+ label="Minimum Cell Size (pixels). Leave at zero for automated recommendation",
380
 
381
  )
382
+ max_size_slider1 = gr.Slider(
383
+ minimum=0,
384
+ maximum=500,
385
+ value=500,
386
+ step=10,
387
+ label="Maximum Cell Size (pixels)",
388
+ )
389
+
390
+
391
  segment_btn1 = gr.Button("🔬 Run Segmentation", variant="primary", size="lg")
392
 
393
  with gr.Column():
 
423
  # Event handlers
424
  segment_btn1.click(
425
  fn=run_segmentation,
426
+ inputs=[img_input, model_dropdown1,min_size_slider1, max_size_slider1],
427
+ outputs=[cell_count_output1, overlay_output1, info_output1, viability_section1, masks_state, image_state, confluency_output1, min_size_slider1]
428
  ).then( # Chain the initial viability assessment after segmentation
429
  fn=update_viability_realtime,
430
  inputs=[blue_threshold1, masks_state, image_state], # Pass stored state as inputs