sundeep-news-scan/scratch_py/_test_boundaries.py

88 lines
3.0 KiB
Python

"""Single-page boundary test: datelines + rules -> article boxes, overlaid.
Usage: venv/bin/python3 _test_boundaries.py <run_dir> <page_num>
"""
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]))