docTR OCR, tested on a real scanned PDF
1,869
of 1,955 scan words, read in order
Ten scanned pages in 13 seconds
13 s for 10 pages, Mac GPU
129 MB model download
- Run
pip install python-doctr. It installs torch. - Save doctr_ocr.py below next to book.pdf.
-
Run
python3 doctr_ocr.py. It writes book.txt. The first run downloads the two models.
doctr_ocr.py
"""docTR on a PDF: book.txt plus page-NN.txt (brasspage.com/doctr-ocr/, Nov 12, 2026)."""
import argparse
import torch
from doctr.io import DocumentFile
from doctr.models import from_hub, ocr_predictor
ap = argparse.ArgumentParser()
ap.add_argument("--det", default="fast_base") # the docTR 1.1.0 default
ap.add_argument("--reco", default="crnn_vgg16_bn") # the default; a name with "/" loads from the Hub
ap.add_argument("--scale", type=float, default=2) # 2 = 144 dpi, the from_pdf default
a = ap.parse_args()
dev = "mps" if torch.backends.mps.is_available() else "cpu"
reco = from_hub(a.reco) if "/" in a.reco else a.reco
model = ocr_predictor(a.det, reco, pretrained=True).to(dev)
pages = DocumentFile.from_pdf("book.pdf", scale=a.scale)
result = model(pages)
for i, page in enumerate(result.pages, 1):
with open(f"page-{i:02d}.txt", "w") as f:
f.write(page.render())
with open("book.txt", "w") as f:
f.write(result.render())
Do not name your script doctr.py. Python then imports your file, not docTR:
ModuleNotFoundError: No module named 'doctr.io'; 'doctr' is not a package
Curly quotes come out as 66 and 99
The scan has 50 double quote marks.
36 came out as "66"
7 came out as "99"
Lines from book.txt:
Hardy; 66 don't talk tommyrot." What in the
66 Who's this smashed-in boat belong to? 99
tone that implied, 66 And what business is it of
yours? 99
50 of the 76 missed places have a double quote mark. If your text has dialog, fix these marks after the run.
It also kept the running headers and page numbers.
Other models did not read more
| Setup | Words | Time |
|---|---|---|
| Default | 1,869 | 13 s |
--scale 3 |
1,871 | 13 s |
--reco parseq |
1,866 | 20 s |
--det db_resnet50 |
1,852 | 14 s |
| Multilingual PARSeq | 1,828 | 12 s |
The default pair is fast_base and crnn_vgg16_bn. Scale 3 renders each page at 216 dpi.
German: 0 of 206 umlauts by default
Every pretrained docTR text model uses its "french" letter set. It has ü, but no ä, ö or ß.
The default model wrote "uber" for "über" and "groBe" for "große". The multilingual PARSeq model on Hugging Face fixes most of it:
python3 doctr_ocr.py --reco Felix92/doctr-torch-parseq-multilingual-v1
| Model | Umlauts | Words |
|---|---|---|
| Default | 0 | 1,239 |
| Multilingual | 147 | 1,374 |
On a clean one-column page, the multilingual model read 519 of 521 words. On a two-column page, it read straight across both columns:
ki. Ilauptmann Boris Mantow hatte bis spät in vollkommen entging, wie dieselben Augen aufmerksam
Vision models read more, but slower
The same 10 scanned pages, scorer and Mac GPU:
| Model | Words | Time |
|---|---|---|
| glm-ocr, Ollama | 1,953 | 104 s |
| Qwen3-VL 8B | 1,951 | 139 s |
| GOT-OCR 2.0 | 1,936 | 134 s |
| docTR | 1,869 | 13 s |
docTR was 8 to 11 times faster. It is the better pick when speed matters more than quote marks.
No GPU: 97 seconds for 10 pages
On the CPU only, with 2 threads, the same script took 97 s. The text was the same as on the GPU, byte for byte.
docTR reads every page as a picture. In an 11-page file with a typed cover page, it read all 13 cover words.
docTR and our Actor: 2 wins, 3 losses
Our Actor, from a file URL, with no install:
curl -s -X POST "https://api.apify.com/v2/acts/brasspage~pdf-ocr-to-markdown/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"documentUrls": ["https://api.apify.com/v2/key-value-stores/sTnmNrGoSsi7tLQvs/records/anythingllm-mixed-11-pages.pdf"]}' \
| jq -r '.[].markdown' > mixed.md
| Test | docTR | Actor |
|---|---|---|
| Scan words in order | 1,869 | 1,874 |
| Typed cover words | 13 | 13 |
| Umlauts | 147 | 192 |
| Two columns, in order | 482 | 954 |
| Time, 11 pages | 11 s | 133 s |
| Time, 3 German pages | 17 s | 59 s |
docTR used the default models for English, multilingual for German.
The Actor needs no GPU and no model download. It costs $0.006 a page.
Apache 2.0, weights included
The docTR license file is Apache 2.0, and the README says the same. I read both in full before the install.
The default models come from the project's own download links, with no other license. The multilingual model card says apache-2.0.
How I tested it
- Scan: the 10 pages of OCR in n8n, proofread Wikisource book scans, 1,955 true words. The 11-page file adds the 13-word typed cover page of AnythingLLM OCR.
- German: the 3 pages of Paperless-ngx OCR, 1,887 words, 206 umlauts. One page has two columns, 972 words.
- Score: true words found in order in book.txt, with the scorer of the tessdata test. German: the scorer of Tesseract German.
- python-doctr 1.1.0 and torch 2.15.0, Python 3.12, on an Apple Silicon Mac GPU, at the lowest priority. Multilingual model revision 8bde31c.
- Times count the whole script, with the model load. The models were already downloaded. One run of each setup.
- The CPU run changes one line of doctr_ocr.py at run time: the device is "cpu".
- The glm-ocr, Qwen3-VL and GOT rows: the same files and scorer, on the same Mac, on November 12. They have their own pages.
- The Actor: two real runs earlier the same day, build 0.1.65, on the same files.
What I did not test
-
Rotated pages,
detect_layout, tables and the KIE predictor. - The other text models, and community models on the Hub.
- The docTR API server and Docker images.
- Windows, Linux and NVIDIA GPUs.
- Handwriting, receipts, math and photos of signs.
Sources
Two ways to skip the work
Run it yourself. Our Apify Actor turns scanned PDFs into Markdown, tables included.
Or send us the scan. Tack, our AI, does the job for $29, up to 50 pages. First page free.
Questions people ask
Does docTR need a GPU?
No. On the CPU only, with 2 threads, it read 10 scanned pages in 97 s, with the same text as on the GPU.
Can docTR read German?
Not with the default models: they wrote 0 of 206 umlauts. The multilingual PARSeq model on Hugging Face wrote 147.
Is docTR free for business use?
Yes. The code is Apache 2.0, and the default models come from the project's own links with no other license.
Related guides
- keras-ocr in 2026: the install fix, tested vs Tesseract
- PaddleOCR on a Mac: CPU setup and results, tested
- Florence-2 OCR on a scanned PDF: Mac setup, tested
- Kraken OCR on a scanned PDF: Mac setup and results, tested
- GOT-OCR 2.0 on a scanned PDF: Mac setup and results, tested
- EasyOCR vs Tesseract vs PaddleOCR vs RapidOCR, tested
- Qwen3-VL OCR on a scanned PDF: tested in Ollama on a Mac
- Ollama OCR on a scanned PDF: 2 models tested on a Mac
- PDF to Markdown in Python: 5 libraries tested (2026)