wormocr 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. wormocr-1.0.0/LICENSE +21 -0
  2. wormocr-1.0.0/PKG-INFO +377 -0
  3. wormocr-1.0.0/README.md +338 -0
  4. wormocr-1.0.0/pyproject.toml +58 -0
  5. wormocr-1.0.0/setup.cfg +4 -0
  6. wormocr-1.0.0/src/wormocr/__init__.py +18 -0
  7. wormocr-1.0.0/src/wormocr/__main__.py +4 -0
  8. wormocr-1.0.0/src/wormocr/api.py +229 -0
  9. wormocr-1.0.0/src/wormocr/benchmark/__init__.py +5 -0
  10. wormocr-1.0.0/src/wormocr/benchmark/report.py +403 -0
  11. wormocr-1.0.0/src/wormocr/checkpoints.py +55 -0
  12. wormocr-1.0.0/src/wormocr/cli.py +308 -0
  13. wormocr-1.0.0/src/wormocr/config.py +220 -0
  14. wormocr-1.0.0/src/wormocr/data/__init__.py +5 -0
  15. wormocr-1.0.0/src/wormocr/data/dataset.py +110 -0
  16. wormocr-1.0.0/src/wormocr/data/fonts.py +173 -0
  17. wormocr-1.0.0/src/wormocr/data/handwriting.py +304 -0
  18. wormocr-1.0.0/src/wormocr/data/lexicon.py +105 -0
  19. wormocr-1.0.0/src/wormocr/data/lexicon_medical.py +48 -0
  20. wormocr-1.0.0/src/wormocr/data/mixed.py +211 -0
  21. wormocr-1.0.0/src/wormocr/data/synthetic.py +445 -0
  22. wormocr-1.0.0/src/wormocr/detection/__init__.py +5 -0
  23. wormocr-1.0.0/src/wormocr/detection/lines.py +382 -0
  24. wormocr-1.0.0/src/wormocr/evaluation/__init__.py +5 -0
  25. wormocr-1.0.0/src/wormocr/evaluation/errors.py +145 -0
  26. wormocr-1.0.0/src/wormocr/evaluation/metrics.py +105 -0
  27. wormocr-1.0.0/src/wormocr/evaluation/robustness.py +43 -0
  28. wormocr-1.0.0/src/wormocr/exceptions.py +25 -0
  29. wormocr-1.0.0/src/wormocr/export/__init__.py +5 -0
  30. wormocr-1.0.0/src/wormocr/export/onnx_export.py +75 -0
  31. wormocr-1.0.0/src/wormocr/hub.py +157 -0
  32. wormocr-1.0.0/src/wormocr/inference/__init__.py +5 -0
  33. wormocr-1.0.0/src/wormocr/inference/decode.py +113 -0
  34. wormocr-1.0.0/src/wormocr/inference/engine.py +122 -0
  35. wormocr-1.0.0/src/wormocr/models/__init__.py +5 -0
  36. wormocr-1.0.0/src/wormocr/models/crnn.py +236 -0
  37. wormocr-1.0.0/src/wormocr/preprocessing.py +135 -0
  38. wormocr-1.0.0/src/wormocr/py.typed +0 -0
  39. wormocr-1.0.0/src/wormocr/runtime.py +56 -0
  40. wormocr-1.0.0/src/wormocr/training/__init__.py +5 -0
  41. wormocr-1.0.0/src/wormocr/training/loop.py +645 -0
  42. wormocr-1.0.0/src/wormocr/vocabulary.py +69 -0
  43. wormocr-1.0.0/src/wormocr/web/__init__.py +5 -0
  44. wormocr-1.0.0/src/wormocr/web/server.py +505 -0
  45. wormocr-1.0.0/src/wormocr/web/static/app.js +514 -0
  46. wormocr-1.0.0/src/wormocr/web/static/index.html +141 -0
  47. wormocr-1.0.0/src/wormocr/web/static/style.css +379 -0
  48. wormocr-1.0.0/src/wormocr.egg-info/PKG-INFO +377 -0
  49. wormocr-1.0.0/src/wormocr.egg-info/SOURCES.txt +65 -0
  50. wormocr-1.0.0/src/wormocr.egg-info/dependency_links.txt +1 -0
  51. wormocr-1.0.0/src/wormocr.egg-info/entry_points.txt +2 -0
  52. wormocr-1.0.0/src/wormocr.egg-info/requires.txt +15 -0
  53. wormocr-1.0.0/src/wormocr.egg-info/top_level.txt +1 -0
  54. wormocr-1.0.0/tests/test_api.py +81 -0
  55. wormocr-1.0.0/tests/test_cli.py +55 -0
  56. wormocr-1.0.0/tests/test_decoder.py +48 -0
  57. wormocr-1.0.0/tests/test_detection.py +81 -0
  58. wormocr-1.0.0/tests/test_hub.py +66 -0
  59. wormocr-1.0.0/tests/test_metrics.py +25 -0
  60. wormocr-1.0.0/tests/test_mixed.py +88 -0
  61. wormocr-1.0.0/tests/test_model.py +55 -0
  62. wormocr-1.0.0/tests/test_onnx.py +49 -0
  63. wormocr-1.0.0/tests/test_preprocess.py +48 -0
  64. wormocr-1.0.0/tests/test_synthetic.py +30 -0
  65. wormocr-1.0.0/tests/test_train.py +184 -0
  66. wormocr-1.0.0/tests/test_vocabulary.py +29 -0
  67. wormocr-1.0.0/tests/test_web.py +101 -0
