LiangLabUMB commited on
Commit
b51bd25
·
verified ·
1 Parent(s): 1320040

Delete app.py

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