| """ |
| create_test_pdf.py β Generate a synthetic scanned medical-schedule PDF |
| with known ground-truth for accuracy measurement. |
| """ |
| import json, os, random |
| import numpy as np |
| from PIL import Image, ImageDraw, ImageFont |
|
|
| GROUND_TRUTH = [ |
| ("1", "07:30", "09:15", "105", "SMITH, J", "JONES, R", "DOE, JOHN A", "45/M", "03/15/1979"), |
| ("2", "08:00", "10:30", "150", "WILLIAMS, K", "BROWN, S", "MARTINEZ, MARIA", "67/F", "11/22/1957"), |
| ("3", "09:00", "11:00", "120", "JOHNSON, M", "DAVIS, T", "CHEN, DAVID L", "32/M", "06/08/1992"), |
| ("1", "10:00", "12:30", "150", "TAYLOR, P", "WILSON, A", "O'BRIEN, SARAH", "55/F", "09/04/1969"), |
| ("4", "11:30", "13:00", "90", "ANDERSON, R", "MOORE, J", "KIM, JAMES H", "28/M", "12/01/1996"), |
| ("2", "12:00", "14:45", "165", "THOMAS, D", "CLARK, M", "PATEL, PRIYA", "41/F", "07/19/1983"), |
| ("3", "13:30", "15:00", "90", "JACKSON, L", "HALL, N", "RODRIGUEZ, CARLOS", "73/M", "02/28/1951"), |
| ("5", "14:00", "16:30", "150", "WHITE, B", "LEWIS, E", "NGUYEN, THI L", "59/F", "04/10/1965"), |
| ] |
|
|
| COL_SPEC = [ |
| ("ROOM", 50, 180), |
| ("BEGIN", 180, 340), |
| ("END", 340, 490), |
| ("MIN", 490, 580), |
| ("SURGEON", 580, 900), |
| ("ANES", 900, 1180), |
| ("PATIENT'S NAME",1180,1750), |
| ("AGE/SEX", 1750,1900), |
| ("DOB", 1900,2150), |
| ] |
|
|
| def _font(size, bold=False): |
| name = "DejaVuSansMono-Bold.ttf" if bold else "DejaVuSansMono.ttf" |
| for d in ["/usr/share/fonts/truetype/dejavu", "/usr/share/fonts/dejavu", |
| "C:/Windows/Fonts"]: |
| p = os.path.join(d, name) |
| if os.path.isfile(p): |
| return ImageFont.truetype(p, size) |
| return ImageFont.load_default() |
|
|
| def make_page(rows, W=2550, H=3300): |
| img = Image.new("RGB", (W, H), "white") |
| dr = ImageDraw.Draw(img) |
| fh = _font(26, bold=True) |
| fd = _font(20) |
|
|
| dr.text((900, 50), "OPERATING ROOM SCHEDULE", fill="black", font=fh) |
| dr.text((1000, 90), "DATE: 01/15/2025", fill="black", font=fd) |
|
|
| y0, rh = 160, 55 |
| |
| for hdr, xs, xe in COL_SPEC: |
| dr.rectangle([(xs, y0), (xe, y0 + rh)], outline="black", width=2) |
| dr.text((xs + 8, y0 + 12), hdr, fill="black", font=fd) |
| |
| for i, vals in enumerate(rows): |
| y = y0 + rh + i * rh |
| for j, (_, xs, xe) in enumerate(COL_SPEC): |
| dr.rectangle([(xs, y), (xe, y + rh)], outline="black", width=2) |
| if j < len(vals): |
| dr.text((xs + 8, y + 14), vals[j], fill="black", font=fd) |
| return img |
|
|
| def add_noise(img, level=8): |
| a = np.array(img, dtype=np.float32) |
| a += np.random.normal(0, level, a.shape) |
| a = np.clip(a, 0, 255).astype(np.uint8) |
| pil = Image.fromarray(a) |
| return pil.rotate(random.uniform(-0.5, 0.5), fillcolor=(245,245,245)) |
|
|
| def create_test_pdf(out_dir="./test_pdfs", pages=2): |
| os.makedirs(out_dir, exist_ok=True) |
| pils, gt = [], [] |
| for pg in range(pages): |
| pil = add_noise(make_page(GROUND_TRUTH)) |
| pils.append(pil) |
| for r in GROUND_TRUTH: |
| gt.append(dict(page=pg+1, |
| room=r[0], begin_time=r[1], end_time=r[2], |
| minutes=r[3], surgeon=r[4], anes=r[5], |
| patient_name=r[6], age_sex=r[7], dob=r[8])) |
| pdf = os.path.join(out_dir, "test_schedule.pdf") |
| pils[0].save(pdf, "PDF", resolution=300, save_all=True, append_images=pils[1:]) |
| gt_path = os.path.join(out_dir, "ground_truth.json") |
| with open(gt_path, "w") as f: |
| json.dump(gt, f, indent=2) |
| print(f"β
PDF β {pdf} ({pages} pages Γ {len(GROUND_TRUTH)} rows)") |
| print(f"β
GT β {gt_path}") |
| return pdf, gt_path |
|
|
| if __name__ == "__main__": |
| create_test_pdf() |
|
|