LiangLabUMB commited on
Commit
16f75c4
·
verified ·
1 Parent(s): 5bd1ae5

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -603
app.py DELETED
@@ -1,603 +0,0 @@
1
- import gradio as gr
2
- import spaces
3
- from cellpose import models
4
- import numpy as np
5
- import cv2
6
- import matplotlib.pyplot as plt
7
- import tempfile
8
- from PIL import Image
9
- import io
10
- from huggingface_hub import hf_hub_download
11
-
12
- HF_REPO_ID = "myang4218/cellposemodel"
13
- MODEL_OPTIONS = {
14
- "Hemocytometer Model": "hemocytometermodel.npy",
15
- "General Model": "generalmodel.npy"
16
- }
17
- loaded_models = {}
18
-
19
- # Global variables to store segmentation results for real-time viability assessment
20
- current_masks = None
21
- current_image = None
22
- current_region_coords = None
23
-
24
- def debug_editor_data(editor_data):
25
- """Debug function to understand the structure of ImageEditor data"""
26
- if editor_data is None:
27
- return "No data provided"
28
- info = f"Type: {type(editor_data)}\n"
29
- if isinstance(editor_data, dict):
30
- info += f"Keys: {list(editor_data.keys())}\n"
31
- for key, value in editor_data.items():
32
- info += f" {key}: {type(value)}\n"
33
- if key == 'layers' and value:
34
- info += f" Layers count: {len(value)}\n"
35
- for i, layer in enumerate(value):
36
- info += f" Layer {i}: {type(layer)}\n"
37
- if hasattr(layer, 'size'):
38
- info += f" Size: {layer.size}\n"
39
- info += f" Mode: {layer.mode}\n"
40
- else: # Direct PIL Image
41
- if hasattr(editor_data, 'size'):
42
- info += f"Size: {editor_data.size}\n"
43
- info += f"Mode: {editor_data.mode}\n"
44
- return info
45
-
46
- def extract_region_from_editor(editor_data):
47
- """Extract the selected region from ImageEditor data"""
48
- if editor_data is None:
49
- print("DEBUG: extract_region_from_editor - editor_data is None")
50
- return None, None
51
-
52
- print(f"DEBUG: extract_region_from_editor - editor_data type: {type(editor_data)}")
53
-
54
- if isinstance(editor_data, dict):
55
- background = editor_data.get('background')
56
- layers = editor_data.get('layers', [])
57
-
58
- if background is None:
59
- print("DEBUG: extract_region_from_editor - background is None")
60
- return None, None
61
-
62
- background_np = np.array(background)
63
- print(f"DEBUG: extract_region_from_editor - background_np shape: {background_np.shape}")
64
-
65
- if layers and len(layers) > 0:
66
- selection_layer = layers[0]
67
- selection_np = np.array(selection_layer)
68
- print(f"DEBUG: extract_region_from_editor - selection_np shape: {selection_np.shape}")
69
-
70
- if len(selection_np.shape) == 3:
71
- if selection_np.shape[2] == 4: # RGBA
72
- mask = selection_np[:, :, 3] > 0
73
- else: # RGB
74
- mask = np.any(selection_np > 0, axis=2)
75
- else:
76
- mask = selection_np > 0
77
-
78
- coords = np.where(mask)
79
- if len(coords[0]) > 0:
80
- y_min, y_max = coords[0].min(), coords[0].max()
81
- x_min, x_max = coords[1].min(), coords[1].max()
82
-
83
- pad = 5
84
- h, w = background_np.shape[:2]
85
- y_min = max(0, y_min - pad)
86
- y_max = min(h, y_max + pad)
87
- x_min = max(0, x_min - pad)
88
- x_max = min(w, x_max + pad)
89
-
90
- region = background_np[y_min:y_max+1, x_min:x_max+1]
91
- print(f"DEBUG: extract_region_from_editor - Extracted region shape: {region.shape}")
92
- return region, (x_min, y_min, x_max, y_max)
93
- print("DEBUG: extract_region_from_editor - No selection or empty selection, returning full background.")
94
- return background_np, None
95
- else: # Assuming it's a direct PIL Image if not dict
96
- if hasattr(editor_data, 'size'):
97
- image_np = np.array(editor_data)
98
- print(f"DEBUG: extract_region_from_editor - Direct PIL image, shape: {image_np.shape}")
99
- return image_np, None
100
- else:
101
- print("DEBUG: extract_region_from_editor - editor_data is not dict and no size attribute, returning None.")
102
- return None, None
103
-
104
- def calculate_blue_intensity(image_np, masks):
105
- """Calculate blue intensity for each cell"""
106
- # Ensure image is in correct format
107
- if len(image_np.shape) == 2:
108
- # Convert grayscale to RGB
109
- image_np = cv2.cvtColor(image_np, cv2.COLOR_GRAY2RGB)
110
- elif len(image_np.shape) == 3 and image_np.shape[2] == 4:
111
- # Handle RGBA
112
- image_np = cv2.cvtColor(image_np, cv2.COLOR_RGBA2RGB)
113
-
114
- # Calculate blue intensity for each cell
115
- cell_blue_intensities = {}
116
- unique_cells = np.unique(masks)
117
-
118
- print(f"DEBUG: Found {len(unique_cells)} unique mask values: {unique_cells}")
119
- print(f"DEBUG: Image shape: {image_np.shape}, dtype: {image_np.dtype}")
120
- print(f"DEBUG: Masks shape: {masks.shape}, dtype: {masks.dtype}")
121
-
122
- for cell_id in unique_cells:
123
- if cell_id == 0: # Skip background
124
- continue
125
-
126
- cell_mask = masks == cell_id
127
- cell_pixels = image_np[cell_mask]
128
-
129
- if len(cell_pixels) > 0:
130
- # Calculate mean blue intensity (blue channel is index 2)
131
- blue_intensity = np.mean(cell_pixels[:, 2]) # Blue channel
132
- cell_blue_intensities[cell_id] = blue_intensity
133
- # print(f"DEBUG: Cell {cell_id}: {len(cell_pixels)} pixels, blue intensity: {blue_intensity:.2f}") # Too verbose, uncomment if needed
134
- print(f"DEBUG: Total cells with blue intensity: {len(cell_blue_intensities)}")
135
- return cell_blue_intensities
136
-
137
- def create_viability_overlay(image_np, masks, blue_threshold):
138
- """Create overlay showing live/dead cells based on blue threshold"""
139
-
140
- if masks is None or image_np is None:
141
- print("DEBUG: create_viability_overlay - masks or image_np is None, returning None.")
142
- return None, 0, 0
143
-
144
- print(f"DEBUG: create_viability_overlay called with threshold: {blue_threshold}")
145
- print(f"DEBUG: Image shape: {image_np.shape}, Masks shape: {masks.shape}, Masks max: {masks.max()}")
146
-
147
- # Ensure image is RGB
148
- if len(image_np.shape) == 2:
149
- image_np = cv2.cvtColor(image_np, cv2.COLOR_GRAY2RGB)
150
- elif len(image_np.shape) == 3 and image_np.shape[2] == 4:
151
- image_np = cv2.cvtColor(image_np, cv2.COLOR_RGBA2RGB)
152
-
153
- # Calculate blue intensities
154
- cell_blue_intensities = calculate_blue_intensity(image_np, masks)
155
-
156
- print(f"DEBUG: Got {len(cell_blue_intensities)} cells with blue intensities")
157
-
158
- # Create overlay - start with original image
159
- overlay = image_np.copy().astype(np.float32)
160
-
161
- live_count = 0
162
- dead_count = 0
163
-
164
- # Color cells based on viability
165
- for cell_id, blue_intensity in cell_blue_intensities.items():
166
- cell_mask = masks == cell_id
167
-
168
- # print(f"DEBUG: Processing cell {cell_id}, blue intensity: {blue_intensity:.2f}, threshold: {blue_threshold}") # Too verbose
169
-
170
- if blue_intensity > blue_threshold:
171
- # Dead cell - color in red
172
- overlay[cell_mask] = [255, 0, 0] # Red
173
- dead_count += 1
174
- # print(f"DEBUG: Cell {cell_id} classified as DEAD") # Too verbose
175
- else:
176
- # Live cell - color in green
177
- overlay[cell_mask] = [0, 255, 0] # Green
178
- live_count += 1
179
- # print(f"DEBUG: Cell {cell_id} classified as LIVE") # Too verbose
180
- print(f"DEBUG: Final counts - Live: {live_count}, Dead: {dead_count}")
181
-
182
- # Blend with original image
183
- alpha = 0.6 # Increased alpha for better visibility
184
- final_overlay = (1 - alpha) * image_np.astype(np.float32) + alpha * overlay
185
- final_overlay = np.clip(final_overlay, 0, 255).astype(np.uint8)
186
-
187
- return Image.fromarray(final_overlay), live_count, dead_count
188
-
189
- @spaces.GPU
190
- def segment_cells(editor_data, model_choice, crop_coords=None):
191
- """
192
- First stage: Segment cells and store results for viability assessment
193
- """
194
- global current_masks, current_image, current_region_coords
195
-
196
- try:
197
- print("DEBUG: --- Starting segment_cells ---")
198
- debug_info = debug_editor_data(editor_data)
199
- print(f"DEBUG: Editor data info:\n{debug_info}")
200
-
201
- # Load the model
202
- model_filename = MODEL_OPTIONS[model_choice]
203
- model_path = hf_hub_download(repo_id=HF_REPO_ID, filename=model_filename)
204
-
205
- if model_filename in loaded_models:
206
- model = loaded_models[model_filename]
207
- else:
208
- model = models.CellposeModel(gpu=True, pretrained_model=model_path)
209
- loaded_models[model_filename] = model
210
-
211
- # Extract region from editor
212
- region_np, region_coords = extract_region_from_editor(editor_data)
213
-
214
- if region_np is None:
215
- print("DEBUG: region_np is None, returning error and hiding viability section.")
216
- current_masks = None # Ensure globals are reset
217
- current_image = None
218
- current_region_coords = None
219
- return 0, None, f"No image provided. Debug info:\n{debug_info}", gr.update(visible=False)
220
-
221
- print(f"DEBUG: Image to segment shape (after extraction/cropping): {region_np.shape}, dtype: {region_np.dtype}")
222
-
223
- # Handle manual crop coordinates (logic already exists, no extra prints needed here unless it's the issue)
224
- if crop_coords and crop_coords.strip():
225
- try:
226
- coords = [int(x.strip()) for x in crop_coords.split(',')]
227
- if len(coords) == 4:
228
- x_min, y_min, x_max, y_max = coords
229
- h, w = region_np.shape[:2]
230
- x_min = max(0, min(x_min, w-1))
231
- y_min = max(0, min(y_min, h-1))
232
- x_max = max(x_min+1, min(x_max, w))
233
- y_max = max(y_min+1, min(y_max, h))
234
-
235
- region_np = region_np[y_min:y_max, x_min:x_max]
236
- current_region_coords = (x_min, y_min, x_max, y_max)
237
- print(f"DEBUG: Applied manual crop: {current_region_coords}, new region shape: {region_np.shape}")
238
- except ValueError:
239
- print(f"DEBUG: Invalid crop coordinates format: {crop_coords}")
240
- pass
241
-
242
- # Process image format
243
- if len(region_np.shape) == 2:
244
- region_np = cv2.cvtColor(region_np, cv2.COLOR_GRAY2RGB)
245
- elif len(region_np.shape) == 3 and region_np.shape[2] == 4:
246
- region_np = cv2.cvtColor(region_np, cv2.COLOR_RGBA2RGB)
247
-
248
- print(f"DEBUG: Image before model.eval shape: {region_np.shape}, dtype: {region_np.dtype}")
249
- # Run Cellpose segmentation
250
- masks, flows, styles = model.eval(region_np, diameter=None, channels=[0, 0])
251
- print(f"DEBUG: After model.eval: masks type: {type(masks)}, shape: {masks.shape if hasattr(masks, 'shape') else 'N/A'}")
252
-
253
- # Store results globally for viability assessment
254
- current_masks = masks
255
- current_image = region_np
256
- current_region_coords = region_coords
257
-
258
- print(f"DEBUG: Globals set - current_masks is None? {current_masks is None}, current_image is None? {current_image is None}")
259
- print(f"DEBUG: Globals set - current_masks shape: {current_masks.shape if current_masks is not None else 'N/A'}")
260
-
261
- # Count unique cells
262
- cell_count = len(np.unique(masks)) - 1
263
- print(f"DEBUG: Cell count: {cell_count}")
264
-
265
- # Create basic segmentation overlay (logic unchanged)
266
- overlay = region_np.copy().astype(np.float32)
267
-
268
- if masks.max() > 0:
269
- np.random.seed(42)
270
- colors = np.random.randint(0, 255, size=(masks.max() + 1, 3))
271
- colors[0] = [0, 0, 0] # Background stays black
272
-
273
- colored_mask = colors[masks]
274
- alpha = 0.4
275
- overlay = (1 - alpha) * overlay + alpha * colored_mask
276
-
277
- overlay = np.clip(overlay, 0, 255).astype(np.uint8)
278
- overlay_image = Image.fromarray(overlay)
279
-
280
- info_msg = f"Segmentation complete! Found {cell_count} cells.\n"
281
- if region_coords:
282
- info_msg += f"Region: {region_coords}\n"
283
- info_msg += f"Now use the blue threshold slider below for viability assessment."
284
-
285
- # Make viability section visible
286
- viability_section_update = gr.update(visible=True)
287
- print("DEBUG: segment_cells returning successfully, viability section visible.")
288
-
289
- return cell_count, overlay_image, info_msg, viability_section_update
290
-
291
- except Exception as e:
292
- print(f"ERROR: An exception occurred in segment_cells: {e}")
293
- current_masks = None
294
- current_image = None
295
- current_region_coords = None
296
- return 0, None, f"Error: {str(e)}", gr.update(visible=False)
297
-
298
- def update_viability_assessment(blue_threshold):
299
- """
300
- Real-time viability assessment based on blue threshold
301
- """
302
- global current_masks, current_image
303
-
304
- print("DEBUG: --- Starting update_viability_assessment ---")
305
- print(f"DEBUG: At start of update_viability_assessment: current_masks is None? {current_masks is None}, current_image is None? {current_image is None}")
306
-
307
- if current_masks is None or current_image is None:
308
- print("DEBUG: current_masks or current_image is None, returning 'Please run segmentation first'.")
309
- return None, 0, 0, "Please run segmentation first"
310
-
311
- try:
312
- # Debug information
313
- unique_cells = np.unique(current_masks)
314
- total_cells = len(unique_cells) - 1 # Subtract background
315
-
316
- debug_info = f"Debug - Total cells found: {total_cells}\n"
317
- debug_info += f"Image shape: {current_image.shape}\n"
318
- debug_info += f"Masks shape: {current_masks.shape}\n"
319
- debug_info += f"Unique mask values (first 10): {unique_cells[:10]}...\n"
320
-
321
- # Create viability overlay
322
- viability_overlay, live_count, dead_count = create_viability_overlay(
323
- current_image, current_masks, blue_threshold
324
- )
325
-
326
- total_count = live_count + dead_count
327
- viability_info = f"Blue threshold: {blue_threshold}\n"
328
- viability_info += f"Total cells: {total_count}\nLive (green): {live_count}\nDead (red): {dead_count}\n"
329
-
330
- if total_count > 0:
331
- viability_percentage = (live_count / total_count) * 100
332
- viability_info += f"Viability: {viability_percentage:.1f}%\n"
333
-
334
- viability_info += f"\n{debug_info}"
335
- print("DEBUG: update_viability_assessment returning successfully.")
336
-
337
- return viability_overlay, live_count, dead_count, viability_info
338
-
339
- except Exception as e:
340
- print(f"ERROR: An exception occurred in update_viability_assessment: {e}")
341
- return None, 0, 0, f"Error in viability assessment: {str(e)}\nMasks available: {current_masks is not None}\nImage available: {current_image is not None}"
342
-
343
- # The segment_with_coords function and Gradio UI setup remain the same
344
- # ... (rest of your app.py code) ...
345
-
346
- @spaces.GPU
347
- def segment_with_coords(image, model_choice, crop_coords):
348
- """Alternative function for manual coordinates input"""
349
- global current_masks, current_image, current_region_coords
350
-
351
- print("DEBUG: --- Starting segment_with_coords ---")
352
- if image is None:
353
- print("DEBUG: No image provided for segment_with_coords, returning error.")
354
- current_masks = None
355
- current_image = None
356
- current_region_coords = None
357
- return 0, None, "No image provided", gr.update(visible=False)
358
-
359
- try:
360
- # Load the model
361
- model_filename = MODEL_OPTIONS[model_choice]
362
- model_path = hf_hub_download(repo_id=HF_REPO_ID, filename=model_filename)
363
-
364
- if model_filename in loaded_models:
365
- model = loaded_models[model_filename]
366
- else:
367
- model = models.CellposeModel(gpu=True, pretrained_model=model_path)
368
- loaded_models[model_filename] = model
369
-
370
- # Convert PIL Image to numpy array
371
- image_np = np.array(image)
372
- print(f"DEBUG: image_np from PIL: shape={image_np.shape}, dtype={image_np.dtype}")
373
-
374
- # Apply crop if coordinates are provided
375
- if crop_coords and crop_coords.strip():
376
- try:
377
- coords = [int(x.strip()) for x in crop_coords.split(',')]
378
- if len(coords) == 4:
379
- x_min, y_min, x_max, y_max = coords
380
- h, w = image_np.shape[:2]
381
- x_min = max(0, min(x_min, w-1))
382
- y_min = max(0, min(y_min, h-1))
383
- x_max = max(x_min+1, min(x_max, w))
384
- y_max = max(y_min+1, min(y_max, h))
385
-
386
- image_np = image_np[y_min:y_max, x_min:x_max]
387
- current_region_coords = (x_min, y_min, x_max, y_max)
388
- print(f"DEBUG: Applied manual crop in segment_with_coords: {current_region_coords}, new image_np shape: {image_np.shape}")
389
- except ValueError:
390
- print(f"DEBUG: Invalid crop coordinates format in segment_with_coords: {crop_coords}")
391
- pass
392
-
393
- # Process image format
394
- if len(image_np.shape) == 2:
395
- image_np = cv2.cvtColor(image_np, cv2.COLOR_GRAY2RGB)
396
- elif len(image_np.shape) == 3 and image_np.shape[2] == 4:
397
- image_np = cv2.cvtColor(image_np, cv2.COLOR_RGBA2RGB)
398
-
399
- print(f"DEBUG: Image before model.eval (segment_with_coords) shape: {image_np.shape}, dtype: {image_np.dtype}")
400
- # Run Cellpose
401
- masks, flows, styles = model.eval(image_np, diameter=None, channels=[0, 0])
402
- print(f"DEBUG: After model.eval (segment_with_coords): masks type: {type(masks)}, shape: {masks.shape if hasattr(masks, 'shape') else 'N/A'}")
403
-
404
- # Store results globally
405
- current_masks = masks
406
- current_image = image_np
407
-
408
- print(f"DEBUG: Globals set (segment_with_coords) - current_masks is None? {current_masks is None}, current_image is None? {current_image is None}")
409
- print(f"DEBUG: Globals set (segment_with_coords) - current_masks shape: {current_masks.shape if current_masks is not None else 'N/A'}")
410
-
411
- # Count unique cells
412
- cell_count = len(np.unique(masks)) - 1
413
-
414
- # Create overlay
415
- overlay = image_np.copy().astype(np.float32)
416
-
417
- if masks.max() > 0:
418
- np.random.seed(42)
419
- colors = np.random.randint(0, 255, size=(masks.max() + 1, 3))
420
- colors[0] = [0, 0, 0]
421
-
422
- colored_mask = colors[masks]
423
- alpha = 0.4
424
- overlay = (1 - alpha) * overlay + alpha * colored_mask
425
-
426
- overlay = np.clip(overlay, 0, 255).astype(np.uint8)
427
- overlay_image = Image.fromarray(overlay)
428
-
429
- info_msg = f"Segmentation complete! Found {cell_count} cells.\n"
430
- info_msg += f"Now use the blue threshold slider below for viability assessment."
431
- print("DEBUG: segment_with_coords returning successfully, viability section visible.")
432
-
433
- return cell_count, overlay_image, info_msg, gr.update(visible=True)
434
-
435
- except Exception as e:
436
- print(f"ERROR: An exception occurred in segment_with_coords: {e}")
437
- current_masks = None
438
- current_image = None
439
- current_region_coords = None
440
- return 0, None, f"Error: {str(e)}", gr.update(visible=False)
441
-
442
- # Create the Gradio interface
443
- with gr.Blocks(title="Cell Counter with Real-time Viability Assessment", theme=gr.themes.Soft()) as demo:
444
- gr.Markdown("# Cell Counter with Real-time Viability Assessment")
445
- gr.Markdown("Upload a microscopy image, run segmentation, then use the blue threshold slider for real-time viability assessment.")
446
-
447
- with gr.Tab("Image Editor (Draw Selection)"):
448
- gr.Markdown("### Step 1: Draw selection and run segmentation")
449
-
450
- with gr.Row():
451
- with gr.Column():
452
- image_editor = gr.ImageEditor(
453
- label="Draw selection on image",
454
- type="pil",
455
- brush=gr.Brush(colors=["#ff0000"], color_mode="fixed", default_size=20),
456
- eraser=gr.Eraser(default_size=20)
457
- )
458
- model_dropdown1 = gr.Dropdown(
459
- choices=list(MODEL_OPTIONS.keys()),
460
- label="Select Model",
461
- value="Hemocytometer Model"
462
- )
463
- segment_btn1 = gr.Button("🔬 Run Segmentation", variant="primary", size="lg")
464
-
465
- with gr.Column():
466
- cell_count_output1 = gr.Number(label="Total Cells Detected", precision=0)
467
- overlay_output1 = gr.Image(type="pil", label="Segmentation Result")
468
- info_output1 = gr.Textbox(label="Processing Info", lines=3)
469
-
470
- # Viability Assessment Section
471
- with gr.Group(visible=False) as viability_section1:
472
- gr.Markdown("### Step 2: Real-time Viability Assessment")
473
- gr.Markdown("Adjust the blue threshold to classify cells as live (green) or dead (red). Higher values = more blue = more dead.")
474
-
475
- with gr.Row():
476
- with gr.Column():
477
- blue_threshold_slider1 = gr.Slider(
478
- minimum=0,
479
- maximum=255,
480
- value=100,
481
- step=1,
482
- label="Blue Threshold",
483
- info="Higher values = more cells classified as dead"
484
- )
485
-
486
- with gr.Column():
487
- live_count_output1 = gr.Number(label="Live Cells (Green)", precision=0)
488
- dead_count_output1 = gr.Number(label="Dead Cells (Red)", precision=0)
489
-
490
- viability_overlay1 = gr.Image(type="pil", label="Viability Assessment (Green=Live, Red=Dead)")
491
- viability_info1 = gr.Textbox(label="Viability Statistics", lines=4)
492
-
493
- # Event handlers
494
- segment_btn1.click(
495
- fn=segment_cells,
496
- inputs=[image_editor, model_dropdown1],
497
- outputs=[cell_count_output1, overlay_output1, info_output1, viability_section1]
498
- ).then( # Chain the viability assessment after segmentation
499
- fn=update_viability_assessment,
500
- inputs=[blue_threshold_slider1],
501
- outputs=[viability_overlay1, live_count_output1, dead_count_output1, viability_info1]
502
- )
503
-
504
- blue_threshold_slider1.change(
505
- fn=update_viability_assessment,
506
- inputs=[blue_threshold_slider1],
507
- outputs=[viability_overlay1, live_count_output1, dead_count_output1, viability_info1]
508
- )
509
-
510
- with gr.Tab("Manual Coordinates"):
511
- gr.Markdown("### Step 1: Upload image and run segmentation")
512
-
513
- with gr.Row():
514
- with gr.Column():
515
- image_input = gr.Image(type="pil", label="Microscopy Image")
516
- model_dropdown2 = gr.Dropdown(
517
- choices=list(MODEL_OPTIONS.keys()),
518
- label="Select Model",
519
- value="Hemocytometer Model"
520
- )
521
- coord_input = gr.Textbox(
522
- label="Crop Coordinates (optional)",
523
- placeholder="e.g., 100,100,400,400",
524
- info="Format: x_min,y_min,x_max,y_max"
525
- )
526
- segment_btn2 = gr.Button("🔬 Run Segmentation", variant="primary", size="lg")
527
-
528
- with gr.Column():
529
- cell_count_output2 = gr.Number(label="Total Cells Detected", precision=0)
530
- overlay_output2 = gr.Image(type="pil", label="Segmentation Result")
531
- info_output2 = gr.Textbox(label="Processing Info", lines=3)
532
-
533
- # Viability Assessment Section
534
- with gr.Group(visible=False) as viability_section2:
535
- gr.Markdown("### Step 2: Real-time Viability Assessment")
536
- gr.Markdown("Adjust the blue threshold to classify cells as live (green) or dead (red). Higher values = more blue = more dead.")
537
-
538
- with gr.Row():
539
- with gr.Column():
540
- blue_threshold_slider2 = gr.Slider(
541
- minimum=0,
542
- maximum=255,
543
- value=100,
544
- step=1,
545
- label="Blue Threshold",
546
- info="Higher values = more cells classified as dead"
547
- )
548
-
549
- with gr.Column():
550
- live_count_output2 = gr.Number(label="Live Cells (Green)", precision=0)
551
- dead_count_output2 = gr.Number(label="Dead Cells (Red)", precision=0)
552
-
553
- viability_overlay2 = gr.Image(type="pil", label="Viability Assessment (Green=Live, Red=Dead)")
554
- viability_info2 = gr.Textbox(label="Viability Statistics", lines=4)
555
-
556
- # Event handlers
557
- segment_btn2.click(
558
- fn=segment_with_coords,
559
- inputs=[image_input, model_dropdown2, coord_input],
560
- outputs=[cell_count_output2, overlay_output2, info_output2, viability_section2]
561
- ).then( # Chain the viability assessment after segmentation
562
- fn=update_viability_assessment,
563
- inputs=[blue_threshold_slider2],
564
- outputs=[viability_overlay2, live_count_output2, dead_count_output2, viability_info2]
565
- )
566
-
567
- blue_threshold_slider2.change(
568
- fn=update_viability_assessment,
569
- inputs=[blue_threshold_slider2],
570
- outputs=[viability_overlay2, live_count_output2, dead_count_output2, viability_info2]
571
- )
572
-
573
- # Instructions
574
- with gr.Accordion("Instructions", open=False):
575
- gr.Markdown("""
576
- ### How to use:
577
-
578
- 1. **Upload and Segment**:
579
- - Upload your microscopy image
580
- - Select a model (Hemocytometer Model for blood cells)
581
- - Draw a selection region or specify coordinates
582
- - Click "Run Segmentation"
583
-
584
- 2. **Viability Assessment**:
585
- - After segmentation, the viability section will appear
586
- - Use the blue threshold slider to classify cells in real-time
587
- - Green cells = Live (low blue intensity)
588
- - Red cells = Dead (high blue intensity)
589
- - Adjust the threshold until the classification looks correct
590
-
591
- 3. **Interpreting Results**:
592
- - The app calculates viability percentage automatically
593
- - Live cells appear green, dead cells appear red
594
- - Statistics show total counts and viability percentage
595
-
596
- ### Tips:
597
- - Start with a threshold around 100 and adjust based on your staining
598
- - Dead cells typically have higher blue intensity due to trypan blue uptake
599
- - The threshold is applied in real-time without re-running segmentation
600
- """)
601
-
602
- if __name__ == "__main__":
603
- demo.launch()