LiangLabUMB commited on
Commit
ec87384
·
verified ·
1 Parent(s): e120ea8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +6 -168
app.py CHANGED
@@ -19,15 +19,11 @@ MODEL_OPTIONS = {
19
  loaded_models = {}
20
 
21
  # ---- mobile-safe size limits (aggressive for Safari) ----
22
- MAX_SIDE = 512 # shrink any side larger than this
23
- MAX_PIXELS = 512 * 512 # ~0.26 megapixels
24
 
25
 
26
  def safe_resize(image_np):
27
- """
28
- Downscale image to fit within MAX_SIDE and MAX_PIXELS while
29
- preserving aspect ratio. Works for RGB / RGBA / grayscale.
30
- """
31
  h, w = image_np.shape[:2]
32
  total = h * w
33
 
@@ -219,11 +215,11 @@ def measure_confluency(masks, image_np):
219
 
220
  @spaces.GPU
221
  def run_segmentation_editor(editor_data, model_choice):
222
- """
223
- Runs cell segmentation using ImageEditor data.
224
- Returns initial segmentation overlay, counts, confluency, and also masks/image for state.
225
- """
226
  try:
 
 
 
227
  model_filename = MODEL_OPTIONS[model_choice]
228
  model_path = hf_hub_download(repo_id=HF_REPO_ID, filename=model_filename)
229
 
@@ -287,93 +283,6 @@ def run_segmentation_editor(editor_data, model_choice):
287
  return 0, None, f"Error during segmentation: {str(e)}", gr.update(visible=False), None, None, 0.0
288
 
289
 
290
- @spaces.GPU
291
- def run_segmentation_manual(image, model_choice, crop_coords):
292
- """
293
- Runs cell segmentation using manual image input and coordinates.
294
- Returns initial segmentation overlay, counts, confluency, and also masks/image for state.
295
- """
296
- if image is None:
297
- return 0, None, "No image provided", gr.update(visible=False), None, None, 0.0
298
-
299
- try:
300
- model_filename = MODEL_OPTIONS[model_choice]
301
- model_path = hf_hub_download(repo_id=HF_REPO_ID, filename=model_filename)
302
-
303
- if model_filename in loaded_models:
304
- model = loaded_models[model_filename]
305
- else:
306
- model = models.CellposeModel(gpu=True, pretrained_model=model_path)
307
- loaded_models[model_filename] = model
308
-
309
- image_np = np.array(image)
310
-
311
- # Enforce mobile-safe size limit immediately
312
- image_np = safe_resize(image_np)
313
-
314
- # Apply crop if coordinates are provided
315
- if crop_coords and crop_coords.strip():
316
- try:
317
- coords = [int(x.strip()) for x in crop_coords.split(',')]
318
- if len(coords) == 4:
319
- x_min, y_min, x_max, y_max = coords
320
- h, w = image_np.shape[:2]
321
- x_min = max(0, min(x_min, w - 1))
322
- y_min = max(0, min(y_min, h - 1))
323
- x_max = max(x_min + 1, min(x_max, w))
324
- y_max = max(y_min + 1, min(y_max, h))
325
-
326
- processed_image_np = image_np[y_min:y_max, x_min:x_max]
327
- else:
328
- processed_image_np = image_np # No valid crop, use original
329
- except ValueError:
330
- processed_image_np = image_np # Invalid crop, use original
331
- else:
332
- processed_image_np = image_np # No crop coords, use original
333
-
334
- # Process image format to RGB
335
- if len(processed_image_np.shape) == 2:
336
- processed_image_np = cv2.cvtColor(processed_image_np, cv2.COLOR_GRAY2RGB)
337
- elif len(processed_image_np.shape) == 3 and processed_image_np.shape[2] == 4:
338
- processed_image_np = cv2.cvtColor(processed_image_np, cv2.COLOR_RGBA2RGB)
339
-
340
- # Run Cellpose
341
- masks, flows, styles = model.eval(processed_image_np, diameter=None, channels=[0, 0])
342
-
343
- cell_count = len(np.unique(masks)) - 1
344
- confluency = measure_confluency(masks, processed_image_np)
345
-
346
- # Create a basic segmentation overlay
347
- segmentation_overlay = processed_image_np.copy().astype(np.float32)
348
- if masks.max() > 0:
349
- np.random.seed(42)
350
- colors = np.random.randint(0, 255, size=(masks.max() + 1, 3))
351
- colors[0] = [0, 0, 0]
352
- colored_mask = colors[masks]
353
- alpha = 0.4
354
- segmentation_overlay = (1 - alpha) * segmentation_overlay + alpha * colored_mask
355
- segmentation_overlay = np.clip(segmentation_overlay, 0, 255).astype(np.uint8)
356
-
357
- info_msg = f"Segmentation complete! Found {cell_count} cells.\n"
358
- info_msg += f"Confluency: {confluency:.1f}%\n"
359
- if crop_coords:
360
- info_msg += f"Processed with coordinates: {crop_coords}\n"
361
- info_msg += f"Now adjust the Blue Threshold for viability assessment."
362
-
363
- return (
364
- cell_count,
365
- Image.fromarray(segmentation_overlay),
366
- info_msg,
367
- gr.update(visible=True),
368
- pack_array(masks),
369
- pack_array(processed_image_np),
370
- confluency,
371
- )
372
-
373
- except Exception as e:
374
- return 0, None, f"Error during segmentation: {str(e)}", gr.update(visible=False), None, None, 0.0
375
-
376
-
377
  def update_viability_realtime(blue_threshold, stored_masks, stored_image_np):
378
  # avoid unpacking None (e.g. slider moved before segmentation)
379
  if stored_masks is None or stored_image_np is None:
@@ -517,77 +426,6 @@ with gr.Blocks(
517
  outputs=[viability_overlay1, live_count_output1, dead_count_output1, viability_percent_output1, viability_info1]
518
  )
519
 
520
- with gr.Tab("Manual Coordinates"):
521
- gr.Markdown("### Upload image and run segmentation")
522
-
523
- with gr.Row():
524
- with gr.Column():
525
- image_input = gr.Image(
526
- type="pil",
527
- label="Microscopy Image",
528
- image_mode="RGB",
529
- height=512 # limit canvas size here too
530
- )
531
- model_dropdown2 = gr.Dropdown(
532
- choices=list(MODEL_OPTIONS.keys()),
533
- label="Select Model",
534
- value="Hemocytometer Model"
535
- )
536
- coord_input = gr.Textbox(
537
- label="Crop Coordinates (optional)",
538
- placeholder="e.g., 100,100,400,400",
539
- info="Format: x_min,y_min,x_max,y_max"
540
- )
541
- segment_btn2 = gr.Button("🔬 Run Segmentation", variant="primary", size="lg")
542
-
543
- with gr.Column():
544
- cell_count_output2 = gr.Number(label="Total Cells Detected", precision=0)
545
- confluency_output2 = gr.Number(label="Confluency (%)", precision=1)
546
- overlay_output2 = gr.Image(type="pil", label="Segmentation Result")
547
- info_output2 = gr.Textbox(label="Processing Info", lines=4)
548
-
549
- # Viability Assessment Section
550
- with gr.Group(visible=False) as viability_section2:
551
- gr.Markdown("### Viability Assessment (Trypan Blue)")
552
- gr.Markdown("Adjust the threshold to classify cells as live (green) or dead (red).")
553
-
554
- with gr.Row():
555
- with gr.Column():
556
- blue_threshold2 = gr.Slider(
557
- minimum=0,
558
- maximum=100,
559
- value=25,
560
- step=1,
561
- label="Blue Threshold (%)",
562
- info="Higher values = more selective for blue cells"
563
- )
564
-
565
- with gr.Column():
566
- live_count_output2 = gr.Number(label="Live Cells (Green)", precision=0)
567
- dead_count_output2 = gr.Number(label="Dead Cells (Red)", precision=0)
568
-
569
- viability_overlay2 = gr.Image(type="pil", label="Viability Assessment (Green=Live, Red=Dead)")
570
- viability_percent_output2 = gr.Number(label="Viability (%)", precision=1)
571
- viability_info2 = gr.Textbox(label="Analysis Results", lines=5)
572
-
573
- # Event handlers
574
- segment_btn2.click(
575
- fn=run_segmentation_manual,
576
- inputs=[image_input, model_dropdown2, coord_input],
577
- outputs=[cell_count_output2, overlay_output2, info_output2, viability_section2, masks_state, image_state, confluency_output2]
578
- ).then( # Chain the initial viability assessment after segmentation
579
- fn=update_viability_realtime,
580
- inputs=[blue_threshold2, masks_state, image_state],
581
- outputs=[viability_overlay2, live_count_output2, dead_count_output2, viability_percent_output2, viability_info2]
582
- )
583
-
584
- # Slider changes update viability in real-time
585
- blue_threshold2.change(
586
- fn=update_viability_realtime,
587
- inputs=[blue_threshold2, masks_state, image_state],
588
- outputs=[viability_overlay2, live_count_output2, dead_count_output2, viability_percent_output2, viability_info2]
589
- )
590
-
591
  # Instructions
592
  with gr.Accordion("Instructions", open=False):
593
  gr.Markdown("""
 
19
  loaded_models = {}
20
 
21
  # ---- mobile-safe size limits (aggressive for Safari) ----
22
+ MAX_SIDE = 384
23
+ MAX_PIXELS = 384 * 384
24
 
25
 
26
  def safe_resize(image_np):
 
 
 
 
27
  h, w = image_np.shape[:2]
28
  total = h * w
29
 
 
215
 
216
  @spaces.GPU
217
  def run_segmentation_editor(editor_data, model_choice):
218
+
 
 
 
219
  try:
220
+ image_np = np.array(image)
221
+ image_np = safe_resize(image_np)
222
+
223
  model_filename = MODEL_OPTIONS[model_choice]
224
  model_path = hf_hub_download(repo_id=HF_REPO_ID, filename=model_filename)
225
 
 
283
  return 0, None, f"Error during segmentation: {str(e)}", gr.update(visible=False), None, None, 0.0
284
 
285
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  def update_viability_realtime(blue_threshold, stored_masks, stored_image_np):
287
  # avoid unpacking None (e.g. slider moved before segmentation)
288
  if stored_masks is None or stored_image_np is None:
 
426
  outputs=[viability_overlay1, live_count_output1, dead_count_output1, viability_percent_output1, viability_info1]
427
  )
428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
  # Instructions
430
  with gr.Accordion("Instructions", open=False):
431
  gr.Markdown("""