Parallel LLM Vision OCR plugin for MarkItDown — extracts text from images in PDF, DOCX, PPTX, and XLSX files with 3x speedup via ThreadPoolExecutor batching.
Project description
MarkItDown OCR Plugin
LLM Vision plugin for MarkItDown that extracts text from images embedded in PDF, DOCX, PPTX, and XLSX files.
Key features:
- Parallel batch OCR via
ThreadPoolExecutor— up to 3x faster than serial processing - Configurable concurrency with
max_workers(default 5) - Uses the same
llm_client/llm_modelpattern that MarkItDown already supports - No new ML libraries or binary dependencies required
Installation
pip install markitdown-ocr-parallel
The plugin uses whatever OpenAI-compatible client you already have:
pip install openai
Quick Start
Python API
from markitdown import MarkItDown
from openai import OpenAI
client = OpenAI()
md = MarkItDown(
enable_plugins=True,
llm_client=client,
llm_model="gpt-4o",
max_workers=5, # new! parallel OCR (default: 5)
)
result = md.convert("document_with_images.pdf")
print(result.text_content)
Command Line
markitdown document.pdf --use-plugins --llm-client openai --llm-model gpt-4o
Any OpenAI-Compatible Provider
# Example: Alibaba Cloud Bailian (通义千问)
from openai import OpenAI
client = OpenAI(
api_key="your-api-key",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)
md = MarkItDown(
enable_plugins=True,
llm_client=client,
llm_model="qwen3.6-flash",
max_workers=10,
)
Performance
Parallel batch processing significantly reduces conversion time for documents with multiple images:
| Document | Images | Serial (1 worker) | Parallel (5 workers) | Speedup |
|---|---|---|---|---|
| Scanned contract (5 pages) | 5 | 74.4s | 24.1s | 3.1x |
| PDF with 3 embedded images | 3 | 4.6s | 3.3s | 1.4x |
| PDF with 2 embedded images | 2 | 2.2s | 1.2s | 1.9x |
| Scanned report (3 pages) | 3 | 6.6s | 2.6s | 2.6x |
Tune max_workers based on your API rate limits and document complexity.
How It Works
When MarkItDown(enable_plugins=True, llm_client=..., llm_model=...) is called:
- MarkItDown discovers the plugin via the
markitdown.pluginentry point group - Four OCR-enhanced converters are registered at priority -1.0 — before built-in converters (priority 0.0)
When a file is converted, the plugin uses a collect → batch → assemble pipeline:
- Collection Phase: All images are extracted from the document upfront (fast, no API calls)
- Batch OCR Phase: All images are sent to the LLM in parallel via
ThreadPoolExecutor - Assembly Phase: OCR results are inserted inline, preserving document structure
This replaces the original serial approach where each image was OCR'd one-by-one.
Supported File Formats
- Embedded images extracted by position, interleaved with text in reading order
- Scanned PDFs detected automatically — full pages rendered at 300 DPI and OCR'd
- Malformed PDFs fall back to PyMuPDF page rendering
- Parallel: all page images OCR'd simultaneously
DOCX
- Images extracted via document part relationships
- Placeholder injection pattern prevents markdown converter from escaping OCR markers
- Parallel: all images OCR'd in one batch before HTML→Markdown conversion
PPTX
- Two-round parallel: Round 1 → LLM caption for all images; Round 2 → OCR backup for failed images
- Recursive shape walking handles groups, placeholders, and picture shapes
- Parallel: images from all slides processed together
XLSX
- Images extracted per sheet with cell position tracking
- Parallel: images from all sheets collected and OCR'd in one batch
Output Format
Every extracted OCR block is wrapped as:
*[Image OCR]
<extracted text>
[End OCR]*
Configuration
| Parameter | Default | Description |
|---|---|---|
llm_client |
— | OpenAI-compatible client (required) |
llm_model |
— | Model name, e.g. gpt-4o, qwen3.6-flash (required) |
llm_prompt |
"Extract all text..." |
Custom OCR extraction prompt |
max_workers |
5 |
Max parallel OCR threads |
Development
Running Tests
git clone https://github.com/echojfree/markitdown-ocr-parallel.git
cd markitdown-ocr
pip install -e ".[llm]"
pytest tests/ -v
Building
pip install build
python -m build
Changelog
0.2.3 — PPTX newline fix
- Fix: PPTX converter emitted literal
\n(backslash-n) instead of real newlines, producing malformed markdown. Now aligned with the upstream core PPTX converter — clean, readable output. - Perf: XLSX now reads all sheets in a single pass instead of re-parsing the workbook once per sheet.
0.2.0 — Parallel Batch OCR
- Parallel OCR:
extract_text_batch()method usingThreadPoolExecutorfor 2-3x speedup max_workersparameter (default 5) onLLMVisionOCRService- PDF: single-pass image collection + batch OCR + Y-position interleaving
- DOCX: batch OCR before HTML→Markdown pipeline
- PPTX: two-round parallel (LLM caption → OCR fallback) with pre-scan phase
- XLSX: cross-sheet image collection + batch OCR
- Fix: scanned PDF fallback detection (regex-strip page headers)
- All 36 tests pass
0.1.0 — Initial Release
- LLM Vision OCR for PDF, DOCX, PPTX, XLSX
- Full-page OCR fallback for scanned PDFs
- Priority-based converter replacement
License
MIT — see LICENSE.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file markitdown_ocr_parallel-0.2.3.tar.gz.
File metadata
- Download URL: markitdown_ocr_parallel-0.2.3.tar.gz
- Upload date:
- Size: 812.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3f7e372604868be6e7a534599b4670d9e9b3aa8f042a98dfb78197450106bc9
|
|
| MD5 |
ee05d73dcff8f62eb47361c52e5d85e4
|
|
| BLAKE2b-256 |
4dfd75168a358ecce0bb399302c345abb0758f2e5f84638ef18aadeaf2e95c98
|
File details
Details for the file markitdown_ocr_parallel-0.2.3-py3-none-any.whl.
File metadata
- Download URL: markitdown_ocr_parallel-0.2.3-py3-none-any.whl
- Upload date:
- Size: 22.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
14e37990d254d44c827e919714ed0587e22bb69d136877ea8e8bf830484598bf
|
|
| MD5 |
46e6f5011c052a497681649114467f87
|
|
| BLAKE2b-256 |
18cc397110bf13aa3c06f950088c6bb73868c72ba6a8db9e0d333bc77914f83c
|