DeepSeek OCR, tested on a real scanned PDF
1,948
of 1,955 scan words, read in order
On a Mac, use llama.cpp
DeepSeek-OCR 2 has a GGUF model, and llama.cpp runs it on the Mac GPU. The model card's own code is for NVIDIA GPUs.
1,948 of 1,955 words, in order
83 s for 10 pages, Mac GPU
Install Poppler for pdftoppm. Put llama.cpp and the two
q8_0 GGUF files next to the PDF, then run sh deepseek.sh.
You get one Markdown file, book.md.
The full deepseek.sh script
pdftoppm -r 144 -png book.pdf page
for p in page-*.png; do
./llama-b12316/llama-mtmd-cli -m gguf/deepseek-ocr-2-q8_0.gguf --mmproj gguf/mmproj-deepseek-ocr-2-q8_0.gguf \
--image "$p" -p "<|grounding|>Convert the document to markdown." --chat-template deepseek-ocr \
--temp 0 --flash-attn off --no-warmup -n 2048 --dry-multiplier 0.8 --dry-base 1.75 \
--dry-allowed-length 2 --dry-sequence-breaker none --threads 2 > "${p%.png}.md" 2>> cli.log || exit 1
done
sed -E 's/<\|ref\|>[^<]*<\|\/ref\|><\|det\|>[^<]*<\|\/det\|>//' page-*.md > book.md
One flag from the PR fails
-
The llama.cpp pull request that added DeepSeek-OCR 2 has
--dry-penalty-last-n -1. llama.cpp b12316 stops on it: invalid dry-penalty-last-n = -1. I left that one flag out. - The document prompt puts a box tag before each block. The last line of the script removes the tags.
Typed pages: 13 of 13 cover words
DeepSeek OCR reads every page as a picture, typed pages too. In the 11-page file, it read all 13 words of the typed cover page. The scan words stayed at 1,948, in 86 s.
German: best umlauts, weaker two columns
The script has no language option. I gave it 3 German pages. One is a magazine page with two columns. It kept the column order, but misread more words there.
193 of 206 umlauts
921 of 972 words in order, two columns
Next to PaddleOCR-VL on the same files: 5 more umlauts and 1 more scan word, but 35 fewer two-column words.
DeepSeek OCR beat our Actor, mostly
Our Actor makes Markdown 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 | DeepSeek | Actor |
|---|---|---|
| Scan words in order | 1,948 | 1,874 |
| Typed cover words | 13 | 13 |
| Umlauts | 193 | 192 |
| Two columns, in order | 921 | 954 |
| Time, 11 pages | 86 s | 133 s |
| Time, 3 German pages | 31 s | 59 s |
The Actor read 33 more words on the two-column page. It needs no GPU and no 3.6 GB download. It costs $0.006 a page.
The license allows business use
DeepSeek-OCR 2 is Apache 2.0. The first DeepSeek-OCR is MIT. I read both full license files before the download. Neither adds use terms.
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.
- Score: true words found in order in book.md, with the scorer of the tessdata test. German: the scorer of Tesseract German.
- llama.cpp b12316 for macOS on an Apple Silicon Mac GPU, at the lowest priority, 2 threads. GGUF revision d08e5af400c6, q8_0 model and q8_0 mmproj. Pages drawn at 144 dpi. One run each. Times include the model start on every page.
- The script above is the file that ran. The Actor: two real runs earlier the same day, build 0.1.65, on the same files.
What I did not test
- The first DeepSeek-OCR, the bf16 and Q4_K_M files, and the Free OCR prompt.
- The DRY penalty over the whole output, the PR's intent with -1.
- The transformers script, vLLM, mlx-vlm and NVIDIA GPUs.
- Handwriting, tables, math and forms.
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 DeepSeek OCR run on a Mac?
Yes. With its GGUF model in llama.cpp on the Mac GPU, DeepSeek-OCR 2 read 10 scanned pages in 83 s. The model card's own code is for NVIDIA GPUs.
Why does llama-mtmd-cli say invalid dry-penalty-last-n = -1?
The command in the llama.cpp pull request for DeepSeek-OCR 2 has --dry-penalty-last-n -1. llama.cpp b12316 does not accept -1. Leave that flag out. The default is 64.
Can a business use DeepSeek OCR?
Yes. DeepSeek-OCR 2 is Apache 2.0 and the first DeepSeek-OCR is MIT, with no added use terms. Keep the license and notices if you share the model.
Related guides
- Ollama OCR on a scanned PDF: 2 models tested on a Mac
- LightOnOCR on a scanned PDF: Mac setup and results, tested
- GLM-OCR on a scanned PDF: Mac setup and results, tested
- PaddleOCR-VL on a scanned PDF: Mac setup and results, tested
- dots.ocr on a scanned PDF: Mac setup and results, tested
- Chandra OCR on a scanned PDF: Mac setup and results, tested
- Surya OCR on a scanned PDF: setup and results, tested
- PDF to Markdown in Python: 5 libraries tested