fcomendoza-site/page2md

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

applesilicon
document-parsing
markdown
mlx
multimodal
ocr
ovisocr2
Browse cluster: Document Format Conversion & Parsing

README

page2md

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.

Quickstart

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 layout

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.

How it works

  1. Ingestpages.py turns images and PDF pages into in-memory pages (PDFs are rasterized to ~2000 px on their longest edge).
  2. Parsemlx_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).
  3. Clean & renderclean.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.
  4. Recordwriter.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

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).

Benchmark

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.

Results on synthetic samples

Measured on a MacBook Pro (M2 Pro) with the MLX backend (mlx-community/OvisOCR2-8bit, 8-bit), watchdog-monitored:

DocumentCharactersFiguresSecondsPeak process RSS
invoice82204.41.8 GB
contract1,04503.61.8 GB
report46312.61.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=16384 against 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 caps max_tokens (4096) and input pixels (~1.5M) by default for any backend. The transformers path remains available via --backend transformers for CUDA hosts.

Limitations

  • Parsing is page-level by design; cross-page reading order is not reconstructed.
  • Like any OCR-grade model, output can be incorrect or incomplete — verify before using in critical workflows (see the model card's disclaimer).
  • Formulas render as LaTeX and tables as raw HTML inside Markdown.

Attribution

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.

License

Apache-2.0 — see LICENSE.

Contributors

zaibaiman-dev

3 commits

fcomendoza-site/page2md

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

applesilicon
document-parsing
markdown
mlx
multimodal
ocr
ovisocr2
Browse cluster: Document Format Conversion & Parsing

README

page2md

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.

Quickstart

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 layout

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.

How it works

  1. Ingestpages.py turns images and PDF pages into in-memory pages (PDFs are rasterized to ~2000 px on their longest edge).
  2. Parsemlx_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).
  3. Clean & renderclean.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.
  4. Recordwriter.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

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).

Benchmark

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.

Results on synthetic samples

Measured on a MacBook Pro (M2 Pro) with the MLX backend (mlx-community/OvisOCR2-8bit, 8-bit), watchdog-monitored:

DocumentCharactersFiguresSecondsPeak process RSS
invoice82204.41.8 GB
contract1,04503.61.8 GB
report46312.61.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=16384 against 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 caps max_tokens (4096) and input pixels (~1.5M) by default for any backend. The transformers path remains available via --backend transformers for CUDA hosts.

Limitations

  • Parsing is page-level by design; cross-page reading order is not reconstructed.
  • Like any OCR-grade model, output can be incorrect or incomplete — verify before using in critical workflows (see the model card's disclaimer).
  • Formulas render as LaTeX and tables as raw HTML inside Markdown.

Attribution

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.

License

Apache-2.0 — see LICENSE.

Contributors

zaibaiman-dev

3 commits

Languages

Python

100.0%