nmariotto commited on
Commit
c84c4b0
·
verified ·
1 Parent(s): 1391243

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +45 -25
app.py CHANGED
@@ -17,12 +17,12 @@ import time
17
 
18
  st.set_page_config(page_title="Scratch Assay Segmentation", layout="wide")
19
 
20
- APP_VERSION = "2.4"
21
  DEFAULT_IMGSZ = 640
22
 
23
  MODEL_OPTIONS = {
24
- "2.4": "24.pt",
25
- "3.7": "37.pt",
26
  }
27
 
28
 
@@ -97,7 +97,13 @@ def find_or_create_folder(folder_name, parent=None):
97
  query = f"name='{folder_name}' and mimeType='application/vnd.google-apps.folder' and trashed=false"
98
  if parent:
99
  query += f" and '{parent}' in parents"
100
- results = drive_service.files().list(q=query, spaces="drive", fields="files(id, name)").execute()
 
 
 
 
 
 
101
  folders = results.get("files", [])
102
  if folders:
103
  return folders[0]["id"]
@@ -105,6 +111,7 @@ def find_or_create_folder(folder_name, parent=None):
105
  file_metadata = {"name": folder_name, "mimeType": "application/vnd.google-apps.folder"}
106
  if parent:
107
  file_metadata["parents"] = [parent]
 
108
  file = drive_service.files().create(body=file_metadata, fields="id").execute()
109
  return file.get("id")
110
 
@@ -122,7 +129,7 @@ def process_image(uploaded_file, model, model_confidence, fov_um=None, pixel_siz
122
  image = Image.open(uploaded_file).convert("RGB")
123
  image_np = np.array(image)
124
 
125
- width_px, _ = image.size
126
 
127
  effective_pixel_size_um = None
128
  if pixel_size_um is not None and pixel_size_um > 0:
@@ -141,6 +148,8 @@ def process_image(uploaded_file, model, model_confidence, fov_um=None, pixel_siz
141
  "SemSegmentacao": True,
142
  "Exibir": image,
143
  "Original": get_image_bytes(image),
 
 
144
  }
145
 
146
  result = results[0]
@@ -153,6 +162,8 @@ def process_image(uploaded_file, model, model_confidence, fov_um=None, pixel_siz
153
  "SemSegmentacao": True,
154
  "Exibir": image,
155
  "Original": get_image_bytes(image),
 
 
156
  }
157
 
158
  best_idx = 0
@@ -168,10 +179,12 @@ def process_image(uploaded_file, model, model_confidence, fov_um=None, pixel_siz
168
  "SemSegmentacao": True,
169
  "Exibir": image,
170
  "Original": get_image_bytes(image),
 
 
171
  }
172
 
173
  points = [
174
- {"x": float(x * width_px), "y": float(y * image.height)}
175
  for x, y in contour_norm
176
  ]
177
 
@@ -218,23 +231,30 @@ def process_image(uploaded_file, model, model_confidence, fov_um=None, pixel_siz
218
  return None
219
 
220
 
221
- def save_feedback(result, avaliacao, observacao):
222
  image_name = result["Imagem"]
 
223
 
224
  # 1) Sheet
225
- sheet.append_row([image_name, avaliacao, observacao])
226
 
227
  # 2) Drive curation
228
  if avaliacao in ["Acceptable", "Bad", "No segmentation"]:
229
- sufixo = "aceitavel" if avaliacao == "Acceptable" else "ruim" if avaliacao == "Bad" else "sem_segmentacao"
 
 
 
 
 
230
  parent_folder = find_or_create_folder("Feedback Segmentacoes")
231
- subfolder = find_or_create_folder(image_name.replace(".png", ""), parent_folder)
 
232
 
233
  resized_original = resize_image(result["Exibir"])
234
  buf = BytesIO()
235
  resized_original.save(buf, format="PNG")
236
  buf.seek(0)
237
- upload_to_drive(buf, f"original_{sufixo}.png", subfolder)
238
 
239
  if avaliacao != "No segmentation" and result.get("Segmentada") and result.get("Poligono"):
240
  resized_segmented = resize_image(Image.open(BytesIO(result["Segmentada"].getvalue())))
@@ -244,7 +264,11 @@ def save_feedback(result, avaliacao, observacao):
244
  buf = BytesIO()
245
  img_obj.save(buf, format="PNG")
246
  buf.seek(0)
247
- upload_to_drive(buf, f"{nome}_{sufixo}.png", subfolder)
 
 
 
 
248
 
249
 
250
  def render_metrics(result):
@@ -258,7 +282,7 @@ def render_metrics(result):
258
  st.markdown(f"- {area_um2:.2f} µm²")
259
 
260
 
261
- def render_feedback_block(result, prefix_key=""):
262
  st.markdown("#### Segmentation quality feedback")
263
  st.caption("User evaluation used for future model refinement.")
264
 
@@ -273,7 +297,7 @@ def render_feedback_block(result, prefix_key=""):
273
  key=f"{prefix_key}obs_{result['Imagem']}",
274
  )
275
  if st.button("Save feedback", key=f"{prefix_key}btn_{result['Imagem']}"):
276
- save_feedback(result, avaliacao, observacao)
277
  st.success("Feedback saved successfully.")
278
 
279
 
@@ -281,11 +305,10 @@ def render_feedback_block(result, prefix_key=""):
281
  # Layout / UI
282
  # =========================
283
  st.title("Scratch Assay Segmentation Tool")
284
- st.caption(f"Version {APP_VERSION} · Deep learning–based wound closure segmentation")
285
 
286
  st.markdown("---")
287
 
288
- # Upload block
289
  st.markdown("### Input")
290
  col_input_1, col_input_2 = st.columns([2, 1])
291
 
@@ -297,7 +320,8 @@ with col_input_2:
297
 
298
  model = load_model(MODEL_OPTIONS[selected_model_label])
299
 
300
- # Advanced settings (collapsed by default)
 
301
  with st.expander("⚙️ Advanced Settings", expanded=False):
302
  model_confidence = st.slider("Model confidence (%)", 20, 100, 80)
303
  st.markdown(
@@ -352,15 +376,14 @@ if upload_option == "Single image":
352
  fov_um=fov_um,
353
  pixel_size_um=pixel_size_um,
354
  )
 
355
  if result:
356
  results.append(result)
357
 
358
  st.markdown(f"#### {result['Imagem']}")
359
 
360
  if result["SemSegmentacao"]:
361
- col = st.columns(1)[0]
362
- with col:
363
- st.image(result["Exibir"], caption="Original", use_container_width=True)
364
  st.warning("No segmentation was detected for this image.")
365
  else:
366
  col1, col2, col3 = st.columns(3)
@@ -382,7 +405,7 @@ if upload_option == "Single image":
382
  )
