OCR models for forms and handwriting: tables and key-value association
Forms and handwriting are two recognition problems wearing one document format, and a pipeline built for one will quietly fail on whichever one it wasn't tuned for.
The job isn't turning pixels into characters, it's proving which field a value belongs to, what order the page reads in, and whether a shaky character deserves trust. Inputs: applications, surveys, checklists, delivery slips, forms with handwritten notes in the margin. Targets: printed labels, ticked boxes, handwriting, a present signature, and field-to-value pairs that survive crossed-out text, touching letters, faint pencil, and notes outside the box meant for them. A vision-language model can quietly swap an unreadable character sequence for whichever word looks more natural.
What a scanned form actually asks you to prove
Extraction has to preserve associations before mapping into a business field. The pipeline starts boring: register the template, crop the regions, run printed OCR, run a separate handwriting recognizer. Bring in a generative model only where it earns its keep, on hard-to-read handwriting, layouts matching nothing on file, label-to-value association across a distance, or exceptions the deterministic stage can't resolve. It shouldn't replace stages that already work for a fraction of the cost.
The audit trail you keep before you normalize anything
A pipeline should hand back bounding regions, header paths, row identities, units, and an arithmetic cross-check alongside every value. Treat the source file as immutable, and assign page and document identifiers before anything touches it. Log the full path each value traveled:
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
Normalization may not overwrite transcription. If a character could be an O or a 0, an I or a 1, a decimal, a minus sign, a unit, or a diacritic, store what the recognizer saw next to what the business rule proposed, as two separate fields.
Character error rate is the wrong scoreboard
The gate that matters is field association, handwriting transcription, checkbox state, blank detection, and whether it knows when to stop and ask a human. CER and WER help, but they score a dropped comma and a transposed digit in an account number as the same mistake. They aren't. Add exact-match scoring for identifiers, dates, amounts, warnings, and units, and score table geometry separately from transcription: a model can get every character right and still misplace the total.
Plausible is worse than broken
The failure worth losing sleep over isn't garbled text. It's a legible handwritten value attached to the wrong field, or a mark the model decided was there when it wasn't, and neither looks broken. Language models continue plausible patterns, so a damaged surname, part number, or total gets completed into whatever alternative fits. Asking a model for final JSON straight off the image is the specific trap here: a tidy answer, none of the evidence needed to catch a swapped field.
Build an abstention path:
- low-res regions, clipped pages, glare, and unreadable handwriting raise an exception, not a guess
- contradictory totals raise the same exception
- confidence scores are model-specific and poorly calibrated, so pair them with image-quality checks, format validators, cross-field arithmetic, and disagreement between recognition passes
Provenance turns review into verification: page number and bounding polygon for every field that matters, crop shown beside the proposed value. A reviewer settles an ambiguous digit in seconds. Skip them and it means scanning the whole page, or hunting for paper.
Where the model earns trust, and where it doesn't
Vision-language reasoning is useful for rebuilding a fragmented table or explaining why two totals disagree, but ask it to keep observation separate from interpretation. Anything writing to a database or calling a tool needs schema validation and business rules outside the model: text printed inside a document is untrusted input, not an instruction channel. A form is a strange place to run into prompt injection, but it happens.
Preserve page order and repeated-header identity across multi-page work, and never let the model merge three invoices because they arrived in one bundle. Split deterministically, attach identifiers, and state whether a table or paragraph may continue across a page boundary. Version model, preprocessing, dictionaries, schema, and validation together; when one changes, replay a stratified set: clean pages, common noise, awkward layouts, consequential fields, examples that should be unreadable.
Deployment is a tradeoff. Local keeps documents under your control and gets cheaper as volume climbs, but you own the storage, backup, model memory, accelerator time, and review staffing. Hosted APIs and frontier vision models buy elasticity and a taller ceiling for hard cases, in exchange for upload time, retention, residency, rate limits, and a bill that scales with pages or tokens. Hybrid routing earns its complexity when privacy rules are strict and escalation stays visible.
I keep the deterministic stage in charge and only let the expensive model near a field once cheaper checks have flagged it uncertain. That costs turnaround time on the hard cases, and I'd rather give up those minutes than give up the paper trail that lets someone else check my work later.