Spaces:
Sleeping
Sleeping
| """ | |
| Generate a synthetic two-arm demo case set (data/cases.json + placeholder SRH-like images) so the app runs | |
| with ZERO real data. Replace with real cases for the study: put images + cases.json in a PRIVATE HF dataset | |
| and set CASES_DATASET. | |
| Case schema (data-driven; the app reads exactly this): | |
| Arm A (realism): {"case_id": "...", "arm": "A", "image": "img/x.png", "true_source": "real"|"synthetic"|"synthetic_ungated"} | |
| Arm B (category): {"case_id": "...", "arm": "B", "cluster_id": "...", "images": ["img/..","img/.."], | |
| "is_control": "none"|"positive"|"negative"} | |
| - true_source (A) and is_control/cluster_id (B) are hidden from the reader; used only in backend analysis. | |
| - The reader is blinded; the app shuffles item order per reader. | |
| """ | |
| import json | |
| import random | |
| from pathlib import Path | |
| from PIL import Image, ImageDraw, ImageFilter | |
| HERE = Path(__file__).parent | |
| DATA = HERE / "data" | |
| IMG = DATA / "img" | |
| IMG.mkdir(parents=True, exist_ok=True) | |
| W = H = 300 | |
| def srh_patch(seed, family=0): | |
| """Pseudo-SRH texture: purple/pink base (CH2/CH3-like) with cellular blobs + noise. family sets a look.""" | |
| rnd = random.Random(seed * 131 + family * 7) | |
| base = [(60, 30, 70), (40, 55, 60), (70, 40, 55)][family % 3] | |
| im = Image.new("RGB", (W, H), base) | |
| d = ImageDraw.Draw(im, "RGBA") | |
| for _ in range(rnd.randint(40, 90)): | |
| x, y = rnd.randint(0, W), rnd.randint(0, H) | |
| r = rnd.randint(4, 16) | |
| col = (rnd.randint(150, 230), rnd.randint(90, 160), rnd.randint(150, 220), rnd.randint(60, 140)) | |
| d.ellipse([x - r, y - r, x + r, y + r], fill=col) | |
| im = im.filter(ImageFilter.GaussianBlur(rnd.uniform(0.4, 1.2))) | |
| return im | |
| cases = [] | |
| # --- Arm A: 8 single patches, half labelled real, half synthetic (+ one ungated synthetic) --- | |
| labels = ["real", "real", "real", "synthetic", "synthetic", "synthetic", "synthetic_ungated", "real"] | |
| for i, src in enumerate(labels): | |
| cid = f"A{i+1:03d}" | |
| srh_patch(i + 1, family=i % 3).save(IMG / f"{cid}.png") | |
| cases.append({"case_id": cid, "arm": "A", "image": f"img/{cid}.png", "true_source": src}) | |
| # --- Arm B: 3 clusters (positive control = coherent, negative control = scrambled, one 'discovered') --- | |
| def cluster(cid, cluster_id, is_control, imgs): | |
| return {"case_id": cid, "arm": "B", "cluster_id": cluster_id, "is_control": is_control, "images": imgs} | |
| # positive control: 6 patches, same family (coherent) | |
| pos = [] | |
| for k in range(6): | |
| p = f"img/Bpos_{k}.png"; srh_patch(100 + k, family=1).save(IMG / f"Bpos_{k}.png"); pos.append(p) | |
| cases.append(cluster("B001", "pos_ctrl", "positive", pos)) | |
| # negative control: 6 patches, mixed families (scrambled / incoherent) | |
| neg = [] | |
| for k in range(6): | |
| p = f"img/Bneg_{k}.png"; srh_patch(200 + k, family=k % 3).save(IMG / f"Bneg_{k}.png"); neg.append(p) | |
| cases.append(cluster("B002", "neg_ctrl", "negative", neg)) | |
| # a 'discovered' cluster: 6 patches, one family with a couple outliers | |
| disc = [] | |
| for k in range(6): | |
| fam = 2 if k < 4 else (k % 3) | |
| p = f"img/Bdisc_{k}.png"; srh_patch(300 + k, family=fam).save(IMG / f"Bdisc_{k}.png"); disc.append(p) | |
| cases.append(cluster("B003", "disc_01", "none", disc)) | |
| with open(DATA / "cases.json", "w", encoding="utf-8") as f: | |
| json.dump({"study": "SRH Pathology Validation Study (demo)", "cases": cases}, f, indent=2) | |
| print(f"wrote {DATA/'cases.json'} with {len(cases)} demo cases " | |
| f"({sum(1 for c in cases if c['arm']=='A')} Arm-A, {sum(1 for c in cases if c['arm']=='B')} Arm-B).") | |