grace-reader-study / build_cases_example.py
DrSyedFaizan's picture
Upload folder using huggingface_hub
74cc258 verified
Raw
History Blame Contribute Delete
3.76 kB
"""
Generate a synthetic demo case set (data/cases.json + placeholder images) so the app
runs immediately with ZERO real data. Replace with real P1/P3 outputs for the study:
put real images + a real cases.json in a PRIVATE HF dataset and set CASES_DATASET.
Case schema (data-driven; the app reads exactly this):
{
"study": "...",
"cases": [
{
"case_id": "c001",
"question": "Is there pneumonia (lung opacity)? If present, where?",
"intro": "optional per-case provenance note (else app default is used)",
"reference_image": "img/c001_ref.png", # the original CXR (reference column)
"groundtruth_image": "img/c001_gt.png", # reference-region / bbox panel (final row)
"items": [ # anonymized systems to score, no names
{"item_id": "c001_sysA", "image": "img/c001_a.png",
"answer": "Right lower lobe opacity, consistent with pneumonia.", "decision": "answer"},
{"item_id": "c001_sysB", "image": "img/c001_b.png",
"answer": "No acute cardiopulmonary abnormality.", "decision": "defer"}
]
}
]
}
NOTE: item_id must be a stable TRUE id (maps to the real system in the backend); the reader
never sees it. The app shuffles item order per (annotator, case) and derives rankings later.
"""
import json
import random
from pathlib import Path
from PIL import Image, ImageDraw
HERE = Path(__file__).parent
DATA = HERE / "data"
IMG = DATA / "img"
IMG.mkdir(parents=True, exist_ok=True)
random.seed(7)
W = H = 512
def base_cxr(seed):
rnd = random.Random(seed)
im = Image.new("RGB", (W, H), (18, 18, 18))
d = ImageDraw.Draw(im, "RGBA")
# two faint lung fields
for cx in (170, 342):
d.ellipse([cx - 90, 120, cx + 90, 400], fill=(60, 60, 60, 255))
# a mediastinum
d.rectangle([236, 120, 276, 420], fill=(40, 40, 40, 255))
# a random faint "opacity"
ox, oy = rnd.choice([(150, 320), (330, 300), (200, 200)])
d.ellipse([ox - 40, oy - 30, ox + 40, oy + 30], fill=(150, 150, 150, 120))
return im, (ox, oy)
def box(im, center, color, label):
d = ImageDraw.Draw(im, "RGBA")
cx, cy = center
d.rectangle([cx - 55, cy - 45, cx + 55, cy + 45], outline=color, width=4)
d.rectangle([cx - 55, cy - 45, cx + 55, cy + 45], fill=color[:3] + (40,))
d.text((cx - 50, cy - 62), label, fill=color)
return im
cases = []
for k in range(4):
cid = f"c{k+1:03d}"
ref, opacity = base_cxr(k)
ref.save(IMG / f"{cid}_ref.png")
gt = ref.copy()
box(gt, opacity, (60, 200, 90, 255), "reference")
gt.save(IMG / f"{cid}_gt.png")
# system A highlights the true region (relevant); system B highlights a wrong region
a = ref.copy(); box(a, opacity, (230, 70, 60, 255), "highlight")
a.save(IMG / f"{cid}_a.png")
wrong = (256, 380)
b = ref.copy(); box(b, wrong, (230, 70, 60, 255), "highlight")
b.save(IMG / f"{cid}_b.png")
cases.append({
"case_id": cid,
"question": "Is there a focal lung opacity (e.g. pneumonia)? If present, where?",
"reference_image": f"img/{cid}_ref.png",
"groundtruth_image": f"img/{cid}_gt.png",
"items": [
{"item_id": f"{cid}_sysA", "image": f"img/{cid}_a.png",
"answer": "Focal opacity present; likely pneumonia.", "decision": "answer"},
{"item_id": f"{cid}_sysB", "image": f"img/{cid}_b.png",
"answer": "Uncertain; recommend radiologist review.", "decision": "defer"},
],
})
with open(DATA / "cases.json", "w", encoding="utf-8") as f:
json.dump({"study": "GRACE reader study (demo)", "cases": cases}, f, indent=2)
print(f"wrote {DATA/'cases.json'} with {len(cases)} demo cases and placeholder images.")