LiangLabUMB commited on
Commit
78e901a
·
verified ·
1 Parent(s): 4deb2a3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +17 -112
app.py CHANGED
@@ -45,93 +45,6 @@ def safe_resize(image_np):
45
 
46
  return cv2.resize(image_np, (new_w, new_h), interpolation=cv2.INTER_AREA)
47
 
48
-
49
- def extract_region_from_editor(editor_data):
50
- """Extract selected region from ImageEditor data (mobile-safe)."""
51
- if editor_data is None:
52
- return None, None
53
-
54
- # CASE 1 — Editor returns a PIL Image directly
55
- if isinstance(editor_data, Image.Image):
56
- return np.array(editor_data), None
57
-
58
- # CASE 2 — Editor returns a NumPy array
59
- if isinstance(editor_data, np.ndarray):
60
- return editor_data, None
61
-
62
- # CASE 3 — Mobile browser returns dict structure
63
- if isinstance(editor_data, dict):
64
-
65
- # --- Get background from multiple possible formats ---
66
- bg = editor_data.get("background")
67
-
68
- if bg is None:
69
- return None, None
70
-
71
- # Mobile ImageEditor sometimes wraps image like: {'image': base64, 'mask': None}
72
- if isinstance(bg, dict) and "image" in bg:
73
- try:
74
- # Decode base64 → PIL → NumPy
75
- img_bytes = io.BytesIO(base64.b64decode(bg["image"].split(",")[-1]))
76
- bg_img = Image.open(img_bytes).convert("RGB")
77
- background_np = np.array(bg_img)
78
- except Exception:
79
- return None, None
80
-
81
- # Desktop / some mobile: bg is a PIL image
82
- elif isinstance(bg, Image.Image):
83
- background_np = np.array(bg)
84
-
85
- # Final fallback
86
- else:
87
- try:
88
- background_np = np.array(bg)
89
- except Exception:
90
- return None, None
91
-
92
- # --- Layers (selection mask) ---
93
- layers = editor_data.get("layers", [])
94
- if layers:
95
- sel = layers[0]
96
-
97
- # Convert to NumPy if needed
98
- if isinstance(sel, Image.Image):
99
- sel_np = np.array(sel)
100
- elif isinstance(sel, dict) and "image" in sel:
101
- try:
102
- sel_bytes = io.BytesIO(base64.b64decode(sel["image"].split(",")[-1]))
103
- sel_np = np.array(Image.open(sel_bytes).convert("RGBA"))
104
- except Exception:
105
- sel_np = None
106
- else:
107
- try:
108
- sel_np = np.array(sel)
109
- except Exception:
110
- sel_np = None
111
-
112
- # If selection exists: compute bounding box
113
- if sel_np is not None:
114
- if sel_np.ndim == 3:
115
- if sel_np.shape[2] == 4:
116
- mask = sel_np[:, :, 3] > 0
117
- else:
118
- mask = np.any(sel_np > 0, axis=2)
119
- else:
120
- mask = sel_np > 0
121
-
122
- coords = np.where(mask)
123
- if coords[0].size > 0:
124
- y0, y1 = coords[0].min(), coords[0].max()
125
- x0, x1 = coords[1].min(), coords[1].max()
126
- return background_np[y0:y1 + 1, x0:x1 + 1], (x0, y0, x1, y1)
127
-
128
- # No selection → return full image
129
- return background_np, None
130
-
131
- # CASE 4 — Unknown structure
132
- return None, None
133
-
134
-
135
  def classify_cells_by_blueness(image_np, masks, blue_threshold):
