The Problem
Document ingestion pipelines often rely on GPU‑heavy OCR engines that add latency and cost. A lightweight, CPU‑friendly OCR that still yields accurate text, layout, and reading‑order information is missing in many production stacks.
What This Does
Indra OCR delivers a 1 M‑parameter model that reads ~49 pages/s on a single CPU core.
indra_ocr/__main__.pyandindra_ocr/cli.pyexpose theindra‑ocrcommand line tool.- The core pipeline lives in
indra_ocr/pipeline.py, which stitches together: indra_ocr/det_post.py– post‑processing of line detections,indra_ocr/rec_preproc.py– image preprocessing for the recognizer,indra_ocr/models.py– ONNX runtime wrappers for the text recognizer,indra_ocr/layout_post.py&indra_ocr/order_post.py– layout block classification and reading‑order inference.indra_ocr/document.pyreads PDFs, preserving existing text layers for digital PDFs.indra_ocr/viewer.pypowers the optional web viewer (indra‑ocr‑viewer).
How It Is Wired
Execution starts at the console script defined in pyproject.toml, which imports indra_ocr/cli.py. cli.main() dispatches to pipeline.process() for each input file. pipeline.process() calls document.load() to read the PDF or image; if the PDF contains a text layer, document.TextLayer objects are returned unchanged. The image is fed to rec_preproc.preprocess() and then to models.Recognizer.recognize(). Recognized text, along with bounding boxes, is passed to det_post.postprocess() to merge lines into blocks. Finally, layout_post.classify_blocks() assigns semantic labels, and order_post.compute_order() produces a reading‑order list. The resulting JSON, markdown, or plain text is written by cli.output_writer(). The viewer command imports viewer.run() which serves the JSON locally.
Key functions with wide blast radius:
pipeline.process()– orchestrates the entire OCR flow.models.Recognizer.recognize()– the ONNX inference engine.det_post.postprocess()– merges lines into logical blocks.
How To Use It
Setup
pip install indra-ocr
Configuration No external config files are required; the model is downloaded automatically from Hugging Face the first run.
Running it
indra-ocr paper.pdf # stdout markdown
indra-ocr *.pdf --out results/ # one .md per file, parallelized
indra-ocr scan.png --format json # JSON with boxes and confidences
indra-ocr-viewer # launch web viewer on localhost:8765
These commands use the entry points defined in pyproject.toml.
Real‑World Use
A batch ingestion service for a publishing house can wire indra-ocr into a Celery queue. Each worker pulls a PDF, runs indra-ocr locally, and pushes the resulting JSON to an S3 bucket for downstream indexing.
# worker/task.py
from indra_ocr import pipeline
def process_pdf(path: str, out_dir: str):
result = pipeline.process(path)
with open(f"{out_dir}/{Path(path).stem}.json", "w") as fp:
json.dump(result, fp)
Code Health & Issues
- Low / Dependency lockfile missing –
pyproject.tomllists all dependencies but nopoetry.lockorpipfile.lockis present, making builds non‑reproducible. (File:pyproject.toml) - No CI for code coverage –
.github/workflows/publish.ymlonly builds; unit tests are present but coverage metrics are absent. (File:.github/workflows/publish.yml)
No other static analysis findings were reported.
The Bottom Line
Indra OCR offers a fast, CPU‑centric OCR solution with full layout awareness and a clean CLI. It is ideal for high‑volume, low‑budget pipelines that still need accurate reading order. The main trade‑off is the lack of a lockfile, which may affect reproducibility in tightly controlled environments.