§12.14

Extract Text from PDFs and Images with Local OCR

Install indra-ocr, run a CPU-only OCR model on an image, pull Markdown from a multi-page PDF, batch files, and find where the weights cache.

by published 19 Oct 2026 checked against docs 19 Oct 2026 3 min in AI media Markdown

On this page7 sections
  1. 1. Pick a local OCR model
  2. 2. Install the package
  3. 3. OCR a single image
  4. 4. Extract a multi-page PDF
  5. 5. Batch many files
  6. 6. Find the cached weights
  7. If it fails

Scanned PDFs and photos of documents hide their text from search, copy-paste and RAG. A local OCR model turns those pixels back into characters on your own machine, with no upload and no per-page API bill. This guide uses datalab-to/indra-ocr: a small, CPU-only model that reads images and PDFs and returns clean Markdown.

1. Pick a local OCR model

OCR models split into two camps. Large vision-language models like baidu/Unlimited-OCR (about 3B parameters) are accurate but want a CUDA GPU; weigh that against your hardware in how much RAM or VRAM a model needs. indra-ocr sits at the other end: it runs on ONNX Runtime on the CPU, needs no PyTorch, and downloads about 76 MB of weights. For a copy-paste pipeline that works on any laptop, start there.

2. Install the package

pip install indra-ocr

That pulls the ONNX runtime and the helper code; the model weights download on first use, and Python 3.10 or newer is required.

3. OCR a single image

import indra_ocr

conv = indra_ocr.load(threads=4)  # mode="auto" | "ocr" | "text"

for i, page in conv.convert("scan.png"):
    print(page.text)

load() fetches the weights once and caches them, and convert() yields one page per input, where page.text holds the plain extracted text.

4. Extract a multi-page PDF

for i, page in conv.convert("paper.pdf"):
    print(f"--- page {i} ---")
    print(page.markdown)

Pass a PDF and the same loop runs page by page; page.markdown keeps headings, lists and tables as structured Markdown instead of a flat text dump.

5. Batch many files

if __name__ == "__main__":
    for path, result in indra_ocr.convert_many(["a.pdf", "b.pdf"], cores=None):
        print(path, "done")

convert_many runs one worker per CPU core and returns results in input order; it spawns processes, so the __main__ guard is mandatory.

6. Find the cached weights

On first run the weights download from the Hub and cache alongside your other models; see where Hugging Face caches files. To pin a fixed copy for an air-gapped box or CI, point INDRA_OCR_MODEL_DIR at a folder holding a pre-downloaded copy and nothing hits the network.

If it fails

  • ModuleNotFoundError: No module named 'indra_ocr' The package installed into a different environment. Activate the right venv and rerun pip install indra-ocr, and confirm python --version is 3.10 or newer.
  • RuntimeError: An attempt has been made to start a new process before the current process has finished its bootstrapping phase convert_many spawned workers without a guard. Wrap the call in if __name__ == "__main__":.
  • First call stalls while “downloading” It is fetching the model from the Hub. Pre-download once, point INDRA_OCR_MODEL_DIR at that folder, and later runs stay offline.

Next: describe and query images with a vision model · run a model with transformers pipeline() · how much RAM or VRAM a model needs.

← All AI media plates · Search all guides

↑↓ move↵ openalt+↵ copy first command

Keyboard

⌘/ctrl+K or /
Search all guides
alt+↵
In search: copy the guide's first command
j / k
Move through a list of guides
c
On a guide: copy its first command
t
Toggle light / dark
?
This list