LiangLabUMB commited on
Commit
f46b66e
·
verified ·
1 Parent(s): 1002e98

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +46 -43
app.py CHANGED
@@ -19,6 +19,7 @@ loaded_models = {}
19
  # Global variable for region coordinates (can remain global if only for info display)
20
  current_region_coords = None
21
 
 
22
  def debug_editor_data(editor_data):
23
  """Debug function to understand the structure of ImageEditor data"""
24
  if editor_data is None:
@@ -39,7 +40,8 @@ def debug_editor_data(editor_data):
39
  if hasattr(editor_data, 'size'):
40
  info += f"Size: {editor_data.size}\n"
41
  info += f"Mode: {editor_data.mode}\n"
42
- return info
 
43
 
44
  def extract_region_from_editor(editor_data):
45
  """Extract the selected region from ImageEditor data"""
@@ -47,23 +49,23 @@ def extract_region_from_editor(editor_data):
47
  print("DEBUG: extract_region_from_editor - editor_data is None")
48
  return None, None
49
 
50
- print(f"DEBUG: extract_region_from_editor - editor_data type: {type(editor_data)}")
51
 
52
  if isinstance(editor_data, dict):
53
  background = editor_data.get('background')
54
  layers = editor_data.get('layers', [])
55
 
56
  if background is None:
57
- print("DEBUG: extract_region_from_editor - background is None")
58
  return None, None
59
 
60
  background_np = np.array(background)
61
- print(f"DEBUG: extract_region_from_editor - background_np shape: {background_np.shape}")
62
 
63
  if layers and len(layers) > 0:
64
  selection_layer = layers[0]
65
  selection_np = np.array(selection_layer)
66
- print(f"DEBUG: extract_region_from_editor - selection_np shape: {selection_np.shape}")
67
 
68
  if len(selection_np.shape) == 3:
69
  if selection_np.shape[2] == 4: # RGBA
@@ -86,17 +88,17 @@ def extract_region_from_editor(editor_data):
86
  x_max = min(w, x_max + pad)
87
 
88
  region = background_np[y_min:y_max+1, x_min:x_max+1]
89
- print(f"DEBUG: extract_region_from_editor - Extracted region shape: {region.shape}")
90
  return region, (x_min, y_min, x_max, y_max)
91
- print("DEBUG: extract_region_from_editor - No selection or empty selection, returning full background.")
92
  return background_np, None
93
  else: # Assuming it's a direct PIL Image if not dict
94
  if hasattr(editor_data, 'size'):
95
  image_np = np.array(editor_data)
96
- print(f"DEBUG: extract_region_from_editor - Direct PIL image, shape: {image_np.shape}")
97
  return image_np, None
98
  else:
99
- print("DEBUG: extract_region_from_editor - editor_data is not dict and no size attribute, returning None.")
100
  return None, None
101
 
102
  def calculate_blue_intensity(image_np, masks):
@@ -113,9 +115,9 @@ def calculate_blue_intensity(image_np, masks):
113
  cell_blue_intensities = {}
114
  unique_cells = np.unique(masks)
115
 
116
- print(f"DEBUG: Found {len(unique_cells)} unique mask values: {unique_cells}")
117
- print(f"DEBUG: Image shape: {image_np.shape}, dtype: {image_np.dtype}")
118
- print(f"DEBUG: Masks shape: {masks.shape}, dtype: {masks.dtype}")
119
 
120
  for cell_id in unique_cells:
121
  if cell_id == 0: # Skip background
@@ -128,8 +130,8 @@ def calculate_blue_intensity(image_np, masks):
128
  # Calculate mean blue intensity (blue channel is index 2)
129
  blue_intensity = np.mean(cell_pixels[:, 2]) # Blue channel
130
  cell_blue_intensities[cell_id] = blue_intensity
131
- print(f"DEBUG: Cell {cell_id}: {len(cell_pixels)} pixels, blue intensity: {blue_intensity:.2f}") # Too verbose, uncomment if needed
132
- print(f"DEBUG: Total cells with blue intensity: {len(cell_blue_intensities)}")
133
  return cell_blue_intensities
134
 
135
  def create_viability_overlay(image_np, masks, blue_threshold):
