Final Rizeh reader INT4 conversions; source 51b8c314b055; full-page qualification limits documented
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- BENCHMARK.md +26 -0
- MANIFEST.json +221 -0
- README.md +55 -0
- RECIPE.md +31 -0
- VALIDATION.json +0 -0
- __pycache__/inference.cpython-311.pyc +0 -0
- checks/browser-decoder-check.json +6 -0
- checks/browser-loader-check.json +12 -0
- checks/package-litert-smoke.json +7 -0
- checks/package-smoke.json +16 -0
- components.json +67 -0
- components/body/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- components/body/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- components/body/models/recognizer-w4-block16.mlpackage/Manifest.json +18 -0
- components/degraded/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- components/degraded/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- components/degraded/models/recognizer-w4-block16.mlpackage/Manifest.json +18 -0
- components/numeric_math/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- components/numeric_math/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- components/numeric_math/models/recognizer-w4-block16.mlpackage/Manifest.json +18 -0
- components/printed/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- components/printed/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- components/printed/models/recognizer-w4-block16.mlpackage/Manifest.json +18 -0
- components/short_labels/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- components/short_labels/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- components/short_labels/models/recognizer-w4-block16.mlpackage/Manifest.json +18 -0
- experimental-detectors/line_guard/config.json +54 -0
- experimental-detectors/line_guard/detector-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- experimental-detectors/line_guard/detector-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- experimental-detectors/line_guard/detector-w4-block16.mlpackage/Manifest.json +18 -0
- experimental-detectors/line_guard/preprocessor_config.json +23 -0
- experimental-detectors/main_detector/config.json +54 -0
- experimental-detectors/main_detector/detector-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- experimental-detectors/main_detector/detector-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- experimental-detectors/main_detector/detector-w4-block16.mlpackage/Manifest.json +18 -0
- experimental-detectors/main_detector/preprocessor_config.json +23 -0
- experimental-detectors/printed_detector/config.json +54 -0
- experimental-detectors/printed_detector/detector-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
- experimental-detectors/printed_detector/detector-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
- experimental-detectors/printed_detector/detector-w4-block16.mlpackage/Manifest.json +18 -0
- experimental-detectors/printed_detector/preprocessor_config.json +23 -0
- experimental/portable_pipeline.py +176 -0
- geometry.py +27 -0
- inference.py +60 -0
- polarity.py +37 -0
- quantization_config.json +79 -0
- recipe/build_recognizer.py +87 -0
- recipe/export_litert.py +45 -0
- recipe/portable_model.py +53 -0
- recipe/requirements-litert.txt +5 -0
BENCHMARK.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Reader throughput measurements
|
| 2 |
+
|
| 3 |
+
Crops/second on the same 300 frozen development crops per reader. Exact-result caches were disabled. This measures preprocessed input to compact CTC outputs, excluding loading, page segmentation, cropping, text assembly and table extraction. The source and ONNX/LiteRT measurements are batch one; tract reports warmed native model execution. Implementation timing boundaries differ slightly; these are indicative measurements, not a controlled cross-runtime ranking.
|
| 4 |
+
|
| 5 |
+
**Mac conversions and browser validation ran concurrently, so Mac timing numbers are contended.** WASM body/short-label tests used two threads and the other three used one. Browser background scheduling may affect these numbers. Linux CPU and GPU measurements must not be treated as the same device. Native Windows, iOS and Android hardware remain untested.
|
| 6 |
+
|
| 7 |
+
| Runtime/device | Body | Short labels | Printed | Numeric/math | Degraded |
|
| 8 |
+
|---|---:|---:|---:|---:|---:|
|
| 9 |
+
| Source FP32 / PyTorch2.10 CUDA / RTX5080 Laptop | 210.08 | 210.76 | 211.76 | 211.45 | 210.52 |
|
| 10 |
+
| Linux / RTX5080 Laptop / ORT CUDA1.23.2 | 347.08 | 342.76 | 315.42 | 330.48 | 323.72 |
|
| 11 |
+
| Linux / RTX5080 Laptop / tract CUDA | 230.45 | 242.53 | 225.46 | 222.40 | 216.53 |
|
| 12 |
+
| Linux / CPU / ORT1.31,2 threads | 14.42 | 14.50 | 10.16 | 13.45 | 13.47 |
|
| 13 |
+
| Linux / CPU / LiteRT2.3,2 threads | 43.50 | 45.07 | 42.84 | 42.01 | 43.30 |
|
| 14 |
+
| M2 Mac / Core ML CPU_AND_GPU | 51.39 | 37.93 | 37.90 | 47.45 | 78.94 |
|
| 15 |
+
| M2 Mac / tract CPU | 21.78 | 21.85 | 17.85 | 17.48 | 27.58 |
|
| 16 |
+
| M2 Mac / tract Metal | 27.30 | 27.76 | 14.09 | 20.46 | 43.57 |
|
| 17 |
+
| M2 Mac / ORT1.31 CPU | 8.34 | 8.34 | 5.11 | 5.23 | 7.32 |
|
| 18 |
+
| M2 Mac / ORT1.31 CPU HQQ | 7.69 | 7.67 | 5.34 | 5.27 | 9.59 |
|
| 19 |
+
| M2 Mac / Chrome WebGPU | 8.43 | 8.82 | 8.62 | 7.43 | 5.81 |
|
| 20 |
+
| M2 Mac / Chrome WASM | 0.91 | 0.99 | 0.55 | 0.45 | 0.47 |
|
| 21 |
+
|
| 22 |
+
All 55 reader/runtime pairs passed the >=98% character-parity gate in every development bucket. The lowest bucket across all pairs was 98.2586%. This is character agreement with the uncompressed model, not exact-line agreement or OCR accuracy.
|
| 23 |
+
|
| 24 |
+
The pure-W4 full-page handwritten-table diagnostic failed (~86.51% parity on the CPU block16 route). An optional image-only route retained FP32 readers for color-separated handwriting and reached 100% on that example. The three public page diagnostics with that mixed-precision policy reached 100%,99.8895%,100%. No table-grid extraction was exercised (table_grids=0); full document qualification remains open.
|
| 25 |
+
|
| 26 |
+
See VALIDATION.json for the complete per-reader bucket metrics and diagnostic scope.
|
MANIFEST.json
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"base_revision": "51b8c314b0552b1884c1f0e28016cb12a143f637",
|
| 3 |
+
"files": {
|
| 4 |
+
"requirements.txt": {
|
| 5 |
+
"bytes": 69,
|
| 6 |
+
"sha256": "9cd44c4193ee2ecb75fa477e10dd43690492120201bf43820151f38030e45df3"
|
| 7 |
+
},
|
| 8 |
+
"polarity.py": {
|
| 9 |
+
"bytes": 1442,
|
| 10 |
+
"sha256": "f006a8a94626496af91f604489efba22d29cca75621c3766a912e271a5139e9c"
|
| 11 |
+
},
|
| 12 |
+
"RECIPE.md": {
|
| 13 |
+
"bytes": 4075,
|
| 14 |
+
"sha256": "34dee9f78a3e9639def9c64926b088eb31f5cb6dca22ee54c92b811f10feb80f"
|
| 15 |
+
},
|
| 16 |
+
"BENCHMARK.md": {
|
| 17 |
+
"bytes": 2584,
|
| 18 |
+
"sha256": "adaab95a33f2e857a2f295a52e1c6fc61c7926c60b28e1e011dcc7865b0c618f"
|
| 19 |
+
},
|
| 20 |
+
"README.md": {
|
| 21 |
+
"bytes": 5015,
|
| 22 |
+
"sha256": "5f1b76bc9a908613348ea5a004374c659b5d27bf96037a8c2b49a61267929c42"
|
| 23 |
+
},
|
| 24 |
+
"quantization_config.json": {
|
| 25 |
+
"bytes": 2590,
|
| 26 |
+
"sha256": "da732a8e71b618ca018705b6e0aec17590fc067f7a8f5a801377d07944c5f18a"
|
| 27 |
+
},
|
| 28 |
+
"geometry.py": {
|
| 29 |
+
"bytes": 1922,
|
| 30 |
+
"sha256": "e8b6e0d5cabeeb5a6eeff6aa2ef9b317f8f34c83e88f0a5f82e17a99f7d0a6cc"
|
| 31 |
+
},
|
| 32 |
+
"VALIDATION.json": {
|
| 33 |
+
"bytes": 140369,
|
| 34 |
+
"sha256": "245ed6184048bfecefa67b188c0eeb04ea45943713cc2fbb5ddda0832f01c749"
|
| 35 |
+
},
|
| 36 |
+
"inference.py": {
|
| 37 |
+
"bytes": 4595,
|
| 38 |
+
"sha256": "2fab88114deb001eba8e0cb4cd4f9af7d190867fea5c84245d9ae78981ff712b"
|
| 39 |
+
},
|
| 40 |
+
"components.json": {
|
| 41 |
+
"bytes": 2016,
|
| 42 |
+
"sha256": "5d49aa0d30d5a7e0eebde35cdf4f149b07e6903d3e1461cf30b22e565ee15be5"
|
| 43 |
+
},
|
| 44 |
+
"vocab.json": {
|
| 45 |
+
"bytes": 20007,
|
| 46 |
+
"sha256": "d4330d46b65dca6f328fa6191bf02240c819ad83d9216bd9c78c13de895048dc"
|
| 47 |
+
},
|
| 48 |
+
"rtl.py": {
|
| 49 |
+
"bytes": 6097,
|
| 50 |
+
"sha256": "d5e90e33913d66d1204a2966cc54f42b7b8fd3f37ab476c493c564fe4d020b8b"
|
| 51 |
+
},
|
| 52 |
+
"experimental/portable_pipeline.py": {
|
| 53 |
+
"bytes": 11120,
|
| 54 |
+
"sha256": "c2d0b4a620b7e04b0ad46766950ead312a59f602a41293e99f7bc85bb174ed97"
|
| 55 |
+
},
|
| 56 |
+
"checks/package-smoke.json": {
|
| 57 |
+
"bytes": 311,
|
| 58 |
+
"sha256": "f4acb4534946a3ac7f8e6bff6cc25e92c62beb6dce3de7337734c3e82729138d"
|
| 59 |
+
},
|
| 60 |
+
"checks/browser-loader-check.json": {
|
| 61 |
+
"bytes": 338,
|
| 62 |
+
"sha256": "6681534f6712a591fb71c09e6a32fe49d4ab61d669036400106475ef94696d5e"
|
| 63 |
+
},
|
| 64 |
+
"checks/package-litert-smoke.json": {
|
| 65 |
+
"bytes": 140,
|
| 66 |
+
"sha256": "f1be9492777c493cf1beeacf99efa53815e0f96718830ebf872787c4f40eeae7"
|
| 67 |
+
},
|
| 68 |
+
"checks/browser-decoder-check.json": {
|
| 69 |
+
"bytes": 84,
|
| 70 |
+
"sha256": "7aa2b6c50334737c5f22721a7d689f7c45fc032e7dc9d94cbc52baabae4105a0"
|
| 71 |
+
},
|
| 72 |
+
"recipe/requirements-macos.txt": {
|
| 73 |
+
"bytes": 93,
|
| 74 |
+
"sha256": "8eb76eb39c4a004abd208f8ebe994a72a2669d07ff04fc3214b68ebf57b0d9cd"
|
| 75 |
+
},
|
| 76 |
+
"recipe/portable_model.py": {
|
| 77 |
+
"bytes": 2129,
|
| 78 |
+
"sha256": "f68eae9b098cd23e38560797f33e986ea922e56c886282b08631fe0d422ee283"
|
| 79 |
+
},
|
| 80 |
+
"recipe/export_litert.py": {
|
| 81 |
+
"bytes": 2807,
|
| 82 |
+
"sha256": "be7054e54d4d491074b49b294e31a9eab8a854756a96334e5eed8c1b30f4fe13"
|
| 83 |
+
},
|
| 84 |
+
"recipe/build_recognizer.py": {
|
| 85 |
+
"bytes": 5638,
|
| 86 |
+
"sha256": "1f0229995217d7265bc9cfba67948c2c9fce84922e86b4ceccc41d53e3da2431"
|
| 87 |
+
},
|
| 88 |
+
"recipe/requirements-litert.txt": {
|
| 89 |
+
"bytes": 97,
|
| 90 |
+
"sha256": "3def04424ae7d7f8023bf6164a16361744a36a6cae79b6ad385a5b0ddfbb017e"
|
| 91 |
+
},
|
| 92 |
+
"recipe/source/recognizer.py": {
|
| 93 |
+
"bytes": 3455,
|
| 94 |
+
"sha256": "03ffcc439ba4ada082c71b1e08e9bdcc8589332ae1e882557d44f004e3515dd2"
|
| 95 |
+
},
|
| 96 |
+
"__pycache__/inference.cpython-311.pyc": {
|
| 97 |
+
"bytes": 11159,
|
| 98 |
+
"sha256": "4daf2b018aa95dc40d25ce5db54ce592d1e96de8d95714fd358af32b2053c0e2"
|
| 99 |
+
},
|
| 100 |
+
"components/degraded/models/recognizer-w4-block16.mlpackage/Manifest.json": {
|
| 101 |
+
"bytes": 617,
|
| 102 |
+
"sha256": "675633649969487f825f5e61559d8fdeec20b22639b384f46f8713d55a3c5fa3"
|
| 103 |
+
},
|
| 104 |
+
"components/degraded/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel": {
|
| 105 |
+
"bytes": 136655,
|
| 106 |
+
"sha256": "fbb59f5a96148ffd4cbcda007e715e76bc7b9beed799b9700a4b6dd6f087280a"
|
| 107 |
+
},
|
| 108 |
+
"components/degraded/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin": {
|
| 109 |
+
"bytes": 26263424,
|
| 110 |
+
"sha256": "b300ceb473e7c9e3d97220cb34be29f7b49b030fa8693e58ae1b45710da8f55d"
|
| 111 |
+
},
|
| 112 |
+
"components/body/models/recognizer-w4-block16.mlpackage/Manifest.json": {
|
| 113 |
+
"bytes": 617,
|
| 114 |
+
"sha256": "2c7c591fa00b408a154d4623215337654358c5b2191ef35934c614f3889ffc4a"
|
| 115 |
+
},
|
| 116 |
+
"components/body/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel": {
|
| 117 |
+
"bytes": 136655,
|
| 118 |
+
"sha256": "068b9dd28a4464a544732ae3972b7da23629eb2fdd0434d855cdcec17be71fa9"
|
| 119 |
+
},
|
| 120 |
+
"components/body/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin": {
|
| 121 |
+
"bytes": 26263424,
|
| 122 |
+
"sha256": "45e4f1c7d1d8e0689dbe458d99014bef4ecf1e3f3e308a1399a94dfda1056e17"
|
| 123 |
+
},
|
| 124 |
+
"components/short_labels/models/recognizer-w4-block16.mlpackage/Manifest.json": {
|
| 125 |
+
"bytes": 617,
|
| 126 |
+
"sha256": "ce7c7bbce2d2f841baf7af009dd827cf0be193bbac12fab75ebbc62eb146f670"
|
| 127 |
+
},
|
| 128 |
+
"components/short_labels/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel": {
|
| 129 |
+
"bytes": 136655,
|
| 130 |
+
"sha256": "236f3f7de9ce5e2b2356333e653d996ff335d0bb6acde2c612c7f3190f57631a"
|
| 131 |
+
},
|
| 132 |
+
"components/short_labels/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin": {
|
| 133 |
+
"bytes": 26263424,
|
| 134 |
+
"sha256": "3d5c895f49c743122580765a36e37ab4a5ed6ae88526e940296e949cdd27cdd8"
|
| 135 |
+
},
|
| 136 |
+
"components/numeric_math/models/recognizer-w4-block16.mlpackage/Manifest.json": {
|
| 137 |
+
"bytes": 617,
|
| 138 |
+
"sha256": "6befae03e35e7c8b0214292bb2c9cfd56946157d4950795c637461cefefaf867"
|
| 139 |
+
},
|
| 140 |
+
"components/numeric_math/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel": {
|
| 141 |
+
"bytes": 136655,
|
| 142 |
+
"sha256": "c29f36bd45b9469a594b67e420efde84dfd8d663c138e0f1bee325c7d059debe"
|
| 143 |
+
},
|
| 144 |
+
"components/numeric_math/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin": {
|
| 145 |
+
"bytes": 26263424,
|
| 146 |
+
"sha256": "d15e99ea723d66fedbe3ab66319266a156273f84a352081b69f9650af4802ba1"
|
| 147 |
+
},
|
| 148 |
+
"components/printed/models/recognizer-w4-block16.mlpackage/Manifest.json": {
|
| 149 |
+
"bytes": 617,
|
| 150 |
+
"sha256": "81016c05409bafc0b082d3b4d3b4a60827adec441c383b41761648f2834b429b"
|
| 151 |
+
},
|
| 152 |
+
"components/printed/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel": {
|
| 153 |
+
"bytes": 136655,
|
| 154 |
+
"sha256": "5bfd191c319ff8ede42f41f35ac4ed609d1373c02f97f14b012649d705d08374"
|
| 155 |
+
},
|
| 156 |
+
"components/printed/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin": {
|
| 157 |
+
"bytes": 26263424,
|
| 158 |
+
"sha256": "b7c7da642b5bb0f429cdde70b0ea8815cce86175a1eb8b2a98a1d04c88c40f79"
|
| 159 |
+
},
|
| 160 |
+
"experimental-detectors/line_guard/config.json": {
|
| 161 |
+
"bytes": 858,
|
| 162 |
+
"sha256": "0f99d8176e3fc194966659cf6f1c52ab9ca1d573f47ba57aca55036ff5267d69"
|
| 163 |
+
},
|
| 164 |
+
"experimental-detectors/line_guard/preprocessor_config.json": {
|
| 165 |
+
"bytes": 373,
|
| 166 |
+
"sha256": "594344843fcd54db03240194c7677b5c459e78d349e68d4a89d7d91e4fa90587"
|
| 167 |
+
},
|
| 168 |
+
"experimental-detectors/line_guard/detector-w4-block16.mlpackage/Manifest.json": {
|
| 169 |
+
"bytes": 617,
|
| 170 |
+
"sha256": "893c116ab8685122991388c4ecf30ee0f9c7b05eb290c94f9e29da429585d26f"
|
| 171 |
+
},
|
| 172 |
+
"experimental-detectors/line_guard/detector-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel": {
|
| 173 |
+
"bytes": 183292,
|
| 174 |
+
"sha256": "3bbc26f64bd5168c8df476ce207d70977a29391ac85fc8745d967f2b573f6064"
|
| 175 |
+
},
|
| 176 |
+
"experimental-detectors/line_guard/detector-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin": {
|
| 177 |
+
"bytes": 30454400,
|
| 178 |
+
"sha256": "e9614d48774aae3a1e28ff645664d9b8f654f71c80bad4585432be258875b58c"
|
| 179 |
+
},
|
| 180 |
+
"experimental-detectors/printed_detector/config.json": {
|
| 181 |
+
"bytes": 858,
|
| 182 |
+
"sha256": "0f99d8176e3fc194966659cf6f1c52ab9ca1d573f47ba57aca55036ff5267d69"
|
| 183 |
+
},
|
| 184 |
+
"experimental-detectors/printed_detector/preprocessor_config.json": {
|
| 185 |
+
"bytes": 373,
|
| 186 |
+
"sha256": "594344843fcd54db03240194c7677b5c459e78d349e68d4a89d7d91e4fa90587"
|
| 187 |
+
},
|
| 188 |
+
"experimental-detectors/printed_detector/detector-w4-block16.mlpackage/Manifest.json": {
|
| 189 |
+
"bytes": 617,
|
| 190 |
+
"sha256": "55e73dae6485700c908feedaaafd055315a5b2fa40e2e2bf1ea741984518f173"
|
| 191 |
+
},
|
| 192 |
+
"experimental-detectors/printed_detector/detector-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel": {
|
| 193 |
+
"bytes": 183292,
|
| 194 |
+
"sha256": "b65e5fc8008ef59de2d91e4c7ec3e44a3d7e037367678564bfd5fb5810b69021"
|
| 195 |
+
},
|
| 196 |
+
"experimental-detectors/printed_detector/detector-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin": {
|
| 197 |
+
"bytes": 30454400,
|
| 198 |
+
"sha256": "02ba967fc37e29fe11eca73bdc77334272af1d2533cb9c7004f5eb0eb6d7591d"
|
| 199 |
+
},
|
| 200 |
+
"experimental-detectors/main_detector/config.json": {
|
| 201 |
+
"bytes": 858,
|
| 202 |
+
"sha256": "0f99d8176e3fc194966659cf6f1c52ab9ca1d573f47ba57aca55036ff5267d69"
|
| 203 |
+
},
|
| 204 |
+
"experimental-detectors/main_detector/preprocessor_config.json": {
|
| 205 |
+
"bytes": 373,
|
| 206 |
+
"sha256": "594344843fcd54db03240194c7677b5c459e78d349e68d4a89d7d91e4fa90587"
|
| 207 |
+
},
|
| 208 |
+
"experimental-detectors/main_detector/detector-w4-block16.mlpackage/Manifest.json": {
|
| 209 |
+
"bytes": 617,
|
| 210 |
+
"sha256": "ba24569c2754a72a8d28dd3eeccd666812aca11b6e98bce29b4afcefa820241e"
|
| 211 |
+
},
|
| 212 |
+
"experimental-detectors/main_detector/detector-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel": {
|
| 213 |
+
"bytes": 183292,
|
| 214 |
+
"sha256": "a7df9637d121d036e2bb2e251b9004240f441d83433cde53226e56a36f6c27a8"
|
| 215 |
+
},
|
| 216 |
+
"experimental-detectors/main_detector/detector-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin": {
|
| 217 |
+
"bytes": 30454400,
|
| 218 |
+
"sha256": "0640641e7f2df4053feb5f831e4974739f7839deee0b7c146229db7f10000267"
|
| 219 |
+
}
|
| 220 |
+
}
|
| 221 |
+
}
|
README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
language:
|
| 3 |
+
- fa
|
| 4 |
+
license: other
|
| 5 |
+
license_name: bina-0.4-rizeh
|
| 6 |
+
license_link: https://huggingface.co/Reza2kn/Bina-0.4-Rizeh/blob/51b8c314b0552b1884c1f0e28016cb12a143f637/README.md
|
| 7 |
+
base_model: Reza2kn/Bina-0.4-Rizeh
|
| 8 |
+
base_model_relation: quantized
|
| 9 |
+
pipeline_tag: image-to-text
|
| 10 |
+
tags:
|
| 11 |
+
- bina
|
| 12 |
+
- ocr
|
| 13 |
+
- int4
|
| 14 |
+
- coreml
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
# Bina0.4 Rizeh — CoreML-INT4
|
| 18 |
+
|
| 19 |
+
Quantized CTC reader components and experimental line-detector conversions of [the final release](https://huggingface.co/Reza2kn/Bina-0.4-Rizeh/tree/51b8c314b0552b1884c1f0e28016cb12a143f637), pinned at `51b8c314b0552b1884c1f0e28016cb12a143f637`. All five readers are included: body95k, short labels, printed, numeric/math and degraded scans. Original pipeline code and nonconverted dependencies remain in the source repository.
|
| 20 |
+
|
| 21 |
+
These are mixed-weight4-bit artifacts. They are not an all-INT4 document pipeline. The reference Python pipeline also uses Surya layout/order, native detector paths, table machinery and small admission/ordering models. No Sherpa-ONNX OCR support is claimed.
|
| 22 |
+
|
| 23 |
+
## Measured reader parity
|
| 24 |
+
|
| 25 |
+
Fresh uncompressed PyTorch2.10 CUDA baselines were generated for each published checkpoint. Each target uses the same300 frozen internal development crops (50 each: English, handwriting, mixed, newspaper, printed, synthetic). No text normalization is applied to parity. At least98% character parity is required in every bucket; exact-line agreement is reported separately in VALIDATION.json. These are development canaries, not held-out accuracy or full-page certification.
|
| 26 |
+
|
| 27 |
+
| Runtime | Reader components | Lowest bucket character parity |
|
| 28 |
+
|---|---:|---:|
|
| 29 |
+
| coreml-w4-block16 | 5/5 | 98.466% |
|
| 30 |
+
|
| 31 |
+
Mac timings were collected during concurrent conversion/browser work and are not controlled speed rankings. Linux CPU/CUDA results are host-specific; native Windows is untested. LiteRT was tested on Linux CPU, without an Android test device. Apple validation used an M2 Mac; iOS hardware was not tested.
|
| 32 |
+
|
| 33 |
+
## Page-pipeline limitation
|
| 34 |
+
|
| 35 |
+
The public release demo matched the original text in a small ONNX page test, and exact reader-cache reuse avoided recognition calls on its repeat. A broader public handwritten-table sample exposed a failure: the pure-W4 pipeline changed the output substantially even though line canaries passed. Therefore this bundle does **not** pass the full-page98% transcription gate and is not a drop-in fully qualified4-bit replacement for the production pipeline. Detector mask IoU is not transcription parity.
|
| 36 |
+
|
| 37 |
+
The experimental Python adapter preserves upstream crop geometry, routing and decoding. It also partitions an upstream geometry-reread cache by actual backend/weight hash, preventing cross-model cache reuse. Its optional image-only safety route uses original FP32 readers on color-separated handwritten pages, before OCR, avoiding a trial quantized pass. That route is mixed-precision and requires separate full-pipeline qualification. It does not make the pure-W4 model pass.
|
| 38 |
+
|
| 39 |
+
## Line-level use
|
| 40 |
+
|
| 41 |
+
Install requirements.txt, then run `python inference.py line.png --role body`. This expects one text line or an explicit line box through `Recognizer.read_line`; it does not detect or assemble a whole document. Persistent sessions and bounded exact-input caching are included. Widths are never squashed or truncated.
|
| 42 |
+
|
| 43 |
+
Inputs are64px-high float32 ink maps with aspect preserved, black=1/paper=0,20% vertical and30% horizontal margins measured in original line height. True widths retain masking semantics. CTC blank is0 and nonblank indices map to vocab.json. Persian visual-to-logical conversion follows the original rtl.py.
|
| 44 |
+
|
| 45 |
+
Core ML ML Program packages require iOS18/macOS15 or later. Inputs: pixels float32 `[B,1,64,W]`, widths int32 `[B]`; batch1..8 and width8..8192. Use CPU_AND_GPU. W4/block16 applies to compatible large weights, retaining floating operations where required. GPU-only/ANE placement is not claimed.
|
| 46 |
+
|
| 47 |
+
## Experimental detectors
|
| 48 |
+
|
| 49 |
+
Three detector roles are included under experimental-detectors/: combined main detector1000, rule-C line guard500 and the printed detector/head. Their procedural-dev heatmaps were compared against their original FP32 models. Their full geometry/table/transcription impact is unqualified. Do not replace the production detectors based on mask IoU alone.
|
| 50 |
+
|
| 51 |
+
## Provenance and licensing
|
| 52 |
+
|
| 53 |
+
Quantization did not alter the source repository. The aggregate model.safetensors was verified tensor-for-tensor against the published native core checkpoints. Each component records its exact source checkpoint SHA256 in quantization_config.json. MANIFEST.json hashes the shipped artifacts. No private calibration images, benchmark fixtures, or private prediction texts are shipped.
|
| 54 |
+
|
| 55 |
+
Original recognizer weights/code remain under the source release CC BY-NC4.0 terms; Surya-derived detector/layout components retain their upstream Surya model terms. These conversions grant no additional rights. Retain the original Bina/Indra/Surya attribution. See the source release model card for details.
|
RECIPE.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Conversion recipe
|
| 2 |
+
|
| 3 |
+
Source: Reza2kn/Bina-0.4-Rizeh, revision51b8c314b0552b1884c1f0e28016cb12a143f637. components.json records the five exact native checkpoint paths and SHA256 values. Verify the checkpoint hash before converting. All five readers have the same architecture and unchanged vocabulary IDs. Body weights matched the previously exported body graph and were reused only after identity verification; every runtime was retested with fresh source baselines.
|
| 4 |
+
|
| 5 |
+
The recipe/source/recognizer.py file comes from that release. portable_model.py algebraically replaces pointwise Conv1d with Linear, preserving masked squeeze-excitation, true widths and CTC lengths. build_recognizer.py checks FP32 logit/argmax equivalence on varied synthetic shapes before exporting. Compact outputs contain tokens, frame confidence and lengths; callers do not need to transfer full logits.
|
| 6 |
+
|
| 7 |
+
Use separate environments for Mac and Linux LiteRT. The requirements files list the measured key converter versions, not complete dependency locks. Python3.11 was used on Mac; LiteRT was built in its existing Linux Python3.12 environment. No upstream training environment was changed.
|
| 8 |
+
|
| 9 |
+
```sh
|
| 10 |
+
# Mac: repeat for each source checkpoint listed in components.json.
|
| 11 |
+
python -m pip install -r recipe/requirements-macos.txt
|
| 12 |
+
python recipe/build_recognizer.py --checkpoint /absolute/path/to/checkpoint.pt --onnx --hqq --coreml --blocks 16
|
| 13 |
+
|
| 14 |
+
# Separate Linux environment; same source checkpoint.
|
| 15 |
+
python -m pip install -r recipe/requirements-litert.txt
|
| 16 |
+
python recipe/export_litert.py --checkpoint /absolute/path/to/checkpoint.pt
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
The LiteRT script's reader branch is portable; its legacy --detector branch requires additional detector helpers and the original Stallion checkout, and is not a standalone detector recipe.
|
| 20 |
+
|
| 21 |
+
ONNX: opset18, dynamic batch/width, ORT1.31 MatMulNBits RTN symmetric4-bit group16, accuracy_level4; HQQ4 group32 is an additional CPU alternative. Eighty-one pointwise linear layers are packed. The stem, depthwise convolutions, biases and other unsupported values stay floating. Graph inputs are float32; accuracy_level4 permits lower internal precision in runtime kernels. HQQ does not require image calibration. The tract importer tested here supports RTN group16 but rejects HQQ floating zero points.
|
| 22 |
+
|
| 23 |
+
Core ML: coremltools9 ML Program, FLOAT32 compute request, iOS18/macOS15 minimum, batch1..8, width8..8192. Apply linear_symmetric int4 per_block16 with weight_threshold512. Validate CPU_AND_GPU on Mac. Quantization storage does not guarantee a particular GPU/ANE placement.
|
| 24 |
+
|
| 25 |
+
LiteRT: litert-torch0.10 converts five static batch-one signatures, widths512/1024/2048/4096/8192. True-width masks remain dynamic inputs. ai-edge-quantizer0.10 recipe.dynamic_wi4b32_afp32 is the accepted reader candidate. recipe.weight_only_wi4_afp32 is produced as an experiment, not the qualified reader artifact shipped here. Keep the dynamic4-block32 file and audit real INT4 buffers; about95.5% of stored constant elements are INT4. This denominator is not a trainable-parameter fraction.
|
| 26 |
+
|
| 27 |
+
Qualification uses the same frozen300 development crops per reader,50 in each of six buckets. Compare every target to a freshly run uncompressed checkpoint without text normalization, requiring character parity>=0.98 in every bucket; report exact-line agreement separately. The frozen private manifest SHA256 is7e7049a3b56323588ae1f6bd38b1753afc2b1149d275c6f0f07c6993be991978. Private images and predictions are deliberately not included. Reproducing this exact canary requires authorized access to those frozen inputs; new public tests do not replace it.
|
| 28 |
+
|
| 29 |
+
The successful recipes above do not use image calibration. A separate GPTQ experiment used144 frozen training-only calibration crops, did not recover the full-page target, and was not promoted or shipped. No public page was used to fit weights. Full-page pure-W4 qualification failed on a handwritten-table diagnostic; see the model card and VALIDATION.json.
|
| 30 |
+
|
| 31 |
+
All original recognizer/code and Surya-derived model licensing and attribution still apply.
|
VALIDATION.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
__pycache__/inference.cpython-311.pyc
ADDED
|
Binary file (11.2 kB). View file
|
|
|
checks/browser-decoder-check.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cases": 1500,
|
| 3 |
+
"failed": 0,
|
| 4 |
+
"passed": true,
|
| 5 |
+
"unicode_version": "13.0.0"
|
| 6 |
+
}
|
checks/browser-loader-check.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"backend": "webgpu",
|
| 3 |
+
"text_matches_measured_candidate": true,
|
| 4 |
+
"concurrent_duplicate_identical": true,
|
| 5 |
+
"repeat_identical": true,
|
| 6 |
+
"repeat_cached": true,
|
| 7 |
+
"model_calls": 1,
|
| 8 |
+
"session_creates": 1,
|
| 9 |
+
"session_reused": true,
|
| 10 |
+
"model_sha256": "2cb6c60a23a2e9948b9df9679de0f8b2c849dd81bc54abbf4b23f64148edf574",
|
| 11 |
+
"passed": true
|
| 12 |
+
}
|
checks/package-litert-smoke.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"format": "LiteRT-INT4",
|
| 3 |
+
"text_matches_measured_candidate": true,
|
| 4 |
+
"repeat_identical": true,
|
| 5 |
+
"model_calls": 1,
|
| 6 |
+
"cache_hits": 1
|
| 7 |
+
}
|
checks/package-smoke.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"format": "ONNX-INT4",
|
| 4 |
+
"text_matches_measured_candidate": true,
|
| 5 |
+
"repeat_identical": true,
|
| 6 |
+
"model_calls": 1,
|
| 7 |
+
"cache_hits": 1
|
| 8 |
+
},
|
| 9 |
+
{
|
| 10 |
+
"format": "CoreML-INT4",
|
| 11 |
+
"text_matches_measured_candidate": true,
|
| 12 |
+
"repeat_identical": true,
|
| 13 |
+
"model_calls": 1,
|
| 14 |
+
"cache_hits": 1
|
| 15 |
+
}
|
| 16 |
+
]
|
components.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"role": "short_labels",
|
| 4 |
+
"path": "stallion/bina04-order-form-batch-20261101/recognizer/weights-001000.pt",
|
| 5 |
+
"sha256": "367084525dc644ef6d69ad981819af07e3c6a41421359594a54ccc989c2fb90a",
|
| 6 |
+
"tensor_sha256": "c6c4db8e963a4a7d0c2ee4108972acf6980b688a464b5a184f2f4fa838b0145b",
|
| 7 |
+
"head": [
|
| 8 |
+
1625,
|
| 9 |
+
512,
|
| 10 |
+
1
|
| 11 |
+
],
|
| 12 |
+
"parameters": 32795225,
|
| 13 |
+
"state_elements": 32796250
|
| 14 |
+
},
|
| 15 |
+
{
|
| 16 |
+
"role": "body",
|
| 17 |
+
"path": "stallion/bina04-pagepath-v3-round3-clean-20261031/run/weights-095000.pt",
|
| 18 |
+
"sha256": "036be44e87c05759c18297426d2d531b0c3ac04af172886071de2ecdd6703981",
|
| 19 |
+
"tensor_sha256": "f9d6e2479d18deee48d563e46121188bb5b7353ab9725948930ad12261fba51e",
|
| 20 |
+
"head": [
|
| 21 |
+
1625,
|
| 22 |
+
512,
|
| 23 |
+
1
|
| 24 |
+
],
|
| 25 |
+
"parameters": 32795225,
|
| 26 |
+
"state_elements": 32796250
|
| 27 |
+
},
|
| 28 |
+
{
|
| 29 |
+
"role": "numeric_math",
|
| 30 |
+
"path": "stallion/bina04-numeric-math-recognizer-95k-20261102/recognizer/selected.pt",
|
| 31 |
+
"sha256": "6e3869a5cdebff89ce6cfbbcde8ce2d5a1aa9cfe342679d469d5da08bb82b443",
|
| 32 |
+
"tensor_sha256": "b77ac34fa6ba5478002c01b0ad702a9a7ad0f8ed91ce11d65b66fd7fb4471e03",
|
| 33 |
+
"head": [
|
| 34 |
+
1625,
|
| 35 |
+
512,
|
| 36 |
+
1
|
| 37 |
+
],
|
| 38 |
+
"parameters": 32795225,
|
| 39 |
+
"state_elements": 32796250
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"role": "printed",
|
| 43 |
+
"path": "stallion/bina04-print-textgate-20261102/run/selected-printed-recognizer.pt",
|
| 44 |
+
"sha256": "28c4ca8f9fbbc8c578bb130d997598d2e9c2c4a2dad0b22cb5cc06818ecb988a",
|
| 45 |
+
"tensor_sha256": "8302658e43d391299491dd0d4921dde0cefb35d15c707bc88ae04acf6c608d09",
|
| 46 |
+
"head": [
|
| 47 |
+
1625,
|
| 48 |
+
512,
|
| 49 |
+
1
|
| 50 |
+
],
|
| 51 |
+
"parameters": 32795225,
|
| 52 |
+
"state_elements": 32796250
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"role": "degraded",
|
| 56 |
+
"path": "stallion/bina04-real-degraded-95k-20261103/recognizer/selected.pt",
|
| 57 |
+
"sha256": "8867b309e881b698685e5b6e06a37c9f495e04dcf7027a044ad44c13cf8abd64",
|
| 58 |
+
"tensor_sha256": "6cb20a3e2d84233758efaabb90994ac3074d9cc4557deb531c859f0bbb7fe5dc",
|
| 59 |
+
"head": [
|
| 60 |
+
1625,
|
| 61 |
+
512,
|
| 62 |
+
1
|
| 63 |
+
],
|
| 64 |
+
"parameters": 32795225,
|
| 65 |
+
"state_elements": 32796250
|
| 66 |
+
}
|
| 67 |
+
]
|
components/body/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:068b9dd28a4464a544732ae3972b7da23629eb2fdd0434d855cdcec17be71fa9
|
| 3 |
+
size 136655
|
components/body/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:45e4f1c7d1d8e0689dbe458d99014bef4ecf1e3f3e308a1399a94dfda1056e17
|
| 3 |
+
size 26263424
|
components/body/models/recognizer-w4-block16.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"7F9E401C-5F1D-40F2-BAB4-296CF4EE443D": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Weights",
|
| 7 |
+
"name": "weights",
|
| 8 |
+
"path": "com.apple.CoreML/weights"
|
| 9 |
+
},
|
| 10 |
+
"C8427A65-5938-46B4-A568-974F293FFEB6": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Specification",
|
| 13 |
+
"name": "model.mlmodel",
|
| 14 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "C8427A65-5938-46B4-A568-974F293FFEB6"
|
| 18 |
+
}
|
components/degraded/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fbb59f5a96148ffd4cbcda007e715e76bc7b9beed799b9700a4b6dd6f087280a
|
| 3 |
+
size 136655
|
components/degraded/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b300ceb473e7c9e3d97220cb34be29f7b49b030fa8693e58ae1b45710da8f55d
|
| 3 |
+
size 26263424
|
components/degraded/models/recognizer-w4-block16.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"B81BD9C2-F516-4993-913D-48720F04B6EB": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Specification",
|
| 7 |
+
"name": "model.mlmodel",
|
| 8 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 9 |
+
},
|
| 10 |
+
"E39C1CFD-C8EA-42D0-A2BE-458AAA9087ED": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Weights",
|
| 13 |
+
"name": "weights",
|
| 14 |
+
"path": "com.apple.CoreML/weights"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "B81BD9C2-F516-4993-913D-48720F04B6EB"
|
| 18 |
+
}
|
components/numeric_math/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c29f36bd45b9469a594b67e420efde84dfd8d663c138e0f1bee325c7d059debe
|
| 3 |
+
size 136655
|
components/numeric_math/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d15e99ea723d66fedbe3ab66319266a156273f84a352081b69f9650af4802ba1
|
| 3 |
+
size 26263424
|
components/numeric_math/models/recognizer-w4-block16.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"D43BAB18-2A03-4550-8D6B-2C5382EB5516": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Weights",
|
| 7 |
+
"name": "weights",
|
| 8 |
+
"path": "com.apple.CoreML/weights"
|
| 9 |
+
},
|
| 10 |
+
"F80FD108-F2CF-476D-A813-0F126C5BA6F7": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Specification",
|
| 13 |
+
"name": "model.mlmodel",
|
| 14 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "F80FD108-F2CF-476D-A813-0F126C5BA6F7"
|
| 18 |
+
}
|
components/printed/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5bfd191c319ff8ede42f41f35ac4ed609d1373c02f97f14b012649d705d08374
|
| 3 |
+
size 136655
|
components/printed/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b7c7da642b5bb0f429cdde70b0ea8815cce86175a1eb8b2a98a1d04c88c40f79
|
| 3 |
+
size 26263424
|
components/printed/models/recognizer-w4-block16.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"129E53A2-6ECD-453B-A355-413C35B490C2": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Specification",
|
| 7 |
+
"name": "model.mlmodel",
|
| 8 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 9 |
+
},
|
| 10 |
+
"9A7DDB3E-8AEE-49FD-8DB8-5C5D7D37CE2B": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Weights",
|
| 13 |
+
"name": "weights",
|
| 14 |
+
"path": "com.apple.CoreML/weights"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "129E53A2-6ECD-453B-A355-413C35B490C2"
|
| 18 |
+
}
|
components/short_labels/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:236f3f7de9ce5e2b2356333e653d996ff335d0bb6acde2c612c7f3190f57631a
|
| 3 |
+
size 136655
|
components/short_labels/models/recognizer-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3d5c895f49c743122580765a36e37ab4a5ed6ae88526e940296e949cdd27cdd8
|
| 3 |
+
size 26263424
|
components/short_labels/models/recognizer-w4-block16.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"8B16FAA1-CC03-401E-A352-4E583220EE42": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Specification",
|
| 7 |
+
"name": "model.mlmodel",
|
| 8 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 9 |
+
},
|
| 10 |
+
"92B0EED9-262B-44CF-8D73-69D65C318DD5": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Weights",
|
| 13 |
+
"name": "weights",
|
| 14 |
+
"path": "com.apple.CoreML/weights"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "8B16FAA1-CC03-401E-A352-4E583220EE42"
|
| 18 |
+
}
|
experimental-detectors/line_guard/config.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_name_or_path": "datalab-to/line_det_2.20",
|
| 3 |
+
"architectures": [
|
| 4 |
+
"EfficientViTForSemanticSegmentation"
|
| 5 |
+
],
|
| 6 |
+
"attention_probs_dropout_prob": 0.0,
|
| 7 |
+
"classifier_dropout_prob": 0.0,
|
| 8 |
+
"decoder_hidden_size": 512,
|
| 9 |
+
"decoder_layer_hidden_size": 128,
|
| 10 |
+
"depths": [
|
| 11 |
+
1,
|
| 12 |
+
1,
|
| 13 |
+
1,
|
| 14 |
+
6,
|
| 15 |
+
6
|
| 16 |
+
],
|
| 17 |
+
"head_dim": 32,
|
| 18 |
+
"hidden_dropout_prob": 0.0,
|
| 19 |
+
"hidden_sizes": [
|
| 20 |
+
32,
|
| 21 |
+
64,
|
| 22 |
+
160,
|
| 23 |
+
256
|
| 24 |
+
],
|
| 25 |
+
"initializer_range": 0.02,
|
| 26 |
+
"layer_norm_eps": 1e-06,
|
| 27 |
+
"model_type": "efficientvit",
|
| 28 |
+
"num_channels": 3,
|
| 29 |
+
"num_classes": 2,
|
| 30 |
+
"num_stages": 4,
|
| 31 |
+
"patch_size": [
|
| 32 |
+
7,
|
| 33 |
+
7
|
| 34 |
+
],
|
| 35 |
+
"pos_weight": 1.0,
|
| 36 |
+
"semantic_loss_ignore_index": -1,
|
| 37 |
+
"strides": [
|
| 38 |
+
2,
|
| 39 |
+
2,
|
| 40 |
+
2,
|
| 41 |
+
2,
|
| 42 |
+
2
|
| 43 |
+
],
|
| 44 |
+
"torch_dtype": "float16",
|
| 45 |
+
"transformers_version": "4.49.0",
|
| 46 |
+
"use_focal": false,
|
| 47 |
+
"widths": [
|
| 48 |
+
32,
|
| 49 |
+
64,
|
| 50 |
+
128,
|
| 51 |
+
256,
|
| 52 |
+
512
|
| 53 |
+
]
|
| 54 |
+
}
|
experimental-detectors/line_guard/detector-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3bbc26f64bd5168c8df476ce207d70977a29391ac85fc8745d967f2b573f6064
|
| 3 |
+
size 183292
|
experimental-detectors/line_guard/detector-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e9614d48774aae3a1e28ff645664d9b8f654f71c80bad4585432be258875b58c
|
| 3 |
+
size 30454400
|
experimental-detectors/line_guard/detector-w4-block16.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"B9835E68-04AC-48C8-B053-470F990C6089": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Weights",
|
| 7 |
+
"name": "weights",
|
| 8 |
+
"path": "com.apple.CoreML/weights"
|
| 9 |
+
},
|
| 10 |
+
"CAB21B1A-9480-4150-8DBC-1AB7DF83912B": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Specification",
|
| 13 |
+
"name": "model.mlmodel",
|
| 14 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "CAB21B1A-9480-4150-8DBC-1AB7DF83912B"
|
| 18 |
+
}
|
experimental-detectors/line_guard/preprocessor_config.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"do_normalize": true,
|
| 3 |
+
"do_reduce_labels": true,
|
| 4 |
+
"do_rescale": true,
|
| 5 |
+
"do_resize": true,
|
| 6 |
+
"image_mean": [
|
| 7 |
+
0.485,
|
| 8 |
+
0.456,
|
| 9 |
+
0.406
|
| 10 |
+
],
|
| 11 |
+
"image_processor_type": "SegformerImageProcessor",
|
| 12 |
+
"image_std": [
|
| 13 |
+
0.229,
|
| 14 |
+
0.224,
|
| 15 |
+
0.225
|
| 16 |
+
],
|
| 17 |
+
"resample": 2,
|
| 18 |
+
"rescale_factor": 0.00392156862745098,
|
| 19 |
+
"size": {
|
| 20 |
+
"height": 1200,
|
| 21 |
+
"width": 1200
|
| 22 |
+
}
|
| 23 |
+
}
|
experimental-detectors/main_detector/config.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_name_or_path": "datalab-to/line_det_2.20",
|
| 3 |
+
"architectures": [
|
| 4 |
+
"EfficientViTForSemanticSegmentation"
|
| 5 |
+
],
|
| 6 |
+
"attention_probs_dropout_prob": 0.0,
|
| 7 |
+
"classifier_dropout_prob": 0.0,
|
| 8 |
+
"decoder_hidden_size": 512,
|
| 9 |
+
"decoder_layer_hidden_size": 128,
|
| 10 |
+
"depths": [
|
| 11 |
+
1,
|
| 12 |
+
1,
|
| 13 |
+
1,
|
| 14 |
+
6,
|
| 15 |
+
6
|
| 16 |
+
],
|
| 17 |
+
"head_dim": 32,
|
| 18 |
+
"hidden_dropout_prob": 0.0,
|
| 19 |
+
"hidden_sizes": [
|
| 20 |
+
32,
|
| 21 |
+
64,
|
| 22 |
+
160,
|
| 23 |
+
256
|
| 24 |
+
],
|
| 25 |
+
"initializer_range": 0.02,
|
| 26 |
+
"layer_norm_eps": 1e-06,
|
| 27 |
+
"model_type": "efficientvit",
|
| 28 |
+
"num_channels": 3,
|
| 29 |
+
"num_classes": 2,
|
| 30 |
+
"num_stages": 4,
|
| 31 |
+
"patch_size": [
|
| 32 |
+
7,
|
| 33 |
+
7
|
| 34 |
+
],
|
| 35 |
+
"pos_weight": 1.0,
|
| 36 |
+
"semantic_loss_ignore_index": -1,
|
| 37 |
+
"strides": [
|
| 38 |
+
2,
|
| 39 |
+
2,
|
| 40 |
+
2,
|
| 41 |
+
2,
|
| 42 |
+
2
|
| 43 |
+
],
|
| 44 |
+
"torch_dtype": "float16",
|
| 45 |
+
"transformers_version": "4.49.0",
|
| 46 |
+
"use_focal": false,
|
| 47 |
+
"widths": [
|
| 48 |
+
32,
|
| 49 |
+
64,
|
| 50 |
+
128,
|
| 51 |
+
256,
|
| 52 |
+
512
|
| 53 |
+
]
|
| 54 |
+
}
|
experimental-detectors/main_detector/detector-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a7df9637d121d036e2bb2e251b9004240f441d83433cde53226e56a36f6c27a8
|
| 3 |
+
size 183292
|
experimental-detectors/main_detector/detector-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0640641e7f2df4053feb5f831e4974739f7839deee0b7c146229db7f10000267
|
| 3 |
+
size 30454400
|
experimental-detectors/main_detector/detector-w4-block16.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"6AD650CC-ED3F-40BF-8974-8446E47C0705": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Weights",
|
| 7 |
+
"name": "weights",
|
| 8 |
+
"path": "com.apple.CoreML/weights"
|
| 9 |
+
},
|
| 10 |
+
"78907290-76FC-41EB-B44C-49D23AB10120": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Specification",
|
| 13 |
+
"name": "model.mlmodel",
|
| 14 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "78907290-76FC-41EB-B44C-49D23AB10120"
|
| 18 |
+
}
|
experimental-detectors/main_detector/preprocessor_config.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"do_normalize": true,
|
| 3 |
+
"do_reduce_labels": true,
|
| 4 |
+
"do_rescale": true,
|
| 5 |
+
"do_resize": true,
|
| 6 |
+
"image_mean": [
|
| 7 |
+
0.485,
|
| 8 |
+
0.456,
|
| 9 |
+
0.406
|
| 10 |
+
],
|
| 11 |
+
"image_processor_type": "SegformerImageProcessor",
|
| 12 |
+
"image_std": [
|
| 13 |
+
0.229,
|
| 14 |
+
0.224,
|
| 15 |
+
0.225
|
| 16 |
+
],
|
| 17 |
+
"resample": 2,
|
| 18 |
+
"rescale_factor": 0.00392156862745098,
|
| 19 |
+
"size": {
|
| 20 |
+
"height": 1200,
|
| 21 |
+
"width": 1200
|
| 22 |
+
}
|
| 23 |
+
}
|
experimental-detectors/printed_detector/config.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_name_or_path": "datalab-to/line_det_2.20",
|
| 3 |
+
"architectures": [
|
| 4 |
+
"EfficientViTForSemanticSegmentation"
|
| 5 |
+
],
|
| 6 |
+
"attention_probs_dropout_prob": 0.0,
|
| 7 |
+
"classifier_dropout_prob": 0.0,
|
| 8 |
+
"decoder_hidden_size": 512,
|
| 9 |
+
"decoder_layer_hidden_size": 128,
|
| 10 |
+
"depths": [
|
| 11 |
+
1,
|
| 12 |
+
1,
|
| 13 |
+
1,
|
| 14 |
+
6,
|
| 15 |
+
6
|
| 16 |
+
],
|
| 17 |
+
"head_dim": 32,
|
| 18 |
+
"hidden_dropout_prob": 0.0,
|
| 19 |
+
"hidden_sizes": [
|
| 20 |
+
32,
|
| 21 |
+
64,
|
| 22 |
+
160,
|
| 23 |
+
256
|
| 24 |
+
],
|
| 25 |
+
"initializer_range": 0.02,
|
| 26 |
+
"layer_norm_eps": 1e-06,
|
| 27 |
+
"model_type": "efficientvit",
|
| 28 |
+
"num_channels": 3,
|
| 29 |
+
"num_classes": 2,
|
| 30 |
+
"num_stages": 4,
|
| 31 |
+
"patch_size": [
|
| 32 |
+
7,
|
| 33 |
+
7
|
| 34 |
+
],
|
| 35 |
+
"pos_weight": 1.0,
|
| 36 |
+
"semantic_loss_ignore_index": -1,
|
| 37 |
+
"strides": [
|
| 38 |
+
2,
|
| 39 |
+
2,
|
| 40 |
+
2,
|
| 41 |
+
2,
|
| 42 |
+
2
|
| 43 |
+
],
|
| 44 |
+
"torch_dtype": "float16",
|
| 45 |
+
"transformers_version": "4.49.0",
|
| 46 |
+
"use_focal": false,
|
| 47 |
+
"widths": [
|
| 48 |
+
32,
|
| 49 |
+
64,
|
| 50 |
+
128,
|
| 51 |
+
256,
|
| 52 |
+
512
|
| 53 |
+
]
|
| 54 |
+
}
|
experimental-detectors/printed_detector/detector-w4-block16.mlpackage/Data/com.apple.CoreML/model.mlmodel
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b65e5fc8008ef59de2d91e4c7ec3e44a3d7e037367678564bfd5fb5810b69021
|
| 3 |
+
size 183292
|
experimental-detectors/printed_detector/detector-w4-block16.mlpackage/Data/com.apple.CoreML/weights/weight.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:02ba967fc37e29fe11eca73bdc77334272af1d2533cb9c7004f5eb0eb6d7591d
|
| 3 |
+
size 30454400
|
experimental-detectors/printed_detector/detector-w4-block16.mlpackage/Manifest.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"fileFormatVersion": "1.0.0",
|
| 3 |
+
"itemInfoEntries": {
|
| 4 |
+
"73141E41-7FD9-48F3-9AB5-9EB5BB522A59": {
|
| 5 |
+
"author": "com.apple.CoreML",
|
| 6 |
+
"description": "CoreML Model Weights",
|
| 7 |
+
"name": "weights",
|
| 8 |
+
"path": "com.apple.CoreML/weights"
|
| 9 |
+
},
|
| 10 |
+
"CBF54C80-86F8-4AA1-90FB-5CE3AE0B0F28": {
|
| 11 |
+
"author": "com.apple.CoreML",
|
| 12 |
+
"description": "CoreML Model Specification",
|
| 13 |
+
"name": "model.mlmodel",
|
| 14 |
+
"path": "com.apple.CoreML/model.mlmodel"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"rootModelIdentifier": "CBF54C80-86F8-4AA1-90FB-5CE3AE0B0F28"
|
| 18 |
+
}
|
experimental-detectors/printed_detector/preprocessor_config.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"do_normalize": true,
|
| 3 |
+
"do_reduce_labels": true,
|
| 4 |
+
"do_rescale": true,
|
| 5 |
+
"do_resize": true,
|
| 6 |
+
"image_mean": [
|
| 7 |
+
0.485,
|
| 8 |
+
0.456,
|
| 9 |
+
0.406
|
| 10 |
+
],
|
| 11 |
+
"image_processor_type": "SegformerImageProcessor",
|
| 12 |
+
"image_std": [
|
| 13 |
+
0.229,
|
| 14 |
+
0.224,
|
| 15 |
+
0.225
|
| 16 |
+
],
|
| 17 |
+
"resample": 2,
|
| 18 |
+
"rescale_factor": 0.00392156862745098,
|
| 19 |
+
"size": {
|
| 20 |
+
"height": 1200,
|
| 21 |
+
"width": 1200
|
| 22 |
+
}
|
| 23 |
+
}
|
experimental/portable_pipeline.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Use quantized CTC readers inside the pinned, otherwise unchanged Rizeh pipeline.
|
| 2 |
+
|
| 3 |
+
Install before constructing Rizeh. Existing crop geometry, routing, decoding,
|
| 4 |
+
confidence thresholds and table assembly remain owned by the upstream code.
|
| 5 |
+
"""
|
| 6 |
+
import collections,hashlib,json,math,sys
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import numpy as np
|
| 9 |
+
|
| 10 |
+
class CompactCTCOutput:
|
| 11 |
+
"""Expose only CTC consumers used upstream, from already computed outputs.
|
| 12 |
+
|
| 13 |
+
This object does not invent logits. Full probability distributions and
|
| 14 |
+
arbitrary tensor operations are deliberately unavailable.
|
| 15 |
+
"""
|
| 16 |
+
def __init__(self,tokens,confidence):
|
| 17 |
+
import torch
|
| 18 |
+
self.tokens=torch.from_numpy(tokens.astype(np.int64));self.confidence=torch.from_numpy(confidence.astype(np.float32))
|
| 19 |
+
def float(self):return self
|
| 20 |
+
def softmax(self,dim):
|
| 21 |
+
if dim!=-1:raise ValueError('Only original CTC class softmax is available')
|
| 22 |
+
return self
|
| 23 |
+
def max(self,dim):
|
| 24 |
+
if dim!=-1:raise ValueError('Only original CTC class maximum is available')
|
| 25 |
+
return self.confidence,self.tokens
|
| 26 |
+
def argmax(self,dim):
|
| 27 |
+
if dim!=-1:raise ValueError('Only original CTC class argmax is available')
|
| 28 |
+
return self.tokens
|
| 29 |
+
|
| 30 |
+
class ReaderModel:
|
| 31 |
+
def __init__(self,root,role,backend,provider,cache_bytes,variant='block16'):
|
| 32 |
+
self.root,self.role,self.backend,self.provider=root,role,backend,provider
|
| 33 |
+
self.cache=collections.OrderedDict();self.used=0;self.capacity=cache_bytes;self.calls=0;self.hits=0
|
| 34 |
+
self.session=None;self.variant=variant
|
| 35 |
+
self.high_precision=False;self.precise_session=None;self.source_checkpoint=None
|
| 36 |
+
def to(self,*args,**kwargs):return self
|
| 37 |
+
def eval(self):return self
|
| 38 |
+
def parameters(self):return iter(())
|
| 39 |
+
def _load(self):
|
| 40 |
+
if self.session is not None:return
|
| 41 |
+
d=self.root/'components'/self.role
|
| 42 |
+
if self.backend=='onnx':
|
| 43 |
+
import onnxruntime as ort
|
| 44 |
+
if self.provider not in ort.get_available_providers():raise RuntimeError('Unavailable provider: '+self.provider)
|
| 45 |
+
name={'hqq32':'recognizer-hqq4-block32.onnx','stable-head':'recognizer-w4-stable-head.onnx','stable-se':'recognizer-w4-stable-se.onnx','accurate':'recognizer-w4-accurate.onnx','fp32':'recognizer-fp32.onnx'}.get(self.variant,'recognizer-w4-block16.onnx')
|
| 46 |
+
options=ort.SessionOptions();options.intra_op_num_threads=2;options.inter_op_num_threads=1
|
| 47 |
+
self.session=ort.InferenceSession(str(d/'models'/name),sess_options=options,providers=[self.provider])
|
| 48 |
+
self.invoke=lambda x,w:self.session.run(None,dict(pixels=x,widths=w))
|
| 49 |
+
elif self.backend=='coreml':
|
| 50 |
+
import coremltools as ct
|
| 51 |
+
self.session=ct.models.MLModel(str(d/'models/recognizer-w4-block16.mlpackage'),compute_units=ct.ComputeUnit.CPU_AND_GPU)
|
| 52 |
+
def invoke(x,w):
|
| 53 |
+
o=self.session.predict(dict(pixels=x,widths=w.astype(np.int32)));return o['tokens'],o['confidence'],o['lengths']
|
| 54 |
+
self.invoke=invoke
|
| 55 |
+
elif self.backend=='litert':
|
| 56 |
+
from ai_edge_litert.interpreter import Interpreter
|
| 57 |
+
self.session=Interpreter(model_path=str(d/'litert/recognizer-dynamic4-block32.tflite'),num_threads=2)
|
| 58 |
+
runners={}
|
| 59 |
+
def invoke(x,w):
|
| 60 |
+
width=x.shape[-1]
|
| 61 |
+
if width not in runners:runners[width]=self.session.get_signature_runner('width'+str(width))
|
| 62 |
+
o=runners[width](args_0=x,args_1=w.astype(np.int32));return o['output_0'],o['output_1'],o['output_2']
|
| 63 |
+
self.invoke=invoke
|
| 64 |
+
elif self.backend=='source':
|
| 65 |
+
import torch
|
| 66 |
+
from recognizer import Recognizer
|
| 67 |
+
state=torch.load(self.source_checkpoint,map_location='cpu',weights_only=True)['model']
|
| 68 |
+
device='cuda'if self.provider=='CUDAExecutionProvider'else'cpu'
|
| 69 |
+
self.session=Recognizer(classes=state['head.weight'].shape[0],gradient_checkpointing=False).eval()
|
| 70 |
+
self.session.load_state_dict(state);self.session.to(device)
|
| 71 |
+
def invoke(x,w):
|
| 72 |
+
with torch.inference_mode():
|
| 73 |
+
logits,lengths=self.session(torch.from_numpy(x).to(device),torch.from_numpy(w).to(device));confidence,tokens=logits.float().softmax(-1).max(-1)
|
| 74 |
+
return tokens.cpu().numpy(),confidence.cpu().numpy(),lengths.cpu().numpy()
|
| 75 |
+
self.invoke=invoke
|
| 76 |
+
else:raise ValueError(self.backend)
|
| 77 |
+
def __call__(self,pixels,widths):
|
| 78 |
+
import torch
|
| 79 |
+
x=pixels.detach().cpu().numpy();w=widths.detach().cpu().numpy().astype(np.int64)
|
| 80 |
+
if x.ndim!=4 or x.shape[1:3]!=(1,64)or len(w)!=len(x):raise ValueError('Original Bx1x64xW input required')
|
| 81 |
+
if self.high_precision:
|
| 82 |
+
if self.precise_session is None:
|
| 83 |
+
self.precise_session=ReaderModel(self.root,self.role,'source',self.provider,self.capacity,'fp32')
|
| 84 |
+
self.precise_session.source_checkpoint=self.source_checkpoint
|
| 85 |
+
return self.precise_session(pixels,widths)
|
| 86 |
+
keys=[];values={};pending={}
|
| 87 |
+
for i,n in enumerate(w):
|
| 88 |
+
if n<1 or n>x.shape[-1]:raise ValueError('Invalid original width')
|
| 89 |
+
crop=np.ascontiguousarray(x[i,0,:,:n]);key=hashlib.sha256(str(crop.shape).encode()+crop.tobytes()).hexdigest();keys.append(key)
|
| 90 |
+
if key in self.cache:self.cache.move_to_end(key);values[key]=self.cache[key][0];self.hits+=1
|
| 91 |
+
elif key not in pending:pending[key]=i
|
| 92 |
+
self._load()
|
| 93 |
+
order=sorted(pending,key=lambda k:w[pending[k]])
|
| 94 |
+
# Core ML is bounded to eight; LiteRT has batch-one signatures.
|
| 95 |
+
batch=1 if self.backend=='litert'else 8
|
| 96 |
+
for start in range(0,len(order),batch):
|
| 97 |
+
group=order[start:start+batch];ids=[pending[k]for k in group];true=w[ids]
|
| 98 |
+
bucket=max(512,2**int(np.ceil(np.log2(int(max(true))))))
|
| 99 |
+
if self.backend in ['coreml','litert']and bucket>8192:raise ValueError('Input exceeds validated8192 width; never truncated')
|
| 100 |
+
canvas=np.zeros((len(ids),1,64,bucket),np.float32)
|
| 101 |
+
for j,i in enumerate(ids):canvas[j,0,:,:w[i]]=x[i,0,:,:w[i]]
|
| 102 |
+
tokens,confidence,lengths=self.invoke(canvas,true);self.calls+=1
|
| 103 |
+
for j,key in enumerate(group):
|
| 104 |
+
n=int(lengths[j]);value=(np.array(tokens[j,:n],copy=True),np.array(confidence[j,:n],copy=True),n);values[key]=value
|
| 105 |
+
size=value[0].nbytes+value[1].nbytes+128
|
| 106 |
+
if size<=self.capacity:
|
| 107 |
+
while self.used+size>self.capacity:
|
| 108 |
+
_,(_,old)=self.cache.popitem(last=False);self.used-=old
|
| 109 |
+
self.cache[key]=(value,size);self.used+=size
|
| 110 |
+
frames=math.ceil(x.shape[-1]/4);tokens=np.zeros((len(x),frames),np.int32);confidence=np.zeros((len(x),frames),np.float32);lengths=[]
|
| 111 |
+
for i,key in enumerate(keys):
|
| 112 |
+
a,b,n=values[key];tokens[i,:n]=a;confidence[i,:n]=b;lengths.append(n)
|
| 113 |
+
return CompactCTCOutput(tokens,confidence),torch.tensor(lengths,dtype=torch.int64)
|
| 114 |
+
|
| 115 |
+
def install(root,runtime,backend='onnx',provider='CPUExecutionProvider',cache_bytes=16*1024*1024,variant='block16'):
|
| 116 |
+
"""Return shared model sessions, keyed by exact published checkpoint hash."""
|
| 117 |
+
root=Path(root);runtime=Path(runtime)
|
| 118 |
+
sys.path.insert(0,str(runtime/'stallion/indra-scale'))
|
| 119 |
+
import rtl_reader,torch
|
| 120 |
+
entries=json.loads((root/'components.json').read_text());lookup={r['sha256']:r['role']for r in entries};sessions={}
|
| 121 |
+
def initialize(reader,checkpoint,vocabulary,device='cpu',frame_budget=8192):
|
| 122 |
+
digest=hashlib.sha256(Path(checkpoint).read_bytes()).hexdigest()
|
| 123 |
+
if digest not in lookup:raise ValueError('Unrecognized published checkpoint: '+digest)
|
| 124 |
+
role=lookup[digest]
|
| 125 |
+
chosen='block16'if variant=='stable-se'and role not in ['body','printed']else variant
|
| 126 |
+
if role not in sessions:sessions[role]=ReaderModel(root,role,backend,provider,cache_bytes,chosen)
|
| 127 |
+
sessions[role].source_checkpoint=Path(checkpoint)
|
| 128 |
+
reader.alphabet=json.loads(Path(vocabulary).read_text())['chars'];reader.model=sessions[role];reader.frame_budget=frame_budget;reader.device=torch.device('cpu')
|
| 129 |
+
rtl_reader.RTLReader.__init__=initialize
|
| 130 |
+
# Upstream .to('cuda') calls are harmless; inputs stay on the CPU until ORT
|
| 131 |
+
# transfers them directly, avoiding a redundant Torch CPU->GPU->CPU copy.
|
| 132 |
+
rtl_reader.RTLReader.device=property(lambda self:torch.device('cpu'),lambda self,value:None)
|
| 133 |
+
return sessions
|
| 134 |
+
|
| 135 |
+
def namespace_intermediate_cache(root,sessions=None,policy='quantized-only'):
|
| 136 |
+
"""Partition upstream geometry reread caches by actual backend/weight identity.
|
| 137 |
+
|
| 138 |
+
The upstream key includes pixels and geometry but omits reader identity.
|
| 139 |
+
Sharing it between original/quantized models can silently reuse old OCR.
|
| 140 |
+
Call after Rizeh construction, before its first image.
|
| 141 |
+
"""
|
| 142 |
+
root=Path(root);identities=[]
|
| 143 |
+
for role,model in sorted((sessions or {}).items()):
|
| 144 |
+
name={'hqq32':'recognizer-hqq4-block32.onnx','stable-head':'recognizer-w4-stable-head.onnx','stable-se':'recognizer-w4-stable-se.onnx','accurate':'recognizer-w4-accurate.onnx','fp32':'recognizer-fp32.onnx'}.get(model.variant,'recognizer-w4-block16.onnx')
|
| 145 |
+
if model.backend=='onnx':
|
| 146 |
+
digest=hashlib.sha256((root/'components'/role/'models'/name).read_bytes()).hexdigest()
|
| 147 |
+
else:digest=model.backend+':'+role+':'+model.variant
|
| 148 |
+
identities.append([role,model.backend,model.provider,digest])
|
| 149 |
+
if not identities:identities=['original-pytorch',json.loads((root/'components.json').read_text())]
|
| 150 |
+
if policy!='quantized-only':
|
| 151 |
+
for role in sorted(sessions or {}):
|
| 152 |
+
precise=(sessions or {})[role].source_checkpoint
|
| 153 |
+
identities.append([role,'safety-original-pytorch',hashlib.sha256(precise.read_bytes()).hexdigest()])
|
| 154 |
+
key=hashlib.sha256(json.dumps([policy,identities],sort_keys=True).encode()).hexdigest()[:24]
|
| 155 |
+
module=sys.modules.get('geometry_fix')
|
| 156 |
+
if module is None:return key
|
| 157 |
+
original=module.apply_geometry
|
| 158 |
+
if getattr(original,'_cache_partitioned',False):return key
|
| 159 |
+
def apply(old,image,reader,expert,cache):return original(old,image,reader,expert,Path(cache)/key)
|
| 160 |
+
apply._cache_partitioned=True
|
| 161 |
+
for loaded in list(sys.modules.values()):
|
| 162 |
+
if loaded is not None and getattr(loaded,'apply_geometry',None)is original:loaded.apply_geometry=apply
|
| 163 |
+
return key
|
| 164 |
+
|
| 165 |
+
def select_page_precision(sessions,image):
|
| 166 |
+
"""Image-only safety route, chosen before OCR; no trial quantized pass.
|
| 167 |
+
|
| 168 |
+
Color-separated handwritten form pages exposed a pure-W4 parity failure.
|
| 169 |
+
This optional hybrid policy uses original FP32 readers for that scope.
|
| 170 |
+
Quantized reader artifacts remain available independently of this policy.
|
| 171 |
+
"""
|
| 172 |
+
from print_scope import handwriting_guard
|
| 173 |
+
scope=handwriting_guard(image)
|
| 174 |
+
fallback=bool(scope.get('protected'))
|
| 175 |
+
for model in sessions.values():model.high_precision=fallback
|
| 176 |
+
return dict(policy='color-separated-handwriting-fp32-v1',original_precision=fallback,scope=scope)
|
geometry.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""One 64px crop contract for training/inference; width is never clamped or squashed.
|
| 2 |
+
|
| 3 |
+
Margins and polarity follow the Apache-2.0 Indra rec_preproc implementation.
|
| 4 |
+
Only right-padding batches changes the canvas; widths retain the unpadded size.
|
| 5 |
+
"""
|
| 6 |
+
import math
|
| 7 |
+
import numpy as np
|
| 8 |
+
def crop_line64(gray,box,*,pad_y=.2,pad_x=.3,dark=None):
|
| 9 |
+
import cv2
|
| 10 |
+
from polarity import is_dark_line
|
| 11 |
+
if gray.ndim!=2 or gray.dtype!=np.uint8:raise ValueError('Original uint8 grayscale required')
|
| 12 |
+
x0,y0,x1,y1=map(float,box[:4])
|
| 13 |
+
if not all(math.isfinite(v)for v in [x0,y0,x1,y1])or x1<=x0 or y1<=y0:raise ValueError('Invalid line geometry')
|
| 14 |
+
height=y1-y0;ix0=math.floor(x0-pad_x*height);ix1=math.ceil(x1+pad_x*height);iy0=math.floor(y0-pad_y*height);iy1=math.ceil(y1+pad_y*height);cw,ch=ix1-ix0,iy1-iy0
|
| 15 |
+
H,W=gray.shape;sx0,sy0,sx1,sy1=max(0,ix0),max(0,iy0),min(W,ix1),min(H,iy1)
|
| 16 |
+
if dark is None:dark=is_dark_line(gray,box)
|
| 17 |
+
canvas=np.full((ch,cw),0 if dark else 255,dtype=np.uint8)
|
| 18 |
+
if sx1>sx0 and sy1>sy0:canvas[sy0-iy0:sy1-iy0,sx0-ix0:sx1-ix0]=gray[sy0:sy1,sx0:sx1]
|
| 19 |
+
width=max(1,round(cw*64/ch));resized=cv2.resize(canvas,(width,64),interpolation=cv2.INTER_AREA if ch>64 else cv2.INTER_LINEAR).astype(np.float32)/255.
|
| 20 |
+
ink=resized if dark else 1.-resized
|
| 21 |
+
receipt=dict(original_WH=[W,H],ink_box=[x0,y0,x1,y1],padded_source_extent=[ix0,iy0,ix1,iy1],padded_source_WH=[cw,ch],resized_WH=[width,64],scale_XY=[width/cw,64/ch],width_clamped=False,aspect_rounding_error_pixels=abs(width-cw*64/ch),dark_polarity=bool(dark))
|
| 22 |
+
return ink,receipt
|
| 23 |
+
def pad_batch(crops,multiple=8):
|
| 24 |
+
if not crops or any(c.ndim!=2 or c.shape[0]!=64 for c in crops):raise ValueError('Every recognizer crop must be 64px high')
|
| 25 |
+
widths=np.array([c.shape[1]for c in crops],dtype=np.int64);padded=math.ceil(int(widths.max())/multiple)*multiple;out=np.zeros((len(crops),1,64,padded),dtype=np.float32)
|
| 26 |
+
for i,c in enumerate(crops):out[i,0,:,:c.shape[1]]=c
|
| 27 |
+
return out,widths
|
inference.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Standalone quantized line recognition; the page pipeline is a separate layer."""
|
| 2 |
+
import argparse,collections,hashlib,json
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
import numpy as np
|
| 5 |
+
from rtl import flip
|
| 6 |
+
|
| 7 |
+
class Recognizer:
|
| 8 |
+
def __init__(self,root=None,role='body',provider='CPUExecutionProvider',cache_entries=512,variant='block16'):
|
| 9 |
+
self.root=Path(root or Path(__file__).resolve().parent);self.role=role
|
| 10 |
+
config=json.loads((self.root/'quantization_config.json').read_text());self.backend=config['format'];self.variant=config.get('default_variant','block16')
|
| 11 |
+
self.alphabet=json.loads((self.root/'vocab.json').read_text())['chars'];d=self.root/'components'/role
|
| 12 |
+
self.cache=collections.OrderedDict();self.cache_entries=cache_entries;self.calls=0;self.cache_hits=0
|
| 13 |
+
if self.backend=='onnx':
|
| 14 |
+
import onnxruntime as ort
|
| 15 |
+
if provider not in ort.get_available_providers():raise RuntimeError('Unavailable provider: '+provider)
|
| 16 |
+
options=ort.SessionOptions();options.intra_op_num_threads=2;options.inter_op_num_threads=1
|
| 17 |
+
if variant=='hqq32'and provider!='CPUExecutionProvider':raise ValueError('HQQ32 helper is qualified for ORT CPU only')
|
| 18 |
+
name='recognizer-hqq4-block32.onnx'if variant=='hqq32'else'recognizer-w4-block16.onnx'
|
| 19 |
+
self.session=ort.InferenceSession(str(d/'models'/name),sess_options=options,providers=[provider]);self.run=lambda x,w:self.session.run(None,dict(pixels=x,widths=w))
|
| 20 |
+
elif self.backend=='coreml':
|
| 21 |
+
import coremltools as ct
|
| 22 |
+
self.session=ct.models.MLModel(str(d/'models/recognizer-w4-block16.mlpackage'),compute_units=ct.ComputeUnit.CPU_AND_GPU)
|
| 23 |
+
def run(x,w):
|
| 24 |
+
o=self.session.predict(dict(pixels=x,widths=w.astype(np.int32)));return o['tokens'],o['confidence'],o['lengths']
|
| 25 |
+
self.run=run
|
| 26 |
+
elif self.backend=='litert':
|
| 27 |
+
from ai_edge_litert.interpreter import Interpreter
|
| 28 |
+
self.session=Interpreter(model_path=str(d/'litert/recognizer-dynamic4-block32.tflite'),num_threads=2);self.runners={}
|
| 29 |
+
def run(x,w):
|
| 30 |
+
width=x.shape[-1]
|
| 31 |
+
if width not in self.runners:self.runners[width]=self.session.get_signature_runner('width'+str(width))
|
| 32 |
+
o=self.runners[width](args_0=x,args_1=w.astype(np.int32));return o['output_0'],o['output_1'],o['output_2']
|
| 33 |
+
self.run=run
|
| 34 |
+
else:raise ValueError(self.backend)
|
| 35 |
+
def predict_ink(self,ink):
|
| 36 |
+
ink=np.asarray(ink,dtype=np.float32)
|
| 37 |
+
if ink.ndim!=2 or ink.shape[0]!=64 or ink.shape[1]<1 or not np.isfinite(ink).all():raise ValueError('Finite float32 ink map [64,true_width] required')
|
| 38 |
+
key=hashlib.sha256(str(ink.shape).encode()+ink.tobytes()).hexdigest()
|
| 39 |
+
if key in self.cache:
|
| 40 |
+
self.cache_hits+=1;self.cache.move_to_end(key);return dict(self.cache[key])
|
| 41 |
+
width=ink.shape[1];bucket=max(512,2**int(np.ceil(np.log2(width))))
|
| 42 |
+
if bucket>8192:raise ValueError('Width exceeds the validated8192 bound; never truncate a line')
|
| 43 |
+
x=np.zeros((1,1,64,bucket),np.float32);x[0,0,:,:width]=ink
|
| 44 |
+
tokens,confidence,lengths=self.run(x,np.array([width],np.int64));self.calls+=1
|
| 45 |
+
n=int(lengths[0]);seq=np.asarray(tokens[0,:n]);keep=(seq!=0)&np.r_[True,seq[1:]!=seq[:-1]];ids=seq[keep].tolist()
|
| 46 |
+
visual=''.join(self.alphabet[int(i)-1]for i in ids)
|
| 47 |
+
result=dict(text=flip(visual,base_rtl=True),visual_text=visual,confidence=float(np.asarray(confidence[0,:n])[keep].mean())if keep.any()else 0.,role=self.role)
|
| 48 |
+
if self.cache_entries>0:
|
| 49 |
+
self.cache[key]=result
|
| 50 |
+
while len(self.cache)>self.cache_entries:self.cache.popitem(last=False)
|
| 51 |
+
return dict(result)
|
| 52 |
+
def read_line(self,image,box=None):
|
| 53 |
+
from geometry import crop_line64
|
| 54 |
+
gray=np.asarray(image.convert('L'));box=box or [0,0,gray.shape[1],gray.shape[0]]
|
| 55 |
+
ink,receipt=crop_line64(gray,box);result=self.predict_ink(ink);result['geometry']=receipt;return result
|
| 56 |
+
|
| 57 |
+
if __name__=='__main__':
|
| 58 |
+
p=argparse.ArgumentParser(description=__doc__);p.add_argument('line_image');p.add_argument('--role',default='body',choices=['body','short_labels','printed','numeric_math','degraded']);p.add_argument('--provider',default='CPUExecutionProvider');p.add_argument('--variant',default='block16',choices=['block16','hqq32']);a=p.parse_args()
|
| 59 |
+
from PIL import Image
|
| 60 |
+
reader=Recognizer(role=a.role,provider=a.provider,variant=a.variant);print(json.dumps(reader.read_line(Image.open(a.line_image)),ensure_ascii=False,indent=2))
|
polarity.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
def _percentiles(b: np.ndarray, qs=(0.02, 0.5, 0.98)) -> list[float]:
|
| 5 |
+
"""np.percentile(b, 100 * qs) (linear) of a uint8 array, from its histogram:
|
| 6 |
+
no sort of the pixels."""
|
| 7 |
+
n = b.size
|
| 8 |
+
cum = np.cumsum(np.bincount(b.ravel(), minlength=256))
|
| 9 |
+
out = []
|
| 10 |
+
for q in qs:
|
| 11 |
+
x = q * (n - 1)
|
| 12 |
+
i = math.floor(x)
|
| 13 |
+
g = x - i
|
| 14 |
+
lo = float(np.searchsorted(cum, i, side="right"))
|
| 15 |
+
hi = float(np.searchsorted(cum, min(i + 1, n - 1), side="right"))
|
| 16 |
+
d = hi - lo
|
| 17 |
+
# numpy's lerp: from the nearer end
|
| 18 |
+
out.append(hi - d * (1 - g) if g >= 0.5 else lo + d * g)
|
| 19 |
+
return out
|
| 20 |
+
|
| 21 |
+
def is_dark_line(gray: np.ndarray, box) -> bool:
|
| 22 |
+
"""Light text on a dark ground (a banner, a dark table head, a slide): the
|
| 23 |
+
ground (the median of the line box) lies nearer the dark end than the text
|
| 24 |
+
does, so the bright tail is the longer one. A shadowed line keeps dark text
|
| 25 |
+
on a dimmer ground. The recognizers read dark text on light paper, so a
|
| 26 |
+
light-on-dark line is read inverted."""
|
| 27 |
+
H, W = gray.shape
|
| 28 |
+
x0, y0, x1, y1 = (float(v) for v in box[:4])
|
| 29 |
+
b = gray[
|
| 30 |
+
max(0, int(y0)) : min(H, int(y1) + 1), max(0, int(x0)) : min(W, int(x1) + 1)
|
| 31 |
+
]
|
| 32 |
+
if not b.size:
|
| 33 |
+
return False
|
| 34 |
+
lo, med, hi = (
|
| 35 |
+
_percentiles(b) if b.dtype == np.uint8 else np.percentile(b, (2, 50, 98))
|
| 36 |
+
)
|
| 37 |
+
return bool(hi - med > 2 * (med - lo) + 8)
|
quantization_config.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"format": "coreml",
|
| 3 |
+
"base_model": "Reza2kn/Bina-0.4-Rizeh",
|
| 4 |
+
"base_revision": "51b8c314b0552b1884c1f0e28016cb12a143f637",
|
| 5 |
+
"weight_bits": 4,
|
| 6 |
+
"mixed_precision": true,
|
| 7 |
+
"reader_components": [
|
| 8 |
+
{
|
| 9 |
+
"role": "short_labels",
|
| 10 |
+
"path": "stallion/bina04-order-form-batch-20261101/recognizer/weights-001000.pt",
|
| 11 |
+
"sha256": "367084525dc644ef6d69ad981819af07e3c6a41421359594a54ccc989c2fb90a",
|
| 12 |
+
"tensor_sha256": "c6c4db8e963a4a7d0c2ee4108972acf6980b688a464b5a184f2f4fa838b0145b",
|
| 13 |
+
"head": [
|
| 14 |
+
1625,
|
| 15 |
+
512,
|
| 16 |
+
1
|
| 17 |
+
],
|
| 18 |
+
"parameters": 32795225,
|
| 19 |
+
"state_elements": 32796250
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
"role": "body",
|
| 23 |
+
"path": "stallion/bina04-pagepath-v3-round3-clean-20261031/run/weights-095000.pt",
|
| 24 |
+
"sha256": "036be44e87c05759c18297426d2d531b0c3ac04af172886071de2ecdd6703981",
|
| 25 |
+
"tensor_sha256": "f9d6e2479d18deee48d563e46121188bb5b7353ab9725948930ad12261fba51e",
|
| 26 |
+
"head": [
|
| 27 |
+
1625,
|
| 28 |
+
512,
|
| 29 |
+
1
|
| 30 |
+
],
|
| 31 |
+
"parameters": 32795225,
|
| 32 |
+
"state_elements": 32796250
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"role": "numeric_math",
|
| 36 |
+
"path": "stallion/bina04-numeric-math-recognizer-95k-20261102/recognizer/selected.pt",
|
| 37 |
+
"sha256": "6e3869a5cdebff89ce6cfbbcde8ce2d5a1aa9cfe342679d469d5da08bb82b443",
|
| 38 |
+
"tensor_sha256": "b77ac34fa6ba5478002c01b0ad702a9a7ad0f8ed91ce11d65b66fd7fb4471e03",
|
| 39 |
+
"head": [
|
| 40 |
+
1625,
|
| 41 |
+
512,
|
| 42 |
+
1
|
| 43 |
+
],
|
| 44 |
+
"parameters": 32795225,
|
| 45 |
+
"state_elements": 32796250
|
| 46 |
+
},
|
| 47 |
+
{
|
| 48 |
+
"role": "printed",
|
| 49 |
+
"path": "stallion/bina04-print-textgate-20261102/run/selected-printed-recognizer.pt",
|
| 50 |
+
"sha256": "28c4ca8f9fbbc8c578bb130d997598d2e9c2c4a2dad0b22cb5cc06818ecb988a",
|
| 51 |
+
"tensor_sha256": "8302658e43d391299491dd0d4921dde0cefb35d15c707bc88ae04acf6c608d09",
|
| 52 |
+
"head": [
|
| 53 |
+
1625,
|
| 54 |
+
512,
|
| 55 |
+
1
|
| 56 |
+
],
|
| 57 |
+
"parameters": 32795225,
|
| 58 |
+
"state_elements": 32796250
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"role": "degraded",
|
| 62 |
+
"path": "stallion/bina04-real-degraded-95k-20261103/recognizer/selected.pt",
|
| 63 |
+
"sha256": "8867b309e881b698685e5b6e06a37c9f495e04dcf7027a044ad44c13cf8abd64",
|
| 64 |
+
"tensor_sha256": "6cb20a3e2d84233758efaabb90994ac3074d9cc4557deb531c859f0bbb7fe5dc",
|
| 65 |
+
"head": [
|
| 66 |
+
1625,
|
| 67 |
+
512,
|
| 68 |
+
1
|
| 69 |
+
],
|
| 70 |
+
"parameters": 32795225,
|
| 71 |
+
"state_elements": 32796250
|
| 72 |
+
}
|
| 73 |
+
],
|
| 74 |
+
"default_variant": "block16",
|
| 75 |
+
"coreml_minimum": "iOS18/macOS15",
|
| 76 |
+
"qualified_widths": "true-width mask; canvas powers of two512..8192; no truncation",
|
| 77 |
+
"full_pipeline_all_int4": false,
|
| 78 |
+
"full_pipeline_transcription_qualified": false
|
| 79 |
+
}
|
recipe/build_recognizer.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse, hashlib, json, sys, time
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import numpy as np
|
| 4 |
+
import torch
|
| 5 |
+
from portable_model import PortableRecognizer, Compact
|
| 6 |
+
|
| 7 |
+
R = Path(__file__).resolve().parent
|
| 8 |
+
sys.path.insert(0, str(R/'source'))
|
| 9 |
+
from recognizer import Recognizer
|
| 10 |
+
|
| 11 |
+
def load(path):
|
| 12 |
+
state = torch.load(path, map_location='cpu', weights_only=True)['model']
|
| 13 |
+
source = Recognizer(classes=state['head.weight'].shape[0], gradient_checkpointing=False).eval()
|
| 14 |
+
source.load_state_dict(state)
|
| 15 |
+
return source
|
| 16 |
+
|
| 17 |
+
def main():
|
| 18 |
+
p=argparse.ArgumentParser()
|
| 19 |
+
p.add_argument('--checkpoint',type=Path,default=R/'source/recognizer-095000.pt')
|
| 20 |
+
p.add_argument('--out',type=Path,default=R/'models')
|
| 21 |
+
p.add_argument('--coreml',action='store_true')
|
| 22 |
+
p.add_argument('--onnx',action='store_true')
|
| 23 |
+
p.add_argument('--hqq',action='store_true')
|
| 24 |
+
p.add_argument('--blocks',default='16,32,64')
|
| 25 |
+
a=p.parse_args();a.out.mkdir(parents=True,exist_ok=True)
|
| 26 |
+
torch.set_num_threads(2);torch.manual_seed(18404)
|
| 27 |
+
source=load(a.checkpoint);model=PortableRecognizer(source).eval()
|
| 28 |
+
controls=[]
|
| 29 |
+
with torch.inference_mode():
|
| 30 |
+
for widths in [[1,7,64],[63,257,511],[769,1025,1537]]:
|
| 31 |
+
w=torch.tensor(widths,dtype=torch.int64);x=torch.rand(len(w),1,64,((max(widths)+7)//8)*8)
|
| 32 |
+
for i,n in enumerate(widths):x[i,:,:,n:]=0
|
| 33 |
+
y,n=source(x,w);z,m=model(x,w)
|
| 34 |
+
error=float((y-z).abs().max()); agreement=float((y.argmax(-1)==z.argmax(-1)).float().mean())
|
| 35 |
+
# Near-empty synthetic crops amplify fp32 summation-order noise in SE.
|
| 36 |
+
tolerance = .02 if min(widths)<8 else 2e-4
|
| 37 |
+
assert error < tolerance and agreement == 1.0 and torch.equal(n,m), (widths,error,agreement)
|
| 38 |
+
controls.append(dict(widths=widths,max_abs_logit_error=error,frame_argmax_agreement=agreement))
|
| 39 |
+
identity=dict(checkpoint=str(a.checkpoint.resolve()),sha256=hashlib.sha256(a.checkpoint.read_bytes()).hexdigest(),
|
| 40 |
+
parameters=sum(t.numel()for t in source.parameters()),lowering_controls=controls,
|
| 41 |
+
torch=torch.__version__,output_contract=['tokens:int32','confidence:float32','lengths:int32'])
|
| 42 |
+
(a.out/'source.json').write_text(json.dumps(identity,indent=2)+'\n')
|
| 43 |
+
compact=Compact(model).eval();x=torch.zeros(1,1,64,512);w=torch.tensor([512])
|
| 44 |
+
if a.onnx:
|
| 45 |
+
import onnx
|
| 46 |
+
from onnxruntime.quantization.matmul_nbits_quantizer import MatMulNBitsQuantizer,DefaultWeightOnlyQuantConfig
|
| 47 |
+
from onnxruntime.quantization.quant_utils import load_model_with_shape_infer
|
| 48 |
+
fp=a.out/'recognizer-fp32.onnx'
|
| 49 |
+
torch.onnx.export(compact,(x,w),str(fp),opset_version=18,dynamo=False,
|
| 50 |
+
input_names=['pixels','widths'],output_names=['tokens','confidence','lengths'],
|
| 51 |
+
dynamic_axes={'pixels':{0:'batch',3:'width'},'widths':{0:'batch'},
|
| 52 |
+
'tokens':{0:'batch',1:'frames'},'confidence':{0:'batch',1:'frames'},'lengths':{0:'batch'}})
|
| 53 |
+
graph=onnx.load(fp);onnx.checker.check_model(graph)
|
| 54 |
+
for block in map(int,a.blocks.split(',')):
|
| 55 |
+
q=MatMulNBitsQuantizer(load_model_with_shape_infer(fp),
|
| 56 |
+
algo_config=DefaultWeightOnlyQuantConfig(block_size=block,is_symmetric=True,accuracy_level=4))
|
| 57 |
+
q.process();target=a.out/f'recognizer-w4-block{block}.onnx'
|
| 58 |
+
q.model.save_model_to_file(str(target),use_external_data_format=False)
|
| 59 |
+
graph=onnx.load(target);onnx.checker.check_model(graph)
|
| 60 |
+
counts={}
|
| 61 |
+
for node in graph.graph.node:counts[node.op_type]=counts.get(node.op_type,0)+1
|
| 62 |
+
(target.with_suffix('.audit.json')).write_text(json.dumps(dict(bytes=target.stat().st_size,
|
| 63 |
+
ops=counts,weight_bits=4,graph_input_bits=32,accuracy_level=4,retained_float_ops=['stem','depthwise convolutions','biases'],
|
| 64 |
+
format='ORT MatMulNBits W4 with runtime-dependent internal precision; mixed-weight precision, not all-INT4'),indent=2)+'\n')
|
| 65 |
+
if a.hqq:
|
| 66 |
+
from onnxruntime.quantization.matmul_nbits_quantizer import HQQWeightOnlyQuantConfig
|
| 67 |
+
q=MatMulNBitsQuantizer(load_model_with_shape_infer(fp),algo_config=HQQWeightOnlyQuantConfig(block_size=32,bits=4))
|
| 68 |
+
q.process();q.model.save_model_to_file(str(a.out/'recognizer-hqq4-block32.onnx'),use_external_data_format=False)
|
| 69 |
+
if a.coreml:
|
| 70 |
+
import coremltools as ct
|
| 71 |
+
from coremltools.optimize.coreml import OptimizationConfig,OpLinearQuantizerConfig,linear_quantize_weights
|
| 72 |
+
# Dynamic width with exact mask semantics; small bounded batch for mobile.
|
| 73 |
+
traced=torch.jit.trace(compact,(x,w))
|
| 74 |
+
ml=ct.convert(traced,inputs=[ct.TensorType(name='pixels',shape=(ct.RangeDim(1,8,default=1),1,64,ct.RangeDim(8,8192,default=512))),
|
| 75 |
+
ct.TensorType(name='widths',shape=(ct.RangeDim(1,8,default=1),),dtype=np.int32)],
|
| 76 |
+
outputs=[ct.TensorType(name='tokens'),ct.TensorType(name='confidence'),ct.TensorType(name='lengths')],
|
| 77 |
+
minimum_deployment_target=ct.target.iOS18,compute_precision=ct.precision.FLOAT32,
|
| 78 |
+
compute_units=ct.ComputeUnit.CPU_AND_GPU)
|
| 79 |
+
ml.save(str(a.out/'recognizer-fp32.mlpackage'))
|
| 80 |
+
for block in map(int,a.blocks.split(',')):
|
| 81 |
+
config=OptimizationConfig(global_config=OpLinearQuantizerConfig(mode='linear_symmetric',dtype='int4',
|
| 82 |
+
granularity='per_block',block_size=block,weight_threshold=512))
|
| 83 |
+
quant=linear_quantize_weights(ml,config=config)
|
| 84 |
+
quant.save(str(a.out/f'recognizer-w4-block{block}.mlpackage'))
|
| 85 |
+
print(json.dumps(identity),flush=True)
|
| 86 |
+
|
| 87 |
+
if __name__=='__main__':main()
|
recipe/export_litert.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Linux CPU exporter; use an isolated venv, never the training environment."""
|
| 2 |
+
import os
|
| 3 |
+
os.environ.update(CUDA_VISIBLE_DEVICES='',OMP_NUM_THREADS='2',OPENBLAS_NUM_THREADS='1',TF_NUM_INTRAOP_THREADS='2',TF_NUM_INTEROP_THREADS='1')
|
| 4 |
+
import argparse,json,time,sys
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
import torch
|
| 7 |
+
import litert_torch
|
| 8 |
+
from ai_edge_quantizer import quantizer,recipe
|
| 9 |
+
from build_recognizer import load,R
|
| 10 |
+
from portable_model import PortableRecognizer,Compact
|
| 11 |
+
|
| 12 |
+
def main():
|
| 13 |
+
parser=argparse.ArgumentParser();parser.add_argument('--detector',action='store_true');parser.add_argument('--checkpoint',type=Path);args=parser.parse_args()
|
| 14 |
+
torch.set_num_threads(2);out=R/'litert';out.mkdir(exist_ok=True)
|
| 15 |
+
if args.detector:
|
| 16 |
+
sys.path.append('/home/rezo/bina02-surya-layout-env/lib/python3.10/site-packages')
|
| 17 |
+
from export_detector import EfficientViTConfig,EfficientViTForSemanticSegmentation,BASE,S,Model
|
| 18 |
+
m=EfficientViTForSemanticSegmentation(EfficientViTConfig.from_pretrained(str(BASE))).eval()
|
| 19 |
+
m.load_state_dict(torch.load(args.checkpoint or S/'run/checkpoints/step-001000.pt',map_location='cpu',weights_only=True)['model'])
|
| 20 |
+
converter=litert_torch.signature('detector',Model(m).eval(),(torch.zeros(1,3,1200,1200),))
|
| 21 |
+
prefix='detector'
|
| 22 |
+
else:
|
| 23 |
+
model=Compact(PortableRecognizer(load(args.checkpoint or R/'source/recognizer-095000.pt'))).eval()
|
| 24 |
+
model.model.index_dtype=torch.int32
|
| 25 |
+
converter=None
|
| 26 |
+
for width in [512,1024,2048,4096,8192]:
|
| 27 |
+
x=torch.zeros(1,1,64,width);w=torch.tensor([width])
|
| 28 |
+
if converter is None:converter=litert_torch.signature('width'+str(width),model,(x,w))
|
| 29 |
+
else:converter.signature('width'+str(width),model,(x,w))
|
| 30 |
+
prefix='recognizer'
|
| 31 |
+
with torch.no_grad():edge=converter.convert()
|
| 32 |
+
fp=out/(prefix+'-fp32.tflite');edge.export(str(fp))
|
| 33 |
+
results=[]
|
| 34 |
+
for name,rp in [('weight-only4',recipe.weight_only_wi4_afp32()),('dynamic4-block32',recipe.dynamic_wi4b32_afp32())]:
|
| 35 |
+
t=time.monotonic()
|
| 36 |
+
try:
|
| 37 |
+
q=quantizer.Quantizer(str(fp));q.load_quantization_recipe(rp)
|
| 38 |
+
target=out/f'{prefix}-{name}.tflite';q.quantize().export_model(str(target))
|
| 39 |
+
results.append(dict(name=name,path=str(target),bytes=target.stat().st_size,recipe=rp,seconds=time.monotonic()-t))
|
| 40 |
+
except Exception as e:results.append(dict(name=name,error=repr(e)))
|
| 41 |
+
(out/(prefix+'-build.json')).write_text(json.dumps(dict(converter='litert-torch 0.10.0',quantizer='ai-edge-quantizer 0.10.0',
|
| 42 |
+
input='Five static width signatures512/1024/2048/4096/8192; true widths input; zero right pad, never truncate',results=results),indent=2,default=str)+'\n')
|
| 43 |
+
print(json.dumps(results,default=str),flush=True)
|
| 44 |
+
|
| 45 |
+
if __name__=='__main__':main()
|
recipe/portable_model.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Algebraic pointwise-Conv -> Linear lowering; unchanged masked SE semantics."""
|
| 2 |
+
import copy
|
| 3 |
+
import torch
|
| 4 |
+
from torch import nn
|
| 5 |
+
from torch.nn import functional as F
|
| 6 |
+
|
| 7 |
+
def linear(conv):
|
| 8 |
+
layer = nn.Linear(conv.in_channels, conv.out_channels)
|
| 9 |
+
with torch.no_grad():
|
| 10 |
+
layer.weight.copy_(conv.weight[..., 0])
|
| 11 |
+
layer.bias.copy_(conv.bias)
|
| 12 |
+
return layer
|
| 13 |
+
|
| 14 |
+
class PortableBlock(nn.Module):
|
| 15 |
+
def __init__(self, block):
|
| 16 |
+
super().__init__()
|
| 17 |
+
self.dw = copy.deepcopy(block.dw)
|
| 18 |
+
for name in ['expand', 'fc1', 'fc2', 'project']:
|
| 19 |
+
setattr(self, name, linear(getattr(block, name)))
|
| 20 |
+
|
| 21 |
+
def forward(self, x, mask):
|
| 22 |
+
z = F.hardswish(self.expand(self.dw(x.transpose(1,2)).transpose(1,2)))
|
| 23 |
+
pool = (z * mask).sum(1, keepdim=True) / mask.sum(1, keepdim=True).clamp_min(1.)
|
| 24 |
+
gate = torch.sigmoid(self.fc2(F.relu(self.fc1(pool))))
|
| 25 |
+
return (x + self.project(z * gate)) * mask
|
| 26 |
+
|
| 27 |
+
class PortableRecognizer(nn.Module):
|
| 28 |
+
"""Input widths are true unpadded widths; canvas must be a multiple of eight."""
|
| 29 |
+
def __init__(self, source):
|
| 30 |
+
super().__init__()
|
| 31 |
+
self.index_dtype = torch.int64
|
| 32 |
+
self.stem = copy.deepcopy(source.stem)
|
| 33 |
+
self.bn = copy.deepcopy(source.bn)
|
| 34 |
+
self.blocks = nn.ModuleList(PortableBlock(b) for b in source.blocks)
|
| 35 |
+
self.head = linear(source.head)
|
| 36 |
+
|
| 37 |
+
def forward(self, pixels, widths):
|
| 38 |
+
z = self.stem(F.pad(pixels, (0,4,0,0))).squeeze(2)
|
| 39 |
+
lengths = (widths + 3) // 4
|
| 40 |
+
mask = (torch.arange(z.shape[-1], device=z.device, dtype=self.index_dtype)[None,:] < lengths[:,None])[:,:,None].to(z.dtype)
|
| 41 |
+
z = F.hardswish(self.bn(z)).transpose(1,2) * mask
|
| 42 |
+
for block in self.blocks:
|
| 43 |
+
z = block(z, mask)
|
| 44 |
+
return self.head(z), lengths
|
| 45 |
+
|
| 46 |
+
class Compact(nn.Module):
|
| 47 |
+
def __init__(self, source):
|
| 48 |
+
super().__init__()
|
| 49 |
+
self.model = source
|
| 50 |
+
def forward(self, pixels, widths):
|
| 51 |
+
logits, lengths = self.model(pixels, widths)
|
| 52 |
+
confidence, tokens = logits.softmax(-1).max(-1)
|
| 53 |
+
return tokens.to(torch.int32), confidence, lengths.to(torch.int32)
|
recipe/requirements-litert.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch==2.10.0
|
| 2 |
+
numpy==1.26.4
|
| 3 |
+
litert-torch==0.10.0
|
| 4 |
+
ai-edge-quantizer==0.10.0
|
| 5 |
+
ai-edge-litert==2.3.0
|