← all posts
// ocr · ocr-models

OCR models for forms and handwriting: local hardware and hybrid OCR deployment

Here's the claim I'll defend: for forms and handwriting, a boring CPU-bound OCR engine does more real work than the vision-language model everyone wants a GPU for. Qualify that fast: it only holds once you've split recognition into measurable stages, because the inputs are ugly, applications, delivery slips, checklists with crossed-out digits, touching characters, faint pencil, check marks, and handwritten notes outside their field.

Route by volume, not by model prestige

Hardware and routing should follow pages per hour, resolution, privacy rules, latency, and exception frequency, not the vendor demo. Start cheap: register the template, crop the regions, run printed OCR, and hand handwriting to a dedicated recognizer. A generative or multimodal model earns its place only with a measured advantage: difficult handwriting, an irregular layout, semantic field association, exception triage. It should never replace the deterministic stages that already work: that's paying accelerator prices for what a CPU pipeline already produced. Local deployment keeps documents under control and gets cheaper at steady volume, once you count storage, backup, model memory, accelerator use, and review labor. Hosted document APIs and frontier vision models buy elasticity and a higher exception ceiling, but add upload time, retention, residency, rate limits, and per-page cost. Hybrid routing earns its complexity when the privacy rule is hard and the escalation is visible.

The paper trail is the product

Treat the source file as immutable and assign a page and document identifier before anything touches it. Log the full path a value took, not just the final string:

source file hash, page, capture device
original dimensions, color, orientation
crop, dewarp, threshold, denoise versions
OCR/layout/VLM model and runtime revision
language hints and dictionaries
raw text, regions, reading order, confidence
normalized field, validation, reviewer change
latency, memory, energy or API cost

Never let normalization overwrite what was read. If a character is ambiguous (O versus 0, I versus 1, decimal, minus sign, unit, diacritic), store what the recognizer saw and what the business rule guessed as two separate values. Character and word error rate are fine for a leaderboard, but a missing comma and a wrong account number are not the same size of mistake. Add exact-match scoring for fields that matter: identifiers, dates, amounts, warnings, units. Score field association and table geometry separately: a page can transcribe perfectly and still attach the right number to the wrong applicant.

Plausible is the dangerous kind of wrong

The failure that costs money doesn't look broken: a legible handwritten value lands on the wrong field, or the model decides a mark is there that never was. Neither looks broken. Language models continue plausible patterns by design, so a smudged surname or a damaged total gets completed into something that reads fine and is wrong. Build an abstention path on purpose: low-resolution regions, a clipped page, glare, unreadable handwriting, and totals that don't add up should raise an exception, not a confident guess. Confidence scores are poorly calibrated, so back them with image-quality checks, format validators, cross-field arithmetic, and agreement between two independent passes. When a vision-language model gets involved, make it separate observation from interpretation, keep schema validation and business rules outside the model, and treat text printed inside the document as data, not an instruction it's allowed to act on. For multi-page batches, preserve page order and header identity yourself; don't let a model decide where one invoice ends and the next begins. Store a bounding box with every important field so a reviewer sees the crop beside the proposed value: that's the difference between verifying a document and retyping it.

Next thing I'd check on a pipeline like this: pull a stratified replay set, clean pages, ordinary noise, ugly layouts, critical fields, a few impossible ones, and rerun it whenever a model, dictionary, or validation rule changes. If the abstention rate moves and nobody notices, that's the actual bug.

#ocr-models#document-ai#vision