wormocr-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 arjun shukla
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
wormocr-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,377 @@
1
+ Metadata-Version: 2.4
2
+ Name: wormocr
3
+ Version: 1.0.0
4
+ Summary: Local OCR for printed and handwritten English text. Small CRNN models, CPU-friendly, no cloud.
5
+ Author: arjun shukla
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/arjun988/wormOCR
8
+ Project-URL: Repository, https://github.com/arjun988/wormOCR
9
+ Project-URL: Issues, https://github.com/arjun988/wormOCR/issues
10
+ Project-URL: Release-notes, https://github.com/arjun988/wormOCR/releases
11
+ Keywords: ocr,ctc,crnn,pytorch,onnx,handwriting,document
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Classifier: Topic :: Scientific/Engineering :: Image Recognition
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: numpy>=1.26
27
+ Requires-Dist: pillow>=10
28
+ Requires-Dist: pyyaml>=6
29
+ Requires-Dist: safetensors>=0.4
30
+ Requires-Dist: torch>=2.2
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=8; extra == "dev"
33
+ Provides-Extra: export
34
+ Requires-Dist: onnx>=1.16; extra == "export"
35
+ Requires-Dist: onnxruntime>=1.18; extra == "export"
36
+ Provides-Extra: pdf
37
+ Requires-Dist: pypdfium2>=4; extra == "pdf"
38
+ Dynamic: license-file
39
+
40
+ # wormOCR
41
+
42
+ **Local OCR for printed and handwritten English text.**
43
+
44
+ [![PyPI](https://img.shields.io/pypi/v/wormocr.svg)](https://pypi.org/project/wormocr/)
45
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
46
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
47
+ [![PyTorch](https://img.shields.io/badge/PyTorch-2.2%2B-ee4c2c.svg)](https://pytorch.org)
48
+
49
+ Install the package, download the weights once, and read text on your machine. No cloud API. No silent downloads on import.
50
+
51
+ ## Quick start
52
+
53
+ ```bash
54
+ pip install wormocr
55
+ wormocr download
56
+ ```
57
+
58
+ ```python
59
+ from wormocr import OCR
60
+
61
+ ocr = OCR()
62
+ print(ocr.predict("invoice_line.png").text)
63
+ # Invoice #1042 Total: $18.50
64
+ ```
65
+
66
+ That uses **wormocr-2M** (2.17M parameters, 8.7 MB). For the larger model:
67
+
68
+ ```bash
69
+ wormocr download wormocr-9M
70
+ ```
71
+
72
+ ```python
73
+ ocr = OCR(model="wormocr-9M")
74
+ ```
75
+
76
+ > **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](#limitations).
77
+
78
+ ---
79
+
80
+ ## Contents
81
+
82
+ - [Highlights](#highlights)
83
+ - [Models](#models)
84
+ - [Results](#results)
85
+ - [Installation](#installation)
86
+ - [Usage](#usage)
87
+ - [Training](#training)
88
+ - [Evaluation, export, and benchmarks](#evaluation-export-and-benchmarks)
89
+ - [How it works](#how-it-works)
90
+ - [Project layout](#project-layout)
91
+ - [Limitations](#limitations)
92
+ - [Contributing](#contributing)
93
+ - [Citation](#citation)
94
+ - [License](#license)
95
+
96
+ ## Highlights
97
+
98
+ - **Small enough to ship.** wormocr-2M is 8.7 MB. wormocr-9M is 33.5 MB. Both are ordinary CRNN + CTC models.
99
+ - **Fast on CPU.** About 14 ms per line end to end with wormocr-2M on a laptop CPU. No GPU required for inference.
100
+ - **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.
101
+ - **Print and handwriting.** One recognizer family for invoices, receipts, handwritten names, notes, and historical lines. No third-party OCR weights.
102
+ - **Full toolkit.** Train, resume, evaluate (CER / WER / exact match), export ONNX, benchmark, page layout, PDFs, and a local browser demo.
103
+
104
+ ## Models
105
+
106
+ | | wormocr-2M | wormocr-9M |
107
+ | --- | --- | --- |
108
+ | Parameters | 2,174,880 | 8,776,416 |
109
+ | Weights (`.safetensors`, FP32) | 8.7 MB | 33.5 MB |
110
+ | ONNX (FP32) | 8.7 MB | 33.5 MB |
111
+ | Architecture | Separable CNN + 2-layer BiGRU (256) + CTC | Same family, BiGRU 512 |
112
+ | Input height | 32 px | 48 px |
113
+ | Output stride | 4 | 4 |
114
+ | Character set | 95 printable ASCII + CTC blank | Same |
115
+ | Training data | Synthetic print & handwriting, GNHK, names, Bentham, CORD. FUNSD / IAM / RxHand evaluation only | Same mix |
116
+ | Best checkpoint | step 58,000 of 60,000 | step 19,000 (early-stopped at 30,000) |
117
+ | Default for `OCR()` | Yes | Pass `model="wormocr-9M"` |
118
+
119
+ ## Results
120
+
121
+ 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](docs/BENCHMARKS.md).
122
+
123
+ | Test set | Lines | wormocr-2M | wormocr-9M |
124
+ | --- | ---: | ---: | ---: |
125
+ | Synthetic printed lines | 1,000 | 3.1% | 3.7% |
126
+ | [FUNSD](https://guillaumejaume.github.io/FUNSD/) form words (eval only) | 8,526 | 11.5% | **10.2%** |
127
+ | CORD receipts | 795 | 3.7% | **3.1%** |
128
+ | Synthetic handwriting | 1,000 | 9.2% | **7.1%** |
129
+ | Handwritten form names | 26,170 | **5.3%** | 5.3% |
130
+ | GNHK handwritten notes | 2,410 | 23.3% | **19.2%** |
131
+ | IAM lines (eval only) | 2,915 | 22.4% | **18.3%** |
132
+ | Bentham historical lines | 1,135 | 15.1% | **8.8%** |
133
+ | RxHand words, case-insensitive (eval only) | 1,107 | 37.9% | **35.2%** |
134
+
135
+ FUNSD, IAM, and RxHand were never used for training.
136
+
137
+ ### Speed (CPU, wormocr-2M)
138
+
139
+ Intel Core i9-14900HX, PyTorch 2.14.1, FP32. Medians after warmup (22 October 2026).
140
+
141
+ | Path | Batch | Median | Throughput |
142
+ | --- | ---: | ---: | ---: |
143
+ | Model only, 256 px wide | 1 | 26.0 ms | 38 lines/s |
144
+ | Model only, 256 px wide | 8 | 51.0 ms | 157 lines/s |
145
+ | Preprocess + model + decode | 1 | 14.4 ms | 69 lines/s |
146
+ | Preprocess + model + decode | 8 | 42.2 ms | 189 lines/s |
147
+
148
+ wormocr-9M is larger and slower. Use it when the handwriting gains matter more than size.
149
+
150
+ ### Training cost
151
+
152
+ | Run | Hardware | Steps | Effective batch | Wall time |
153
+ | --- | --- | ---: | ---: | ---: |
154
+ | wormocr-2M | RTX 5060 Laptop, mixed precision | 60,000 | 64 | 125 min |
155
+ | wormocr-9M | RTX 5060 Laptop, mixed precision | 30,000 (best at 19,000) | 64 (16 × 4 accum) | about 150 min |
156
+
157
+ ## Installation
158
+
159
+ Requires Python 3.10+.
160
+
161
+ ### From PyPI (recommended)
162
+
163
+ ```bash
164
+ pip install wormocr
165
+ wormocr download # wormocr-2M
166
+ # wormocr download wormocr-9M
167
+ ```
168
+
169
+ Optional extras:
170
+
171
+ ```bash
172
+ pip install "wormocr[pdf]" # PDF input
173
+ pip install "wormocr[export]" # ONNX export
174
+ pip install "wormocr[dev]" # pytest
175
+ ```
176
+
177
+ A CPU build of PyTorch is enough for inference. For GPU training, install a CUDA build from [pytorch.org](https://pytorch.org/get-started/locally/) first, then install wormOCR.
178
+
179
+ ### From source
180
+
181
+ ```bash
182
+ git clone https://github.com/arjun988/wormOCR.git
183
+ cd wormOCR
184
+ python -m venv .venv
185
+ .venv\Scripts\activate # Windows
186
+ # source .venv/bin/activate # macOS / Linux
187
+ pip install -e ".[dev,export,pdf]"
188
+ wormocr download
189
+ ```
190
+
191
+ ```bash
192
+ wormocr info
193
+ ```
194
+
195
+ ### Weights
196
+
197
+ Weights are **not** on PyPI and **not** in git. They are GitHub release assets, verified by SHA-256 hashes baked into the package:
198
+
199
+ | Release | Size |
200
+ | --- | ---: |
201
+ | [`wormocr-2M`](https://github.com/arjun988/wormOCR/releases/tag/wormocr-2M) | 8.7 MB |
202
+ | [`wormocr-9M`](https://github.com/arjun988/wormOCR/releases/tag/wormocr-9M) | 33.5 MB |
203
+
204
+ ```bash
205
+ wormocr download
206
+ wormocr download wormocr-9M
207
+ wormocr download wormocr-2M --force
208
+ ```
209
+
210
+ 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:
211
+
212
+ ```python
213
+ OCR(checkpoint="path/to/wormocr-2M.safetensors")
214
+ OCR(model="wormocr-9M")
215
+ OCR(checkpoint="artifacts/checkpoints/tiny/best.pt")
216
+ ```
217
+
218
+ Training checkpoints (`.pt`) are PyTorch pickles. Load only files you trust ([SECURITY.md](SECURITY.md)).
219
+
220
+ ## Usage
221
+
222
+ ### Python
223
+
224
+ ```python
225
+ from wormocr import OCR
226
+
227
+ ocr = OCR()
228
+ print(ocr.predict("line.png", mode="line").text)
229
+
230
+ page = ocr.predict("page.png") # mode="auto" by default
231
+ for item in page.lines:
232
+ print(item.order, item.text, item.confidence, item.bbox)
233
+ ```
234
+
235
+ `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.
236
+
237
+ | Mode | Behavior |
238
+ | --- | --- |
239
+ | `line` | Whole image is one text line |
240
+ | `page` | Line detection, deskew, reading order |
241
+ | `auto` | `line` for short wide images, else `page` |
242
+
243
+ ### Command line
244
+
245
+ ```bash
246
+ wormocr download
247
+ wormocr predict --image line.png
248
+ wormocr predict --image page.png --json
249
+ wormocr predict --image document.pdf --model wormocr-9M
250
+ ```
251
+
252
+ Commands: `info`, `download`, `predict`, `train`, `evaluate`, `analyze`, `export`, `benchmark`, `generate`, `serve`.
253
+
254
+ ### Web UI
255
+
256
+ ```bash
257
+ wormocr serve # http://127.0.0.1:8000
258
+ ```
259
+
260
+ Drop in an image or PDF, paste with <kbd>Ctrl</kbd>+<kbd>V</kbd>, or try the sample invoice. Recognition streams while the model works. The server listens on localhost by default; files stay on your machine.
261
+
262
+ ```bash
263
+ wormocr serve --model wormocr-9M --device cuda --port 8000
264
+ ```
265
+
266
+ ## Training
267
+
268
+ 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).
269
+
270
+ ```bash
271
+ wormocr train --config configs/unified.yaml --mode baseline
272
+ wormocr train --config configs/wormocr-9M.yaml --mode baseline
273
+ ```
274
+
275
+ Override any field with `--set section.key=value`. Training writes `best.pt`, `last.pt`, `best_weights.pt`, and `train_log.jsonl`.
276
+
277
+ | Mode | Purpose |
278
+ | --- | --- |
279
+ | `smoke` | Short check that data, loss, validation, and checkpoints run |
280
+ | `debug` | Tiny network experiment |
281
+ | `baseline` | Full run as written in the config |
282
+ | `resume` | Continue from `last.pt` |
283
+
284
+ Own labels: `relative/path.png<TAB>exact text`. See [docs/DATASETS.md](docs/DATASETS.md).
285
+
286
+ ```bash
287
+ wormocr train --config configs/tiny.yaml --mode baseline \
288
+ --set data.labels=data/train.tsv --set data.image_root=data/images \
289
+ --set data.val_labels=data/val.tsv --set data.val_image_root=data/images
290
+ ```
291
+
292
+ ## Evaluation, export, and benchmarks
293
+
294
+ ```bash
295
+ wormocr evaluate --model artifacts/checkpoints/tiny/best.pt --synthetic-samples 800
296
+ wormocr evaluate --model artifacts/checkpoints/tiny/best.pt --labels data/val.tsv --image-root data/images --errors errors.md
297
+
298
+ python scripts/prepare_funsd.py artifacts/datasets/funsd/dataset/testing_data artifacts/datasets/funsd_test
299
+ wormocr evaluate --model artifacts/checkpoints/unified/best.pt --labels artifacts/datasets/funsd_test/labels.tsv
300
+
301
+ wormocr export --model artifacts/checkpoints/tiny/best.pt --format onnx --output artifacts/export/tiny.onnx
302
+ wormocr benchmark --model artifacts/checkpoints/tiny/best.pt --output artifacts/benchmarks/report.md
303
+ ```
304
+
305
+ 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.
306
+
307
+ ## How it works
308
+
309
+ 1. **Preprocessing.** Grayscale, resize to model height (32 or 48), normalize to `[-1, 1]`, pad with the line's background.
310
+ 2. **Encoder.** Four-stage depthwise-separable CNN. Height pools to 2; width by 4.
311
+ 3. **Sequence model.** Linear projection into a 2-layer bidirectional GRU (256 or 512).
312
+ 4. **Decoder.** CTC over 96 classes, greedy by default; beam search optional.
313
+ 5. **Page layout.** Deskew, Otsu, projection profiles, gutters, reading order.
314
+
315
+ Training uses CTC + AdamW, warmup then cosine decay, EMA 0.999, AMP on CUDA, and scan-style augmentation. Details: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), [docs/DATASETS.md](docs/DATASETS.md).
316
+
317
+ ## Project layout
318
+
319
+ ```text
320
+ wormOCR/
321
+ ├── configs/ unified.yaml → wormocr-2M; wormocr-9M.yaml → wormocr-9M
322
+ ├── docs/ Architecture, datasets, benchmarks, roadmap
323
+ ├── scripts/ Dataset prep and helpers
324
+ ├── src/wormocr/
325
+ │ ├── api.py OCR class
326
+ │ ├── cli.py wormocr command
327
+ │ ├── hub.py Releases, download, SHA-256 checks
328
+ │ ├── models/ CRNN
329
+ │ ├── training/ Loop and EMA
330
+ │ ├── inference/ Engine and CTC decode
331
+ │ ├── detection/ Line finding and reading order
332
+ │ ├── evaluation/ CER / WER
333
+ │ ├── export/ ONNX
334
+ │ └── web/ Local serve UI
335
+ └── tests/
336
+ ```
337
+
338
+ ## Limitations
339
+
340
+ - **English printable ASCII only.** Accented letters and other scripts are rejected, not silently dropped.
341
+ - **Messy handwriting is still hard.** On wormocr-9M: GNHK 19.2%, IAM 18.3%, Bentham 8.8%, RxHand 35.2% case-insensitive (eval only).
342
+ - **Real scans beat clean synth.** FUNSD form words are about 10–11.5% CER. Tiny one-character crops remain weak.
343
+ - **Simple page layout.** Projection profiles, not a learned detector. Tables and heavy clutter can fail.
344
+ - **No language-model correction.** Output is exactly what the decoder emits.
345
+ - **No third-party OCR baseline yet.** Tesseract / PaddleOCR were not measured on this machine.
346
+
347
+ See [docs/ROADMAP.md](docs/ROADMAP.md).
348
+
349
+ ## Contributing
350
+
351
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
352
+
353
+ ```bash
354
+ python -m pytest
355
+ ```
356
+
357
+ When you report accuracy, include the dataset, line count, exact command, and a set the model was not trained on.
358
+
359
+ ## Citation
360
+
361
+ ```bibtex
362
+ @software{shukla2026wormocr,
363
+ author = {Shukla, Arjun},
364
+ title = {wormOCR: a small local OCR engine for printed and handwritten English text},
365
+ year = {2026},
366
+ version = {1.0.0},
367
+ url = {https://github.com/arjun988/wormOCR}
368
+ }
369
+ ```
370
+
371
+ Also [CITATION.cff](CITATION.cff).
372
+
373
+ ## License
374
+
375
+ [MIT License](LICENSE). Copyright (c) 2026 arjun shukla.
376
+
377
+ wormOCR ships no third-party OCR weights and redistributes no fonts. FUNSD is evaluation-only and is not included in this repository.