anaksor-ocr 0.1.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.
- anaksor_ocr-0.1.0/.github/workflows/README.md +18 -0
- anaksor_ocr-0.1.0/.gitignore +14 -0
- anaksor_ocr-0.1.0/CHANGELOG.md +34 -0
- anaksor_ocr-0.1.0/LICENSE +202 -0
- anaksor_ocr-0.1.0/NOTICE +7 -0
- anaksor_ocr-0.1.0/PKG-INFO +265 -0
- anaksor_ocr-0.1.0/README.md +215 -0
- anaksor_ocr-0.1.0/SECURITY.md +29 -0
- anaksor_ocr-0.1.0/pyproject.toml +89 -0
- anaksor_ocr-0.1.0/src/anaksor/__init__.py +54 -0
- anaksor_ocr-0.1.0/src/anaksor/__main__.py +3 -0
- anaksor_ocr-0.1.0/src/anaksor/cli.py +151 -0
- anaksor_ocr-0.1.0/src/anaksor/data/det_repvit_db_v2.yml +33 -0
- anaksor_ocr-0.1.0/src/anaksor/data/det_repvit_db_v3.yml +22 -0
- anaksor_ocr-0.1.0/src/anaksor/data/glyphless.ttf +0 -0
- anaksor_ocr-0.1.0/src/anaksor/data/khmer_dict_v3.txt +211 -0
- anaksor_ocr-0.1.0/src/anaksor/data/rec_svtrv2_v3.yml +51 -0
- anaksor_ocr-0.1.0/src/anaksor/data/rec_svtrv2_v4.yml +57 -0
- anaksor_ocr-0.1.0/src/anaksor/db_boxes.py +105 -0
- anaksor_ocr-0.1.0/src/anaksor/decoding.py +107 -0
- anaksor_ocr-0.1.0/src/anaksor/detector.py +136 -0
- anaksor_ocr-0.1.0/src/anaksor/export/__init__.py +33 -0
- anaksor_ocr-0.1.0/src/anaksor/export/searchable_pdf.py +147 -0
- anaksor_ocr-0.1.0/src/anaksor/export/word.py +189 -0
- anaksor_ocr-0.1.0/src/anaksor/geometry.py +109 -0
- anaksor_ocr-0.1.0/src/anaksor/inputs.py +138 -0
- anaksor_ocr-0.1.0/src/anaksor/layout.py +209 -0
- anaksor_ocr-0.1.0/src/anaksor/longline.py +101 -0
- anaksor_ocr-0.1.0/src/anaksor/models.py +236 -0
- anaksor_ocr-0.1.0/src/anaksor/nets/__init__.py +66 -0
- anaksor_ocr-0.1.0/src/anaksor/nets/cppd.py +103 -0
- anaksor_ocr-0.1.0/src/anaksor/nets/dbnet.py +108 -0
- anaksor_ocr-0.1.0/src/anaksor/nets/fuse.py +109 -0
- anaksor_ocr-0.1.0/src/anaksor/nets/layers.py +106 -0
- anaksor_ocr-0.1.0/src/anaksor/nets/rctc.py +40 -0
- anaksor_ocr-0.1.0/src/anaksor/nets/repvit.py +123 -0
- anaksor_ocr-0.1.0/src/anaksor/nets/smtr.py +132 -0
- anaksor_ocr-0.1.0/src/anaksor/nets/svtrv2.py +221 -0
- anaksor_ocr-0.1.0/src/anaksor/nets/textline.py +62 -0
- anaksor_ocr-0.1.0/src/anaksor/ocr.py +214 -0
- anaksor_ocr-0.1.0/src/anaksor/onnx_export.py +109 -0
- anaksor_ocr-0.1.0/src/anaksor/py.typed +0 -0
- anaksor_ocr-0.1.0/src/anaksor/recognizer.py +215 -0
- anaksor_ocr-0.1.0/src/anaksor/runtime.py +101 -0
- anaksor_ocr-0.1.0/src/anaksor/text.py +190 -0
- anaksor_ocr-0.1.0/tests/conftest.py +80 -0
- anaksor_ocr-0.1.0/tests/data/checkpoint_layout.json +4346 -0
- anaksor_ocr-0.1.0/tests/data/golden.npz +0 -0
- anaksor_ocr-0.1.0/tests/test_cli.py +66 -0
- anaksor_ocr-0.1.0/tests/test_decoding.py +39 -0
- anaksor_ocr-0.1.0/tests/test_inputs.py +58 -0
- anaksor_ocr-0.1.0/tests/test_layout.py +64 -0
- anaksor_ocr-0.1.0/tests/test_models.py +139 -0
- anaksor_ocr-0.1.0/tests/test_native_models.py +167 -0
- anaksor_ocr-0.1.0/tests/test_ocr_export.py +101 -0
- anaksor_ocr-0.1.0/tests/test_onnx_backend.py +164 -0
- anaksor_ocr-0.1.0/tests/test_real_weights.py +34 -0
- anaksor_ocr-0.1.0/tests/test_spec_from_config.py +98 -0
- anaksor_ocr-0.1.0/tests/test_text_geometry.py +79 -0
- anaksor_ocr-0.1.0/tools/convert_weights.py +42 -0
- anaksor_ocr-0.1.0/tools/export_onnx.py +44 -0
- anaksor_ocr-0.1.0/tools/spec_from_config.py +113 -0
- anaksor_ocr-0.1.0/web/README.md +119 -0
- anaksor_ocr-0.1.0/web/deploy/README.md +96 -0
- anaksor_ocr-0.1.0/web/src/fonts/README.md +8 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# CI and release workflows (to activate)
|
|
2
|
+
|
|
3
|
+
These are the repository's workflows. They live here because the app that pushed this
|
|
4
|
+
branch may not write to `.github/workflows/`. To turn them on:
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
git mv .github/workflows-pending .github/workflows
|
|
8
|
+
git rm .github/workflows/README.md
|
|
9
|
+
git commit -m "ci: enable workflows" && git push
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
- `ci.yml`: tests (Python 3.11 to 3.13, CPU torch), ruff, lock check, pip-audit of the
|
|
13
|
+
locked and of the lowest allowed dependencies (weekly as well), wheel build and a clean
|
|
14
|
+
install of it.
|
|
15
|
+
- `release.yml`: on a `vX.Y.Z` tag, audit, build, and publish to PyPI with trusted publishing.
|
|
16
|
+
- `web.yml`: lint, typecheck, build, browser smoke test and dependency scan of the website
|
|
17
|
+
(`web/`, on Bun); on `master`, the deploy to the server behind ocr.sokhengdin.dev
|
|
18
|
+
(`web/deploy/README.md`: the `production` environment and its secrets).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (unreleased)
|
|
4
|
+
|
|
5
|
+
First release, extracted from the Alpha-OCR project that serves https://ocr.sokhengdin.dev.
|
|
6
|
+
|
|
7
|
+
- `anaksor.OCR`: detection (DBNet, RepViT) and recognition (SVTRv2, CTC) of Khmer text,
|
|
8
|
+
reading order with columns, receipts and paragraphs, long lines read in stitched windows.
|
|
9
|
+
- PDF input (`anaksor-ocr[pdf]`), searchable PDF and Word export (`anaksor-ocr[docx]`).
|
|
10
|
+
- `anaksor` command line.
|
|
11
|
+
- Weights as `.safetensors`, downloaded on first use and checksum-verified.
|
|
12
|
+
- Line crop margin 0.22 / 0.20 of the line height for the v3 recogniser.
|
|
13
|
+
- Default models: recognition v4 (SVTRv2-B, crop margin 0.16 / 0.10) and detection v3,
|
|
14
|
+
published at https://huggingface.co/Sokheng/anaksor-ocr (tag `rec-v4-det-v3`). The v2 / v3
|
|
15
|
+
specs stay bundled (`"det-repvit-db-v2"`, `"rec-svtrv2-v3"`); their weights aren't published.
|
|
16
|
+
- Weights are only downloaded when the spec pins their SHA-256 (`ANAKSOR_ALLOW_UNVERIFIED=1`
|
|
17
|
+
for your own unhashed uploads). They come from the release's Hub commit (`5c9f76c`, the
|
|
18
|
+
repo's `_latest` files there), not `main`; `ANAKSOR_HF_REVISION` picks another.
|
|
19
|
+
- Own implementation of the networks (`anaksor.nets`: RepViT + DBNet, SVTRv2 with RCTC and
|
|
20
|
+
SMTR heads), the DB box extraction and the CTC / SMTR decoding; no vendored OpenOCR code.
|
|
21
|
+
- `[export]` needs `onnx>=1.22` (earlier releases have the external-data path traversal and
|
|
22
|
+
shape-inference advisories fixed in 1.21 / 1.22).
|
|
23
|
+
Checkpoints trained in Alpha-OCR (`.pth` or converted `.safetensors`) load unchanged, and
|
|
24
|
+
the outputs match the training-time implementation (golden tests in `tests/data`).
|
|
25
|
+
- CPPD recognisers (`CPPDDecoder`); long lines without CTC positions are joined on the
|
|
26
|
+
overlap of their windows' texts.
|
|
27
|
+
- `tools/spec_from_config.py`: a spec from any Alpha-OCR training config (rec v2/v3/v4,
|
|
28
|
+
CPPD, det v2/v3), so its `best.pth` loads directly.
|
|
29
|
+
- Two backends: ONNX Runtime (default install, no PyTorch) and PyTorch (`[torch]`), chosen
|
|
30
|
+
with `backend=` / `--backend`; same boxes and text. `tools/export_onnx.py` exports the
|
|
31
|
+
detector and the CTC / CPPD recognisers with dynamic shapes, checked against PyTorch.
|
|
32
|
+
Specs name the graph in `weights.onnx` (+ `onnx_sha256`).
|
|
33
|
+
- Batch norms and RepViT's re-parameterisable branches are folded into single convolutions
|
|
34
|
+
when a model loads (`fuse=True`): same output, detection about 2.5x faster on CPU.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
anaksor_ocr-0.1.0/NOTICE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
anaksor-ocr
|
|
2
|
+
Copyright 2026 Sokheng Din
|
|
3
|
+
|
|
4
|
+
The networks in src/anaksor/nets are written for anaksor from the papers they implement
|
|
5
|
+
(DBNet, RepViT, SVTRv2, SMTR, CPPD; see README.md). Their layer layout and tensor names follow
|
|
6
|
+
the OpenOCR implementations (https://github.com/Topdu/OpenOCR, Apache License 2.0) that
|
|
7
|
+
the released models were trained with, so those checkpoints load unchanged.
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: anaksor-ocr
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Khmer OCR: photos, scans and PDFs to Khmer text, with layout, searchable PDF and Word export
|
|
5
|
+
Project-URL: Homepage, https://ocr.sokhengdin.dev
|
|
6
|
+
Project-URL: Documentation, https://sokhengdin.github.io/anaksor-ocr/
|
|
7
|
+
Project-URL: Repository, https://github.com/SokhengDin/anaksor-ocr
|
|
8
|
+
Project-URL: Issues, https://github.com/SokhengDin/anaksor-ocr/issues
|
|
9
|
+
Author: Sokheng Din
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
License-File: NOTICE
|
|
13
|
+
Keywords: cambodia,document-ai,khmer,ocr,pdf,text-detection,text-recognition
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Image Recognition
|
|
23
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: numpy>=1.26
|
|
26
|
+
Requires-Dist: onnxruntime>=1.20
|
|
27
|
+
Requires-Dist: opencv-python-headless>=4.10.0.84
|
|
28
|
+
Requires-Dist: pillow>=13.0.0
|
|
29
|
+
Requires-Dist: pyclipper>=1.3.0.post6
|
|
30
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
31
|
+
Requires-Dist: shapely>=2.0.6
|
|
32
|
+
Provides-Extra: all
|
|
33
|
+
Requires-Dist: pypdfium2>=4.30; extra == 'all'
|
|
34
|
+
Requires-Dist: python-docx>=1.1.2; extra == 'all'
|
|
35
|
+
Requires-Dist: safetensors>=0.4.5; extra == 'all'
|
|
36
|
+
Requires-Dist: torch>=2.13; extra == 'all'
|
|
37
|
+
Provides-Extra: docx
|
|
38
|
+
Requires-Dist: python-docx>=1.1.2; extra == 'docx'
|
|
39
|
+
Provides-Extra: export
|
|
40
|
+
Requires-Dist: onnx>=1.22; extra == 'export'
|
|
41
|
+
Requires-Dist: onnxscript>=0.2; extra == 'export'
|
|
42
|
+
Requires-Dist: safetensors>=0.4.5; extra == 'export'
|
|
43
|
+
Requires-Dist: torch>=2.13; extra == 'export'
|
|
44
|
+
Provides-Extra: pdf
|
|
45
|
+
Requires-Dist: pypdfium2>=4.30; extra == 'pdf'
|
|
46
|
+
Provides-Extra: torch
|
|
47
|
+
Requires-Dist: safetensors>=0.4.5; extra == 'torch'
|
|
48
|
+
Requires-Dist: torch>=2.13; extra == 'torch'
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
|
|
51
|
+
# anaksor-ocr
|
|
52
|
+
|
|
53
|
+
**អានអក្សរ**: Khmer OCR for photos, scans and PDFs. It finds the text lines, reads them,
|
|
54
|
+
puts them in reading order (columns, receipts, paragraphs) and exports plain text, JSON, a
|
|
55
|
+
searchable PDF or a Word file.
|
|
56
|
+
|
|
57
|
+
Website, with a live demo and the documentation in Khmer and English:
|
|
58
|
+
**[ocr.sokhengdin.dev](https://ocr.sokhengdin.dev)**.
|
|
59
|
+
|
|
60
|
+
| | |
|
|
61
|
+
|---|---|
|
|
62
|
+
| Detection | DBNet with a RepViT backbone, trained on KhmerST photos and synthetic pages |
|
|
63
|
+
| Recognition | SVTRv2 with a CTC head, trained on public Khmer datasets and synthetic lines |
|
|
64
|
+
| Layout | deskew, column detection, line growing, paragraphs, long lines read in stitched windows |
|
|
65
|
+
| Runs on | ONNX Runtime (CPU, CUDA) by default; PyTorch (CPU, CUDA, Apple MPS) with `[torch]` |
|
|
66
|
+
|
|
67
|
+
## Install
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install anaksor-ocr # images, ONNX Runtime backend (no PyTorch)
|
|
71
|
+
pip install "anaksor-ocr[torch]" # + PyTorch backend: CUDA / Apple MPS, SMTR head, .pth weights
|
|
72
|
+
pip install "anaksor-ocr[all]" # PyTorch + PDF input + Word export
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Python 3.11+. For a specific CUDA build of PyTorch, install `torch` from
|
|
76
|
+
[pytorch.org](https://pytorch.org/get-started/locally/) first; for ONNX Runtime on CUDA,
|
|
77
|
+
`pip install onnxruntime-gpu`. The weights (about 95 MB for ONNX Runtime, 113 MB for PyTorch) download to `~/.cache/anaksor`
|
|
78
|
+
on first use.
|
|
79
|
+
|
|
80
|
+
### Backends
|
|
81
|
+
|
|
82
|
+
| | `onnx` (default install) | `torch` (`[torch]`) |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| Install | no PyTorch (with CUDA, PyTorch is several GB) | PyTorch |
|
|
85
|
+
| Devices | CPU; CUDA with `onnxruntime-gpu` | CPU, CUDA, Apple MPS |
|
|
86
|
+
| Weights | exported `.onnx` graphs | `.safetensors`, `.pth` checkpoints |
|
|
87
|
+
| Heads | CTC, CPPD | CTC, SMTR (`decoder="gtc"` / `"auto"`), CPPD |
|
|
88
|
+
|
|
89
|
+
`backend="auto"` (also `--backend` on the command line) uses PyTorch when it is installed,
|
|
90
|
+
else ONNX Runtime; an `.onnx` weights file always runs in ONNX Runtime. Both give the same
|
|
91
|
+
boxes and text. On PyTorch the batch norms are folded into the convolutions when a model
|
|
92
|
+
loads (`fuse=True`): same output, detection about 2.5x faster on CPU.
|
|
93
|
+
|
|
94
|
+
On CPU the two are close; PyTorch is a little faster (6 bench pages, rec v3: 79 s and
|
|
95
|
+
0.7 GB peak memory on PyTorch, 99 s and 0.9 GB on ONNX Runtime). ONNX Runtime's gain is the
|
|
96
|
+
install. Wide crops are recognised in smaller batches on both, as attention memory grows
|
|
97
|
+
with the square of the width.
|
|
98
|
+
|
|
99
|
+
## Command line
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
anaksor photo.jpg # text to stdout
|
|
103
|
+
anaksor scan.pdf -o scan.docx # Word, layout rebuilt
|
|
104
|
+
anaksor scan.pdf -o scan-searchable.pdf # page images + selectable text
|
|
105
|
+
anaksor *.jpg -o out/ -f json # one JSON per image
|
|
106
|
+
anaksor scan.pdf --pages 1-3,7 --device cuda
|
|
107
|
+
anaksor --fetch-models # download the weights ahead of time
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Python
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import anaksor
|
|
114
|
+
|
|
115
|
+
ocr = anaksor.OCR() # backend and device picked automatically, see Backends
|
|
116
|
+
result = ocr("page.jpg")
|
|
117
|
+
print(result["text"]) # reading order; paragraphs split by blank lines
|
|
118
|
+
|
|
119
|
+
for line in result["lines"]: # text, box, quads, parts, rec_score, det_score, block
|
|
120
|
+
print(line["rec_score"], line["text"])
|
|
121
|
+
|
|
122
|
+
pages = ocr.read("scan.pdf", keep_images=True) # one result per page
|
|
123
|
+
open("scan.pdf", "wb").write(anaksor.export.to_pdf(pages))
|
|
124
|
+
open("scan.docx", "wb").write(anaksor.export.to_docx(pages))
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`ocr(image)` takes a path, bytes, a PIL image or an RGB numpy array. Useful options:
|
|
128
|
+
|
|
129
|
+
| Option | Default | Effect |
|
|
130
|
+
|---|---|---|
|
|
131
|
+
| `pad_y`, `pad_x` | 0.16, 0.10 | margin around each line before reading, as a fraction of its height |
|
|
132
|
+
| `min_rec_score` | 0.5 | drop lines read with less confidence |
|
|
133
|
+
| `unclip_ratio` | 1.5 | how much detected boxes are grown (bigger joins more) |
|
|
134
|
+
| `scales` | `(1.0,)` | extra detection scales, e.g. `(1.0, 1.6)` for small text |
|
|
135
|
+
|
|
136
|
+
The detector and recogniser can be used on their own: `anaksor.Detector().detect(img)` and
|
|
137
|
+
`anaksor.Recognizer().recognize([crop, ...])`.
|
|
138
|
+
|
|
139
|
+
## Accuracy and limitations
|
|
140
|
+
|
|
141
|
+
| Part | Evaluated on | Result |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| Detection v3 | KhmerST validation photos | precision 0.75, recall 0.79 |
|
|
144
|
+
| Detection v3 | HierText validation: English on photos | precision 0.76, recall 0.69 |
|
|
145
|
+
| Detection v3 | synthetic pages | precision 0.998, recall 0.998 |
|
|
146
|
+
| Recognition v4 | 3,878 real validation lines: Khmer, Latin and mixed | 86.7% exactly right, 1.38% character error |
|
|
147
|
+
| Recognition v4 | the 2,497 Khmer-only lines among them | 90.1% exactly right, 0.73% character error |
|
|
148
|
+
|
|
149
|
+
KhmerST labels only some of the text in each photo; for detection, unlabelled text found by
|
|
150
|
+
another detector counts as "don't care", which flatters precision a little.
|
|
151
|
+
|
|
152
|
+
- Printed Khmer, documents, receipts and signs read best.
|
|
153
|
+
- English and other Latin text on photos is weaker: the detector can miss it on signs, and the
|
|
154
|
+
recogniser has seen little real English.
|
|
155
|
+
- Handwriting and decorative lettering are weak. Check each line's `rec_score`, or use
|
|
156
|
+
`--highlight-low` for Word output.
|
|
157
|
+
|
|
158
|
+
Training data and its licences are listed in the model card on
|
|
159
|
+
[Hugging Face](https://huggingface.co/Sokheng/anaksor-ocr).
|
|
160
|
+
|
|
161
|
+
## Weights
|
|
162
|
+
|
|
163
|
+
Each model has two files: an ONNX graph (`.onnx`, used by the onnx backend) and PyTorch
|
|
164
|
+
weights (`.safetensors`, used by the torch backend). Only the files the chosen backend needs
|
|
165
|
+
are fetched, from the first of: an explicit `det_weights=` / `rec_weights=` path,
|
|
166
|
+
`$ANAKSOR_WEIGHTS_DIR`, then the cache (`$ANAKSOR_HOME`, default `~/.cache/anaksor`), which
|
|
167
|
+
is filled from the Hugging Face repo `Sokheng/anaksor-ocr` and checked against the
|
|
168
|
+
SHA-256 in the model spec before it is used; a file whose spec has no SHA-256 is not
|
|
169
|
+
downloaded at all. Neither format can run code when loaded. Files come from the Hub commit
|
|
170
|
+
of the release the package was built for (rec v4 + det v3, `5c9f76c`), not from `main`,
|
|
171
|
+
where the `_latest` files move on with each release; `ANAKSOR_HF_REVISION` picks another. Offline machines: copy
|
|
172
|
+
the two files for your backend into `$ANAKSOR_WEIGHTS_DIR` (`anaksor --fetch-models`
|
|
173
|
+
downloads them ahead of time).
|
|
174
|
+
|
|
175
|
+
### Other models
|
|
176
|
+
|
|
177
|
+
Models trained with OpenOCR-style configs (the format these models were trained with) run
|
|
178
|
+
with a spec made from the training config. The tools are in the repository, not the
|
|
179
|
+
installed package, so run them from a clone:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
git clone https://github.com/SokhengDin/anaksor-ocr && cd anaksor-ocr
|
|
183
|
+
python tools/spec_from_config.py path/to/svtrv2_cppd_khmer.yml --out specs/
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
rec = anaksor.Recognizer("specs/rec-svtrv2-cppd-khmer.yml", weights="best.pth")
|
|
188
|
+
ocr = anaksor.OCR(recognizer=rec)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
For the onnx backend, export the weights (needs `[export]`); the spec already names the
|
|
192
|
+
`.onnx` file, so only the printed SHA-256 goes into `weights.onnx_sha256`:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
python tools/export_onnx.py --rec best.pth --rec-spec specs/rec-svtrv2-cppd-khmer.yml --out onnx/
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Supported: detection RepViT + DBNet; recognition SVTRv2 (any size) with RCTC + SMTR heads
|
|
199
|
+
(`GTCDecoder`, also with the training-only count head), RCTC alone, or CPPD. The `.pth`
|
|
200
|
+
checkpoints load as they are (no code runs: `weights_only`); `tools/convert_weights.py`
|
|
201
|
+
turns them into `.safetensors`. A CPPD head has no per-column output, so lines wider than
|
|
202
|
+
its input are read in overlapping windows joined where their texts overlap, instead of by
|
|
203
|
+
CTC character positions.
|
|
204
|
+
|
|
205
|
+
## Security
|
|
206
|
+
|
|
207
|
+
Image inputs are decoded only as JPEG, PNG, WEBP, BMP or TIFF, weights are loaded without
|
|
208
|
+
running code, and dependency floors exclude versions with known vulnerabilities (checked with
|
|
209
|
+
`pip-audit` in CI). See [SECURITY.md](SECURITY.md).
|
|
210
|
+
|
|
211
|
+
## Development
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
uv venv && uv pip install torch --index-url https://download.pytorch.org/whl/cpu
|
|
215
|
+
uv pip install -e ".[all]" pytest ruff pip-audit onnx onnxscript
|
|
216
|
+
pytest # no weights or network needed
|
|
217
|
+
ANAKSOR_TEST_WEIGHTS=/path/to/weights pytest -m weights
|
|
218
|
+
ruff check src tests tools && ruff format --check src tests tools
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The website (landing page, demo, documentation) is a Next.js app in `web/`, see
|
|
222
|
+
[web/README.md](web/README.md).
|
|
223
|
+
|
|
224
|
+
Layout of the package:
|
|
225
|
+
|
|
226
|
+
| Module | Role |
|
|
227
|
+
|---|---|
|
|
228
|
+
| `ocr.py` | `OCR`: detect, crop, recognise, lay out |
|
|
229
|
+
| `detector.py`, `recognizer.py` | the two models |
|
|
230
|
+
| `layout.py` | reading order: XY-cut columns, lines, paragraphs |
|
|
231
|
+
| `longline.py` | lines wider than the recogniser's input: windows stitched by CTC positions (CPPD: by text overlap) |
|
|
232
|
+
| `geometry.py` | box maths and crops |
|
|
233
|
+
| `text.py` | Khmer normalisation and the recognition charset |
|
|
234
|
+
| `inputs.py` | images (format allowlist) and PDF pages |
|
|
235
|
+
| `export/` | searchable PDF and Word |
|
|
236
|
+
| `models.py` | model specs, weights download, checksum and safe loading |
|
|
237
|
+
| `runtime.py` | where the networks run: PyTorch or ONNX Runtime |
|
|
238
|
+
| `onnx_export.py` | ONNX export (fused, dynamic shapes, checked against PyTorch) |
|
|
239
|
+
| `nets/` | the networks: RepViT + DBNet, SVTRv2 with RCTC, SMTR or CPPD heads; `fuse.py` folds batch norms |
|
|
240
|
+
| `db_boxes.py` | text boxes from the detector's probability map |
|
|
241
|
+
| `decoding.py` | charsets, CTC, SMTR and CPPD decoding |
|
|
242
|
+
|
|
243
|
+
### Releasing
|
|
244
|
+
|
|
245
|
+
1. CI: move `.github/workflows-pending/` to `.github/workflows/` (see the README there).
|
|
246
|
+
2. Weights: `python tools/convert_weights.py best.pth` for each model, then
|
|
247
|
+
`python tools/export_onnx.py --det ... --rec ...`; upload the `.safetensors` and `.onnx`
|
|
248
|
+
files to the Hugging Face repo and put the printed SHA-256s into `src/anaksor/data/*.yml`
|
|
249
|
+
(`weights.sha256`, `weights.onnx_sha256`). Add a model card to the Hugging Face repo:
|
|
250
|
+
training data, its licences, and the evaluation results above.
|
|
251
|
+
3. Bump `version` in `pyproject.toml`, update `CHANGELOG.md`, then tag `vX.Y.Z`.
|
|
252
|
+
The release workflow audits the dependencies, builds, and publishes to PyPI through
|
|
253
|
+
trusted publishing.
|
|
254
|
+
|
|
255
|
+
## Credits and licence
|
|
256
|
+
|
|
257
|
+
Apache-2.0. The networks are implemented from their papers, with the layer layout and
|
|
258
|
+
tensor names of the [OpenOCR](https://github.com/Topdu/OpenOCR) implementations the models
|
|
259
|
+
were trained with (see [NOTICE](NOTICE)).
|
|
260
|
+
|
|
261
|
+
- DBNet: Liao et al., *Real-time Scene Text Detection with Differentiable Binarization*, AAAI 2020
|
|
262
|
+
- RepViT: Wang et al., *RepViT: Revisiting Mobile CNN From ViT Perspective*, CVPR 2024
|
|
263
|
+
- SVTRv2: Du et al., *SVTRv2: CTC Beats Encoder-Decoder Models in Scene Text Recognition*, ICCV 2025
|
|
264
|
+
- SMTR: Du et al., *Out of Length Text Recognition with Sub-String Matching*, AAAI 2025
|
|
265
|
+
- CPPD: Du et al., *Context Perception Parallel Decoder for Scene Text Recognition*, TPAMI 2025
|