tesserocr vs pytesseract, tested on 55 scans
3.2x
less CPU with tesserocr on small images
Same text, less work per image
Both ran Tesseract 5.5.4. From a file path, both wrote the
tesseract command's text.
55 of 55 book scans, the same text
13,858 of 14,693 words found, both
| Job | pytesseract | tesserocr |
|---|---|---|
| 259 line images | 18.9 s | 5.9 s |
| 55 full pages | 61.6 s | 58.1 s |
pytesseract starts the tesseract program for every image,
and the program loads the model each time. That cost 50 to 64 ms of
CPU an image.
The code: open the API once
pytesseract needs the tesseract program installed:
import pytesseract
text = pytesseract.image_to_string("scan.jpg", config="--dpi 300")
tesserocr keeps Tesseract loaded. Make one API and reuse it for every image:
from tesserocr import PyTessBaseAPI
with PyTessBaseAPI(path="/opt/homebrew/share/tessdata/") as api:
api.SetVariable("user_defined_dpi", "300")
for name in ["scan1.jpg", "scan2.jpg"]:
api.SetImageFile(name)
print(api.GetUTF8Text())
path is the folder with your models.
tesseract --list-langs prints it.
Both re-save a Pillow JPEG
Pass Image.open("scan.jpg") and both libraries save it
again as a JPEG before Tesseract reads it. That copy loses detail.
6 of 55 pages kept the command's text
13,854 words found, 4 fewer
Pass the path instead. If you already hold the image, call it a PNG, so the copy is lossless:
img = Image.open("scan.jpg")
img.format = "PNG"
With that line, both matched the command on every page.
pip install tesserocr has no models
The wheel I got (2.12.0, Mac arm64, Python 3.13) carries its own Tesseract 5.5.3 and no language files. The first API call fails:
RuntimeError: Failed to init API, possibly an invalid tessdata path: ./
Pass path as above. Or build it against the Tesseract you
already have, and it finds the models itself:
pip install --no-binary tesserocr tesserocr
How I tested it
- Pages: 55 proofread Wikisource scans from 11 printed books, JPEG, 1,920 pixels wide, no dpi in the file. The same set as Tesseract in Docker.
- Score: true words found in order, with the scorer of the tessdata test.
- Apple Silicon Mac, Homebrew Tesseract 5.5.4, tessdata_fast English, Python 3.13, pytesseract 0.3.13, Pillow 13.0.0, and tesserocr 2.12.0 built from source.
- Time is CPU time of the process and its children, so other jobs on the Mac did not count. The 3 rounds stayed within 4%.
- Line images: 259 lines cut from 10 scans, saved as PNG, read with psm 7. Both wrote the same text on every line.
- The wheel's own 5.5.3 wrote the same text as 5.5.4 on every page, in 54.9 s. I did not find out why it ran faster.
What I did not test
- Threads. I read one image at a time.
- Windows and Linux installs, and wall time on an idle Mac.
- Other languages, tessdata_best, and photos.
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
Which one should I use?
pytesseract for a few pages: it needs only pip and the tesseract program. tesserocr for many small images or video frames, with one API kept open.
Does the JPEG trap hit numpy or OpenCV images?
Not numpy arrays. An array has no file format, so both save it without loss. As arrays, all 55 scans matched the command. I tested RGB arrays from Pillow, not OpenCV's BGR.
Can tesserocr give me line or word boxes?
Yes. api.GetComponentImages(RIL.TEXTLINE, True) returns each line with its box, and RIL.WORD gives words. I cut the 259 line images with it. pytesseract has image_to_data.