Layout detection and OCR engines
A VLM did not make the classical document stack obsolete; it changed what the stack is for. Layout detection, OCR and table recognition are now the cheap, deterministic layer that finds regions, reads characters exactly and returns coordinates — the three things a VLM does badly — and the VLM sits on top for meaning. Knowing the engines by name is the “nice to have” line; knowing how they combine is the job.
What each layer does
page image
├─ orientation + deskew (Tesseract OSD, Hough)
├─ layout detection (DocLayout-YOLO, Surya,
│ └─ regions with boxes PP-DocLayout, Docling)
├─ reading order (Surya, Docling, XY-cut)
├─ OCR per text region (PaddleOCR, Tesseract,
│ └─ words + boxes + conf docTR, Surya OCR)
├─ table structure (TATR, SLANet, TableFormer)
└─ VLM per region or page (semantics, fields)Layout detection is object detection with document classes — title, paragraph, table, figure, formula, header, footer. DocLayout-YOLO is the fast one (a YOLO trained on synthetic and real pages); Surya, Docling’s RT-DETR model and PaddleOCR’s PP-DocLayout are the alternatives with different licences and speed profiles.
All are fine-tunable on a few hundred labelled pages, exactly like any detector (Object detection).
Reading order turns regions into a sequence. Multi-column pages, side bars and footnotes are where naive top-to-bottom fails; the classical XY-cut recursion still works on clean layouts, and the learned models (Surya’s order model, Docling) handle the rest.
Table structure recognition turns a table crop into cells with row and column indices — Table Transformer (TATR), PaddleOCR’s SLANet, IBM’s TableFormer — and is evaluated with TEDS. It is the component most often replaced by a VLM, and the one where the VLM’s merged-cell handling is genuinely better on messy tables.
The OCR engines
| Engine | Character |
|---|---|
| Tesseract 5 | CPU, 100+ languages, clean print only |
| PaddleOCR (PP-OCRv4/v5) | detection + recognition, rotated text, CJK |
| docTR | PyTorch, modern detectors, easy to fine-tune |
| Surya OCR | 90+ languages, strong accuracy, gated licence |
| EasyOCR | simplest API, slowest |
| Cloud (Textract, Azure DI) | forms and tables, per-page price |
Tesseract is an LSTM recogniser that assumes a page: upright, printed,
300 dpi, with lines of text. Within that it is free, fast on CPU and gives
per-word confidence; outside it — skew, low resolution, scene text,
handwriting — it degrades quickly. Its --psm page-segmentation modes and
image_to_osd orientation detection are the two flags worth knowing.
PaddleOCR is the modern default: a text detector (DB) finds arbitrary text boxes, a recogniser reads each, and it handles rotation, curved text and mixed scripts that break Tesseract. PP-StructureV3 adds layout, tables and full document parsing; PaddleOCR-VL is the 0.9B VLM successor for the same job. Apache-2.0.
from paddleocr import PaddleOCR
ocr = PaddleOCR(lang="en", use_textline_orientation=True)
result = ocr.predict("page.png")[0]
words = list(zip(result["rec_texts"], result["rec_scores"],
result["rec_polys"])) # text, confidence, 4 points
low = [w for w in words if w[1] < 0.8] # route these to reviewThe API changed across major versions — the call above is the 3.x shape; 2.x returned nested lists. Pin the version, as with everything here.
Where the classical layer beats the VLM
- Exact characters. OCR errors are character-level and visible; a VLM
rewrites
Oto0, drops a digit, or “corrects” a misspelt name. For IDs, IBANs and reference numbers, OCR text is the ground truth and the VLM’s value is checked against it. - Coordinates. Every word comes with a box, which is what a review UI needs to highlight a value on the page, and what a fine-tune needs for crops.
- Determinism and speed. Milliseconds per page on a CPU, the same
output every time, no
max_tokens, no repetition loop. - Confidence that means something. Per-word scores from a recogniser are better calibrated than anything a VLM says about itself.
Where it loses: reading order on complex layouts, semantics of any kind, handwriting, merged cells, and any question that needs the page understood rather than transcribed.
The hybrid that ships
The combination worth describing in an interview: layout detection finds the regions; OCR reads the text regions into words with boxes; the VLM gets the page image plus the OCR text and returns the fields with quotes; the quotes are matched to OCR words, which gives every extracted value a bounding box and a confidence for free.
from rapidfuzz import fuzz
def locate(quote: str, words: list[tuple[str, float, list]], n: int = 6):
"""Find the OCR word run that best matches a VLM quote; return its box."""
best = (0, None)
for i in range(len(words)):
for j in range(i + 1, min(i + n, len(words)) + 1):
run = " ".join(w[0] for w in words[i:j])
score = fuzz.ratio(run, quote)
if score > best[0]:
best = (score, words[i:j])
score, run = best
if score < 85 or run is None:
return None # quote not on the page: flag it
return union_box([w[2] for w in run]), min(w[1] for w in run)Feeding the OCR text to the VLM alongside the image measurably improves digit accuracy — the model can copy rather than read — and turns the evidence check in Structured extraction from documents into a lookup.
Tables go to the table model or the VLM depending on which wins on your TEDS numbers; stamps, signatures and checkboxes go to a small custom YOLO, which is a few hundred labelled pages of work and more reliable than any model that reads.
Preprocessing carries the accuracy
The same rules as any CV pipeline
(OpenCV and classical preprocessing):
render at 300 dpi, detect and fix orientation, deskew with a Hough or
minAreaRect angle, adaptive-threshold noisy scans, and remove form lines
before OCR so they do not become | characters.
Each step is a few lines of OpenCV and each is worth several points of accuracy on scanned input. None of it is needed for born-digital PDFs, which should not be rendered at all — take the text layer.
Where it breaks in production
- Tesseract on a photo. Skewed, shadowed, low-resolution: near-zero output. Detection-based engines or a VLM for anything not a flat scan.
- Licence surprises. Several strong models are non-commercial or revenue-gated (LayoutLMv3, Surya’s models); the Apache ones (PaddleOCR, docTR, Tesseract) are the safe defaults. Check before the demo.
- Reading order by y-coordinate. Two columns interleave into nonsense and the VLM extracts from the nonsense. Use a reading-order model or XY-cut on the layout regions.
- OCR text as the only input. Fields that depend on position — which column a number sits in — vanish when the page becomes a string. Send the image too.
- Version drift. Engine APIs and default models change between major versions; the OCR output for the same page changes; the evidence check starts failing. Pin, and re-run the golden set on upgrade.
Related
Interview angle 4
- “Why keep OCR if you have a VLM?” - exact characters, coordinates, determinism and calibrated per-word confidence, at milliseconds per page. The VLM reads meaning; the OCR text is the ground truth its values are checked against, and the boxes are what the review UI highlights.
- “Tesseract or PaddleOCR?” - Tesseract for clean upright scans on CPU with no dependencies; PaddleOCR for anything rotated, photographed, curved or CJK, and when you want layout and tables from the same toolkit. Both Apache; Surya is stronger on some languages but gated.
- “How do you get a bounding box for an extracted value?” - match the VLM’s quote to the OCR word run with a fuzzy ratio and take the union of the word boxes. A quote that matches nothing is a hallucination flag. Follow-up: what if the layout is two columns? A reading-order model before OCR, or the match runs per region.
- “Where would you use a custom YOLO on documents?” - layout classes the pretrained detectors lack, and binary marks a VLM misreads: stamps, signatures, checkboxes, logos. A few hundred labelled pages, and it is more reliable than any model that reads.