DEV Community

Deduped
Deduped

Posted on

A faster Tesseract alternative: OCR PDFs to markdown on CPU with Indra OCR

Tesseract is still the default OCR engine in a lot of pipelines, mostly because it is free, local and needs no GPU. Indra OCR from Datalab keeps all three of those properties and adds layout, reading order and handwriting. This post shows how to install it, convert PDFs to markdown or JSON, and use it from Python.

datalab-to/indra describes Indra OCR as a 1M-parameter OCR model that runs on CPU. It turns PDFs and images into markdown, text, or JSON with boxes, and reads handwriting, 80+ languages and pages at any angle. It runs on ONNX Runtime, with no PyTorch and no GPU.

The numbers from the README

  • Accuracy: 66.7 on OmniParseBench (71.4 with --math), ahead of PaddleOCR's full PP-StructureV3 pipeline at 58.4. Its text score of 46.7 is ahead of PaddleOCR's PP-OCR text models (35.8) and Tesseract (19.3).
  • Speed: about 44 pages per second on 32 CPU cores, which the README puts at 15x Tesseract and 10x PaddleOCR's PP-OCR text models on the same machine type and pages, and about 210 ms per page on 4 cores.
  • Size: the default models are about 76 MB, 30 MB of networks and 46 MB of language models for unsure lines.

These are the project's own benchmarks, so treat them as a reason to test on your documents, not as a guarantee.

Step 1: install

You need Python 3.10 or newer. The README lists testing on Linux (x86_64 and aarch64) and macOS on Apple silicon.

pip install indra-ocr
Enter fullscreen mode Exit fullscreen mode

The first run downloads the models from Hugging Face and caches them. To use a local copy, set INDRA_OCR_MODEL_DIR or pass --model-dir, which is what you want on an air-gapped server.

Step 2: convert PDFs from the command line

indra-ocr paper.pdf                       # markdown to stdout
indra-ocr *.pdf --out results/            # one .md per input, all cores
indra-ocr scan.png --format json          # lines, boxes, layout blocks, confidences
indra-ocr paper.pdf --format text         # plain text, one line per detected line
indra-ocr paper.pdf --mode ocr --timings  # ignore the text layer, OCR every page
indra-ocr paper.pdf --math                # write math as LaTeX
Enter fullscreen mode Exit fullscreen mode

The default --mode auto is the part that saves time on mixed archives: it reads the PDF text layer where it passes the checks and only OCRs what is missing. Use --mode ocr when a text layer is present but garbage, which happens a lot with old scans. Page headers and footers are left out of the markdown unless you pass --keep-furniture.

The CLI keeps going past files it cannot read and exits with 1 if any failed, so it is safe to point at a big folder.

Step 3: check results in the viewer

indra-ocr-viewer --port 8765
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:8765 and drop in a PDF or image. Each page shows its lines, layout blocks and reading order drawn over the page, with the rendered markdown, the lines with their confidences and the JSON. Files stay on your machine. Install pip install "indra-ocr[tesseract]" plus a Tesseract binary and the viewer can compare the two side by side, which is the fastest way to decide whether to switch.

Step 4: use it from Python

import indra_ocr

conv = indra_ocr.load(threads=4)          # mode="auto" | "ocr" | "text"
for i, page in conv.convert("paper.pdf"):
    print(page.source, page.timings["total_ms"])
    print(page.markdown)                  # headings, lists, tables as HTML
    page.blocks                           # [{"label", "bbox", "text"}] in reading order
    page.lines                            # [Line(box, text, conf)] in reading order
Enter fullscreen mode Exit fullscreen mode

For batches, convert_many runs one single-threaded worker per core and returns results in input order. Keep the if __name__ == "__main__": guard on macOS and Windows:

import indra_ocr

if __name__ == "__main__":
    for path, result in indra_ocr.convert_many(["a.pdf", "b.pdf"], cores=None):
        if isinstance(result, indra_ocr.InputError):
            continue                      # unreadable or locked file
        for i, page in result:
            print(path, i, page.source)
Enter fullscreen mode Exit fullscreen mode

Every block carries a label (Text, Section-Header, Table, Caption, Footnote, Form and more) and a bounding box, so feeding clean chunks to an LLM or a search index is a filter, not a regex project.

What people have built on it

The builds collected on the Indra page are mostly about replacing Tesseract in existing tools:

  • An OCRmyPDF engine plugin that runs Indra OCR instead of Tesseract and ships in a Paperless-ngx image, so Paperless keeps its skip, redo and force modes and only the recognition engine changes.
  • A Paperless-ngx 3.x parser plugin that runs Indra on ONNX Runtime on the CPU and produces searchable PDF/A archives with a text layer, with no extra service.
  • A WebAssembly port with a TypeScript API on npm that does the whole thing in the browser with no server.
  • A C# and .NET port on NuGet that keeps the same command line, viewer and output, with no Python needed.

If you run Paperless-ngx at home, those two plugins are the quickest real-world win.

When to reach for something else

Indra is built for speed on CPUs. For very long documents, Baidu's Unlimited OCR is an open OCR model aimed at parsing documents of any length within a finite context window, and it is worth comparing on that kind of input. The Indra README itself points to Datalab's larger Chandra model for the hardest handwriting, math, complex tables and degraded scans.

Caveats

  • Handwriting works but is the weak spot: the README reports 25.2% on the OmniParseBench handwriting tests.
  • The code is Apache 2.0, but the model weights use a modified OpenRAIL-M license: free for research, personal use and startups under $20M funding or revenue, and not for use that competes with Datalab's API. Check that before you ship it in a product.
  • This walkthrough follows the README and has not been run end to end by us.

Source repo: datalab-to/indra.

Want the setup commands for 50 AI repos people actually ship with? Get the free AI Repos That Actually Ship pack: https://dedupedai.com/ship-pack/?utm_source=devto&utm_medium=article&utm_campaign=shippack

Top comments (0)