LiangLabUMB commited on
Commit
28ebb60
·
verified ·
1 Parent(s): 5ca7d24

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +61 -53
app.py CHANGED
@@ -216,69 +216,66 @@ def measure_confluency(masks, image_np):
216
  confluency = cell_pixels / tot_pixels * 100
217
  return confluency
218
 
219
- def filter_mask_by_size(masks,minimum_pixels):
220
- filtered_masks=masks.copy()
221
  cell_ids = np.unique(masks)
222
  cell_ids = cell_ids[cell_ids > 0]
223
 
224
  removed_count = 0
225
-
226
  for cell_id in cell_ids:
227
  cell_mask = (masks == cell_id)
228
  cell_pixels = np.count_nonzero(cell_mask)
229
-
230
  if cell_pixels < minimum_pixels:
231
  filtered_masks[cell_mask] = 0
232
- removed_count +=1
233
 
234
  unique_ids = np.unique(filtered_masks)
235
  unique_ids = unique_ids[unique_ids > 0]
236
-
237
  renumbered_masks = np.zeros_like(filtered_masks)
238
  for new_id, old_id in enumerate(unique_ids, start=1):
239
  renumbered_masks[filtered_masks == old_id] = new_id
240
 
241
-
242
  return renumbered_masks, removed_count
243
-
244
- def filter_mask_by_maxsize(masks,maximum_pixels):
245
- filtered_masks=masks.copy()
 
246
  cell_ids = np.unique(masks)
247
  cell_ids = cell_ids[cell_ids > 0]
248
 
249
  removed_count = 0
250
-
251
  for cell_id in cell_ids:
252
  cell_mask = (masks == cell_id)
253
  cell_pixels = np.count_nonzero(cell_mask)
254
-
255
  if cell_pixels > maximum_pixels:
256
  filtered_masks[cell_mask] = 0
257
- removed_count +=1
258
 
259
  unique_ids = np.unique(filtered_masks)
260
  unique_ids = unique_ids[unique_ids > 0]
261
-
262
  renumbered_masks = np.zeros_like(filtered_masks)
263
  for new_id, old_id in enumerate(unique_ids, start=1):
264
  renumbered_masks[filtered_masks == old_id] = new_id
265
 
266
-
267
  return renumbered_masks, removed_count
268
-
 
269
  def rec_min_size(masks):
270
- num_cells = len(np.unique(masks)) - 1
 
 
271
  if num_cells <= 0:
272
  return 0
273
  mean_cell_size = np.count_nonzero(masks) / num_cells
274
  return int(round(mean_cell_size))
275
 
 
276
  @spaces.GPU
277
  def run_segmentation_editor(editor_data, model_choice, min_cell_size, max_cell_size):
278
- """
279
- Runs cell segmentation using ImageEditor data.
280
- Returns initial segmentation overlay, counts, confluency, and also masks/image for state.
281
- """
282
  try:
283
  model_filename = MODEL_OPTIONS[model_choice]
284
  model_path = hf_hub_download(repo_id=HF_REPO_ID, filename=model_filename)
