olmOCR on a Mac, tested on a real scanned PDF
1,954
of 1,955 scan words, read in order
Ten scanned pages in 77 seconds
77 s for 10 pages, Mac GPU
9.5 GB model download
-
Run
pip install olmocrandbrew install poppler. - Unpack the llama.cpp macOS arm64 build into llama-b12316/. Put the Q8_0 model and the F16 mmproj in gguf/.
-
Save olmocr.sh below next to book.pdf. Run
sh olmocr.sh. It writes ws/markdown/book.md.
olmocr.sh
./llama-b12316/llama-server -m gguf/olmOCR-2-7B-1025-Q8_0.gguf --mmproj gguf/mmproj-olmOCR-2-7B-1025-F16.gguf \
-c 16384 -np 1 --host 127.0.0.1 --port 8095 --threads 2 --no-warmup > server.log 2>&1 &
S=$!
until curl -sf http://127.0.0.1:8095/health > /dev/null; do kill -0 $S || exit 1; sleep 1; done
BEAKER_ASSIGNED_CPU_COUNT=2 python -m olmocr.pipeline ws --server http://127.0.0.1:8095/v1 --model olmocr --markdown \
--pdfs book.pdf --workers 1 --max_concurrent_requests 1 > pipeline.log 2>&1
R=$?
kill $S; wait $S
exit $R
The GGUF files are the LM Studio community copy. The
BEAKER_ASSIGNED_CPU_COUNT line caps page renders at 2.
The plain command wants an NVIDIA GPU
The README asks for a recent NVIDIA GPU with 12 GB or more for local runs.
Without --server, the toolkit tries to start its own
vLLM. The base install has no torch, so on the Mac it stopped after
1.5 seconds:
No module named 'torch'
With --server, it skips that check. It sends each page to
llama-server, like it does to a paid API.
What the toolkit adds
- It draws each page 1,288 pixels on the long side.
- It asks for a page language, a rotation check and table flags. It tagged the scan pages en and the German pages de.
- It retries a page at a higher temperature. Here it needed 0 retries.
- A page that still fails gets pdftotext, which is blank on a scan.
Watch one default: if more than 1 page in 250 falls back, the code drops the whole document. That did not happen here.
One quote mark and one running head
The only missed word was a closing quote mark after "course--". The only extra line was the running head on top of page 9:
THE UNCOMMON PRAYER-BOOK
The 11-page file with a typed cover page took 106 s. It read all 13 cover words, and the scan pages came out byte for byte the same.
German: good umlauts, weaker columns
The model card lists English only. It still wrote 194 of 206 umlauts, in 98 s.
| Model | Umlauts | Columns |
|---|---|---|
| LightOnOCR-3 | 202 | 961 |
| LightOnOCR-2 | 199 | 959 |
| olmOCR-2 | 194 | 935 |
The two-column page has 972 words.
Tied with LightOnOCR-2 for the most words
The same 10 scanned pages, scorer and Mac GPU:
| Model | Words | Time |
|---|---|---|
| LightOnOCR-2 | 1,954 | 72 s |
| olmOCR-2 | 1,954 | 77 s |
| glm-ocr, Ollama | 1,953 | 104 s |
| GOT-OCR 2.0 | 1,936 | 134 s |
| Florence-2 | 1,887 | 158 s |
olmOCR and our Actor: 3 wins, 2 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 | olmOCR | Actor |
|---|---|---|
| Scan words in order | 1,954 | 1,874 |
| Typed cover words | 13 | 13 |
| Umlauts | 194 | 192 |
| Two columns, in order | 935 | 954 |
| Time, 11 pages | 106 s | 133 s |
| Time, 3 German pages | 98 s | 59 s |
The Actor needs no GPU and no model download. It costs $0.006 a page.
Apache 2.0, from Ai2
The toolkit and the model are Apache 2.0. I read the license, the model card and Ai2's use guidelines in full before the download.
The card says the model is for research and education. The guidelines it points to list harmful uses, with no ban on business use.
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.md, with the scorer of the tessdata test. German: the scorer of Tesseract German.
- olmocr 0.4.27 and llama.cpp b12316 on an Apple Silicon Mac GPU, at the lowest priority, 2 CPU threads, one page at a time.
- GGUF: lmstudio-community/olmOCR-2-7B-1025-GGUF, revision 9191740, Q8_0 model (8.1 GB) and F16 mmproj (1.35 GB). Their SHA-256 sums match the Hub.
- Times count the whole script, with the model load. The files were already downloaded. One run of each file.
- The other model 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
- The full BF16 or FP8 model, and smaller GGUF sizes.
- vLLM on an NVIDIA GPU, the toolkit's own path.
- Paid olmOCR APIs.
- Tables, math, handwriting and rotated pages.
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
Can olmOCR run on a Mac?
Yes, with the toolkit's --server flag. Serve a GGUF copy of the model with llama.cpp on the Mac GPU. The plain command wants vLLM and an NVIDIA GPU.
Can olmOCR read German?
Yes. It wrote 194 of 206 umlauts on German pages and tagged each page as German. Both LightOnOCR models read more words in order on a two-column page.
Is olmOCR free for business use?
The toolkit and the model are Apache 2.0, from Ai2. Ai2's use guidelines list harmful uses. They do not forbid business use.
Related guides
- MinerU on a Mac: setup and a scanned PDF test
- LightOnOCR-3 4B on a scanned PDF: Mac setup and results
- Florence-2 OCR on a scanned PDF: Mac setup, tested
- GOT-OCR 2.0 on a scanned PDF: Mac setup and results, tested
- Ollama OCR on a scanned PDF: 2 models tested on a Mac
- PDF to Markdown in Python: 5 libraries tested (2026)