Open Source · Research

The islands that vanish inside Hebrew.

A line of Hebrew with an English word in the middle is completely ordinary — an institution, a date, a catalogue number. Most OCR models hand that line back with the middle silently missing. We traced it to the decoder, fixed it, and released the whole stack under Apache 2.0.

01 — The failure

A compression bias at the boundary

Hebrew documents are rarely purely Hebrew. An institution name, a gene symbol, a catalogue number, a citation, a date, a URL — real pages are full of short left-to-right runs sitting inside right-to-left text. We call them islands.

To understand why they disappear, it helps to know how a CTC decoder reads. It scans the image in vertical slices, left to right, and emits one character — or a blank — per slice. Built into that is a hard assumption: the order of the slices is the order of the characters.

Hebrew runs right to left, so its characters arrive in the reverse of the scanning order. A model trained on Hebrew learns to handle that consistently. But an embedded English word runs left to right inside that reversed line, so at each of its two edges the direction of the text and the direction of the scan disagree. There is no single alignment that satisfies both the island and the Hebrew around it.

CTC has an escape hatch for exactly this kind of conflict: the blank symbol. Emitting blanks across the whole island is cheaper than committing to an alignment that contradicts the rest of the line. So the island isn't misread — it is emitted as nothing at all.

What makes this diagnosable rather than mysterious is where it doesn't happen. Isolated Latin reads at 1.20% CER. Isolated digits at 1.41%. Pure Hebrew at 0.64%. The model can plainly see every glyph involved. The failure is specific to the mixed boundary, and it isn't an ordering bug either — the pure-reorder rate is near zero. It is a compression bias, and it is silent: you get back clean, fluent, confident Hebrew with a hole in it.

1.20%
Latin alone
1.41%
Digits alone
0.64%
Hebrew alone
12.63%
Hebrew + embedded Latin

Per-slice micro-CER, SVTRv2 CTC head. Every glyph class is solved in isolation. Only the seam fails.

02 — The fix

Swap the head, not the model

An attention decoder has no monotonic constraint. It attends over the whole encoded line and emits the next character from context, so an embedded island is just more context. We kept the SVTRv2 backbone exactly as it was and changed only the decoding head.

Same backbone, different head — micro-CER
SliceCTCAttentionGain
Real Hebrew + Latin (n=233)12.63%2.33%5.4×
Hebrew + digits4.99%0.56%8.9×

Attention is slower, so running it on every crop would be wasteful — roughly 90% of real crops are single-script and CTC handles them perfectly. So production runs a script-gated cascade: CTC first on everything, then fall back to attention only when the CTC output contains a Latin or digit character, or comes back low-confidence.

That buys the attention ceiling on mixed text at about 1.25–1.36× CTC latency, with no retraining and no second model to maintain.

Why this generalises

Nothing in the mechanism is specific to Hebrew. Any bidirectional script that embeds LTR runs — Arabic, Farsi, Urdu, Hebrew — should exhibit the same CTC boundary collapse, and should respond to the same fix. If you are benchmarking a bidirectional OCR model, measure the mixed-script slice separately. A single aggregate CER will hide this completely.

03 — The long-line wall

A 52% plateau that turned out to be the wrong question

Long Hebrew lines — 61 to 80 characters — sat at 52–55% CER across every line-level model we trained. No amount of additional data moved it, and three independent levers (learning rate, input width, data up-weighting) all failed the same way.

Long-sequence degradation is not a Hebrew problem, though. It shows up in the PaddleOCR issue tracker independently of script: issue #11482 reports that recognition accuracy on long sequences "never matches the accuracy of single words" across CRNN, SAR, ABINET and SVTR, and issue #13938 documents a Chinese model stuck at ~50% accuracy on ~135-character lines. Different scripts, different metrics, same shape of failure — which was the clue that ours wasn't a Hebrew data problem either.

We first beat it with sliding-window inference: process the line in overlapping ~320px windows and stitch the seams by Levenshtein alignment. That was a genuine 45-point drop — 52% down to 6.93%. It was also a trap. It only worked on one exact model at one exact scale, cost 3–5× the recognition passes, and every seam was a fresh opportunity for duplication garbage. The next backbone couldn't slide at all.

