"""Single-page boundary test: datelines + rules -> article boxes, overlaid. Usage: venv/bin/python3 _test_boundaries.py """ import json import sys from pathlib import Path import cv2 from PIL import Image from _lines import detect_separator_lines from _boundaries import construct_boundaries from smart_extractor import find_article_starts_by_dateline def paper_of_height(h): if 7040 <= h <= 7060: return "sakshi" if h in (6833, 6549): return "andhra_jyothi" if h == 6422: return "namaste_telangana" return "unknown" def iou(a, b): ix = max(0, min(a[2], b[2]) - max(a[0], b[0])) iy = max(0, min(a[3], b[3]) - max(a[1], b[1])) inter = ix * iy if inter == 0: return 0.0 ua = (a[2] - a[0]) * (a[3] - a[1]) + (b[2] - b[0]) * (b[3] - b[1]) - inter return inter / ua if ua else 0.0 def main(run_dir, page_num): run = Path(run_dir) png = run / "pages" / f"page_{page_num:03d}.png" regs = json.loads((run / "pages" / f"page_{page_num:03d}.regions.json").read_text())["regions"] rmap = {r["id"]: r for r in regs} paper = paper_of_height(Image.open(png).height) lines = detect_separator_lines(png) dls = find_article_starts_by_dateline(regs, str(png), paper) boxes = construct_boundaries(regs, dls, lines, lines["size"]) print(f"\npaper={paper} datelines={len(dls)} boxes={len(boxes)} " f"rules H={len(lines['h'])} V={len(lines['v'])}") # validation fails = [] for bx in boxes: db = rmap[bx["region_id"]]["bbox"] bb = bx["bbox"] contains = bb[0] <= db[0] and bb[1] <= db[1] and bb[2] >= db[2] and bb[3] >= db[3] if not contains: fails.append(f"R{bx['region_id']} box does NOT contain its dateline body") if bb[2] - bb[0] < 50 or bb[3] - bb[1] < 50: fails.append(f"R{bx['region_id']} degenerate box {bb}") for i in range(len(boxes)): for j in range(i + 1, len(boxes)): ov = iou(boxes[i]["bbox"], boxes[j]["bbox"]) if ov > 0.25: fails.append(f"R{boxes[i]['region_id']} & R{boxes[j]['region_id']} overlap iou={ov:.2f}") for bx in sorted(boxes, key=lambda z: z["bbox"][1]): print(f" R{bx['region_id']:>3} hl=R{bx['headline_region']} {bx['dateline'][:22]!r:24} " f"bbox={bx['bbox']} walls={bx['walls']['ceil_rule']}/{bx['walls']['floor_rule']}") print(f"\nVALIDATION: {'PASS' if not fails else f'{len(fails)} ISSUE(S)'}") for f in fails: print(" ⚠", f) img = cv2.imread(str(png)) for L in lines["h"]: cv2.line(img, (L["x1"], L["y1"]), (L["x2"], L["y2"]), (0, 0, 255), 4) for L in lines["v"]: cv2.line(img, (L["x1"], L["y1"]), (L["x2"], L["y2"]), (255, 0, 0), 4) for bx in boxes: b = bx["bbox"] cv2.rectangle(img, (b[0], b[1]), (b[2], b[3]), (0, 180, 0), 8) out = Path(f"/tmp/boundaries_{run.name}_p{page_num:03d}.png") cv2.imwrite(str(out), img) print("overlay:", out) if __name__ == "__main__": main(sys.argv[1], int(sys.argv[2]))