136
  """
137
  Classify cells as dead (blue) or alive based on single blueness metric
@@ -218,11 +131,10 @@ def measure_confluency(masks, image_np):
218
 
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)
@@ -233,19 +145,14 @@ def run_segmentation_editor(editor_data, model_choice):
233
  model = models.CellposeModel(gpu=True, pretrained_model=model_path)
234
  loaded_models[model_filename] = model
235
 
236
- region_np, region_coords = extract_region_from_editor(editor_data)
237
 
238
- if region_np is None:
239
- return 0, None, f"No image provided.", gr.update(visible=False), None, None, 0.0
240
-
241
- # Enforce mobile-safe size limit immediately
242
- region_np = safe_resize(region_np)
243
 
244
  # Process image format to RGB
245
- if len(region_np.shape) == 2:
246
  processed_image_np = cv2.cvtColor(region_np, cv2.COLOR_GRAY2RGB)
247
- elif len(region_np.shape) == 3 and region_np.shape[2] == 4:
248
- processed_image_np = cv2.cvtColor(region_np, cv2.COLOR_RGBA2RGB)
249
  else:
250
  processed_image_np = region_np
251
 
@@ -268,8 +175,6 @@ def run_segmentation_editor(editor_data, model_choice):
268
 
269
  info_msg = f"Segmentation complete! Found {cell_count} cells.\n"
270
  info_msg += f"Confluency: {confluency:.1f}%\n"
271
- if region_coords:
272
- info_msg += f"Processed region: {region_coords[0]},{region_coords[1]} to {region_coords[2]},{region_coords[3]}\n"
273
  info_msg += f"Now adjust the Blue Threshold for viability assessment."
274
 
275
  # Return initial segmentation display and state variables (packed for Gradio State)
@@ -339,18 +244,18 @@ with gr.Blocks(
339
  masks_state = gr.State(value=None)
340
  image_state = gr.State(value=None)
341
 
342
- with gr.Tab("Image Editor (Draw Selection)"):
343
- gr.Markdown("### Draw selection and run segmentation")
344
 
345
  with gr.Row():
346
  with gr.Column():
347
- image_editor = gr.ImageEditor(
348
- label="Draw selection on image",
349
  type="pil",
 
350
  image_mode="RGB",
351
- height=512, # limit canvas size on client (important for mobile)
352
- brush=gr.Brush(colors=["#ff0000"], color_mode="fixed", default_size=20),
353
- eraser=gr.Eraser(default_size=20)
354
  )
355
  model_dropdown1 = gr.Dropdown(
356
  choices=list(MODEL_OPTIONS.keys()),
@@ -391,8 +296,8 @@ with gr.Blocks(
391
 
392
  # Event handlers
393
  segment_btn1.click(
394
- fn=run_segmentation_editor,
395
- inputs=[image_editor, model_dropdown1],
396
  outputs=[cell_count_output1, overlay_output1, info_output1, viability_section1, masks_state, image_state, confluency_output1]
397
  ).then( # Chain the initial viability assessment after segmentation
398
  fn=update_viability_realtime,
 
45
 
46
  return cv2.resize(image_np, (new_w, new_h), interpolation=cv2.INTER_AREA)
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  def classify_cells_by_blueness(image_np, masks, blue_threshold):
49
  """
50
  Classify cells as dead (blue) or alive based on single blueness metric
 
131
 
132
 
133
  @spaces.GPU
134
+ def run_segmentation(image, model_choice):
135
+ image_np = np.array(image)
136
+ image_np = safe_resize(image_np)
137
+
 
138
  try:
139
  model_filename = MODEL_OPTIONS[model_choice]
140
  model_path = hf_hub_download(repo_id=HF_REPO_ID, filename=model_filename)
 
145
  model = models.CellposeModel(gpu=True, pretrained_model=model_path)
146
  loaded_models[model_filename] = model
147
 
 
148
 
149
+
 
 
 
 
150
 
151
  # Process image format to RGB
152
+ if len(image_np.shape) == 2:
153
  processed_image_np = cv2.cvtColor(region_np, cv2.COLOR_GRAY2RGB)
154
+ elif len(image_np.shape) == 3 and image_np.shape[2] == 4:
155
+ processed_image_np = cv2.cvtColor(image_np, cv2.COLOR_RGBA2RGB)
156
  else:
157
  processed_image_np = region_np
158
 
 
175
 
176
  info_msg = f"Segmentation complete! Found {cell_count} cells.\n"
177
  info_msg += f"Confluency: {confluency:.1f}%\n"
 
 
178
  info_msg += f"Now adjust the Blue Threshold for viability assessment."
179
 
180
  # Return initial segmentation display and state variables (packed for Gradio State)
 
244
  masks_state = gr.State(value=None)
245
  image_state = gr.State(value=None)
246
 
247
+ with gr.Tab("Cell Quantification"):
248
+ gr.Markdown("Run segmentation")
249
 
250
  with gr.Row():
251
  with gr.Column():
252
+ img_input=gr.Image(
 
253
  type="pil",
254
+ label="Microscopy image",
255
  image_mode="RGB",
256
+ height=512
257
+ )
258
+
259
  )
260
  model_dropdown1 = gr.Dropdown(
261
  choices=list(MODEL_OPTIONS.keys()),
 
296
 
297
  # Event handlers
298
  segment_btn1.click(
299
+ fn=run_segmentation,
300
+ inputs=[img_input, model_dropdown1],
301
  outputs=[cell_count_output1, overlay_output1, info_output1, viability_section1, masks_state, image_state, confluency_output1]
302
  ).then( # Chain the initial viability assessment after segmentation
303
  fn=update_viability_realtime,