Skip to main content

Batch OCR for PDFs with heading restoration and visual content integration

Project description

mistocr

PDF OCR is a critical bottleneck in AI pipelines. It’s often mentioned in passing, as if it’s a trivial step. Practice shows it’s far from it. Poorly converted PDFs mean garbage-in-garbage-out for downstream AI-system (RAG, …).

When Mistral AI released their state-of-the-art OCR model in March 2025, it opened new possibilities for large-scale document processing. While alternatives like datalab.to and docling.ai offer viable solutions, Mistral OCR delivers exceptional accuracy at a compelling price point.

mistocr emerged from months of real-world usage across projects requiring large-scale processing of niche-domain PDFs. It addresses two fundamental challenges that raw OCR output leaves unsolved:

  • Heading hierarchy restoration: Even state-of-the-art OCR sometimes produces inconsistent heading levels in large documents—a complex task to get right. mistocr uses LLM-based analysis to restore proper document structure, essential for downstream AI tasks.

  • Visual content integration: Charts, figures and diagrams are automatically classified and described, then integrated into the markdown. This makes visual information searchable and accessible for downstream applications.

  • Cost-efficient batch processing: By exclusively using Mistral’s batch API, mistocr cuts costs by 50% ($0.50 vs $1.00 per 1000 pages) while eliminating the boilerplate code typically required.

In short: Production-ready batch OCR with intelligent postprocessing that ensures your documents are actually usable for AI systems.

Get Started

Install latest from pypi, then:

$ pip install mistocr

Set your API keys:

import os
os.environ['MISTRAL_API_KEY'] = 'your-key-here'
os.environ['ANTHROPIC_API_KEY'] = 'your-key-here'  # for refine features (see Advanced Usage for other LLMs)

Complete Pipeline

Full pipeline with all features:

from mistocr.pipeline import pdf_to_md
await pdf_to_md('files/test/resnet.pdf', 'files/test/md_test')
Step 1/3: Running OCR on files/test/resnet.pdf...
Mistral batch job status: QUEUED
Mistral batch job status: RUNNING
Mistral batch job status: RUNNING
Step 2/3: Fixing heading hierarchy...
Step 3/3: Adding image descriptions...
Describing 7 images...
Saved descriptions to ocr_temp/resnet/img_descriptions.json
Adding descriptions to 12 pages...
Done! Enriched pages saved to files/test/md_test
Done!

This will (as indicated by the output):

  1. OCR the PDF using Mistral’s batch API
  2. Fix heading hierarchy inconsistencies
  3. Describe images (charts, diagrams) and add those descriptions into the markdown Save everything to files/test/md_test

The output structure will be:

files/test/md_test/
├── img/
│   ├── img-0.jpeg
│   ├── img-1.jpeg
│   └── ...
├── page_1.md
├── page_2.md
└── ...

Each page’s markdown will include inline image descriptions:

```markdown
![Figure 1](img/img-0.jpeg)
AI-generated image description:
___
A residual learning block...
___
```

To print the the processed markdown, you can use the read_pgs function. Here’s how:

Then to read the fully processed document:

from mistocr.pipeline import read_pgs
md = read_pgs('files/test/md_test')
print(md[:500])
# Deep Residual Learning for Image Recognition  ... page 1

Kaiming He Xiangyu Zhang Shaoqing Ren Jian Sun<br>Microsoft Research<br>\{kahe, v-xiangz, v-shren, jiansun\}@microsoft.com


## Abstract ... page 1

Deeper neural networks are more difficult to train. We present a residual learning framework to ease the training of networks that are substantially deeper than those used previously. We explicitly reformulate the layers as learning residual functions with reference to the layer inputs, ins

By default, read_pgs() joins all pages. Pass join=False to get a list of individual pages instead.

Advanced Usage

Batch process entire folders:

from mistocr.core import ocr_pdf

# Process all PDFs in a folder
output_dirs = ocr_pdf('path/to/pdf_folder', dst='output_folder')

Custom models and prompts for heading fixes:

from mistocr.refine import fix_hdgs

# Use a different model or custom prompt
fix_hdgs('ocr_output/doc1', 
         model='gpt-4o',
         prompt=your_custom_prompt)

Custom image description with rate limiting:

from mistocr.refine import add_img_descs

# Control API usage and customize descriptions
await add_img_descs('ocr_output/doc1',
                    model='claude-opus-4',
                    semaphore=5,  # More concurrent requests
                    delay=0.5)    # Shorter delay between calls

For complete control over each pipeline step, see the core, refine, and pipeline module documentation.

Known Limitations & Future Work

mistocr is under active development. Current limitations include:

  • No timeout on batch jobs: Jobs poll indefinitely until completion. If a job stalls, manual intervention is required.
  • Limited error handling: When batch jobs fail, error reporting and recovery options are minimal.
  • Progress monitoring: Currently limited to periodic status prints. Future versions will support callbacks or streaming updates for better real-time monitoring.

Contributions are welcome! If you encounter issues or have ideas for improvements, please open an issue or discussion on GitHub.

Developer Guide

If you are new to using nbdev here are some useful pointers to get you started.

Install mistocr in Development mode

# make sure mistocr package is installed in development mode
$ pip install -e .

# make changes under nbs/ directory
# ...

# compile to have changes apply to mistocr
$ nbdev_prepare

Documentation

Documentation can be found hosted on this GitHub repository’s pages. Additionally you can find package manager specific guidelines on conda and pypi respectively.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mistocr-0.2.1.tar.gz (20.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mistocr-0.2.1-py3-none-any.whl (17.6 kB view details)

Uploaded Python 3

File details

Details for the file mistocr-0.2.1.tar.gz.

File metadata

  • Download URL: mistocr-0.2.1.tar.gz
  • Upload date:
  • Size: 20.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for mistocr-0.2.1.tar.gz
Algorithm Hash digest
SHA256 d3b1e302d183ae8ea527a12c3ff974862104f2728b541d3e8d3b3c3764f85ece
MD5 47c7ccce3354476b48fca91721f62987
BLAKE2b-256 1e602edd005c60be298c6c6dbd7f844a9bc1d78a4fc2eb88acd922cb6868a3a2

See more details on using hashes here.

File details

Details for the file mistocr-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: mistocr-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 17.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for mistocr-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 689106b9263fe7b2873330d6e51ef4d0c938439ccf7e5d7aef508097a26f2e9c
MD5 72e18aa8185233a48276e7d880b605f9
BLAKE2b-256 9c00aa97aaadd29671cd00babd9ab6bc091f9bed67b7e9e38abdad0c1973f49d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page