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.
On this page7 sections
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 rerunpip install indra-ocr, and confirmpython --versionis 3.10 or newer.RuntimeError: An attempt has been made to start a new process before the current process has finished its bootstrapping phaseconvert_manyspawned workers without a guard. Wrap the call inif __name__ == "__main__":.- First call stalls while “downloading” It is fetching the model from the Hub. Pre-download once, point
INDRA_OCR_MODEL_DIRat 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.