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.
@@ -0,0 +1,3 @@
1
+ Apache License 2.0 — see https://www.apache.org/licenses/LICENSE-2.0
2
+
3
+ Copyright 2026 Chethan (anandkaman). Base models: PaddleOCR (Apache-2.0), OpenOCR SVTRv2 (Apache-2.0).
@@ -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,4 @@
1
+ [console_scripts]
2
+ arjuna = kanen_infer.arjuna_cli:main
3
+ arjuna-ocr = kanen_infer.cli:main
4
+ kanen-ocr = kanen_infer.cli:main
@@ -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)