← all posts
// ocr · ocr-models

OCR models for technical documents and labels: capture quality before recognition

A model that reads a blurred part number and hands back a clean, confident string is doing something worse than failing: it succeeded at the wrong task. I'd defend that, with one qualifier: put the model downstream of a system that decided whether there's enough signal to justify a character.

The documents that expose this hardest are the ones nobody designs OCR for: manuals, schematics, equipment plates, part labels, maintenance records, where what matters is part numbers and warnings, plus how pieces relate on the page. The damage is mundane: engraving, curved surfaces, rotated callouts, line art mistaken for text. A vision-language model can tie a label to a distant value, or easily dress up an ambiguous character as something that reads more naturally than the page does.

make the pixel budget the actual constraint

The rule that matters most is unglamorous: the smallest critical character needs enough clean pixels before any model looks at it. That's a lighting problem, not a prompting one. Checks run first: blur, glare, clipping, skew, resolution. A bad capture gets rejected, not guessed at.

High-resolution region OCR takes the first pass, checked against dictionaries that validate a value's shape without substituting a cleaner one. A generative model earns its place only where it beats that baseline: hard handwriting, a layout the deterministic stage can't resolve. I log the chain per page:

source_hash, capture_device, page_id
crop / dewarp / denoise versions
ocr_model, vlm_model, runtime_rev
raw_text, region, confidence
normalized_value, rule_applied, reviewer

When a character is genuinely ambiguous, an O that might be a zero, the log keeps what the recognizer saw next to what the rule proposed instead of merging the two.

the failure that doesn't look like one

Character error rate is a fine summary statistic and a poor acceptance gate: it scores a missing comma the same as a wrong account number. What matters is exact-match accuracy on identifiers and dates, amounts and warnings, plus unit preservation.

The failure worth hunting for is a model normalizing a damaged part number or safety label into something familiar but wrong, since language models continue plausible patterns: a smudged surname or clipped total gets completed into whatever a human would have written. Nothing about the output looks broken. That's what makes it worse than a garbled string: one gets flagged, the other gets filed.

The countermeasure is an abstention path people actually use: a low-resolution region, glare, unreadable handwriting, or a total that doesn't add up should raise an exception, not a guess with a confidence number attached. Pair confidence with image-quality checks and cross-pass disagreement. Give a reviewer the crop next to the value. Verifying a digit takes seconds; reconstructing where it came from does not.

Let the model reason about structure, tying a label to a distant value or explaining why totals disagree, but keep it away from writes. Anything reaching a database goes through validation outside the model: text on a label is data the document handed you, not permission to change your instructions. Split multi-page bundles deterministically and keep header identity explicit.

I run high-resolution OCR locally and escalate only exceptions to a hosted vision model, keeping documents under our own control and steady volume cheap. That's a tradeoff I take on purpose: the ceiling on genuinely hard cases is lower than if everything routed to the biggest hosted model instead. Some ugly labels stay in the exception queue, and I've made peace with that.

#ocr-models#document-ai#vision