Stop dropping strip pixels, so panel bboxes tile the page

Answering whether the caption merge breaks the page-space key in
eval/chapter-truth.json. It does not. _merge_plan only groups adjacent
fragments and slice_webtoon cuts at gutter midpoints, so a vstacked panel's
[x, y0, w, sum(heights)] is the exact page region.

The defect is one line up. slice_webtoon skipped any segment shorter than
min_seg instead of absorbing it, so this chapter's 116 panels have a 62px hole
between panel_order 70 and 71 and lose 18px at the strip's end. A merge
spanning that hole reports a bbox 62px short of the page span it covers, and
every consumer mapping a detection back to the page is then wrong by that much,
silently. The 62px held the credits line, found at p_050.png row 338.

A short band is now held and absorbed by the next segment, or by the previous
one when it is last. min_seg still suppresses a tiny panel and no strip pixel
is discarded. The self-check asserts the bboxes tile the strip end to end.

Checks: worker_crop.py self-check passes and fails on the old code, where the
first band starts at 49 instead of 0. ruff check . exits 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 23:41:11 +04:00
parent bc19f9e9fd
commit c19a726455
6 changed files with 124 additions and 72 deletions
+8
View File
@@ -98,6 +98,14 @@ Four numbers the audit was misreporting, found while building the truth file on
The dialogue half is not written and needs the chapter watched panel by panel, 30 lines with their
true speaker. It is the only part of step 0 still open.
**The key held up under a real check, after one crop fix.** A merged panel's bbox is exact.
`_merge_plan` only groups adjacent fragments, and `slice_webtoon` cuts at gutter midpoints. One thing
was not exact. A segment under `min_seg=64` was dropped outright, so the 116 panels had a 62px hole
between `panel_order` 70 and 71, and lost 18px at the strip's end. A merge spanning that hole would
have reported a bbox 62px short. `slice_webtoon` now absorbs a short band instead of dropping it
(`decisions/measurement-spine.md#slices-tile-the-strip`). The dropped 62px held the credits line, so
nothing of value was lost this time.
Steps 1 and 2 are independent and cheap: `plane` and `species` per detection, and the
face-plus-person embedding already queued in item 1 above. The weak cannot-link is step 3 and depends
on step 2, not on `plane` alone. Co-presence as a hard constraint is load-bearing while cosine cannot