kaveh commited on
Commit
b01d4cd
Β·
1 Parent(s): dc31c70

S2FApp: record input image SHA-256 in reports

Browse files
Files changed (2) hide show
  1. S2FApp/app.py +20 -1
  2. S2FApp/utils/report.py +1 -0
S2FApp/app.py CHANGED
@@ -1,6 +1,7 @@
1
  """
2
  Shape2Force (S2F) - GUI for force map prediction from bright field microscopy images.
3
  """
 
4
  import os
5
  import sys
6
  import traceback
@@ -163,6 +164,22 @@ def get_cached_sample_thumbnails(model_type, sample_folder, sample_files):
163
  return cache[cache_key]
164
 
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  def _render_sample_selector(model_type, batch_mode):
167
  """
168
  Render sample image selector (Example mode). Returns (img, imgs_batch, selected_sample, selected_samples).
@@ -388,6 +405,7 @@ if batch_mode:
388
  uploaded_list = uploaded_list[:BATCH_MAX_IMAGES]
389
  for u in uploaded_list:
390
  bytes_data = u.read()
 
391
  nparr = np.frombuffer(bytes_data, np.uint8)
392
  decoded = cv2.imdecode(nparr, cv2.IMREAD_GRAYSCALE)
393
  if decoded is not None:
@@ -405,6 +423,7 @@ else:
405
  )
406
  if uploaded:
407
  bytes_data = uploaded.read()
 
408
  nparr = np.frombuffer(bytes_data, np.uint8)
409
  img = cv2.imdecode(nparr, cv2.IMREAD_GRAYSCALE)
410
  uploaded.seek(0)
@@ -468,7 +487,7 @@ def _build_info(predictor, key_img, img, pred_time, image_um_per_px, display_mod
468
  **predictor.substrate_channels(sub_val_for_info, substrate_config if use_manual else None)}
469
  rng = (clip_min * smax, clip_max * smax) if display_mode == "Range" else (0.0, smax)
470
  return {
471
- "input_file": key_img or "image", "time": pred_time, "model": predictor.describe(), "substrate": sub,
472
  "image_shape": tuple(img.shape[:2]), "image_um_per_px": image_um_per_px, "stress_max_pa": smax,
473
  "display": {"mode": display_mode, "range_pa": rng, "colormap": colormap_name},
474
  "auto_boundary": bool(auto_cell_boundary), "app_version": APP_VERSION,
 
1
  """
2
  Shape2Force (S2F) - GUI for force map prediction from bright field microscopy images.
3
  """
4
+ import hashlib
5
  import os
6
  import sys
7
  import traceback
 
164
  return cache[cache_key]
165
 
166
 
167
+ def _remember_input_hash(name, data):
168
+ """SHA-256 of the uploaded file bytes, so the report identifies the exact input image."""
169
+ st.session_state.setdefault("input_sha256", {})[name] = hashlib.sha256(data).hexdigest()
170
+
171
+
172
+ def _input_sha256(name):
173
+ h = st.session_state.get("input_sha256", {}).get(name)
174
+ if h is None and name:
175
+ p = os.path.join(get_sample_folder(S2F_ROOT, model_type), name)
176
+ if os.path.isfile(p):
177
+ with open(p, "rb") as f:
178
+ h = hashlib.sha256(f.read()).hexdigest()
179
+ st.session_state.setdefault("input_sha256", {})[name] = h
180
+ return h or "–"
181
+
182
+
183
  def _render_sample_selector(model_type, batch_mode):
184
  """
185
  Render sample image selector (Example mode). Returns (img, imgs_batch, selected_sample, selected_samples).
 
405
  uploaded_list = uploaded_list[:BATCH_MAX_IMAGES]
406
  for u in uploaded_list:
407
  bytes_data = u.read()
408
+ _remember_input_hash(u.name, bytes_data)
409
  nparr = np.frombuffer(bytes_data, np.uint8)
410
  decoded = cv2.imdecode(nparr, cv2.IMREAD_GRAYSCALE)
411
  if decoded is not None:
 
423
  )
424
  if uploaded:
425
  bytes_data = uploaded.read()
426
+ _remember_input_hash(uploaded.name, bytes_data)
427
  nparr = np.frombuffer(bytes_data, np.uint8)
428
  img = cv2.imdecode(nparr, cv2.IMREAD_GRAYSCALE)
429
  uploaded.seek(0)
 
487
  **predictor.substrate_channels(sub_val_for_info, substrate_config if use_manual else None)}
488
  rng = (clip_min * smax, clip_max * smax) if display_mode == "Range" else (0.0, smax)
489
  return {
490
+ "input_file": key_img or "image", "input_sha256": _input_sha256(key_img), "time": pred_time, "model": predictor.describe(), "substrate": sub,
491
  "image_shape": tuple(img.shape[:2]), "image_um_per_px": image_um_per_px, "stress_max_pa": smax,
492
  "display": {"mode": display_mode, "range_pa": rng, "colormap": colormap_name},
493
  "auto_boundary": bool(auto_cell_boundary), "app_version": APP_VERSION,
S2FApp/utils/report.py CHANGED
@@ -90,6 +90,7 @@ def info_rows(info):
90
  m = info.get("model", {})
91
  rows = [
92
  ("Input file", info.get("input_file", "–")),
 
93
  ("Prediction time (UTC)", info.get("time", "–")),
94
  ("Model type", {"single_cell": "Single cell", "spheroid": "Spheroid"}.get(m.get("model_type"), "–")),
95
  ("Architecture", ARCH_LABELS.get(m.get("architecture"), m.get("architecture", "–"))),
 
90
  m = info.get("model", {})
91
  rows = [
92
  ("Input file", info.get("input_file", "–")),
93
+ ("Input file SHA-256", info.get("input_sha256", "–")),
94
  ("Prediction time (UTC)", info.get("time", "–")),
95
  ("Model type", {"single_cell": "Single cell", "spheroid": "Spheroid"}.get(m.get("model_type"), "–")),
96
  ("Architecture", ARCH_LABELS.get(m.get("architecture"), m.get("architecture", "–"))),