@@ -294,7 +291,6 @@ def run_segmentation_editor(editor_data, model_choice, min_cell_size, max_cell_s
294
  if region_np is None:
295
  return 0, None, "No image provided.", gr.update(visible=False), None, None, 0.0, gr.update()
296
 
297
-
298
  # Enforce mobile-safe size limit immediately
299
  region_np = safe_resize(region_np)
300
 
@@ -307,55 +303,59 @@ def run_segmentation_editor(editor_data, model_choice, min_cell_size, max_cell_s
307
  processed_image_np = region_np
308
 
309
  # Run Cellpose segmentation
310
- masks, flows, styles = model.eval(processed_image_np, diameter=None, channels=[0, 0])
 
 
 
311
 
 
 
 
 
 
 
312
  removed_small = 0
313
  removed_large = 0
314
-
315
- # Minimum size filtering
316
 
317
- recommend_min = rec_min_size(masks)
318
-
319
- if min_cell_size > 0:
320
- masks, removed_small = filter_mask_by_size(masks, min_cell_size)
321
- filter_msg = f"Removed {removed_small} small objects (< {min_cell_size} pixels).\n"
322
- else:
323
- removed_small = 0
324
- filter_msg=""
325
-
326
-
327
 
328
- # Maximum size filtering
329
  if max_cell_size > 0:
330
- masks, removed_large = filter_mask_by_maxsize(masks, max_cell_size)
331
- filter_msg = f"Removed {removed_large} large objects (> {max_cell_size} pixels).\n"
332
- else:
333
- removed_large = 0
334
- filter_msg=""
335
 
336
- removed_count = removed_small + removed_large
337
 
 
 
 
 
 
 
338
  cell_count = len(np.unique(masks)) - 1
339
  confluency = measure_confluency(masks, processed_image_np)
 
340
  # Create a basic segmentation overlay (without viability)
341
  segmentation_overlay = processed_image_np.copy().astype(np.float32)
342
  if masks.max() > 0:
343
  np.random.seed(42) # For consistent random colors
344
  colors = np.random.randint(0, 255, size=(masks.max() + 1, 3))
345
- colors[0] = [0, 0, 0] # Background color
346
  colored_mask = colors[masks]
347
  alpha = 0.4
348
  segmentation_overlay = (1 - alpha) * segmentation_overlay + alpha * colored_mask
349
  segmentation_overlay = np.clip(segmentation_overlay, 0, 255).astype(np.uint8)
350
 
351
- info_msg = f"Segmentation complete! Found {cell_count} cells.\n"
 
 
 
352
  info_msg += f"Confluency: {confluency:.1f}%\n"
353
- info_msg = filter_msg + info_msg
354
  if region_coords:
355
- info_msg += f"Processed region: {region_coords[0]},{region_coords[1]} to {region_coords[2]},{region_coords[3]}\n"
356
- info_msg += f"Now adjust the Blue Threshold for viability assessment."
 
 
 
357
 
358
- # Return initial segmentation display and state variables (packed for Gradio State)
359
  return (
360
  cell_count,
361
  Image.fromarray(segmentation_overlay),
@@ -364,12 +364,20 @@ def run_segmentation_editor(editor_data, model_choice, min_cell_size, max_cell_s
364
  pack_array(masks),
365
  pack_array(processed_image_np),
366
  confluency,
367
- gr.update(value = recommend_min)
368
  )
369
 
370
  except Exception as e:
371
- return 0, None, f"Error during segmentation: {str(e)}", gr.update(visible=False), None, None, 0.0, gr.update()
372
-
 
 
 
 
 
 
 
 
373
 
374
 
375
 
@@ -448,9 +456,9 @@ with gr.Blocks(
448
  min_size_slider1 = gr.Slider(
449
  minimum=0,
450
  maximum=500,
451
- value=50,
452
  step=10,
453
- label="Minimum Cell Size (pixels)",
454
 
455
  )
456
  max_size_slider1 = gr.Slider(
 
216
  confluency = cell_pixels / tot_pixels * 100
217
  return confluency
218
 
219
+ def filter_mask_by_size(masks, minimum_pixels):
220
+ filtered_masks = masks.copy()
221
  cell_ids = np.unique(masks)
222
  cell_ids = cell_ids[cell_ids > 0]
223
 
224
  removed_count = 0
225
+
226
  for cell_id in cell_ids:
227
  cell_mask = (masks == cell_id)
228
  cell_pixels = np.count_nonzero(cell_mask)
 
229
  if cell_pixels < minimum_pixels:
230
  filtered_masks[cell_mask] = 0
231
+ removed_count += 1
232
 
233
  unique_ids = np.unique(filtered_masks)
234
  unique_ids = unique_ids[unique_ids > 0]
235
+
236
  renumbered_masks = np.zeros_like(filtered_masks)
237
  for new_id, old_id in enumerate(unique_ids, start=1):
238
  renumbered_masks[filtered_masks == old_id] = new_id
239
 
 
240
  return renumbered_masks, removed_count
241
+
242
+
243
+ def filter_mask_by_maxsize(masks, maximum_pixels):
244
+ filtered_masks = masks.copy()
245
  cell_ids = np.unique(masks)
246
  cell_ids = cell_ids[cell_ids > 0]
247
 
248
  removed_count = 0
 
249
  for cell_id in cell_ids:
250
  cell_mask = (masks == cell_id)
251
  cell_pixels = np.count_nonzero(cell_mask)
 
252
  if cell_pixels > maximum_pixels:
253
  filtered_masks[cell_mask] = 0
254
+ removed_count += 1
255
 
256
  unique_ids = np.unique(filtered_masks)
257
  unique_ids = unique_ids[unique_ids > 0]
258
+
259
  renumbered_masks = np.zeros_like(filtered_masks)
260
  for new_id, old_id in enumerate(unique_ids, start=1):
261
  renumbered_masks[filtered_masks == old_id] = new_id
262
 
 
263
  return renumbered_masks, removed_count
264
+
265
+
266
  def rec_min_size(masks):
267
+ ids = np.unique(masks)
268
+ ids = ids[ids > 0]
269
+ num_cells = len(ids)
270
  if num_cells <= 0:
271
  return 0
272
  mean_cell_size = np.count_nonzero(masks) / num_cells
273
  return int(round(mean_cell_size))
274
 
275
+
276
  @spaces.GPU
277
  def run_segmentation_editor(editor_data, model_choice, min_cell_size, max_cell_size):
278
+
 
 
 
279
  try:
280
  model_filename = MODEL_OPTIONS[model_choice]
281
  model_path = hf_hub_download(repo_id=HF_REPO_ID, filename=model_filename)
 
291
  if region_np is None:
292
  return 0, None, "No image provided.", gr.update(visible=False), None, None, 0.0, gr.update()
293
 
 
294
  # Enforce mobile-safe size limit immediately
295
  region_np = safe_resize(region_np)
296
 
 
303
  processed_image_np = region_np
304
 
305
  # Run Cellpose segmentation
306
+ masks_raw, flows, styles = model.eval(processed_image_np, diameter=None, channels=[0, 0])
307
+
308
+ # Compute recommendation from RAW masks
309
+ recommend_min = rec_min_size(masks_raw)
310
 
311
+
312
+ # If user sets slider to 0, use the recommendation
313
+ min_used = recommend_min if (min_cell_size == 0) else int(min_cell_size)
314
+
315
+ # Apply filters
316
+ masks = masks_raw
317
  removed_small = 0
318
  removed_large = 0
 
 
319
 
320
+ if min_used > 0:
321
+ masks, removed_small = filter_mask_by_size(masks, min_used)
 
 
 
 
 
 
 
 
322
 
 
323
  if max_cell_size > 0:
324
+ masks, removed_large = filter_mask_by_maxsize(masks, int(max_cell_size))
 
 
 
 
325
 
 
326
 
327
+ filter_msg = ""
328
+ if removed_small:
329
+ filter_msg += f"Removed {removed_small} small objects (< {min_used} pixels).\n"
330
+ if removed_large:
331
+ filter_msg += f"Removed {removed_large} large objects (> {int(max_cell_size)} pixels).\n"
332
+
333
  cell_count = len(np.unique(masks)) - 1
334
  confluency = measure_confluency(masks, processed_image_np)
335
+
336
  # Create a basic segmentation overlay (without viability)
337
  segmentation_overlay = processed_image_np.copy().astype(np.float32)
338
  if masks.max() > 0:
339
  np.random.seed(42) # For consistent random colors
340
  colors = np.random.randint(0, 255, size=(masks.max() + 1, 3))
341
+ colors[0] = [0, 0, 0]
342
  colored_mask = colors[masks]
343
  alpha = 0.4
344
  segmentation_overlay = (1 - alpha) * segmentation_overlay + alpha * colored_mask
345
  segmentation_overlay = np.clip(segmentation_overlay, 0, 255).astype(np.uint8)
346
 
347
+ info_msg = ""
348
+ if filter_msg:
349
+ info_msg += filter_msg
350
+ info_msg += f"Segmentation complete! Found {cell_count} cells.\n"
351
  info_msg += f"Confluency: {confluency:.1f}%\n"
 
352
  if region_coords:
353
+ info_msg += (
354
+ f"Processed region: {region_coords[0]},{region_coords[1]} "
355
+ f"to {region_coords[2]},{region_coords[3]}\n"
356
+ )
357
+ info_msg += "Now adjust the Blue Threshold for viability assessment."
358
 
 
359
  return (
360
  cell_count,
361
  Image.fromarray(segmentation_overlay),
 
364
  pack_array(masks),
365
  pack_array(processed_image_np),
366
  confluency,
367
+ gr.update(value=recommend_min), # update slider display to recommended
368
  )
369
 
370
  except Exception as e:
371
+ return (
372
+ 0,
373
+ None,
374
+ f"Error during segmentation: {str(e)}",
375
+ gr.update(visible=False),
376
+ None,
377
+ None,
378
+ 0.0,
379
+ gr.update(),
380
+ )
381
 
382
 
383
 
 
456
  min_size_slider1 = gr.Slider(
457
  minimum=0,
458
  maximum=500,
459
+ value=0,
460
  step=10,
461
+ label="Minimum Cell Size (pixels). Leave at zero for automated recommendation",
462
 
463
  )
464
  max_size_slider1 = gr.Slider(