Files
Ignacy Łątka b8d729bb1f fix(screenshot-diff): rescale OCR text bounds into the normalized diff space (#442)
## Problem

When two same-aspect screenshots of different resolution are compared
(the explicitly supported flow, e.g. a 0.3x saved baseline vs a full-res
live capture), `diffPngFiles` calls `normalizeToCommonSize` to downscale
both framebuffers to a common size for the pixel diff and sets
`imageSize` to those normalized dimensions. The OCR/text pass, however,
ran OCR on the original decoded images: baseline OCR bounds came back in
baseline-original coordinates and current OCR bounds in current-original
coordinates - two different coordinate spaces.

`analyzeTextRegions` then compared those bounds directly (boundsDelta /
centerDistance / IoU), so identical text at the same logical position
was flagged as a half-image `moved` change. The summary compounded it by
normalizing the baseline bounds against the smaller normalized
`imageSize`, clamping the reported "from" width to 1. A uniform rescale
of one framebuffer is not a content change.

### Behavior repro

The same "Settings" text rendered at 480x240 vs 240x120 (2:1, same
aspect) produced one `kind:"moved"` change with a delta of roughly
negative half-image and a "from" normalized width of 1.

## Fix

Rescale each image's OCR text bounds (and per-word bounds) into the
normalized common coordinate space before the cross-image geometric
comparison and the normalized-coordinate reporting. Baseline bounds are
multiplied by `(normalizedW / baselineOrigW, normalizedH /
baselineOrigH)` and current bounds by `(normalizedW / currentOrigW,
normalizedH / currentOrigH)`, so both sets live in the same space the
pixel diff already uses. The font-geometry pass is handed the normalized
images to match the rescaled bounds, and the top cutoff is derived from
the normalized height. The scale factors are 1/1 when the inputs already
share dimensions, so same-size comparisons are unchanged.

A small pure helper `rescaleTextRegions(regions, scale)` performs the
mapping; `analyzeScreenshotTextChanges` accepts optional
`baselineRegionScale` / `currentRegionScale` and applies it to the OCR
output, and `diffPngFiles` computes the factors from the original vs
normalized dimensions.

## Test


`packages/tool-server/test/screenshot-diff-ocr-coordinate-space.test.ts`
is a pure unit test with no OCR engine dependency - it never shells out
to tesseract, python, or PIL. It supplies synthetic OCR regions for
"Settings" at the same logical location in the 480x240 baseline space
and the 240x120 current space, documents the spurious half-image `moved`
change from mismatched spaces, and asserts that after rescaling into the
common space the geometry produces no `moved` change and sane normalized
coordinates. A pipeline-level case mocks the OCR module and verifies the
same through `analyzeScreenshotTextChanges` with per-image scales.
2026-07-02 12:40:19 +02:00
..