383
 
384
  st.markdown("---")
385
- render_feedback_block(result, prefix_key="single_")
386
 
387
 
388
  # =========================
@@ -408,7 +431,6 @@ elif upload_option == "Image folder":
408
  pixel_size_um=pixel_size_um,
409
  )
410
 
411
- # Local inference is more stable with a single worker.
412
  with ThreadPoolExecutor(max_workers=1) as executor:
413
  processed = list(executor.map(process_wrapper, uploaded_files))
414
 
@@ -442,15 +464,13 @@ elif upload_option == "Image folder":
442
 
443
  render_metrics(result)
444
 
445
- # Build ZIP
446
  zip_file.writestr(f"segmentada_{result['Imagem']}.png", result["Segmentada"].getvalue())
447
  zip_file.writestr(f"poligono_{result['Imagem']}.png", result["Poligono"].getvalue())
448
 
449
- render_feedback_block(result, prefix_key="folder_")
450
 
451
  zip_images_buffer.seek(0)
452
 
453
- # Summary table + exports
454
  if results:
455
  st.markdown("---")
456
  st.markdown("### Quantitative results")
 
17
 
18
  st.set_page_config(page_title="Scratch Assay Segmentation", layout="wide")
19
 
20
+ APP_VERSION = "2.5"
21
  DEFAULT_IMGSZ = 640
22
 
23
  MODEL_OPTIONS = {
24
+ "24": "24.pt",
25
+ "37": "37.pt",
26
  }
27
 
28
 
 
97
  query = f"name='{folder_name}' and mimeType='application/vnd.google-apps.folder' and trashed=false"
98
  if parent:
99
  query += f" and '{parent}' in parents"
100
+
101
+ results = drive_service.files().list(
102
+ q=query,
103
+ spaces="drive",
104
+ fields="files(id, name)",
105
+ ).execute()
106
+
107
  folders = results.get("files", [])
108
  if folders:
109
  return folders[0]["id"]
 
111
  file_metadata = {"name": folder_name, "mimeType": "application/vnd.google-apps.folder"}
112
  if parent:
113
  file_metadata["parents"] = [parent]
114
+
115
  file = drive_service.files().create(body=file_metadata, fields="id").execute()
116
  return file.get("id")
117
 
 
129
  image = Image.open(uploaded_file).convert("RGB")
130
  image_np = np.array(image)
131
 
132
+ width_px, height_px = image.size
133
 
134
  effective_pixel_size_um = None
135
  if pixel_size_um is not None and pixel_size_um > 0:
 
148
  "SemSegmentacao": True,
149
  "Exibir": image,
150
  "Original": get_image_bytes(image),
151
+ "Segmentada": None,
152
+ "Poligono": None,
153
  }
154
 
155
  result = results[0]
 
162
  "SemSegmentacao": True,
163
  "Exibir": image,
164
  "Original": get_image_bytes(image),
165
+ "Segmentada": None,
166
+ "Poligono": None,
167
  }
168
 
169
  best_idx = 0
 
179
  "SemSegmentacao": True,
180
  "Exibir": image,
