Page-level document parsing to Markdown with OvisOCR2 — runs locally on Apple Silicon (MLX), ~1.9GB RAM
0
stars
3
commits
Python
primary language
Aug 20, 2026
updated
Page-level document parsing to Markdown, powered by OvisOCR2 — a compact 0.8B multimodal model that reads a document page image and writes structured Markdown in natural reading order: body text, LaTeX formulas, HTML tables, and cropped figure regions.
page2md wraps that capability into a small, production-shaped CLI: point it
at images or PDFs and get one Markdown tree per document, with figure crops
saved alongside the text and a summary.json recording characters, figures,
and per-page timing.
Runs locally on macOS (MPS), CUDA, or CPU — no vLLM or GPU cluster required.
uv venv --python 3.12 && source .venv/bin/activate
uv pip install -e . # installs the MLX backend (Apple Silicon)
# Parse every image/PDF in a folder
page2md samples -o output
# Or single files, dropping figure crops and using portable ![]() images
page2md invoice.png report.pdf -o output --no-figures
page2md contract.png -o output --md-images
# CUDA hosts: use the transformers backend instead
uv pip install -e ".[transformers]"
page2md samples -o output --backend transformers
The first run downloads the model (~1.7 GB) from the Hugging Face Hub and caches it locally.
output/
├── invoice/
│ ├── page-1.md # cleaned Markdown for the page
│ └── images/ # figure crops referenced by the Markdown
│ └── bbox_12_40_640_310.jpg
├── contract/
│ └── page-1.md
└── summary.json # per-page characters, figures, seconds
Multi-page PDFs produce one page-N.md per parsed page; --max-pages caps
how deep a PDF is read.
pages.py turns images and PDF pages into in-memory pages
(PDFs are rasterized to ~2000 px on their longest edge).mlx_runner.py (default, Apple Silicon) loads OvisOCR2 through
mlx-vlm with fused Metal kernels; model.py offers the transformers path
for CUDA hosts. Both follow the official parsing prompt with greedy
decoding and bounded pixel/token budgets (contract.py).clean.py ports the model card's truncated-repeat
cleanup; figures.py converts the model's [0, 1000)-scaled figure
placeholders into real JPEG crops next to the Markdown.writer.py emits summary.json so measurements are
reproducible.Source documents never leave your machine; only the page images are passed to the locally-loaded model.
samples/ contains three synthetic, license-clean pages (generated by
tools/make_samples.py) that exercise the three hard cases: a table-heavy
invoice, a dense contract, and a report with a chart region. examples/ holds
the parsed Markdown produced from them on a MacBook Pro (M2 Pro).
Throughput, extrapolation, and cost comparisons (local MLX vs rented cloud
GPU vs managed document-AI APIs) live in benchmark/ —
measured on n=20 stratified pages, with every estimate labeled.
Measured on a MacBook Pro (M2 Pro) with the MLX backend
(mlx-community/OvisOCR2-8bit, 8-bit), watchdog-monitored:
| Document | Characters | Figures | Seconds | Peak process RSS |
|---|---|---|---|---|
| invoice | 822 | 0 | 4.4 | 1.8 GB |
| contract | 1,045 | 0 | 3.6 | 1.8 GB |
| report | 463 | 1 | 2.6 | 1.8 GB |
The invoice's 4×5 table extracts cell-perfect as HTML; the report's chart is detected as a visual region and cropped next to the Markdown. Expect OCR-grade noise on dense text (split words, merged spaces) — verify before critical use.
Why not just transformers? The official card recipe targets vLLM on CUDA with flash-attention and triton GDN kernels. On macOS, the transformers fallback materializes quadratic full-attention matrices in float32 — a run with the card's
max_tokens=16384against a 4k-token page consumed ~37 GB of RAM before being killed. The MLX backend runs fused Metal kernels and stays under 2 GB; this package also capsmax_tokens(4096) and input pixels (~1.5M) by default for any backend. The transformers path remains available via--backend transformersfor CUDA hosts.
This project wraps ATH-MaaS/OvisOCR2 (Apache-2.0). If it helps you, cite the technical report:
@article{lu2026ovisocr2,
title={OvisOCR2 Technical Report},
author={Shiyin Lu and Yinglun Li and Yu Xia and Yuhui Chen and An-Yang Ji and
Jun-Peng Jiang and Qing-Guo Chen and Jianshan Zhao and En Lin and
Haijun Li and Cheng Qin and Zhao Xu and Weihua Luo},
journal={arXiv preprint arXiv:2607.13639},
year={2026}
}
Independent community project; not affiliated with or endorsed by ATH-MaaS.
Apache-2.0 — see LICENSE.
3 commits
Python
100.0%
Page-level document parsing to Markdown with OvisOCR2 — runs locally on Apple Silicon (MLX), ~1.9GB RAM
0
stars
3
commits
Python
primary language
Aug 20, 2026
updated
Page-level document parsing to Markdown, powered by OvisOCR2 — a compact 0.8B multimodal model that reads a document page image and writes structured Markdown in natural reading order: body text, LaTeX formulas, HTML tables, and cropped figure regions.
page2md wraps that capability into a small, production-shaped CLI: point it
at images or PDFs and get one Markdown tree per document, with figure crops
saved alongside the text and a summary.json recording characters, figures,
and per-page timing.
Runs locally on macOS (MPS), CUDA, or CPU — no vLLM or GPU cluster required.
uv venv --python 3.12 && source .venv/bin/activate
uv pip install -e . # installs the MLX backend (Apple Silicon)
# Parse every image/PDF in a folder
page2md samples -o output
# Or single files, dropping figure crops and using portable ![]() images
page2md invoice.png report.pdf -o output --no-figures
page2md contract.png -o output --md-images
# CUDA hosts: use the transformers backend instead
uv pip install -e ".[transformers]"
page2md samples -o output --backend transformers
The first run downloads the model (~1.7 GB) from the Hugging Face Hub and caches it locally.
output/
├── invoice/
│ ├── page-1.md # cleaned Markdown for the page
│ └── images/ # figure crops referenced by the Markdown
│ └── bbox_12_40_640_310.jpg
├── contract/
│ └── page-1.md
└── summary.json # per-page characters, figures, seconds
Multi-page PDFs produce one page-N.md per parsed page; --max-pages caps
how deep a PDF is read.
pages.py turns images and PDF pages into in-memory pages
(PDFs are rasterized to ~2000 px on their longest edge).mlx_runner.py (default, Apple Silicon) loads OvisOCR2 through
mlx-vlm with fused Metal kernels; model.py offers the transformers path
for CUDA hosts. Both follow the official parsing prompt with greedy
decoding and bounded pixel/token budgets (contract.py).clean.py ports the model card's truncated-repeat
cleanup; figures.py converts the model's [0, 1000)-scaled figure
placeholders into real JPEG crops next to the Markdown.writer.py emits summary.json so measurements are
reproducible.Source documents never leave your machine; only the page images are passed to the locally-loaded model.
samples/ contains three synthetic, license-clean pages (generated by
tools/make_samples.py) that exercise the three hard cases: a table-heavy
invoice, a dense contract, and a report with a chart region. examples/ holds
the parsed Markdown produced from them on a MacBook Pro (M2 Pro).
Throughput, extrapolation, and cost comparisons (local MLX vs rented cloud
GPU vs managed document-AI APIs) live in benchmark/ —
measured on n=20 stratified pages, with every estimate labeled.
Measured on a MacBook Pro (M2 Pro) with the MLX backend
(mlx-community/OvisOCR2-8bit, 8-bit), watchdog-monitored:
| Document | Characters | Figures | Seconds | Peak process RSS |
|---|---|---|---|---|
| invoice | 822 | 0 | 4.4 | 1.8 GB |
| contract | 1,045 | 0 | 3.6 | 1.8 GB |
| report | 463 | 1 | 2.6 | 1.8 GB |
The invoice's 4×5 table extracts cell-perfect as HTML; the report's chart is detected as a visual region and cropped next to the Markdown. Expect OCR-grade noise on dense text (split words, merged spaces) — verify before critical use.
Why not just transformers? The official card recipe targets vLLM on CUDA with flash-attention and triton GDN kernels. On macOS, the transformers fallback materializes quadratic full-attention matrices in float32 — a run with the card's
max_tokens=16384against a 4k-token page consumed ~37 GB of RAM before being killed. The MLX backend runs fused Metal kernels and stays under 2 GB; this package also capsmax_tokens(4096) and input pixels (~1.5M) by default for any backend. The transformers path remains available via--backend transformersfor CUDA hosts.
This project wraps ATH-MaaS/OvisOCR2 (Apache-2.0). If it helps you, cite the technical report:
@article{lu2026ovisocr2,
title={OvisOCR2 Technical Report},
author={Shiyin Lu and Yinglun Li and Yu Xia and Yuhui Chen and An-Yang Ji and
Jun-Peng Jiang and Qing-Guo Chen and Jianshan Zhao and En Lin and
Haijun Li and Cheng Qin and Zhao Xu and Weihua Luo},
journal={arXiv preprint arXiv:2607.13639},
year={2026}
}
Independent community project; not affiliated with or endorsed by ATH-MaaS.
Apache-2.0 — see LICENSE.
3 commits
Python
100.0%