S2FApp: record input image SHA-256 in reports
Browse files- S2FApp/app.py +20 -1
- 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", "β"))),
|