arjuna-ocr 1.2.1__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.
- arjuna_ocr-1.2.1/LICENSE +3 -0
- arjuna_ocr-1.2.1/MANIFEST.in +8 -0
- arjuna_ocr-1.2.1/PKG-INFO +83 -0
- arjuna_ocr-1.2.1/PYPI_README.md +31 -0
- arjuna_ocr-1.2.1/README.md +149 -0
- arjuna_ocr-1.2.1/arjuna_ocr.egg-info/PKG-INFO +83 -0
- arjuna_ocr-1.2.1/arjuna_ocr.egg-info/SOURCES.txt +27 -0
- arjuna_ocr-1.2.1/arjuna_ocr.egg-info/dependency_links.txt +1 -0
- arjuna_ocr-1.2.1/arjuna_ocr.egg-info/entry_points.txt +4 -0
- arjuna_ocr-1.2.1/arjuna_ocr.egg-info/requires.txt +35 -0
- arjuna_ocr-1.2.1/arjuna_ocr.egg-info/top_level.txt +1 -0
- arjuna_ocr-1.2.1/kanen_infer/__init__.py +9 -0
- arjuna_ocr-1.2.1/kanen_infer/api.py +119 -0
- arjuna_ocr-1.2.1/kanen_infer/arjuna_cli.py +111 -0
- arjuna_ocr-1.2.1/kanen_infer/assemble.py +261 -0
- arjuna_ocr-1.2.1/kanen_infer/batch_pipeline.py +230 -0
- arjuna_ocr-1.2.1/kanen_infer/charset.py +72 -0
- arjuna_ocr-1.2.1/kanen_infer/cli.py +71 -0
- arjuna_ocr-1.2.1/kanen_infer/config.py +104 -0
- arjuna_ocr-1.2.1/kanen_infer/formats.py +76 -0
- arjuna_ocr-1.2.1/kanen_infer/geometry.py +19 -0
- arjuna_ocr-1.2.1/kanen_infer/hardware.py +148 -0
- arjuna_ocr-1.2.1/kanen_infer/io.py +68 -0
- arjuna_ocr-1.2.1/kanen_infer/models.py +57 -0
- arjuna_ocr-1.2.1/kanen_infer/pipeline_base.py +229 -0
- arjuna_ocr-1.2.1/kanen_infer/providers.py +229 -0
- arjuna_ocr-1.2.1/kanen_infer/rec_onnx.py +75 -0
- arjuna_ocr-1.2.1/pyproject.toml +50 -0
- arjuna_ocr-1.2.1/setup.cfg +4 -0
arjuna_ocr-1.2.1/LICENSE
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# the wheel is code only: the models (~170 MB) are downloaded from the Hugging Face repo on first use
|
|
2
|
+
include LICENSE
|
|
3
|
+
include README.md
|
|
4
|
+
exclude kanen.yaml VERSION handler.py serve.py run_ocr.py Dockerfile Dockerfile.cuda
|
|
5
|
+
prune models
|
|
6
|
+
prune samples
|
|
7
|
+
prune tests
|
|
8
|
+
prune trt_cache
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arjuna-ocr
|
|
3
|
+
Version: 1.2.1
|
|
4
|
+
Summary: Arjuna-OCR [Kn&En]: layout-aware Kannada + English document OCR (ONNX Runtime, optional TensorRT)
|
|
5
|
+
Author: Chethan (anandkaman)
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://huggingface.co/anandkaman/arjuna-ocr
|
|
8
|
+
Project-URL: Models, https://huggingface.co/anandkaman/arjuna-ocr
|
|
9
|
+
Project-URL: Releases, https://huggingface.co/anandkaman/arjuna-ocr-kn-en
|
|
10
|
+
Project-URL: Demo, https://huggingface.co/spaces/anandkaman/arjuna_ocr_demo
|
|
11
|
+
Keywords: ocr,kannada,document-ai,layout-analysis,onnx,tensorrt,indic
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Image Recognition
|
|
18
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: onnxruntime>=1.20
|
|
23
|
+
Requires-Dist: numpy
|
|
24
|
+
Requires-Dist: opencv-python-headless
|
|
25
|
+
Requires-Dist: pyclipper
|
|
26
|
+
Requires-Dist: shapely
|
|
27
|
+
Requires-Dist: pyyaml
|
|
28
|
+
Requires-Dist: regex
|
|
29
|
+
Requires-Dist: onnx
|
|
30
|
+
Requires-Dist: huggingface_hub>=0.23
|
|
31
|
+
Provides-Extra: gpu
|
|
32
|
+
Requires-Dist: onnxruntime-gpu>=1.20; extra == "gpu"
|
|
33
|
+
Provides-Extra: trt
|
|
34
|
+
Requires-Dist: onnxruntime-gpu>=1.20; extra == "trt"
|
|
35
|
+
Requires-Dist: tensorrt>=10.0; extra == "trt"
|
|
36
|
+
Provides-Extra: server
|
|
37
|
+
Requires-Dist: fastapi>=0.110; extra == "server"
|
|
38
|
+
Requires-Dist: uvicorn>=0.29; extra == "server"
|
|
39
|
+
Requires-Dist: python-multipart; extra == "server"
|
|
40
|
+
Provides-Extra: pdf
|
|
41
|
+
Requires-Dist: pypdfium2>=4.0; extra == "pdf"
|
|
42
|
+
Provides-Extra: cpu
|
|
43
|
+
Requires-Dist: onnxruntime>=1.20; extra == "cpu"
|
|
44
|
+
Provides-Extra: all
|
|
45
|
+
Requires-Dist: onnxruntime-gpu>=1.20; extra == "all"
|
|
46
|
+
Requires-Dist: tensorrt>=10.0; extra == "all"
|
|
47
|
+
Requires-Dist: fastapi>=0.110; extra == "all"
|
|
48
|
+
Requires-Dist: uvicorn>=0.29; extra == "all"
|
|
49
|
+
Requires-Dist: python-multipart; extra == "all"
|
|
50
|
+
Requires-Dist: pypdfium2>=4.0; extra == "all"
|
|
51
|
+
Dynamic: license-file
|
|
52
|
+
|
|
53
|
+
# Arjuna-OCR [Kn&En]
|
|
54
|
+
|
|
55
|
+
Layout-aware **Kannada + English** document OCR: a layout model, a text detector and a CTC recognizer in ONNX, with
|
|
56
|
+
tables, reading order, per-line confidence and review flags. No language model, no GPU required.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install arjuna-ocr # CPU, works anywhere
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
arjuna doctor # what this machine can do and what the pipeline will use
|
|
63
|
+
arjuna ocr page.pdf -o out/ # files, folders, globs or PDFs -> JSON / Markdown / hOCR / ALTO / TSV
|
|
64
|
+
arjuna serve --port 8080 # HTTP server
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from kanen_infer import KanEnOCR
|
|
69
|
+
ocr = KanEnOCR() # detects hardware, configures itself
|
|
70
|
+
doc = ocr.page("page.png") # kanen-ocr/1.0 JSON
|
|
71
|
+
print(ocr.render(doc, "md"))
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The models (~170 MB) download once from [anandkaman/arjuna-ocr](https://huggingface.co/anandkaman/arjuna-ocr) at the
|
|
75
|
+
revision matching this package version, and are cached. Point `ARJUNA_MODELS` at a local copy for air-gapped installs.
|
|
76
|
+
|
|
77
|
+
**Speed and accuracy.** 12.8 pages/s on an RTX 5060 Ti with TensorRT, ~4.6 on the CUDA provider, seconds per page on CPU.
|
|
78
|
+
On a frozen 40-page private-document set: word F1 0.967, page CER 3.1 %; 96.3 % word accuracy on Mozhi, 93.7 % on MILE.
|
|
79
|
+
TensorRT engines are built in the background on first use — the pipeline serves immediately on CUDA and swaps each engine
|
|
80
|
+
in only after it reproduces the output of the session it replaces.
|
|
81
|
+
|
|
82
|
+
Full documentation, hosted-inference instructions and the evaluation record: the model repositories linked above.
|
|
83
|
+
**Licences**: code, recognizer and detector Apache-2.0; the layout model is AGPL-3.0 (see its NOTICE).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Arjuna-OCR [Kn&En]
|
|
2
|
+
|
|
3
|
+
Layout-aware **Kannada + English** document OCR: a layout model, a text detector and a CTC recognizer in ONNX, with
|
|
4
|
+
tables, reading order, per-line confidence and review flags. No language model, no GPU required.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install arjuna-ocr # CPU, works anywhere
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
arjuna doctor # what this machine can do and what the pipeline will use
|
|
11
|
+
arjuna ocr page.pdf -o out/ # files, folders, globs or PDFs -> JSON / Markdown / hOCR / ALTO / TSV
|
|
12
|
+
arjuna serve --port 8080 # HTTP server
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from kanen_infer import KanEnOCR
|
|
17
|
+
ocr = KanEnOCR() # detects hardware, configures itself
|
|
18
|
+
doc = ocr.page("page.png") # kanen-ocr/1.0 JSON
|
|
19
|
+
print(ocr.render(doc, "md"))
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The models (~170 MB) download once from [anandkaman/arjuna-ocr](https://huggingface.co/anandkaman/arjuna-ocr) at the
|
|
23
|
+
revision matching this package version, and are cached. Point `ARJUNA_MODELS` at a local copy for air-gapped installs.
|
|
24
|
+
|
|
25
|
+
**Speed and accuracy.** 12.8 pages/s on an RTX 5060 Ti with TensorRT, ~4.6 on the CUDA provider, seconds per page on CPU.
|
|
26
|
+
On a frozen 40-page private-document set: word F1 0.967, page CER 3.1 %; 96.3 % word accuracy on Mozhi, 93.7 % on MILE.
|
|
27
|
+
TensorRT engines are built in the background on first use — the pipeline serves immediately on CUDA and swaps each engine
|
|
28
|
+
in only after it reproduces the output of the session it replaces.
|
|
29
|
+
|
|
30
|
+
Full documentation, hosted-inference instructions and the evaluation record: the model repositories linked above.
|
|
31
|
+
**Licences**: code, recognizer and detector Apache-2.0; the layout model is AGPL-3.0 (see its NOTICE).
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
license: apache-2.0
|
|
3
|
+
language:
|
|
4
|
+
- kn
|
|
5
|
+
- en
|
|
6
|
+
pipeline_tag: image-to-text
|
|
7
|
+
tags:
|
|
8
|
+
- ocr
|
|
9
|
+
- kannada
|
|
10
|
+
- document-ai
|
|
11
|
+
- layout-analysis
|
|
12
|
+
- onnx
|
|
13
|
+
- tensorrt
|
|
14
|
+
library_name: onnxruntime
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Arjuna-OCR [Kn&En] — inference
|
|
18
|
+
|
|
19
|
+
**Kannada + English document OCR that runs anywhere and configures itself.** This repository is the *deployment* package:
|
|
20
|
+
one supported version (v1.2.1), the three models, the inference code, an HTTP server, a Hugging Face Inference Endpoints
|
|
21
|
+
handler and Docker files. Every release, the full evaluation record and the training story live in the archive repository
|
|
22
|
+
[anandkaman/arjuna-ocr-kn-en](https://huggingface.co/anandkaman/arjuna-ocr-kn-en).
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install arjuna-ocr # CPU everywhere
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
arjuna doctor # what this machine can do, and what the pipeline will use
|
|
29
|
+
arjuna ocr page.pdf -o out/ # OCR files, folders, globs or PDFs
|
|
30
|
+
arjuna serve --port 8080 # HTTP server
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or without installing anything:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
docker run --rm -p 8080:8080 arjuna-ocr:cpu # CPU
|
|
37
|
+
docker run --rm --gpus all -p 8080:8080 arjuna-ocr:cuda # NVIDIA
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## It configures itself
|
|
41
|
+
|
|
42
|
+
On start it detects the GPU, driver, CUDA and TensorRT, picks the fastest provider it can actually load, and scales batch
|
|
43
|
+
sizes and thread counts to the machine's VRAM and cores. **Only speed settings change — canvases, thresholds, padding and
|
|
44
|
+
the models are exactly as shipped, so results do not depend on the hardware** (beyond fp16 rounding).
|
|
45
|
+
|
|
46
|
+
TensorRT engines are specific to the GPU and TensorRT version, so they cannot be shipped for every machine. Instead of
|
|
47
|
+
making you wait for a build, the pipeline **starts serving immediately on the CUDA provider and builds the engines in the
|
|
48
|
+
background**; each engine is swapped in only after it reproduces the output of the session it replaces (for the layout
|
|
49
|
+
model, the reference blocks on the bundled page). If a build fails or produces a wrong engine, the pipeline keeps running
|
|
50
|
+
on what it had and says so. Cold start on an unseen GPU: **~1.5 s to first request**, full speed a minute or two later.
|
|
51
|
+
|
|
52
|
+
`arjuna doctor` prints all of it: hardware, driver, usable providers, the plan, every auto-configuration change, and the
|
|
53
|
+
reason for any fallback.
|
|
54
|
+
|
|
55
|
+
## What runs where
|
|
56
|
+
|
|
57
|
+
| provider | hardware | status |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| TensorRT (fp16) | NVIDIA + TensorRT | supported — 12.8 pages/s on an RTX 5060 Ti |
|
|
60
|
+
| CUDA | any NVIDIA with a working driver | supported — ~4.6 pages/s, and the path used while engines build |
|
|
61
|
+
| CPU | x86-64 / ARM64, no GPU needed | supported — seconds per page, scales with cores |
|
|
62
|
+
| DirectML, CoreML, OpenVINO | Windows, Apple Silicon, Intel | planned; the graphs are ordinary CNNs, so these are provider swaps |
|
|
63
|
+
| ROCm | AMD | best-effort only (ONNX Runtime support ends after ROCm 7.0) |
|
|
64
|
+
|
|
65
|
+
**Not supported, by design:** vLLM, Ollama / llama.cpp, TGI and the Hugging Face serverless Inference API or Inference
|
|
66
|
+
Providers catalogue. These serve autoregressive language models; Arjuna is three CNN/CTC vision models with their own
|
|
67
|
+
pre- and post-processing. Use the Docker image, the HTTP server, Inference Endpoints or Triton instead.
|
|
68
|
+
|
|
69
|
+
## NVIDIA
|
|
70
|
+
|
|
71
|
+
`pip install arjuna-ocr` installs the CPU runtime, which works everywhere. For GPU speed, replace the runtime with a
|
|
72
|
+
build that matches your CUDA — `onnxruntime` and `onnxruntime-gpu` share an import name, so only one may be installed:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# CUDA 13 (current default wheels)
|
|
76
|
+
pip uninstall -y onnxruntime && pip install "arjuna-ocr[trt]"
|
|
77
|
+
|
|
78
|
+
# CUDA 12
|
|
79
|
+
pip uninstall -y onnxruntime onnxruntime-gpu
|
|
80
|
+
pip install onnxruntime-gpu --index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/
|
|
81
|
+
pip install tensorrt-cu12 nvidia-cublas-cu12 nvidia-cudnn-cu12 nvidia-cuda-runtime-cu12
|
|
82
|
+
|
|
83
|
+
# or skip all of it
|
|
84
|
+
docker run --rm --gpus all -p 8080:8080 arjuna-ocr:cuda
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`arjuna doctor` tells you which providers actually load on your machine and why any of them was rejected. If it reports
|
|
88
|
+
CPU while you expect GPU, the CUDA libraries the runtime wants are missing or belong to a different CUDA major version.
|
|
89
|
+
|
|
90
|
+
## Hosted inference
|
|
91
|
+
|
|
92
|
+
**Hugging Face Inference Endpoints** — this repo ships `handler.py`, so *Deploy → Inference Endpoints* works directly.
|
|
93
|
+
Pick a CPU or GPU instance; engines build in the background on GPU instances.
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import base64, requests
|
|
97
|
+
img = base64.b64encode(open("page.png", "rb").read()).decode()
|
|
98
|
+
r = requests.post(URL, headers={"Authorization": f"Bearer {TOKEN}"},
|
|
99
|
+
json={"inputs": img, "parameters": {"format": "json"}})
|
|
100
|
+
doc = r.json()["documents"][0]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Anywhere that runs a container** — Modal, RunPod, Replicate, fal, Kubernetes, your own server: use the Docker images.
|
|
104
|
+
**Multi-model serving** — the three ONNX graphs drop into NVIDIA Triton with the pre/post-processing as a Python backend.
|
|
105
|
+
|
|
106
|
+
## Python
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from kanen_infer import KanEnOCR
|
|
110
|
+
ocr = KanEnOCR() # auto-detects hardware
|
|
111
|
+
ocr.optimize() # optional: build TensorRT engines in the background
|
|
112
|
+
doc = ocr.page("page.png") # kanen-ocr/1.0 JSON
|
|
113
|
+
md = ocr.render(doc, "md") # json | md | txt | hocr | alto | tsv
|
|
114
|
+
docs = ocr.pages(["a.png", "b.pdf"]) # batched
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Every line carries `text`, `confidence`, `min_conf` (weakest character), `bbox`, `script` and review `flags`
|
|
118
|
+
(`low_conf`, `check`, `weak_char`, `script_mix`, `oversize`, `in_seal`, `in_figure`, `in_id_panel`, `in_handwriting`).
|
|
119
|
+
Blocks are `text`, `title`, `table`, `figure`, `seal`, `id_panel`, `handwriting`, `header`, `footer`; tables carry
|
|
120
|
+
`rows[].cells[]`. **Nothing is filtered or rewritten** — low-confidence lines are flagged, never dropped.
|
|
121
|
+
|
|
122
|
+
## Accuracy (v1.2.1)
|
|
123
|
+
|
|
124
|
+
| set | measure | result |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| private documents, 40 frozen pages | word F1 end-to-end / page CER | **0.967** / 3.11 % |
|
|
127
|
+
| private document lines (1,324) | word accuracy / CER | **98.8 %** / 0.17 |
|
|
128
|
+
| Mozhi test (IIIT-H, public) | word accuracy | **96.3 %** |
|
|
129
|
+
| MILE (IISc, public) | word accuracy | **93.7 %** |
|
|
130
|
+
| English PDF text | word accuracy | **97.2 %** |
|
|
131
|
+
| vs Surya OCR 1, clean pages / unseen MILE pages | word F1 | **0.991 / 0.872** vs 0.929 / 0.751 |
|
|
132
|
+
|
|
133
|
+
Method, per-benchmark analysis and the full history: see the archive repo's `docs/`.
|
|
134
|
+
|
|
135
|
+
## Known limitations
|
|
136
|
+
|
|
137
|
+
- Very small or fax-quality English text (≤ 11 px x-height) and thermal/dot-matrix receipts lose accuracy.
|
|
138
|
+
- Historical Kannada is strong but below modern print; line splitting on letter-spaced or letterpress faces is much
|
|
139
|
+
reduced in v1.2.1 but not gone.
|
|
140
|
+
- Dense multi-column tables (10+ columns) and unruled form boxes come out as text lines without cell structure.
|
|
141
|
+
- Handwriting, stamps and seals produce low-confidence flagged output rather than nothing.
|
|
142
|
+
- The layout model was trained on 174 pages of the target document families; other families are typed less reliably.
|
|
143
|
+
|
|
144
|
+
## Licences
|
|
145
|
+
|
|
146
|
+
Package code, recognizer and text detector: Apache-2.0. **The layout model `models/layout/kanen_layout_v12.onnx` is
|
|
147
|
+
AGPL-3.0** — see `models/layout/NOTICE.md`. Run with `layout.mode: never` if that does not suit your deployment.
|
|
148
|
+
|
|
149
|
+
Contact: kamananand98@gmail.com
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: arjuna-ocr
|
|
3
|
+
Version: 1.2.1
|
|
4
|
+
Summary: Arjuna-OCR [Kn&En]: layout-aware Kannada + English document OCR (ONNX Runtime, optional TensorRT)
|
|
5
|
+
Author: Chethan (anandkaman)
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://huggingface.co/anandkaman/arjuna-ocr
|
|
8
|
+
Project-URL: Models, https://huggingface.co/anandkaman/arjuna-ocr
|
|
9
|
+
Project-URL: Releases, https://huggingface.co/anandkaman/arjuna-ocr-kn-en
|
|
10
|
+
Project-URL: Demo, https://huggingface.co/spaces/anandkaman/arjuna_ocr_demo
|
|
11
|
+
Keywords: ocr,kannada,document-ai,layout-analysis,onnx,tensorrt,indic
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Image Recognition
|
|
18
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: onnxruntime>=1.20
|
|
23
|
+
Requires-Dist: numpy
|
|
24
|
+
Requires-Dist: opencv-python-headless
|
|
25
|
+
Requires-Dist: pyclipper
|
|
26
|
+
Requires-Dist: shapely
|
|
27
|
+
Requires-Dist: pyyaml
|
|
28
|
+
Requires-Dist: regex
|
|
29
|
+
Requires-Dist: onnx
|
|
30
|
+
Requires-Dist: huggingface_hub>=0.23
|
|
31
|
+
Provides-Extra: gpu
|
|
32
|
+
Requires-Dist: onnxruntime-gpu>=1.20; extra == "gpu"
|
|
33
|
+
Provides-Extra: trt
|
|
34
|
+
Requires-Dist: onnxruntime-gpu>=1.20; extra == "trt"
|
|
35
|
+
Requires-Dist: tensorrt>=10.0; extra == "trt"
|
|
36
|
+
Provides-Extra: server
|
|
37
|
+
Requires-Dist: fastapi>=0.110; extra == "server"
|
|
38
|
+
Requires-Dist: uvicorn>=0.29; extra == "server"
|
|
39
|
+
Requires-Dist: python-multipart; extra == "server"
|
|
40
|
+
Provides-Extra: pdf
|
|
41
|
+
Requires-Dist: pypdfium2>=4.0; extra == "pdf"
|
|
42
|
+
Provides-Extra: cpu
|
|
43
|
+
Requires-Dist: onnxruntime>=1.20; extra == "cpu"
|
|
44
|
+
Provides-Extra: all
|
|
45
|
+
Requires-Dist: onnxruntime-gpu>=1.20; extra == "all"
|
|
46
|
+
Requires-Dist: tensorrt>=10.0; extra == "all"
|
|
47
|
+
Requires-Dist: fastapi>=0.110; extra == "all"
|
|
48
|
+
Requires-Dist: uvicorn>=0.29; extra == "all"
|
|
49
|
+
Requires-Dist: python-multipart; extra == "all"
|
|
50
|
+
Requires-Dist: pypdfium2>=4.0; extra == "all"
|
|
51
|
+
Dynamic: license-file
|
|
52
|
+
|
|
53
|
+
# Arjuna-OCR [Kn&En]
|
|
54
|
+
|
|
55
|
+
Layout-aware **Kannada + English** document OCR: a layout model, a text detector and a CTC recognizer in ONNX, with
|
|
56
|
+
tables, reading order, per-line confidence and review flags. No language model, no GPU required.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install arjuna-ocr # CPU, works anywhere
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
arjuna doctor # what this machine can do and what the pipeline will use
|
|
63
|
+
arjuna ocr page.pdf -o out/ # files, folders, globs or PDFs -> JSON / Markdown / hOCR / ALTO / TSV
|
|
64
|
+
arjuna serve --port 8080 # HTTP server
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from kanen_infer import KanEnOCR
|
|
69
|
+
ocr = KanEnOCR() # detects hardware, configures itself
|
|
70
|
+
doc = ocr.page("page.png") # kanen-ocr/1.0 JSON
|
|
71
|
+
print(ocr.render(doc, "md"))
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The models (~170 MB) download once from [anandkaman/arjuna-ocr](https://huggingface.co/anandkaman/arjuna-ocr) at the
|
|
75
|
+
revision matching this package version, and are cached. Point `ARJUNA_MODELS` at a local copy for air-gapped installs.
|
|
76
|
+
|
|
77
|
+
**Speed and accuracy.** 12.8 pages/s on an RTX 5060 Ti with TensorRT, ~4.6 on the CUDA provider, seconds per page on CPU.
|
|
78
|
+
On a frozen 40-page private-document set: word F1 0.967, page CER 3.1 %; 96.3 % word accuracy on Mozhi, 93.7 % on MILE.
|
|
79
|
+
TensorRT engines are built in the background on first use — the pipeline serves immediately on CUDA and swaps each engine
|
|
80
|
+
in only after it reproduces the output of the session it replaces.
|
|
81
|
+
|
|
82
|
+
Full documentation, hosted-inference instructions and the evaluation record: the model repositories linked above.
|
|
83
|
+
**Licences**: code, recognizer and detector Apache-2.0; the layout model is AGPL-3.0 (see its NOTICE).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
PYPI_README.md
|
|
4
|
+
README.md
|
|
5
|
+
pyproject.toml
|
|
6
|
+
arjuna_ocr.egg-info/PKG-INFO
|
|
7
|
+
arjuna_ocr.egg-info/SOURCES.txt
|
|
8
|
+
arjuna_ocr.egg-info/dependency_links.txt
|
|
9
|
+
arjuna_ocr.egg-info/entry_points.txt
|
|
10
|
+
arjuna_ocr.egg-info/requires.txt
|
|
11
|
+
arjuna_ocr.egg-info/top_level.txt
|
|
12
|
+
kanen_infer/__init__.py
|
|
13
|
+
kanen_infer/api.py
|
|
14
|
+
kanen_infer/arjuna_cli.py
|
|
15
|
+
kanen_infer/assemble.py
|
|
16
|
+
kanen_infer/batch_pipeline.py
|
|
17
|
+
kanen_infer/charset.py
|
|
18
|
+
kanen_infer/cli.py
|
|
19
|
+
kanen_infer/config.py
|
|
20
|
+
kanen_infer/formats.py
|
|
21
|
+
kanen_infer/geometry.py
|
|
22
|
+
kanen_infer/hardware.py
|
|
23
|
+
kanen_infer/io.py
|
|
24
|
+
kanen_infer/models.py
|
|
25
|
+
kanen_infer/pipeline_base.py
|
|
26
|
+
kanen_infer/providers.py
|
|
27
|
+
kanen_infer/rec_onnx.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
onnxruntime>=1.20
|
|
2
|
+
numpy
|
|
3
|
+
opencv-python-headless
|
|
4
|
+
pyclipper
|
|
5
|
+
shapely
|
|
6
|
+
pyyaml
|
|
7
|
+
regex
|
|
8
|
+
onnx
|
|
9
|
+
huggingface_hub>=0.23
|
|
10
|
+
|
|
11
|
+
[all]
|
|
12
|
+
onnxruntime-gpu>=1.20
|
|
13
|
+
tensorrt>=10.0
|
|
14
|
+
fastapi>=0.110
|
|
15
|
+
uvicorn>=0.29
|
|
16
|
+
python-multipart
|
|
17
|
+
pypdfium2>=4.0
|
|
18
|
+
|
|
19
|
+
[cpu]
|
|
20
|
+
onnxruntime>=1.20
|
|
21
|
+
|
|
22
|
+
[gpu]
|
|
23
|
+
onnxruntime-gpu>=1.20
|
|
24
|
+
|
|
25
|
+
[pdf]
|
|
26
|
+
pypdfium2>=4.0
|
|
27
|
+
|
|
28
|
+
[server]
|
|
29
|
+
fastapi>=0.110
|
|
30
|
+
uvicorn>=0.29
|
|
31
|
+
python-multipart
|
|
32
|
+
|
|
33
|
+
[trt]
|
|
34
|
+
onnxruntime-gpu>=1.20
|
|
35
|
+
tensorrt>=10.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
kanen_infer
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Arjuna-OCR [Kn&En] inference package (Kannada + English document OCR): ONNX Runtime + optional TensorRT, no PyTorch."""
|
|
2
|
+
from pathlib import Path as _P
|
|
3
|
+
try: __version__ = (_P(__file__).resolve().parent.parent / "VERSION").read_text(encoding="utf-8").strip()
|
|
4
|
+
except Exception: __version__ = "1.2.1"
|
|
5
|
+
NAME = "Arjuna-OCR [Kn&En]" # product display name — also `name:` in kanen.yaml (the config value wins at run time)
|
|
6
|
+
from .config import load_config, PKG_ROOT # noqa: E402
|
|
7
|
+
from .api import KanEnOCR # noqa: E402
|
|
8
|
+
from .formats import to_json, to_markdown, to_text, to_hocr, to_alto, to_tsv, render # noqa: E402
|
|
9
|
+
__all__ = ["KanEnOCR", "load_config", "PKG_ROOT", "NAME", "__version__", "to_json", "to_markdown", "to_text", "to_hocr", "to_alto", "to_tsv", "render"]
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Arjuna-OCR [Kn&En] Python API.
|
|
2
|
+
|
|
3
|
+
from kanen_infer import KanEnOCR
|
|
4
|
+
ocr = KanEnOCR() # loads kanen.yaml, builds/validates engines (warm-up), see ocr.ready()
|
|
5
|
+
doc = ocr.page("scan.png") # one kanen-ocr/1.0 document (dict)
|
|
6
|
+
docs = ocr.pages(["a.png", "b.pdf"]) # batched; ocr.stream(...) yields documents as they finish
|
|
7
|
+
boxes = ocr.detect(img) # text-line polygons only
|
|
8
|
+
texts = ocr.recognize(crops) # [(text, conf, min_conf)] for line images
|
|
9
|
+
blocks = ocr.layout(img) # layout blocks only
|
|
10
|
+
ocr.render(doc, "hocr") # json | md | txt | hocr | alto | tsv
|
|
11
|
+
|
|
12
|
+
Inputs: file path, bytes, numpy BGR array, PIL image, file object; PDFs through `pages()`/`stream()` (pypdfium2 or pdftoppm,
|
|
13
|
+
optional). The output document never depends on the provider or the batch composition beyond fp16 noise on garbage lines;
|
|
14
|
+
no confidence filtering, no text rewriting, no re-ordering happens here."""
|
|
15
|
+
import logging, time
|
|
16
|
+
import numpy as np, cv2
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from .config import load_config, PKG_ROOT
|
|
19
|
+
from .providers import Providers
|
|
20
|
+
from .rec_onnx import KanEnRecognizer
|
|
21
|
+
from .pipeline_base import Detector, Layout, crop
|
|
22
|
+
from .batch_pipeline import BatchOCR
|
|
23
|
+
from .io import to_bgr, expand_inputs, iter_page_sources
|
|
24
|
+
from . import formats
|
|
25
|
+
|
|
26
|
+
LOG = logging.getLogger("kanen")
|
|
27
|
+
|
|
28
|
+
class KanEnOCR:
|
|
29
|
+
def __init__(self, config=None, providers=None, layout_mode=None, workers=None, warmup=True, **overrides):
|
|
30
|
+
"""config: path to a kanen.yaml/json, a dict, or None (package default). providers: 'trt' | 'cuda' | 'cpu' | 'auto'
|
|
31
|
+
(overrides config provider.policy). Other keyword overrides use dotted section names, e.g. layout={'mode': 'auto'}."""
|
|
32
|
+
ov = dict(overrides)
|
|
33
|
+
if providers: ov.setdefault("provider", {})["policy"] = providers
|
|
34
|
+
if layout_mode: ov.setdefault("layout", {})["mode"] = layout_mode
|
|
35
|
+
if workers: ov.setdefault("runtime", {})["workers"] = workers
|
|
36
|
+
self.cfg = load_config(config, ov); self.name = self.cfg.get("name", "Arjuna-OCR [Kn&En]"); self.version = str(self.cfg.get("version", "1.0.0"))
|
|
37
|
+
t0 = time.time(); self.prov = Providers(self.cfg)
|
|
38
|
+
self.rec = KanEnRecognizer(cfg=self.cfg, providers=self.prov); self.det = Detector(self.cfg, self.prov)
|
|
39
|
+
self.lay = Layout(self.cfg, self.prov) if self.cfg["layout"].get("mode", "always") != "never" else None
|
|
40
|
+
self.batch = BatchOCR(self.rec, cfg=self.cfg, providers=self.prov, det=self.det, lay=self.lay)
|
|
41
|
+
self._load_s = round(time.time() - t0, 1); self._warm = None
|
|
42
|
+
if warmup: self.warmup()
|
|
43
|
+
|
|
44
|
+
# ---- status --------------------------------------------------------------------------------------------------
|
|
45
|
+
def warmup(self):
|
|
46
|
+
"""Run the bundled self-test page through the full pipeline once: builds/validates TensorRT engines for the
|
|
47
|
+
default shapes (layout self-test, detector canvas, recognizer profile) so the first real page is not slow."""
|
|
48
|
+
t0 = time.time(); p = self.cfg.path("models", "layout_selftest")
|
|
49
|
+
if p.exists():
|
|
50
|
+
img = to_bgr(str(p)); self.batch.run([img], page_batch=1, loader=lambda x: x)
|
|
51
|
+
self._warm = round(time.time() - t0, 1); return self.ready()
|
|
52
|
+
|
|
53
|
+
def optimize(self, wait=False, timeout=None):
|
|
54
|
+
"""Build the TensorRT engines for this machine in the background and swap them in once each one is validated
|
|
55
|
+
against the session it replaces. Safe to call at any time; returns immediately unless `wait` is set.
|
|
56
|
+
Long-lived processes (server, endpoint handler, batch jobs) should call this at startup: the pipeline keeps
|
|
57
|
+
serving on the CUDA provider meanwhile, and gets 2-3x faster when the engines are ready."""
|
|
58
|
+
n = self.prov.start_background_builds() if hasattr(self.prov, "start_background_builds") else 0
|
|
59
|
+
if wait and n: return self.prov.wait_for_engines(timeout)
|
|
60
|
+
return {"builds_started": n}
|
|
61
|
+
|
|
62
|
+
def ready(self):
|
|
63
|
+
"""Provider each model ended on, fallbacks that happened, engine-cache state, model hashes."""
|
|
64
|
+
st = self.prov.summary()
|
|
65
|
+
if self.lay is not None: st.setdefault("layout", {})["engine"] = self.lay.stage
|
|
66
|
+
return {"name": self.name, "version": self.version, "config": self.cfg.source, "gpu": self.prov.gpu, "tensorrt": self.prov.trt_ver,
|
|
67
|
+
"providers": st, "events": list(self.prov.events), "engine_cache": str(self.prov.cache_root), "load_s": self._load_s, "warmup_s": self._warm,
|
|
68
|
+
"model": dict(self.batch.model_ids)}
|
|
69
|
+
|
|
70
|
+
# ---- full documents -------------------------------------------------------------------------------------------
|
|
71
|
+
def page(self, image, polygons=None, dpi=None):
|
|
72
|
+
"""Single page (latency path): one kanen-ocr/1.0 document."""
|
|
73
|
+
img = to_bgr(image); docs, _ = self.batch.process([img], dpi=dpi, with_polygons=self._poly(polygons)); d = docs[0]
|
|
74
|
+
if isinstance(image, (str, Path)): d["page"]["file"] = Path(image).name
|
|
75
|
+
return d
|
|
76
|
+
|
|
77
|
+
def pages(self, inputs, page_batch=None, polygons=None, pdf_dpi=200):
|
|
78
|
+
"""Batched: list/dir/glob of image or PDF paths, or a list of arrays / PIL images / bytes -> list of documents."""
|
|
79
|
+
return [d for d in self.stream(inputs, page_batch, polygons, pdf_dpi)]
|
|
80
|
+
|
|
81
|
+
def stream(self, inputs, page_batch=None, polygons=None, pdf_dpi=200):
|
|
82
|
+
"""Generator variant of pages(): yields documents in input order as each page batch completes."""
|
|
83
|
+
sources = self._sources(inputs, pdf_dpi); labels = [s[0] for s in sources]; items = [s[1] for s in sources]
|
|
84
|
+
page_batch = page_batch or self.cfg["runtime"].get("page_batch", 8); k = 0
|
|
85
|
+
for doc, st in self.batch.stream(items, page_batch=page_batch, loader=to_bgr, with_polygons=self._poly(polygons)):
|
|
86
|
+
if labels[k]: doc["page"]["file"] = labels[k]
|
|
87
|
+
self.last_stats = st; k += 1; yield doc
|
|
88
|
+
|
|
89
|
+
def _sources(self, inputs, pdf_dpi):
|
|
90
|
+
if isinstance(inputs, (str, Path)) or (isinstance(inputs, (list, tuple)) and inputs and all(isinstance(x, (str, Path)) for x in inputs)):
|
|
91
|
+
return list(iter_page_sources(expand_inputs(inputs), dpi=pdf_dpi))
|
|
92
|
+
if isinstance(inputs, (list, tuple)): return [(getattr(x, "name", None) or "", x) for x in inputs]
|
|
93
|
+
return [("", inputs)]
|
|
94
|
+
|
|
95
|
+
def _poly(self, polygons): return bool(self.cfg["runtime"].get("polygons", False)) if polygons is None else bool(polygons)
|
|
96
|
+
|
|
97
|
+
# ---- single stages --------------------------------------------------------------------------------------------
|
|
98
|
+
def detect(self, image):
|
|
99
|
+
"""Text-line detection only -> list of (polygon 4x2 float32 in page pixels, score)."""
|
|
100
|
+
return self.det(to_bgr(image))
|
|
101
|
+
|
|
102
|
+
def recognize(self, crops, batch=None):
|
|
103
|
+
"""Recognition only on a list of line images (any size, BGR or gray) -> [(text, confidence, min_conf)]."""
|
|
104
|
+
res = self.rec([to_bgr(c) for c in crops], bs=batch)
|
|
105
|
+
return [(t, float(c), float(getattr(c, "min_char", c))) for t, c in res]
|
|
106
|
+
|
|
107
|
+
def layout(self, image):
|
|
108
|
+
"""Layout blocks only -> [{type, score, bbox, order}] (labels: title, text, table, figure, seal, id_panel, header, footer, handwriting)."""
|
|
109
|
+
if self.lay is None: self.lay = Layout(self.cfg, self.prov); self.batch.lay = self.lay
|
|
110
|
+
return self.lay(to_bgr(image))[0]
|
|
111
|
+
|
|
112
|
+
def crops(self, image, boxes=None):
|
|
113
|
+
"""Perspective line crops for detect() boxes (helper for recognize())."""
|
|
114
|
+
img = to_bgr(image); boxes = self.detect(img) if boxes is None else boxes
|
|
115
|
+
return [crop(img, b) for b, _ in boxes]
|
|
116
|
+
|
|
117
|
+
# ---- serialisation --------------------------------------------------------------------------------------------
|
|
118
|
+
def render(self, doc, fmt="json"):
|
|
119
|
+
return formats.render(doc, fmt)
|