181
  "Original": get_image_bytes(image),
182
+ "Segmentada": None,
183
+ "Poligono": None,
184
  }
185
 
186
  points = [
187
+ {"x": float(x * width_px), "y": float(y * height_px)}
188
  for x, y in contour_norm
189
  ]
190
 
 
231
  return None
232
 
233
 
234
+ def save_feedback(result, avaliacao, observacao, selected_model_label):
235
  image_name = result["Imagem"]
236
+ image_base_name = image_name.rsplit(".", 1)[0]
237
 
238
  # 1) Sheet
239
+ sheet.append_row([image_name, avaliacao, observacao, selected_model_label, APP_VERSION])
240
 
241
  # 2) Drive curation
242
  if avaliacao in ["Acceptable", "Bad", "No segmentation"]:
243
+ sufixo = (
244
+ "aceitavel" if avaliacao == "Acceptable"
245
+ else "ruim" if avaliacao == "Bad"
246
+ else "sem_segmentacao"
247
+ )
248
+
249
  parent_folder = find_or_create_folder("Feedback Segmentacoes")
250
+ model_folder = find_or_create_folder(f"model_{selected_model_label}", parent_folder)
251
+ subfolder = find_or_create_folder(image_base_name, model_folder)
252
 
253
  resized_original = resize_image(result["Exibir"])
254
  buf = BytesIO()
255
  resized_original.save(buf, format="PNG")
256
  buf.seek(0)
257
+ upload_to_drive(buf, f"original_model_{selected_model_label}_v{APP_VERSION}_{sufixo}.png", subfolder)
258
 
259
  if avaliacao != "No segmentation" and result.get("Segmentada") and result.get("Poligono"):
260
  resized_segmented = resize_image(Image.open(BytesIO(result["Segmentada"].getvalue())))
 
264
  buf = BytesIO()
265
  img_obj.save(buf, format="PNG")
266
  buf.seek(0)
267
+ upload_to_drive(
268
+ buf,
269
+ f"{nome}_model_{selected_model_label}_v{APP_VERSION}_{sufixo}.png",
270
+ subfolder,
271
+ )
272
 
273
 
274
  def render_metrics(result):
 
282
  st.markdown(f"- {area_um2:.2f} µm²")
283
 
284
 
285
+ def render_feedback_block(result, selected_model_label, prefix_key=""):
286
  st.markdown("#### Segmentation quality feedback")
287
  st.caption("User evaluation used for future model refinement.")
288
 
 
297
  key=f"{prefix_key}obs_{result['Imagem']}",
298
  )
299
  if st.button("Save feedback", key=f"{prefix_key}btn_{result['Imagem']}"):
300
+ save_feedback(result, avaliacao, observacao, selected_model_label)
301
  st.success("Feedback saved successfully.")
302
 
303
 
 
305
  # Layout / UI
306
  # =========================
307
  st.title("Scratch Assay Segmentation Tool")
308
+ st.caption(f"Platform version {APP_VERSION}")
309
 
310
  st.markdown("---")
311
 
 
312
  st.markdown("### Input")
313
  col_input_1, col_input_2 = st.columns([2, 1])
314
 
 
320
 
321
  model = load_model(MODEL_OPTIONS[selected_model_label])
322
 
323
+ st.caption(f"Selected model checkpoint: {selected_model_label}")
324
+
325
  with st.expander("⚙️ Advanced Settings", expanded=False):
326
  model_confidence = st.slider("Model confidence (%)", 20, 100, 80)
327
  st.markdown(
 
376
  fov_um=fov_um,
377
  pixel_size_um=pixel_size_um,
378
  )
379
+
380
  if result:
381
  results.append(result)
382
 
383
  st.markdown(f"#### {result['Imagem']}")
384
 
385
  if result["SemSegmentacao"]:
386
+ st.image(result["Exibir"], caption="Original", use_container_width=True)
 
 
387
  st.warning("No segmentation was detected for this image.")
388
  else:
389
  col1, col2, col3 = st.columns(3)
 
405
  )
406
 
407
  st.markdown("---")
408
+ render_feedback_block(result, selected_model_label, prefix_key="single_")
409
 
410
 
411
  # =========================
 
431
  pixel_size_um=pixel_size_um,
432
  )
433
 
 
434
  with ThreadPoolExecutor(max_workers=1) as executor:
435
  processed = list(executor.map(process_wrapper, uploaded_files))
436
 
 
464
 
465
  render_metrics(result)
466
 
 
467
  zip_file.writestr(f"segmentada_{result['Imagem']}.png", result["Segmentada"].getvalue())
468
  zip_file.writestr(f"poligono_{result['Imagem']}.png", result["Poligono"].getvalue())
469
 
470
+ render_feedback_block(result, selected_model_label, prefix_key="folder_")
471
 
472
  zip_images_buffer.seek(0)
473
 
 
474
  if results:
475
  st.markdown("---")
476
  st.markdown("### Quantitative results")