Tools,

Tesseract --oem modes: use 1, and check what 3 does

91.6%

of book words found with --oem 1

Try it on your PDF, $0.006 a page

Four modes, and the one to pick

Flag Engine
--oem 0 Legacy only
--oem 1 Neural LSTM only. Use this one.
--oem 2 Legacy and LSTM together
--oem 3 Default, based on what is available

This is the engine, not the page layout. Layout is --psm. To print this list, run tesseract --help-oem.

--oem 3 means two different things

Model file --oem 3 runs --oem 0 or 2
tessdata_fast LSTM, same as --oem 1 Error
tessdata_best LSTM, same as --oem 1 Error
tessdata Both, same as --oem 2 Runs

Same means the same text on all 60 scans. Homebrew, Ubuntu 24.04 and a Debian image all held tessdata_fast. If you use plain tessdata, pass --oem 1 yourself.

--oem 1 won on real book scans

Words found, in order, exact case
Mode, file 60 pages, 15,622 words 55 printed pages, 14,693 words
1, fast 14,310 13,858
1, best 14,313 13,840
1, tessdata 14,312 13,848
2, tessdata 14,050 13,748
0, tessdata 13,827 13,719

One book is a handwritten manuscript, and legacy read very little of it. That is most of the gap, so read the printed column too.

Against LSTM on the same file, legacy was worse on 42 of 60 pages, and both engines together on 45. Legacy took less CPU than that LSTM run, and more than tessdata_fast.

Legacy beat LSTM on made-up invoices

I expected legacy to lose everywhere. It did not.

Found, best LSTM model on each row
Test LSTM Legacy
Made-up invoices, 2,454 numbers 2,218 2,409
Real receipts, 1,117 digit words 345 252
Book scans, 235 digit words 174 172

Those invoices use common computer fonts with a made-up scan look. The gap was widest in Courier: 612 of 612 for legacy, 514 at best for LSTM. Georgia was the one font where LSTM led, and the book row is a tie.

My advice: if your pages are typed tables, run some both ways and count. Otherwise stay on --oem 1.

Fix the legacy engine error

Error: Tesseract (legacy) engine requested, but components are not present in /opt/homebrew/share/tessdata/eng.traineddata!!

Your file is LSTM only, so you have nothing to repair unless you want legacy. This prints 0 for LSTM only, and 1 when legacy is inside:

combine_tessdata -d "$(brew --prefix)/share/tessdata/eng.traineddata" 2>&1 | grep -c inttemp

To run legacy, get plain tessdata. This ran as printed, exit 0:

mkdir ~/tessdata-legacy
curl -fsSL -o ~/tessdata-legacy/eng.traineddata \
  https://raw.githubusercontent.com/tesseract-ocr/tessdata/4.1.0/eng.traineddata
tesseract page.jpg stdout --tessdata-dir ~/tessdata-legacy --oem 0

What else people type

You typed Result
--oem=1, -oem 1 Exit 1, unknown argument
--oem 4 Exit 1, Invalid OEM value
--oem lstm_only Works, same text as --oem 1
-c tessedit_ocr_engine_mode=1 Works, same text as --oem 1
--psm 0 --oem 1 Prints an LSTM error, still exit 0, same answer

From Python: put it in config

import os

import pytesseract

print(len(pytesseract.image_to_string("page.jpg", config="--oem 1").split()))

legacy = os.path.expanduser("~/tessdata-legacy")
text = pytesseract.image_to_string("page.jpg", config=f"--tessdata-dir {legacy} --oem 0")
print(len(text.split()))

It printed 212 and 214 words for one page. With an LSTM-only file, --oem 0 raises TesseractError with the same message.

Our Actor stays on LSTM

PDF OCR to Markdown, our Apify Actor, has no engine setting. It runs Tesseract with tessdata_fast, reads tables cell by cell, checks the math, and sends unsure numbers the math does not back to tessdata_best.

Actor build 0.1.43 code on this Mac
Same pages Read right
Book scans, of 15,622 words 14,532
Invoices, of 2,454 numbers 2,451

Questions people ask

Can I set --oem and --psm together?

Yes. With --psm 4, LSTM on tessdata_best read 2,226 invoice numbers and legacy read 2,414, so layout did not close the gap.

Can I whitelist characters without legacy?

Yes. On one invoice, --oem 1 with tessedit_char_whitelist put out no character outside the list. I did not score its accuracy.

Method, limits and sources

How I tested it

Every mode ran on all 3 English model files on the book scans, and every mode that starts ran on the invoices. The runs that repeat /tessdata-best-vs-fast/ gave the same scores.

  • Book scans: 60 pages from 12 Wikisource books, each checked by two people. A seeded draw picked the books.
  • Invoices: 73 made-up pages, each scored by the exact text on the right row.
  • Receipts: 100 CORD v2 phone photos, without --oem 3. Their truth holds only item and total lines.
  • Digit words: true words with a digit, found anywhere on the page.
  • Raw phone photos of full pages: every mode found under 4% of the words, legacy the most. That set decides nothing.

CPU is the median of 3 rounds with 8 workers, on a Mac running other jobs, so treat it as rough. Memory is one run on one page.

The Actor ran locally from its build code, not as a paid Apify run. Scripts and results are in research/bench/tesseract-oem-2026-11-05/.

CPU and memory by mode
Mode, file CPU seconds Peak memory
1, fast 70.4 110 MB
0, tessdata 87.0 172 MB
1, tessdata 103.3 174 MB
2, tessdata 106.7 178 MB
1, best 137.5 160 MB
What I did not test
  • Why legacy does better on those invoices.
  • Real invoice scans: I have none with checked text.
  • Other languages and custom models.
  • Book pages with tables: the draw left them out.
  • Other psm values: my invoice scorer needs rows, so only --psm 3 and 4 count.
  • Windows, and Linux in this run.
Versions and sources

Two ways to skip the work

Run it yourself. Our Apify Actor turns scanned PDFs into Markdown, tables included.

Try the Actor

Or send us the scan. Tack, our AI, does the job for $29, up to 50 pages. First page free.

Get one page free