File size: 3,510 Bytes
028d06c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
"""
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).")