Skip to content

Text capture: photograph the label, then tap the line you want - #8

Merged
MadsenDev merged 1 commit into
mainfrom
claude/ocr-product-keys-m54tkj
Aug 9, 2026
Merged

Text capture: photograph the label, then tap the line you want#8
MadsenDev merged 1 commit into
mainfrom
claude/ocr-product-keys-m54tkj

Conversation

@MadsenDev

Copy link
Copy Markdown
Contributor

Follow-up to #7, from testing it on a real phone. One commit.

A receipt in frame, brackets full of text, "No text found".

Why it found nothing

Not the camera. #7 set page segmentation to "single text line", which is better when there is one line. Handed five, Tesseract does not return a poor answer — it returns nothing. Every benchmark in #7 had put exactly one line in the band, so the mode that helped single lines was silently failing on everything else.

Measured on a three-line crop:

result
single-line mode (what #7 shipped) empty
block mode all three lines, cleanly
either mode, one genuine line identical

The band is gone

The bug is fixable in one line, but the band was the wrong shape for the job regardless. Lining a single line up inside a guide is hardest exactly when the print is small and the phone is far away — which is when text capture is wanted at all.

So: photograph the label, every line found is boxed on the photo, tap the one you want. Pointing at a line on a picture is a better instruction than aiming a camera at one, and both engines already return the geometry — Tesseract per line, TextDetector per block.

Two passes, two jobs. The first reads the whole frame in block mode: enough to find and mark the lines, not enough to trust a product key from. Tapping one crops it and reads it three times at different scales — the close read that used to be spent on a guessed-at band, now spent on the line the user actually asked for. Single-line mode is kept for the second pass, where the claim is true and it is 2× faster than block mode for identical accuracy.

The accuracy work from #7 is unchanged and still applies: three scales, agreement counts (3/3, 1/3) instead of the engine's own confidence, whitespace-only differences counted as agreement.

Layout

The photo is zoomed to the text it found. A landscape frame letterboxed into a portrait viewport uses about a third of the screen and leaves receipt lines a few pixels tall; the label's margins do not need the space.

The picture and the boxes are placed from the same numbers, and re-placed by a ResizeObserver. Worth calling out, because the first cut looked fine and was wrong: it positioned the boxes once, before the panel below had finished rendering, so all twelve sat five lines beneath their own text. The boxes are the entire promise of this UI — a box drawn anywhere but over its own text is worse than drawing none — so they cannot be allowed to drift.

Zoom

Added, where the lens reports the capability. A phone cannot focus closer than ten or fifteen centimetres, so filling the frame with small print by moving nearer runs out of focus before it runs out of room. The control is absent, not disabled, on a lens without it.

Verification

On the case that failed, in a browser:

  • twelve receipt lines found; every box over its own text and in reading order
  • tapped the eighth → close re-read agrees 3/3 → receiver gets that line byte-for-byte, captioned OCR TEXT
  • box positions confirmed stable while the panel below grows (drift 0.0000)
  • barcode path unchanged, including a text-and-back round trip
  • 64 relay tests pass; typecheck and build clean

Still open

  • The native TextDetector path has still never executed anywhere I can test. On Android it is ML Kit and should be better than these numbers.
  • Picking among tightly-spaced receipt lines is possible but fiddly at ~9 px per line. Pinch-zoom on the photo is the obvious next step; it is in the roadmap.
  • The edit step named in Text capture: read printed labels that have no barcode #7 is still the clearest remaining win.

🤖 Generated with Claude Code

https://claude.ai/code/session_012ooGSXdKf1ZKVKSiTycKFs


Generated by Claude Code

Reported from a real phone: a receipt in frame, brackets full of text, "No text
found". Reproduced it and the cause was not the camera. PSM 7 tells Tesseract
"this image is a single text line"; handed five lines it does not return a poor
answer, it returns *nothing*. Every benchmark so far had put exactly one line in
the band, so the mode that made single lines better was silently failing on
everything else. Measured on a three-line crop: single-line mode empty, block
mode all three cleanly, and on a genuine single line the two are identical.

The band was the wrong shape for the job anyway. Lining one line up inside a
guide is hardest exactly when the print is small and the phone is far away —
which is when text capture is wanted at all.

So: photograph the label, mark every line found, and tap the one you want.
Pointing at a line on a picture is a better instruction than aiming a camera at
one, and both engines already return the boxes needed to draw it — Tesseract per
line, TextDetector per block.

Two passes, for two different jobs. The first reads the whole frame in block mode:
enough to find the lines and mark them, not enough to trust a product key from.
Tapping one crops it and reads it three times at different scales — the close
read that used to be spent on a guessed-at band, now spent on the line the user
actually asked for. Single-line mode is kept for that second pass, where the
claim is true and it is twice as fast as block mode for the same accuracy.

The photo is zoomed to the text it found. A landscape frame letterboxed into a
portrait viewport uses about a third of the screen and leaves receipt lines a few
pixels tall; the margins of the label do not need the space. The picture and the
boxes are placed from the same numbers, and re-placed by a ResizeObserver — the
first cut positioned them once, before the panel below had finished rendering,
and every box sat five lines below its own text.

Also adds zoom, where the lens reports it: a phone cannot focus closer than ten
or fifteen centimetres, so filling the frame with small print by moving nearer
runs out of focus before it runs out of room.

Verified in a browser on the case that failed: twelve receipt lines found, each
box over its own text and in reading order, tap the eighth, close re-read agrees
3/3, and the receiver gets that line byte-for-byte. Barcode path unchanged,
including a text-and-back round trip.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ooGSXdKf1ZKVKSiTycKFs
@MadsenDev
MadsenDev merged commit 688eb9f into main Aug 9, 2026
2 checks passed
@MadsenDev
MadsenDev deleted the claude/ocr-product-keys-m54tkj branch August 9, 2026 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants