Skip to main content

wormOCR

Local OCR for printed and handwritten English text.

PyPI License: MIT Python 3.10+ PyTorch

Install the package, download the weights once, and read text on your machine. No cloud API. No silent downloads on import.

Quick start

pip install wormocr
wormocr download
from wormocr import OCR

ocr = OCR()
print(ocr.predict("invoice_line.png").text)
# Invoice #1042 Total: $18.50

That uses wormocr-2M (2.17M parameters, 8.7 MB). For the larger model:

wormocr download wormocr-9M
ocr = OCR(model="wormocr-9M")

v1.0.0. Clean printed lines and receipts are strong. Messy notes and historical handwriting are usable and still short of a careful human reading. See Limitations.


Contents

Highlights

  • Small enough to ship. wormocr-2M is 8.7 MB. wormocr-9M is 33.5 MB. Both are ordinary CRNN + CTC models.
  • Fast on CPU. About 14 ms per line end to end with wormocr-2M on a laptop CPU. No GPU required for inference.
  • Fully local. Importing the package never touches the network. Weights come from a pinned GitHub release only when you run wormocr download, and every file is checked against a SHA-256 hash.
  • Print and handwriting. One recognizer family for invoices, receipts, handwritten names, notes, and historical lines. No third-party OCR weights.
  • Full toolkit. Train, resume, evaluate (CER / WER / exact match), export ONNX, benchmark, page layout, PDFs, and a local browser demo.

Models

wormocr-2M wormocr-9M
Parameters 2,174,880 8,776,416
Weights (.safetensors, FP32) 8.7 MB 33.5 MB
ONNX (FP32) 8.7 MB 33.5 MB
Architecture Separable CNN + 2-layer BiGRU (256) + CTC Same family, BiGRU 512
Input height 32 px 48 px
Output stride 4 4
Character set 95 printable ASCII + CTC blank Same
Training data Synthetic print & handwriting, GNHK, names, Bentham, CORD. FUNSD / IAM / RxHand evaluation only Same mix
Best checkpoint step 58,000 of 60,000 step 19,000 (early-stopped at 30,000)
Default for OCR() Yes Pass model="wormocr-9M"

Results

Character error rate (CER), greedy decode, NVIDIA RTX 5060 Laptop GPU. wormocr-2M measured 23 October 2026. wormocr-9M measured 6 November 2026 on the same frozen test lines. Lower is better. Older printed-only scores live in docs/BENCHMARKS.md.

Test set Lines wormocr-2M wormocr-9M
Synthetic printed lines 1,000 3.1% 3.7%
FUNSD form words (eval only) 8,526 11.5% 10.2%
CORD receipts 795 3.7% 3.1%
Synthetic handwriting 1,000 9.2% 7.1%
Handwritten form names 26,170 5.3% 5.3%
GNHK handwritten notes 2,410 23.3% 19.2%
IAM lines (eval only) 2,915 22.4% 18.3%
Bentham historical lines 1,135 15.1% 8.8%
RxHand words, case-insensitive (eval only) 1,107 37.9% 35.2%

FUNSD, IAM, and RxHand were never used for training.

Speed (CPU, wormocr-2M)

Intel Core i9-14900HX, PyTorch 2.14.1, FP32. Medians after warmup (22 October 2026).

Path Batch Median Throughput
Model only, 256 px wide 1 26.0 ms 38 lines/s
Model only, 256 px wide 8 51.0 ms 157 lines/s
Preprocess + model + decode 1 14.4 ms 69 lines/s
Preprocess + model + decode 8 42.2 ms 189 lines/s

wormocr-9M is larger and slower. Use it when the handwriting gains matter more than size.

Training cost

Run Hardware Steps Effective batch Wall time
wormocr-2M RTX 5060 Laptop, mixed precision 60,000 64 125 min
wormocr-9M RTX 5060 Laptop, mixed precision 30,000 (best at 19,000) 64 (16 × 4 accum) about 150 min

Installation

Requires Python 3.10+.

pip install wormocr
wormocr download              # wormocr-2M
# wormocr download wormocr-9M

Optional extras:

pip install "wormocr[pdf]"      # PDF input
pip install "wormocr[export]"   # ONNX export
pip install "wormocr[dev]"      # pytest

A CPU build of PyTorch is enough for inference. For GPU training, install a CUDA build from pytorch.org first, then install wormOCR.

From source

git clone https://github.com/arjun988/wormOCR.git
cd wormOCR
python -m venv .venv
.venv\Scripts\activate            # Windows
# source .venv/bin/activate       # macOS / Linux
pip install -e ".[dev,export,pdf]"
wormocr download
wormocr info

Weights

Weights are not on PyPI and not in git. They are GitHub release assets, verified by SHA-256 hashes baked into the package:

Release Size
wormocr-2M 8.7 MB
wormocr-9M 33.5 MB
wormocr download
wormocr download wormocr-9M
wormocr download wormocr-2M --force

Files land in ~/.cache/wormocr/<name>/ (override with WORMOCR_HOME or --dir). If weights are missing, OCR() tells you to run the download command. You can also pass a local file:

OCR(checkpoint="path/to/wormocr-2M.safetensors")
OCR(model="wormocr-9M")
OCR(checkpoint="artifacts/checkpoints/tiny/best.pt")

Training checkpoints (.pt) are PyTorch pickles. Load only files you trust (SECURITY.md).

Usage

Python

from wormocr import OCR

ocr = OCR()
print(ocr.predict("line.png", mode="line").text)

page = ocr.predict("page.png")   # mode="auto" by default
for item in page.lines:
    print(item.order, item.text, item.confidence, item.bbox)

predict accepts a path, a PIL image, or a PDF (with the pdf extra). Pass device="cuda" or beam_width=8 if you need them.

Mode Behavior
line Whole image is one text line
page Line detection, deskew, reading order
auto line for short wide images, else page

Command line

wormocr download
wormocr predict --image line.png
wormocr predict --image page.png --json
wormocr predict --image document.pdf --model wormocr-9M

Commands: info, download, predict, train, evaluate, analyze, export, benchmark, generate, serve.

Web UI

wormocr serve            # http://127.0.0.1:8000

Drop in an image or PDF, paste with Ctrl+V, or try the sample invoice. Recognition streams while the model works. The server listens on localhost by default; files stay on your machine.

wormocr serve --model wormocr-9M --device cuda --port 8000

Training

Clone the repository for configs and scripts. Synthetic lines are rendered from fonts on your machine. The released weights come from configs/unified.yaml (wormocr-2M) and configs/wormocr-9M.yaml (wormocr-9M).

wormocr train --config configs/unified.yaml --mode baseline
wormocr train --config configs/wormocr-9M.yaml --mode baseline

Override any field with --set section.key=value. Training writes best.pt, last.pt, best_weights.pt, and train_log.jsonl.

Mode Purpose
smoke Short check that data, loss, validation, and checkpoints run
debug Tiny network experiment
baseline Full run as written in the config
resume Continue from last.pt

Own labels: relative/path.png<TAB>exact text. See docs/DATASETS.md.

wormocr train --config configs/tiny.yaml --mode baseline \
  --set data.labels=data/train.tsv --set data.image_root=data/images \
  --set data.val_labels=data/val.tsv --set data.val_image_root=data/images

Evaluation, export, and benchmarks

wormocr evaluate --model artifacts/checkpoints/tiny/best.pt --synthetic-samples 800
wormocr evaluate --model artifacts/checkpoints/tiny/best.pt --labels data/val.tsv --image-root data/images --errors errors.md

python scripts/prepare_funsd.py artifacts/datasets/funsd/dataset/testing_data artifacts/datasets/funsd_test
wormocr evaluate --model artifacts/checkpoints/unified/best.pt --labels artifacts/datasets/funsd_test/labels.tsv

wormocr export --model artifacts/checkpoints/tiny/best.pt --format onnx --output artifacts/export/tiny.onnx
wormocr benchmark --model artifacts/checkpoints/tiny/best.pt --output artifacts/benchmarks/report.md

CER counts every character, including spaces. INT8 ONNX exists (--format onnx-int8) but is not recommended: it barely shrinks the file and drifts from FP32.

How it works

  1. Preprocessing. Grayscale, resize to model height (32 or 48), normalize to [-1, 1], pad with the line's background.
  2. Encoder. Four-stage depthwise-separable CNN. Height pools to 2; width by 4.
  3. Sequence model. Linear projection into a 2-layer bidirectional GRU (256 or 512).
  4. Decoder. CTC over 96 classes, greedy by default; beam search optional.
  5. Page layout. Deskew, Otsu, projection profiles, gutters, reading order.

Training uses CTC + AdamW, warmup then cosine decay, EMA 0.999, AMP on CUDA, and scan-style augmentation. Details: docs/ARCHITECTURE.md, docs/DATASETS.md.

Project layout

wormOCR/
├── configs/            unified.yaml → wormocr-2M; wormocr-9M.yaml → wormocr-9M
├── docs/               Architecture, datasets, benchmarks, roadmap
├── scripts/            Dataset prep and helpers
├── src/wormocr/
│   ├── api.py          OCR class
│   ├── cli.py          wormocr command
│   ├── hub.py          Releases, download, SHA-256 checks
│   ├── models/         CRNN
│   ├── training/       Loop and EMA
│   ├── inference/      Engine and CTC decode
│   ├── detection/      Line finding and reading order
│   ├── evaluation/     CER / WER
│   ├── export/         ONNX
│   └── web/            Local serve UI
└── tests/

Limitations

  • English printable ASCII only. Accented letters and other scripts are rejected, not silently dropped.
  • Messy handwriting is still hard. On wormocr-9M: GNHK 19.2%, IAM 18.3%, Bentham 8.8%, RxHand 35.2% case-insensitive (eval only).
  • Real scans beat clean synth. FUNSD form words are about 10–11.5% CER. Tiny one-character crops remain weak.
  • Simple page layout. Projection profiles, not a learned detector. Tables and heavy clutter can fail.
  • No language-model correction. Output is exactly what the decoder emits.
  • No third-party OCR baseline yet. Tesseract / PaddleOCR were not measured on this machine.

See docs/ROADMAP.md.

Contributing

See CONTRIBUTING.md.

python -m pytest

When you report accuracy, include the dataset, line count, exact command, and a set the model was not trained on.

Citation

@software{shukla2026wormocr,
  author  = {Shukla, Arjun},
  title   = {wormOCR: a small local OCR engine for printed and handwritten English text},
  year    = {2026},
  version = {1.0.0},
  url     = {https://github.com/arjun988/wormOCR}
}

Also CITATION.cff.

License

MIT License. Copyright (c) 2026 arjun shukla.

wormOCR ships no third-party OCR weights and redistributes no fonts. FUNSD is evaluation-only and is not included in this repository.

Metadata

Release files for wormocr 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wormocr 1.0.0
File Size Uploaded Provenance Actions
wormocr-1.0.0.tar.gz 99.0 kB Without provenance Details for wormocr-1.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for wormocr 1.0.0
File Interpreter ABI Platform Actions
wormocr-1.0.0-py3-none-any.whl Python 3 none any Details for wormocr-1.0.0-py3-none-any.whl

Total release size: 193.7 kB

Release files / wormocr-1.0.0.tar.gz

Download URL wormocr-1.0.0.tar.gz
Size 99.0 kB
Tags Source
SHA-256 checksum
How to use checksums
7465322cdaae2257152ee4f771b0e77ce9e743b5c2ecc939c88289fb6ffc4b1f
BLAKE2b-256 checksum
How to use checksums
191b67bd9caff0fba44badd846ed36b40d37ae63f361ed5d2bcc136dfabaa1af
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release files / wormocr-1.0.0-py3-none-any.whl

Download URL wormocr-1.0.0-py3-none-any.whl
Size 94.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
df10baacae0509409201899e9b7fc56056aee54ac091bd1aec6a39be1c43054a
BLAKE2b-256 checksum
How to use checksums
32b321c38c4e16b1e5daa49c6c628258a8f2ac1aae8b9f8fc0f1f15182db95dd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Security for wormocr 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page