The actual answer was to stop patching a short-crop model and use a reader built for the sequence length. SVTRv2 reads the whole line in a single pass, no windows, no seams — and drops 71–80 character Hebrew to 0.49% CER. The wall wasn't a data problem or an inference problem. It was an architecture mismatch.

52%
Community plateau, long lines
6.93%
Sliding window (a dead end)
0.49%
SVTRv2, single pass
04 — Benchmarks

Robustness is the edge, not pristine text

The obvious target — beat Tesseract on clean Hebrew — is the wrong one. On clean academic Hebrew, Tesseract is genuinely good, and our earlier word-level stack narrowly lost to it. Chasing that margin was low-value work.

What matters is what happens on the documents people actually scan. Tesseract collapses on those; we don't.

About these numbers

Every CER figure on this page comes from our own in-house evaluation harness, run on our own test sets. They are measured rather than estimated, and BiDi-normalised on both sides before the Levenshtein distance — but they are vendor-reported and have not been independently verified. The methodology, the caveats and the per-slice breakdowns are all in the repository, so you can disagree with them specifically.

Real-domain crop CER — where documents actually come from
DomainTesseractOurs
Thermal receipts26.32%2.09%
Menus12.41%2.80%
Invoices7.99%2.68%
Page-level, 71 pages, recogniser held constant
Page pipelinemicro-CER
Word detection + SVTRv2 cascade7.56%
Hebrew-finetuned line detection10.36%
Stock line detection10.67%
Tesseract 514.20%

Word-level detection beats line-level on Hebrew pages, and we tested that fairly: our first comparison was confounded because the word detector was Hebrew-finetuned while the line detector was stock. So we trained a Hebrew line detector on the same architecture and the same warm-start base, converged it to 0.913 val hmean, and re-ran. The finetune closed only 0.31pt. The gap is the word-vs-line difference itself, not detector tuning.

And we did close the academic gap too. On clean rendered public-domain Hebrew with perfect ground truth (n=600), the attention reader now beats Tesseract outright: 0.35% versus 1.34% micro-CER. But robustness is what makes the thing worth shipping.

05 — Get it

Seven models, finetuned from PaddleOCR

Every model here is a finetune of a PaddleOCR model (PaddleOCR v3.7.0, Apache-2.0), released in the same licence. All of them ship as ONNX and share one 120-character charset.

What's in the release
ModelRoleFinetuned from
server-svtrv2Flagship recogniser (CTC + attention)SVTRv2, Chinese pretrain
server-v6Word-level server recogniserPP-OCRv6, PPLCNetV4-medium
server-v5Word-level server recogniserPP-OCRv5, PPHGNetV2-B4
light-svtrv2smallEdge / CPU recogniserSVTRv2-small, distilled from the flagship
mobile-wordMobile recogniser (7.4 MB)PP-OCRv5 mobile, PPLCNetV3
word-detFlagship word detectorPP-OCR mobile DBNet
line-detLine detector (hard layouts)PP-OCR mobile DBNet

The flagship pairs word-det with server-svtrv2. Start there; the rest are alternatives for smaller footprints or harder layouts.

pip install -e . huggingface_hub

from ocr import HebrewOCR
ocr = HebrewOCR.word(models_dir)   # flagship
result = ocr.read("page.pdf")      # image or PDF
for line in result["lines"]:
    print(line["text"])            # logical order

Apache-2.0, code and weights, finetuned from PaddleOCR models under the same licence. The training weights and configs are not published — the ONNX files are all you need to run the models. If you want to finetune on your own Hebrew data, adapt the charset, or collaborate, open an issue or write to ronen@rivoklabs.com.

Known limitation

Reading-order assembly uses paragraph-direction sorting rather than full UAX#9 BiDi. A Hebrew-dominant line containing a long embedded Latin run can therefore order the runs by position rather than by strict logical BiDi. This is fine for most documents and it is documented in the repository — but if your corpus is heavily bilingual at the line level, test it before you rely on it.