The rebuild after 1457556 came out byte-identical to the broken file,
which proved the xfade fix never runs for this chapter. An all-cut chapter
goes down the concat demuxer with -c copy, which writes the output in the
FIRST input's time_base and reinterprets every later packet in it. 14 of
49 clips are 30/1 at 1/15360 against 35 at 25/1 at 1/12800, so those 14
play 15360/12800 = 1.2 too long with their audio untouched. collage_cmd
hardcoded -r 30 and yesterday's FPS sweep missed it.
collage_cmd now emits -r FPS, and assemble probes r_frame_rate across the
clips and routes mixed rates through the re-encoding tree. Rebuilt
chapter.mp4 is 364.120s video against 364.122s audio at 25/1, from
436.392 over 363.675.
Also settle the bbox coordinate space, measured over all 113 detections:
47 boxes have x2 past the 900px panel width, none has y2 past 1000 on
panels up to 2307px tall, and the range is exactly [0, 1000]. It is
gemma's normalized grid, not pixels, whatever the prompt asks for.
/vision converts before returning, which fixes identity's crop, the gated
face pairing that was comparing pixel face boxes against grid boxes, the
set-of-mark boxes and the review UI at once. Checked by eye on panel 7:
five of six boxes now land on their subject, including the foreground
character who had no identity.
The registry still holds boxes and embeddings enrolled from the wrong
space. vision and identity have to re-run, which is GPU work and was not
started.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2.5 KiB
identity-bbox
The coordinate space of a vision character box, and what reads it.
A vision bbox arrives on gemma's 0-1000 grid, and /vision converts it to pixels
Closed, 2026-08-12.
build_detect_prompt asks for a "pixel bounding box". The model answers on its own normalized grid
regardless. Measured over all 113 detections of job 778297bc, read straight from /review/identity:
| test | result |
|---|---|
boxes with x2 past the 900px panel width |
47 of 113 |
boxes with y2 past 1000, on panels 1257 to 2307px tall |
0 of 113 |
| boxes clamped at exactly 1000 | 21 in x, 5 in y |
| coordinate range over every box | [0, 1000] |
Pixels cannot behave that way. A person standing in the lower half of a 2307px panel needs y2 near
2000, and it never once exceeds 1000.
Consumed as pixels the boxes collapse into the top-left corner of the panel. Four consumers were reading them:
worker_identity._crop_bboxatworker_identity.py:200, which embeds the crop. This is why a crop of a speech balloon's edge matchedChoi Haeseonat 0.9._pair_faces_to_presentinworker_vision.py, which compares real detector face boxes, in pixels, against these. The gate could almost never pass, which is the mechanism behind the 7unknownresults out of 7som_facelines already recorded atworker_vision.py:169.- the set-of-mark boxes drawn for attribution.
- the review UI, which crops client-side off the panel PNG.
/vision now calls _bbox_to_pixels(characters, w, h) before returning, so all four see pixels and no
consumer needs to know the grid existed. Verified by drawing the converted boxes on panel 7. Five of six
land on their subject, including person_5, who is Seonho in the foreground and had no identity.
person_1 still frames a window mullion with nobody in it, which is #extras-as-cast, not this.
The prompt text still says "pixel bounding box". Rewording it changes what the model emits and needs a
GPU run to re-verify, so the boundary converts instead. The ponytail: note on _bbox_to_pixels records
that. It also records the trap: a model that really answered in pixels would be scaled down here.
Consequence: every assignment in the registry came from a wrong crop. The existing embeddings and
ref_image_uris are enrolled on balloons and window frames. Re-running identity
is what makes the registry mean anything. The anonymous-identity and extra-versus-cast work cannot be
judged until that rerun happens.