@@ -139,8 +141,8 @@ def create_viability_overlay(image_np, masks, blue_threshold):
139
  print("DEBUG: create_viability_overlay - masks or image_np is None, returning None.")
140
  return None, 0, 0
141
 
142
- print(f"DEBUG: create_viability_overlay called with threshold: {blue_threshold}")
143
- print(f"DEBUG: Image shape: {image_np.shape}, Masks shape: {masks.shape}, Masks max: {masks.max()}")
144
 
145
  # Ensure image is RGB
146
  if len(image_np.shape) == 2:
@@ -151,7 +153,7 @@ def create_viability_overlay(image_np, masks, blue_threshold):
151
  # Calculate blue intensities
152
  cell_blue_intensities = calculate_blue_intensity(image_np, masks)
153
 
154
- print(f"DEBUG: Got {len(cell_blue_intensities)} cells with blue intensities")
155
 
156
  # Create overlay - start with original image
157
  overlay = image_np.copy().astype(np.float32)
@@ -163,19 +165,19 @@ def create_viability_overlay(image_np, masks, blue_threshold):
163
  for cell_id, blue_intensity in cell_blue_intensities.items():
164
  cell_mask = masks == cell_id
165
 
166
- print(f"DEBUG: Processing cell {cell_id}, blue intensity: {blue_intensity:.2f}, threshold: {blue_threshold}") # Too verbose
167
 
168
  if blue_intensity > blue_threshold:
169
  # Dead cell - color in red
170
  overlay[cell_mask] = [255, 0, 0] # Red
171
  dead_count += 1
172
- print(f"DEBUG: Cell {cell_id} classified as DEAD") # Too verbose
173
  else:
174
  # Live cell - color in green
175
  overlay[cell_mask] = [0, 255, 0] # Green
176
  live_count += 1
177
- print(f"DEBUG: Cell {cell_id} classified as LIVE") # Too verbose
178
- print(f"DEBUG: Final counts - Live: {live_count}, Dead: {dead_count}")
179
 
180
  # Blend with original image
181
  alpha = 0.6 # Increased alpha for better visibility
@@ -192,9 +194,9 @@ def segment_cells(editor_data, model_choice): # crop_coords removed from here, h
192
  global current_region_coords # Keep global for info display if needed later
193
 
194
  try:
195
- print("DEBUG: --- Starting segment_cells ---")
196
  debug_info = debug_editor_data(editor_data)
197
- print(f"DEBUG: Editor data info:\n{debug_info}")
198
 
199
  # Load the model
200
  model_filename = MODEL_OPTIONS[model_choice]
@@ -210,12 +212,12 @@ def segment_cells(editor_data, model_choice): # crop_coords removed from here, h
210
  region_np, region_coords = extract_region_from_editor(editor_data)
211
 
212
  if region_np is None:
213
- print("DEBUG: region_np is None, returning error and hiding viability section.")
214
  current_region_coords = None
215
  # Also return None for masks and image for the gr.State components
216
  return 0, None, f"No image provided. Debug info:\n{debug_info}", gr.update(visible=False), None, None
217
 
218
- print(f"DEBUG: Image to segment shape (after extraction/cropping): {region_np.shape}, dtype: {region_np.dtype}")
219
 
220
  # Process image format
221
  if len(region_np.shape) == 2:
@@ -225,18 +227,18 @@ def segment_cells(editor_data, model_choice): # crop_coords removed from here, h
225
  else:
226
  region_np_processed = region_np # Already RGB or suitable
227
 
228
- print(f"DEBUG: Image before model.eval shape: {region_np_processed.shape}, dtype: {region_np_processed.dtype}")
229
 
230
  # Run Cellpose segmentation
231
  masks, flows, styles = model.eval(region_np_processed, diameter=None, channels=[0, 0])
232
- print(f"DEBUG: After model.eval: masks type: {type(masks)}, shape: {masks.shape if hasattr(masks, 'shape') else 'N/A'}")
233
 
234
  # Update global region coords for info display
235
  current_region_coords = region_coords
236
 
237
  # Count unique cells
238
  cell_count = len(np.unique(masks)) - 1
239
- print(f"DEBUG: Cell count: {cell_count}")
240
 
241
  # Create basic segmentation overlay
242
  overlay = region_np_processed.copy().astype(np.float32)
