kaveh commited on
Commit
dee51a1
·
1 Parent(s): 3f08c92

more stable options

Browse files
Files changed (3) hide show
  1. app.py +0 -4
  2. ui/result_display.py +36 -9
  3. utils/segmentation.py +9 -1
app.py CHANGED
@@ -222,8 +222,6 @@ with st.sidebar:
222
  </div>
223
  """, unsafe_allow_html=True)
224
 
225
- st.markdown('<div class="sidebar-section"><span class="section-title">Model</span></div>', unsafe_allow_html=True)
226
-
227
  model_type = st.radio(
228
  "Model type",
229
  ["single_cell", "spheroid"],
@@ -280,8 +278,6 @@ with st.sidebar:
280
  except FileNotFoundError:
281
  st.error("config/substrate_settings.json not found")
282
 
283
- st.markdown('<div class="sidebar-section"><span class="section-title">Analysis</span></div>', unsafe_allow_html=True)
284
-
285
  batch_mode = st.toggle(
286
  "Batch mode",
287
  value=False,
 
222
  </div>
223
  """, unsafe_allow_html=True)
224
 
 
 
225
  model_type = st.radio(
226
  "Model type",
227
  ["single_cell", "spheroid"],
 
278
  except FileNotFoundError:
279
  st.error("config/substrate_settings.json not found")
280
 
 
 
281
  batch_mode = st.toggle(
282
  "Batch mode",
283
  value=False,
ui/result_display.py CHANGED
@@ -24,6 +24,9 @@ from ui.measure_tool import (
24
 
25
  # Histogram bar color (matches static/s2f_styles.css accent)
26
  _HISTOGRAM_ACCENT = "#0d9488"
 
 
 
27
 
28
 
29
  def render_batch_results(batch_results, colormap_name="Jet", display_mode="Default",
@@ -70,25 +73,49 @@ def render_batch_results(batch_results, colormap_name="Jet", display_mode="Defau
70
  csv_rows.append([os.path.splitext(key)[0]] + row[1:])
71
  st.markdown('<div class="result-label"><span class="result-badge input">INPUT</span> Bright-field images</div>', unsafe_allow_html=True)
72
  n_cols = min(5, len(batch_results))
73
- bf_cols = st.columns(n_cols)
74
- for i, r in enumerate(batch_results):
 
 
 
75
  img = r["img"]
76
  if img.ndim == 2:
77
  img_rgb = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
78
  else:
79
  img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
80
- with bf_cols[i % n_cols]:
81
  st.image(img_rgb, caption=r["key_img"], use_container_width=True)
82
  is_rescale_b = display_mode == "Range" and clip_max > clip_min and not (clip_min == 0 and clip_max == 1)
83
  st.markdown('<div class="result-label"><span class="result-badge output">OUTPUT</span> Predicted force maps</div>', unsafe_allow_html=True)
84
- hm_cols = st.columns(n_cols)
85
- for i, r in enumerate(batch_results):
86
  hm_rgb = heatmap_to_rgb_with_contour(
87
  r["_display_heatmap"], colormap_name,
88
  r.get("_cell_mask") if auto_cell_boundary else None,
89
  )
90
- with hm_cols[i % n_cols]:
91
  st.image(hm_rgb, caption=r["key_img"], use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  render_horizontal_colorbar(
93
  colormap_name, clip_min, clip_max, is_rescale_b,
94
  caption=(
@@ -114,7 +141,7 @@ def render_batch_results(batch_results, colormap_name="Jet", display_mode="Defau
114
  if len(vals) > 0:
115
  fig = go.Figure(data=[go.Histogram(x=vals, nbinsx=50, marker_color=_HISTOGRAM_ACCENT)])
116
  fig.update_layout(
117
- height=220, margin=dict(l=40, r=20, t=10, b=40),
118
  xaxis_title="Force value", yaxis_title="Count",
119
  showlegend=False,
120
  )
@@ -214,7 +241,7 @@ def render_result_display(img, raw_heatmap, display_heatmap, pixel_sum, force, k
214
  colorbar=colorbar_cfg), row=1, col=2)
215
  add_cell_contour_to_fig(fig_pl, cell_mask, row=1, col=2)
216
  fig_pl.update_layout(
217
- height=400,
218
  margin=dict(l=10, r=10, t=10, b=10),
219
  xaxis=dict(scaleanchor="y", scaleratio=1),
220
  xaxis2=dict(scaleanchor="y2", scaleratio=1),
@@ -264,7 +291,7 @@ def render_result_display(img, raw_heatmap, display_heatmap, pixel_sum, force, k
264
  st.markdown("**Histogram**")
265
  hist_fig = go.Figure(data=[go.Histogram(x=vals, nbinsx=50, marker_color=_HISTOGRAM_ACCENT)])
266
  hist_fig.update_layout(
267
- height=220, margin=dict(l=40, r=20, t=20, b=40),
268
  xaxis_title="Force value", yaxis_title="Count",
269
  showlegend=False,
270
  )
 
24
 
25
  # Histogram bar color (matches static/s2f_styles.css accent)
26
  _HISTOGRAM_ACCENT = "#0d9488"
27
+ _RESULT_FIG_HEIGHT = 320
28
+ _HISTOGRAM_HEIGHT = 180
29
+ _BATCH_PREVIEW_LIMIT = 3
30
 
31
 
32
  def render_batch_results(batch_results, colormap_name="Jet", display_mode="Default",
 
73
  csv_rows.append([os.path.splitext(key)[0]] + row[1:])
74
  st.markdown('<div class="result-label"><span class="result-badge input">INPUT</span> Bright-field images</div>', unsafe_allow_html=True)
75
  n_cols = min(5, len(batch_results))
76
+ preview_count = min(_BATCH_PREVIEW_LIMIT, len(batch_results))
77
+ preview_results = batch_results[:preview_count]
78
+ remaining_results = batch_results[preview_count:]
79
+ bf_cols = st.columns(min(n_cols, preview_count))
80
+ for i, r in enumerate(preview_results):
81
  img = r["img"]
82
  if img.ndim == 2:
83
  img_rgb = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
84
  else:
85
  img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
86
+ with bf_cols[i % min(n_cols, preview_count)]:
87
  st.image(img_rgb, caption=r["key_img"], use_container_width=True)
88
  is_rescale_b = display_mode == "Range" and clip_max > clip_min and not (clip_min == 0 and clip_max == 1)
89
  st.markdown('<div class="result-label"><span class="result-badge output">OUTPUT</span> Predicted force maps</div>', unsafe_allow_html=True)
90
+ hm_cols = st.columns(min(n_cols, preview_count))
91
+ for i, r in enumerate(preview_results):
92
  hm_rgb = heatmap_to_rgb_with_contour(
93
  r["_display_heatmap"], colormap_name,
94
  r.get("_cell_mask") if auto_cell_boundary else None,
95
  )
96
+ with hm_cols[i % min(n_cols, preview_count)]:
97
  st.image(hm_rgb, caption=r["key_img"], use_container_width=True)
98
+ if remaining_results:
99
+ with st.expander(f"Show remaining batch previews ({len(remaining_results)})", expanded=False):
100
+ st.markdown('<div class="result-label"><span class="result-badge input">INPUT</span> Remaining bright-field images</div>', unsafe_allow_html=True)
101
+ rem_bf_cols = st.columns(min(5, len(remaining_results)))
102
+ for i, r in enumerate(remaining_results):
103
+ img = r["img"]
104
+ if img.ndim == 2:
105
+ img_rgb = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
106
+ else:
107
+ img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
108
+ with rem_bf_cols[i % min(5, len(remaining_results))]:
109
+ st.image(img_rgb, caption=r["key_img"], use_container_width=True)
110
+ st.markdown('<div class="result-label"><span class="result-badge output">OUTPUT</span> Remaining predicted force maps</div>', unsafe_allow_html=True)
111
+ rem_hm_cols = st.columns(min(5, len(remaining_results)))
112
+ for i, r in enumerate(remaining_results):
113
+ hm_rgb = heatmap_to_rgb_with_contour(
114
+ r["_display_heatmap"], colormap_name,
115
+ r.get("_cell_mask") if auto_cell_boundary else None,
116
+ )
117
+ with rem_hm_cols[i % min(5, len(remaining_results))]:
118
+ st.image(hm_rgb, caption=r["key_img"], use_container_width=True)
119
  render_horizontal_colorbar(
120
  colormap_name, clip_min, clip_max, is_rescale_b,
121
  caption=(
 
141
  if len(vals) > 0:
142
  fig = go.Figure(data=[go.Histogram(x=vals, nbinsx=50, marker_color=_HISTOGRAM_ACCENT)])
143
  fig.update_layout(
144
+ height=_HISTOGRAM_HEIGHT, margin=dict(l=40, r=20, t=10, b=40),
145
  xaxis_title="Force value", yaxis_title="Count",
146
  showlegend=False,
147
  )
 
241
  colorbar=colorbar_cfg), row=1, col=2)
242
  add_cell_contour_to_fig(fig_pl, cell_mask, row=1, col=2)
243
  fig_pl.update_layout(
244
+ height=_RESULT_FIG_HEIGHT,
245
  margin=dict(l=10, r=10, t=10, b=10),
246
  xaxis=dict(scaleanchor="y", scaleratio=1),
247
  xaxis2=dict(scaleanchor="y2", scaleratio=1),
 
291
  st.markdown("**Histogram**")
292
  hist_fig = go.Figure(data=[go.Histogram(x=vals, nbinsx=50, marker_color=_HISTOGRAM_ACCENT)])
293
  hist_fig.update_layout(
294
+ height=_HISTOGRAM_HEIGHT, margin=dict(l=40, r=20, t=20, b=40),
295
  xaxis_title="Force value", yaxis_title="Count",
296
  showlegend=False,
297
  )
utils/segmentation.py CHANGED
@@ -45,7 +45,15 @@ def estimate_cell_mask(heatmap, sigma=2, min_size=200, exclude_full_image=True,
45
  # Morphological cleanup
46
  mask = closing(mask, disk(5)).astype(np.uint8)
47
  mask = opening(mask, disk(3)).astype(np.uint8)
48
- mask = remove_small_objects(mask.astype(bool), min_size=min_size).astype(np.uint8)
 
 
 
 
 
 
 
 
49
 
50
  # Select component(s): optionally exclude full-image background, then merge
51
  # all significant components (handles multiple disconnected force regions)
 
45
  # Morphological cleanup
46
  mask = closing(mask, disk(5)).astype(np.uint8)
47
  mask = opening(mask, disk(3)).astype(np.uint8)
48
+ mask_bool = mask.astype(bool)
49
+ min_size_int = max(int(min_size), 0)
50
+ # skimage >=0.26 deprecates `min_size` in favor of `max_size` (inclusive threshold).
51
+ # Use `min_size - 1` so behavior stays equivalent to the prior strict `< min_size` rule.
52
+ try:
53
+ mask_bool = remove_small_objects(mask_bool, max_size=max(min_size_int - 1, 0))
54
+ except TypeError:
55
+ mask_bool = remove_small_objects(mask_bool, min_size=min_size_int)
56
+ mask = mask_bool.astype(np.uint8)
57
 
58
  # Select component(s): optionally exclude full-image background, then merge
59
  # all significant components (handles multiple disconnected force regions)