87 lines
4.9 KiB
Python
87 lines
4.9 KiB
Python
"""TEST (read-only): finalize article boundaries using the RIGHTMOST dead-end (nearest big
|
|
vertical wall to the right, else page edge) and the TOPMOST dead-end (masthead bottom, for
|
|
top-row articles). Draw both the original block boundary (thin gray) and the dead-end-snapped
|
|
boundary (thick colour) on a blank canvas, plus the big vertical walls (red) and the masthead
|
|
band. No smart_extractor change, no API. Test on 1-2 pages."""
|
|
import os, tempfile, json, io, contextlib, statistics
|
|
tempfile.tempdir = os.path.abspath("_tess_tmp"); os.environ["TMPDIR"] = tempfile.tempdir
|
|
os.makedirs("_tess_tmp", exist_ok=True)
|
|
from pathlib import Path
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
import smart_extractor as se
|
|
from _lines import separator_barriers, detect_separator_lines
|
|
|
|
BIG_FRAC = 0.45
|
|
|
|
def big_walls(png):
|
|
res = detect_separator_lines(png); W, H = res["size"]
|
|
out = []
|
|
for L in res["v"]:
|
|
y1, y2 = min(L["y1"], L["y2"]), max(L["y1"], L["y2"])
|
|
if (y2 - y1) >= BIG_FRAC * H:
|
|
out.append({"x": (L["x1"]+L["x2"])//2, "y1": y1, "y2": y2})
|
|
return out, (W, H)
|
|
|
|
def masthead_bottom(png, raw, paper, pg, W, H):
|
|
# compute from RAW regions (before masthead removal) so the dropped band's bottom is visible
|
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
dd = se.dedup_overlapping_regions([dict(r) for r in raw])
|
|
kept = se.drop_masthead_regions([dict(r) for r in dd], paper, pg, png)
|
|
drop_bottom = max((r["bbox"][3] for r in dd
|
|
if r["id"] not in {k["id"] for k in kept}), default=0)
|
|
res = detect_separator_lines(png)
|
|
wide = sorted([L for L in res["h"] if (L["x2"]-L["x1"]) >= 0.30*W
|
|
and L["y1"] < 0.20*H], key=lambda L: L["y1"])
|
|
top_rule = wide[0]["y1"] if wide else 0
|
|
return max(drop_bottom, top_rule)
|
|
|
|
def run(pdir, pg, paper, tag):
|
|
png = f"{pdir}/page_{pg:03d}.png"
|
|
raw = json.loads(Path(f"{pdir}/page_{pg:03d}.regions.json").read_text())["regions"]
|
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
regs = se.dedup_overlapping_regions(raw); regs = se.merge_stacked_title_lines(regs, png)
|
|
regs = se.drop_masthead_regions(regs, paper, pg, png); regs = se.promote_paragraph_titles(regs, png, paper); regs = se.mark_bullet_titles(regs, png)
|
|
ds = se.find_article_starts_by_dateline(regs, png, paper)
|
|
_p = se.cluster_all_article_blocks(regs, ds, sep_lines=separator_barriers(png, regs))
|
|
if se.recover_orphan_text_headlines(_p, regs, png): ds = se.find_article_starts_by_dateline(regs, png, paper)
|
|
bars = separator_barriers(png, regs)
|
|
blocks = se.cluster_all_article_blocks(regs, ds, sep_lines=bars)
|
|
walls, (W, H) = big_walls(png)
|
|
cright = max((r["bbox"][2] for r in regs), default=W)
|
|
mh = masthead_bottom(png, raw, paper, pg, W, H)
|
|
top_row_cut = mh + 320 # an article whose top is within this band is "top row"
|
|
|
|
def deadends(b):
|
|
x1, y1, x2, y2 = b["bbox"]; cx = (x1 + x2) / 2; bh = y2 - y1
|
|
# rightmost dead-end: nearest big wall right of the block centre that spans it; else page edge
|
|
rcand = [w["x"] for w in walls if w["x"] > cx
|
|
and max(0, min(y2, w["y2"]) - max(y1, w["y1"])) >= 0.4 * bh]
|
|
right = min(rcand) if rcand else cright
|
|
right = max(right, x2) # never shrink
|
|
# topmost dead-end: top-row article → up to masthead bottom
|
|
top = mh if (y1 <= top_row_cut and mh > 0) else y1
|
|
top = min(top, y1) # never shrink downward
|
|
return [x1, top, right, y2]
|
|
|
|
img = Image.new("RGB", (W, H), "white"); d = ImageDraw.Draw(img)
|
|
try: font = ImageFont.truetype("DejaVuSans-Bold.ttf", 30)
|
|
except Exception: font = ImageFont.load_default()
|
|
if mh > 4:
|
|
d.rectangle([0, 0, W, mh], fill=(232,232,232)); d.line([0,mh,W,mh], fill=(140,140,140), width=5)
|
|
d.text((14, max(2, mh//2-16)), "MASTHEAD (top dead-end)", fill=(110,110,110), font=font)
|
|
for w in walls:
|
|
d.line([w["x"], w["y1"], w["x"], w["y2"]], fill=(235,70,70), width=8)
|
|
pal = se._ARTICLE_COLORS
|
|
for i, b in enumerate(blocks):
|
|
ox1, oy1, ox2, oy2 = b["bbox"]; nx1, ny1, nx2, ny2 = deadends(b); c = pal[i % len(pal)]
|
|
d.rectangle([ox1, oy1, ox2, oy2], outline=(180,180,180), width=3) # original (gray)
|
|
d.rectangle([nx1, ny1, nx2, ny2], outline=c, width=8) # dead-end (color)
|
|
lab = f"#{b['anchor_id']} [{b['kind']}]"
|
|
d.rectangle([nx1+5, ny1+6, nx1+16+len(lab)*15, ny1+42], fill=c)
|
|
d.text((nx1+11, ny1+9), lab, fill="white", font=font)
|
|
out = f"{pdir}/page_{pg:03d}_partition_debug.png"; img.save(out)
|
|
print(f"{tag} pg{pg}: {len(blocks)} blocks, {len(walls)} walls, masthead_bottom={int(mh)} -> {out}")
|
|
|
|
run("output/NamastheTelangana_Siddipet_20260602_20260604_214937/pages", 2, "namaste_telangana", "NT")
|
|
run("output/AndhraJyothi_Siddipet District_20260602_20260603_144919/pages", 1, "andhra_jyothi", "AJ")
|