@@ -260,7 +262,7 @@ def segment_cells(editor_data, model_choice): # crop_coords removed from here, h
260
 
261
  # Make viability section visible
262
  viability_section_update = gr.update(visible=True)
263
- print("DEBUG: segment_cells returning successfully, viability section visible.")
264
 
265
  # Return masks and original processed image for state components
266
  return cell_count, overlay_image, info_msg, viability_section_update, masks, region_np_processed
@@ -276,22 +278,23 @@ def update_viability_assessment(blue_threshold, stored_masks, stored_image):
276
  Real-time viability assessment based on blue threshold
277
  """
278
 
279
- print("DEBUG: --- Starting update_viability_assessment ---")
280
- 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}")
281
 
282
  if stored_masks is None or stored_image is None:
283
- print("DEBUG: Passed-in stored_masks or stored_image is None, returning 'Please run segmentation first'.")
284
  return None, 0, 0, "Please run segmentation first"
285
 
286
  try:
287
  # Debug information
288
- unique_cells = np.unique(stored_masks)
289
  total_cells = len(unique_cells) - 1 # Subtract background
290
 
291
  debug_info = f"Debug - Total cells found: {total_cells}\n"
292
  debug_info += f"Image shape: {stored_image.shape}\n"
293
  debug_info += f"Masks shape: {stored_masks.shape}\n"
294
  debug_info += f"Unique mask values (first 10): {unique_cells[:10]}...\n"
 
295
 
296
  # Create viability overlay
297
  viability_overlay, live_count, dead_count = create_viability_overlay(
@@ -306,8 +309,8 @@ def update_viability_assessment(blue_threshold, stored_masks, stored_image):
306
  viability_percentage = (live_count / total_count) * 100
307
  viability_info += f"Viability: {viability_percentage:.1f}%\n"
308
 
309
- viability_info += f"\n{debug_info}"
310
- print("DEBUG: update_viability_assessment returning successfully.")
311
 
312
  return viability_overlay, live_count, dead_count, viability_info
313
 
@@ -320,9 +323,9 @@ def segment_with_coords(image, model_choice, crop_coords):
320
  """Alternative function for manual coordinates input"""
321
  global current_region_coords
322
 
323
- print("DEBUG: --- Starting segment_with_coords ---")
324
  if image is None:
325
- print("DEBUG: No image provided for segment_with_coords, returning error.")
326
  current_region_coords = None
327
  return 0, None, "No image provided", gr.update(visible=False), None, None
328
 
@@ -339,7 +342,7 @@ def segment_with_coords(image, model_choice, crop_coords):
339
 
340
  # Convert PIL Image to numpy array
341
  image_np = np.array(image)
342
- print(f"DEBUG: image_np from PIL: shape={image_np.shape}, dtype={image_np.dtype}")
343
 
344
  # Apply crop if coordinates are provided
345
  if crop_coords and crop_coords.strip():
@@ -355,11 +358,11 @@ def segment_with_coords(image, model_choice, crop_coords):
355
 
356
  image_np_processed = image_np[y_min:y_max, x_min:x_max]
357
  current_region_coords = (x_min, y_min, x_max, y_max)
358
- print(f"DEBUG: Applied manual crop in segment_with_coords: {current_region_coords}, new image_np_processed shape: {image_np_processed.shape}")
359
  else:
360
  image_np_processed = image_np # No valid crop, use original
361
  except ValueError:
362
- print(f"DEBUG: Invalid crop coordinates format in segment_with_coords: {crop_coords}")
363
  image_np_processed = image_np # Invalid crop, use original
364
  else:
365
  image_np_processed = image_np # No crop coords, use original
@@ -372,10 +375,10 @@ def segment_with_coords(image, model_choice, crop_coords):
372
  else:
373
  image_np_final = image_np_processed
374
 
375
- print(f"DEBUG: Image before model.eval (segment_with_coords) shape: {image_np_final.shape}, dtype: {image_np_final.dtype}")
376
  # Run Cellpose
377
  masks, flows, styles = model.eval(image_np_final, diameter=None, channels=[0, 0])
378
- print(f"DEBUG: After model.eval (segment_with_coords): masks type: {type(masks)}, shape: {masks.shape if hasattr(masks, 'shape') else 'N/A'}")
379
 
380
  # Count unique cells
381
  cell_count = len(np.unique(masks)) - 1
@@ -398,7 +401,7 @@ def segment_with_coords(image, model_choice, crop_coords):
398
  if current_region_coords: # Use global if set by crop
399
  info_msg += f"Region: {current_region_coords}\n"
400
  info_msg += f"Now use the blue threshold slider below for viability assessment."
401
- print("DEBUG: segment_with_coords returning successfully, viability section visible.")
402
 
403
  return cell_count, overlay_image, info_msg, gr.update(visible=True), masks, image_np_final
404
 
 
19
  # Global variable for region coordinates (can remain global if only for info display)
20
  current_region_coords = None
21
 
22
+ '''
23
  def debug_editor_data(editor_data):
24
  """Debug function to understand the structure of ImageEditor data"""
25
  if editor_data is None:
 
40
  if hasattr(editor_data, 'size'):
41
  info += f"Size: {editor_data.size}\n"
42
  info += f"Mode: {editor_data.mode}\n"
43
+ return info
44
+ '''
45
 
46
  def extract_region_from_editor(editor_data):
47
  """Extract the selected region from ImageEditor data"""
 
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
 
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):
 
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
 
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):
 
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:
 
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)
 
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
 
194
  global current_region_coords # Keep global for info display if needed later
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]
 
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_region_coords = None
217
  # Also return None for masks and image for the gr.State components
218
  return 0, None, f"No image provided. Debug info:\n{debug_info}", gr.update(visible=False), None, None
219
 
220
+ #print(f"DEBUG: Image to segment shape (after extraction/cropping): {region_np.shape}, dtype: {region_np.dtype}")
221
 
222
  # Process image format
223
  if len(region_np.shape) == 2:
 
227
  else:
228
  region_np_processed = region_np # Already RGB or suitable
229
 
230
+ #print(f"DEBUG: Image before model.eval shape: {region_np_processed.shape}, dtype: {region_np_processed.dtype}")
231
 
232
  # Run Cellpose segmentation
233
  masks, flows, styles = model.eval(region_np_processed, diameter=None, channels=[0, 0])
234
+ #print(f"DEBUG: After model.eval: masks type: {type(masks)}, shape: {masks.shape if hasattr(masks, 'shape') else 'N/A'}")
235
 
236
  # Update global region coords for info display
237
  current_region_coords = region_coords
238
 
239
  # Count unique cells
240
  cell_count = len(np.unique(masks)) - 1
241
+ #print(f"DEBUG: Cell count: {cell_count}")
242
 
243
  # Create basic segmentation overlay
244
  overlay = region_np_processed.copy().astype(np.float32)
 
262
 
263
  # Make viability section visible
264
  viability_section_update = gr.update(visible=True)
265
+ #print("DEBUG: segment_cells returning successfully, viability section visible.")
266
 
267
  # Return masks and original processed image for state components
268
  return cell_count, overlay_image, info_msg, viability_section_update, masks, region_np_processed
 
278
  Real-time viability assessment based on blue threshold
279
  """
280
 
281
+ #print("DEBUG: --- Starting update_viability_assessment ---")
282
+ #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}")
283
 
284
  if stored_masks is None or stored_image is None:
285
+ #print("DEBUG: Passed-in stored_masks or stored_image is None, returning 'Please run segmentation first'.")
286
  return None, 0, 0, "Please run segmentation first"
287
 
288
  try:
289
  # Debug information
290
+ '''unique_cells = np.unique(stored_masks)
291
  total_cells = len(unique_cells) - 1 # Subtract background
292
 
293
  debug_info = f"Debug - Total cells found: {total_cells}\n"
294
  debug_info += f"Image shape: {stored_image.shape}\n"
295
  debug_info += f"Masks shape: {stored_masks.shape}\n"
296
  debug_info += f"Unique mask values (first 10): {unique_cells[:10]}...\n"
297
+ '''
298
 
299
  # Create viability overlay
300
  viability_overlay, live_count, dead_count = create_viability_overlay(
 
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}"
313
+ #print("DEBUG: update_viability_assessment returning successfully.")
314
 
315
  return viability_overlay, live_count, dead_count, viability_info
316
 
 
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
 
 
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():
 
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
 
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
 
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