openocr-mlt 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. openocr_mlt-0.3.0/MANIFEST.in +7 -0
  2. openocr_mlt-0.3.0/PKG-INFO +149 -0
  3. openocr_mlt-0.3.0/README.md +120 -0
  4. openocr_mlt-0.3.0/THIRD_PARTY_NOTICES.md +26 -0
  5. openocr_mlt-0.3.0/docs/INSTALL.md +9 -0
  6. openocr_mlt-0.3.0/docs/MODELS.md +5 -0
  7. openocr_mlt-0.3.0/docs/VALIDATION.md +5 -0
  8. openocr_mlt-0.3.0/docs/verification/acceptance-summary.json +48 -0
  9. openocr_mlt-0.3.0/docs/verification/fp32-v14-summary.json +268 -0
  10. openocr_mlt-0.3.0/examples/demo-crop.png +0 -0
  11. openocr_mlt-0.3.0/examples/demo-page.png +0 -0
  12. openocr_mlt-0.3.0/examples/ocr_page.py +9 -0
  13. openocr_mlt-0.3.0/examples/prepared_ocr.py +13 -0
  14. openocr_mlt-0.3.0/examples/recognize.py +8 -0
  15. openocr_mlt-0.3.0/pyproject.toml +30 -0
  16. openocr_mlt-0.3.0/setup.cfg +4 -0
  17. openocr_mlt-0.3.0/src/openocr_mlt/__init__.py +11 -0
  18. openocr_mlt-0.3.0/src/openocr_mlt/__main__.py +2 -0
  19. openocr_mlt-0.3.0/src/openocr_mlt/api.py +144 -0
  20. openocr_mlt-0.3.0/src/openocr_mlt/assets/THIRD_PARTY_NOTICES.txt +26 -0
  21. openocr_mlt-0.3.0/src/openocr_mlt/assets/detector-models.json +37 -0
  22. openocr_mlt-0.3.0/src/openocr_mlt/assets/dict.txt +19683 -0
  23. openocr_mlt-0.3.0/src/openocr_mlt/assets/manifest.json +21 -0
  24. openocr_mlt-0.3.0/src/openocr_mlt/assets/model-bundle.json +171 -0
  25. openocr_mlt-0.3.0/src/openocr_mlt/assets/onnx-model.json +1565 -0
  26. openocr_mlt-0.3.0/src/openocr_mlt/assets/ppocrv6_onnx-LICENSE.txt +21 -0
  27. openocr_mlt-0.3.0/src/openocr_mlt/assets.py +37 -0
  28. openocr_mlt-0.3.0/src/openocr_mlt/cli.py +59 -0
  29. openocr_mlt-0.3.0/src/openocr_mlt/config.py +45 -0
  30. openocr_mlt-0.3.0/src/openocr_mlt/detection/__init__.py +4 -0
  31. openocr_mlt-0.3.0/src/openocr_mlt/detection/config.py +34 -0
  32. openocr_mlt-0.3.0/src/openocr_mlt/detection/cuda_runtime.py +44 -0
  33. openocr_mlt-0.3.0/src/openocr_mlt/detection/db.py +268 -0
  34. openocr_mlt-0.3.0/src/openocr_mlt/detection/onnx_backend.py +85 -0
  35. openocr_mlt-0.3.0/src/openocr_mlt/geometry.py +37 -0
  36. openocr_mlt-0.3.0/src/openocr_mlt/model_assets.py +34 -0
  37. openocr_mlt-0.3.0/src/openocr_mlt/model_fetch.py +144 -0
  38. openocr_mlt-0.3.0/src/openocr_mlt/onnx_runtime.py +74 -0
  39. openocr_mlt-0.3.0/src/openocr_mlt/processing.py +82 -0
  40. openocr_mlt-0.3.0/src/openocr_mlt/recognition/__init__.py +1 -0
  41. openocr_mlt-0.3.0/src/openocr_mlt/recognition/cuda_stream.py +39 -0
  42. openocr_mlt-0.3.0/src/openocr_mlt/recognition/modeling.py +777 -0
  43. openocr_mlt-0.3.0/src/openocr_mlt/recognition/onnx_backend.py +100 -0
  44. openocr_mlt-0.3.0/src/openocr_mlt/recognition/partitioned_prefill.py +67 -0
  45. openocr_mlt-0.3.0/src/openocr_mlt/recognition/postprocess.py +87 -0
  46. openocr_mlt-0.3.0/src/openocr_mlt/recognition/torch_backend.py +31 -0
  47. openocr_mlt-0.3.0/src/openocr_mlt/types.py +71 -0
  48. openocr_mlt-0.3.0/src/openocr_mlt.egg-info/PKG-INFO +149 -0
  49. openocr_mlt-0.3.0/src/openocr_mlt.egg-info/SOURCES.txt +56 -0
  50. openocr_mlt-0.3.0/src/openocr_mlt.egg-info/dependency_links.txt +1 -0
  51. openocr_mlt-0.3.0/src/openocr_mlt.egg-info/entry_points.txt +2 -0
  52. openocr_mlt-0.3.0/src/openocr_mlt.egg-info/requires.txt +22 -0
  53. openocr_mlt-0.3.0/src/openocr_mlt.egg-info/top_level.txt +1 -0
  54. openocr_mlt-0.3.0/tools/export_scriptmoe.py +120 -0
  55. openocr_mlt-0.3.0/tools/prepare_decode_views.py +44 -0
  56. openocr_mlt-0.3.0/tools/prepare_onnx_models.py +4 -0
  57. openocr_mlt-0.3.0/tools/prepare_prefill_pipeline.py +79 -0
@@ -0,0 +1,7 @@
1
+ include README.md THIRD_PARTY_NOTICES.md
2
+ recursive-include examples *.py *.png
3
+ recursive-include tools *.py
4
+ recursive-include constraints *.txt *.lock
5
+ prune tests
6
+ recursive-include docs *.md
7
+ recursive-include docs *.json
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.4
2
+ Name: openocr-mlt
3
+ Version: 0.3.0
4
+ Summary: Offline multilingual OCR with ONNX Prefill/Decode recognition and PP-OCRv6 medium detection
5
+ Maintainer: ericleyu
6
+ Project-URL: Models, https://huggingface.co/ericleyu/openocr-mlt-models
7
+ Project-URL: Upstream recognition, https://github.com/YesianRohn/ScriptMoE
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: numpy<2.3,>=2.2
11
+ Requires-Dist: Pillow<12.4,>=12.3
12
+ Requires-Dist: opencv-python-headless<5.1,>=5.0
13
+ Provides-Extra: cpu
14
+ Requires-Dist: onnxruntime==1.20.0; extra == "cpu"
15
+ Requires-Dist: pyclipper==1.4.0; extra == "cpu"
16
+ Provides-Extra: cuda
17
+ Requires-Dist: onnxruntime-gpu==1.20.0; extra == "cuda"
18
+ Requires-Dist: pyclipper==1.4.0; extra == "cuda"
19
+ Requires-Dist: nvidia-cuda-runtime-cu12==12.4.127; extra == "cuda"
20
+ Requires-Dist: nvidia-cublas-cu12==12.4.5.8; extra == "cuda"
21
+ Requires-Dist: nvidia-cudnn-cu12==9.1.0.70; extra == "cuda"
22
+ Requires-Dist: nvidia-cuda-nvrtc-cu12==12.4.127; extra == "cuda"
23
+ Requires-Dist: nvidia-cufft-cu12==11.2.1.3; extra == "cuda"
24
+ Requires-Dist: nvidia-curand-cu12==10.3.5.147; extra == "cuda"
25
+ Provides-Extra: torch
26
+ Requires-Dist: torch<2.6,>=2.5; extra == "torch"
27
+ Requires-Dist: torchvision<0.21,>=0.20; extra == "torch"
28
+ Requires-Dist: safetensors<0.9,>=0.8; extra == "torch"
29
+
30
+ # openocr-mlt
31
+
32
+ Multilingual OCR with ScriptMoE recognition and PP-OCRv6 medium text detection. The package provides cropped text recognition, whole-page OCR, a Python API, and a command-line interface.
33
+
34
+ Both models run with ONNX Runtime. Recognition uses autoregressive Prefill/Decode and KV caching. CPU and CUDA installations use separate runtime dependencies.
35
+
36
+ ## Installation
37
+
38
+ Use Linux x86_64 with Python 3.10.12 for the verified installation path. Select the CPU or CUDA extra in a virtual environment.
39
+
40
+ ~~~bash
41
+ python3.10 -m venv .venv-cpu
42
+ .venv-cpu/bin/python -m pip install "openocr-mlt[cpu]"
43
+ ~~~
44
+
45
+ For CUDA, use a separate environment:
46
+
47
+ ~~~bash
48
+ python3.10 -m venv .venv-cuda
49
+ .venv-cuda/bin/python -m pip install "openocr-mlt[cuda]"
50
+ ~~~
51
+
52
+ The CPU extra installs ONNX Runtime; the CUDA extra installs ONNX Runtime GPU and the required CUDA 12/cuDNN 9 user-space libraries. CUDA requires an NVIDIA driver. PyTorch is not required for ONNX inference. Install the CPU and CUDA extras in separate environments.
53
+
54
+ ## Model Preparation
55
+
56
+ Activate the environment you installed, for example `source .venv-cpu/bin/activate`.
57
+
58
+ Download and verify the registered model bundle:
59
+
60
+ ~~~bash
61
+ python -m openocr_mlt fetch-models --cache-dir ./models
62
+ ~~~
63
+
64
+ Activate the selected virtual environment before running these commands. The command downloads the pinned FP32 bundle, verifies the archive and 40 model files, and returns the recognition and detector directories. Subsequent calls verify and reuse the cache.
65
+
66
+ The bundle is 213,794,128 bytes and is hosted in [ericleyu/openocr-mlt-models](https://huggingface.co/ericleyu/openocr-mlt-models). It contains recognition graphs, shared weights, the character dictionary, the model manifest, and the PP-OCRv6 medium detector.
67
+
68
+ For offline use, copy the model archive to the target machine and import it locally:
69
+
70
+ ~~~bash
71
+ python -m openocr_mlt fetch-models \
72
+ --source ./scriptmoe-medium-fp32-20261101-v1.tar.gz \
73
+ --cache-dir ./models --offline
74
+ ~~~
75
+
76
+ The default cache location is $XDG_CACHE_HOME/openocr-mlt or ~/.cache/openocr-mlt. Download the [model archive](https://huggingface.co/ericleyu/openocr-mlt-models/resolve/7ef39160a893fc7e605e146c1544ba64c1a57f43/scriptmoe-medium-fp32-20261101-v1.tar.gz) on a machine with access to Hugging Face and transfer it to the target machine when necessary. Offline package installation also requires local copies of the Python dependencies.
77
+
78
+ ## Python API
79
+
80
+ ~~~python
81
+ from openocr_mlt import OpenOCRMLT, fetch_models
82
+
83
+ models = fetch_models(cache_dir="./models")
84
+
85
+ with OpenOCRMLT(
86
+ model_dir=models["recognition_dir"],
87
+ det_model_dir=models["detector_dir"],
88
+ device="cpu",
89
+ ) as ocr:
90
+ result = ocr.ocr("./example.png")
91
+ print(result.to_dict())
92
+ ~~~
93
+
94
+ Replace ./example.png with an existing image. Set device="cuda:0" in the CUDA environment to run detection and recognition on CUDA. The det_device and rec_device parameters select the two devices independently.
95
+
96
+ For cropped text recognition, initialize OpenOCRMLT with model_dir and call recognize(image). Results include the raw and filtered text, confidence score, status, decoding termination, and device information.
97
+
98
+ ## Command Line
99
+
100
+ Whole-page OCR:
101
+
102
+ ~~~bash
103
+ python -m openocr_mlt ocr ./example.png \
104
+ --model-dir ./models/scriptmoe-medium-fp32-20261101-v1/recognition \
105
+ --det-model-dir ./models/scriptmoe-medium-fp32-20261101-v1/ppocrv6-medium-det-bce-v1 \
106
+ --device cpu
107
+ ~~~
108
+
109
+ Cropped text recognition:
110
+
111
+ ~~~bash
112
+ python -m openocr_mlt recognize ./crop.png \
113
+ --model-dir ./models/scriptmoe-medium-fp32-20261101-v1/recognition \
114
+ --device cpu
115
+ ~~~
116
+
117
+ Replace the image arguments with existing files. Run python -m openocr_mlt --help for all commands.
118
+
119
+ ## Configuration
120
+
121
+ | Setting | Default |
122
+ |---|---|
123
+ | Precision | FP32 |
124
+ | Effective batch size | 1; images are processed serially |
125
+ | Maximum recognition length | 100 decoding steps |
126
+ | Maximum input ratio | 20 |
127
+ | Recognition confidence threshold | 0.5 |
128
+ | Maximum page side | 0, resizing disabled |
129
+
130
+ The evaluation configuration uses recognition threshold 0.7 and maximum page side 2,000 pixels. Set rec_threshold=0.7 and page_max_side=2000 in the API, or pass --rec-threshold 0.7 --page-max-side 2000 to the OCR command.
131
+
132
+ ## Model Evaluation
133
+
134
+ The FP32 graphs were evaluated with openocr-mlt 0.3.0.dev14. The model bundle is unchanged.
135
+
136
+ | Task | Data | Result |
137
+ |---|---|---|
138
+ | CUDA recognition | TextMuSS, 10,899 crops | Official normalized weighted accuracy 85.0812%; ten-group macro accuracy 81.0928% |
139
+ | Whole-page OCR, CPU and CUDA | Fixed CC-OCR subset, 200 pages across ten languages, development/held-out split 100/100 | Held-out text F1: macro 72.5849%, micro 79.4545% |
140
+
141
+ Deployment validation used Linux x86_64, Python 3.10.12, ONNX Runtime 1.20.0, an Intel Xeon Gold 6226R CPU, and an NVIDIA RTX 3090 GPU. One CPU/CUDA recognition whitespace difference was observed on TextMuSS. Additional platforms, hardware and quantized execution remain future work.
142
+
143
+ ## Sources
144
+
145
+ - [ScriptMoE](https://github.com/YesianRohn/ScriptMoE): multilingual recognition model and decoding design.
146
+ - [ppocrv6_onnx](https://github.com/AIwork4me/ppocrv6_onnx): detection preprocessing and postprocessing reference.
147
+ - PP-OCRv6 medium: detector distributed through Paddle's official model storage.
148
+
149
+ Component attribution and licensing information are included in the package's THIRD_PARTY_NOTICES.txt and in the model repository's [THIRD_PARTY_NOTICES.md](https://huggingface.co/ericleyu/openocr-mlt-models/blob/7ef39160a893fc7e605e146c1544ba64c1a57f43/THIRD_PARTY_NOTICES.md).
@@ -0,0 +1,120 @@
1
+ # openocr-mlt
2
+
3
+ Multilingual OCR with ScriptMoE recognition and PP-OCRv6 medium text detection. The package provides cropped text recognition, whole-page OCR, a Python API, and a command-line interface.
4
+
5
+ Both models run with ONNX Runtime. Recognition uses autoregressive Prefill/Decode and KV caching. CPU and CUDA installations use separate runtime dependencies.
6
+
7
+ ## Installation
8
+
9
+ Use Linux x86_64 with Python 3.10.12 for the verified installation path. Select the CPU or CUDA extra in a virtual environment.
10
+
11
+ ~~~bash
12
+ python3.10 -m venv .venv-cpu
13
+ .venv-cpu/bin/python -m pip install "openocr-mlt[cpu]"
14
+ ~~~
15
+
16
+ For CUDA, use a separate environment:
17
+
18
+ ~~~bash
19
+ python3.10 -m venv .venv-cuda
20
+ .venv-cuda/bin/python -m pip install "openocr-mlt[cuda]"
21
+ ~~~
22
+
23
+ The CPU extra installs ONNX Runtime; the CUDA extra installs ONNX Runtime GPU and the required CUDA 12/cuDNN 9 user-space libraries. CUDA requires an NVIDIA driver. PyTorch is not required for ONNX inference. Install the CPU and CUDA extras in separate environments.
24
+
25
+ ## Model Preparation
26
+
27
+ Activate the environment you installed, for example `source .venv-cpu/bin/activate`.
28
+
29
+ Download and verify the registered model bundle:
30
+
31
+ ~~~bash
32
+ python -m openocr_mlt fetch-models --cache-dir ./models
33
+ ~~~
34
+
35
+ Activate the selected virtual environment before running these commands. The command downloads the pinned FP32 bundle, verifies the archive and 40 model files, and returns the recognition and detector directories. Subsequent calls verify and reuse the cache.
36
+
37
+ The bundle is 213,794,128 bytes and is hosted in [ericleyu/openocr-mlt-models](https://huggingface.co/ericleyu/openocr-mlt-models). It contains recognition graphs, shared weights, the character dictionary, the model manifest, and the PP-OCRv6 medium detector.
38
+
39
+ For offline use, copy the model archive to the target machine and import it locally:
40
+
41
+ ~~~bash
42
+ python -m openocr_mlt fetch-models \
43
+ --source ./scriptmoe-medium-fp32-20261101-v1.tar.gz \
44
+ --cache-dir ./models --offline
45
+ ~~~
46
+
47
+ The default cache location is $XDG_CACHE_HOME/openocr-mlt or ~/.cache/openocr-mlt. Download the [model archive](https://huggingface.co/ericleyu/openocr-mlt-models/resolve/7ef39160a893fc7e605e146c1544ba64c1a57f43/scriptmoe-medium-fp32-20261101-v1.tar.gz) on a machine with access to Hugging Face and transfer it to the target machine when necessary. Offline package installation also requires local copies of the Python dependencies.
48
+
49
+ ## Python API
50
+
51
+ ~~~python
52
+ from openocr_mlt import OpenOCRMLT, fetch_models
53
+
54
+ models = fetch_models(cache_dir="./models")
55
+
56
+ with OpenOCRMLT(
57
+ model_dir=models["recognition_dir"],
58
+ det_model_dir=models["detector_dir"],
59
+ device="cpu",
60
+ ) as ocr:
61
+ result = ocr.ocr("./example.png")
62
+ print(result.to_dict())
63
+ ~~~
64
+
65
+ Replace ./example.png with an existing image. Set device="cuda:0" in the CUDA environment to run detection and recognition on CUDA. The det_device and rec_device parameters select the two devices independently.
66
+
67
+ For cropped text recognition, initialize OpenOCRMLT with model_dir and call recognize(image). Results include the raw and filtered text, confidence score, status, decoding termination, and device information.
68
+
69
+ ## Command Line
70
+
71
+ Whole-page OCR:
72
+
73
+ ~~~bash
74
+ python -m openocr_mlt ocr ./example.png \
75
+ --model-dir ./models/scriptmoe-medium-fp32-20261101-v1/recognition \
76
+ --det-model-dir ./models/scriptmoe-medium-fp32-20261101-v1/ppocrv6-medium-det-bce-v1 \
77
+ --device cpu
78
+ ~~~
79
+
80
+ Cropped text recognition:
81
+
82
+ ~~~bash
83
+ python -m openocr_mlt recognize ./crop.png \
84
+ --model-dir ./models/scriptmoe-medium-fp32-20261101-v1/recognition \
85
+ --device cpu
86
+ ~~~
87
+
88
+ Replace the image arguments with existing files. Run python -m openocr_mlt --help for all commands.
89
+
90
+ ## Configuration
91
+
92
+ | Setting | Default |
93
+ |---|---|
94
+ | Precision | FP32 |
95
+ | Effective batch size | 1; images are processed serially |
96
+ | Maximum recognition length | 100 decoding steps |
97
+ | Maximum input ratio | 20 |
98
+ | Recognition confidence threshold | 0.5 |
99
+ | Maximum page side | 0, resizing disabled |
100
+
101
+ The evaluation configuration uses recognition threshold 0.7 and maximum page side 2,000 pixels. Set rec_threshold=0.7 and page_max_side=2000 in the API, or pass --rec-threshold 0.7 --page-max-side 2000 to the OCR command.
102
+
103
+ ## Model Evaluation
104
+
105
+ The FP32 graphs were evaluated with openocr-mlt 0.3.0.dev14. The model bundle is unchanged.
106
+
107
+ | Task | Data | Result |
108
+ |---|---|---|
109
+ | CUDA recognition | TextMuSS, 10,899 crops | Official normalized weighted accuracy 85.0812%; ten-group macro accuracy 81.0928% |
110
+ | Whole-page OCR, CPU and CUDA | Fixed CC-OCR subset, 200 pages across ten languages, development/held-out split 100/100 | Held-out text F1: macro 72.5849%, micro 79.4545% |
111
+
112
+ Deployment validation used Linux x86_64, Python 3.10.12, ONNX Runtime 1.20.0, an Intel Xeon Gold 6226R CPU, and an NVIDIA RTX 3090 GPU. One CPU/CUDA recognition whitespace difference was observed on TextMuSS. Additional platforms, hardware and quantized execution remain future work.
113
+
114
+ ## Sources
115
+
116
+ - [ScriptMoE](https://github.com/YesianRohn/ScriptMoE): multilingual recognition model and decoding design.
117
+ - [ppocrv6_onnx](https://github.com/AIwork4me/ppocrv6_onnx): detection preprocessing and postprocessing reference.
118
+ - PP-OCRv6 medium: detector distributed through Paddle's official model storage.
119
+
120
+ Component attribution and licensing information are included in the package's THIRD_PARTY_NOTICES.txt and in the model repository's [THIRD_PARTY_NOTICES.md](https://huggingface.co/ericleyu/openocr-mlt-models/blob/7ef39160a893fc7e605e146c1544ba64c1a57f43/THIRD_PARTY_NOTICES.md).
@@ -0,0 +1,26 @@
1
+ # Third-Party Model Sources and Licensing
2
+
3
+ ## ScriptMoE Recognition
4
+
5
+ Recognition models are derived from [ScriptMoE](https://github.com/YesianRohn/ScriptMoE) at upstream revision `5dde99867aaee6b4af3e75a3020b225eab17ac53`. ONNX conversion preserves the character dictionary and autoregressive decoding rules and uses KV caching and optimized MoE execution.
6
+
7
+ | Artifact | SHA256 |
8
+ |---|---|
9
+ | Original recognition weights | 60c74a924404b652c3a9550ec370c74d78850151785341729bdac268c014650d |
10
+ | Character dictionary | 7b4eb2ca5fd6c9b6c6ecf423f69d35d8da48088697303e53624af196ee9ab6c0 |
11
+
12
+ ## PP-OCRv6 Medium Detection
13
+
14
+ The detector is distributed by Paddle as [PP-OCRv6_medium_det_onnx.tar](https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0.0/tmp/PP-OCRv6_medium_det_onnx.tar).
15
+
16
+ | Property | Value |
17
+ |---|---|
18
+ | File | inference.onnx |
19
+ | Size | 62,026,563 bytes |
20
+ | SHA256 | a0d14865d3a297970899b5c246f0e47c43879bb406c3a1fa4e505aba84b134e4 |
21
+
22
+ The accompanying Python package adapts detection preprocessing and postprocessing from [ppocrv6_onnx](https://github.com/AIwork4me/ppocrv6_onnx), revision `d638eb0bb5d8222522abcc1bae4394cbeeb636f0`, under its MIT license. The package includes the license text for that code.
23
+
24
+ ## Licensing Status
25
+
26
+ A unified redistribution license has not been specified for this combined model bundle. The MIT license of the detection adapter applies to that code; model weights have separate licensing requirements.
@@ -0,0 +1,9 @@
1
+ # Installation
2
+
3
+ See the [installation instructions](../README.md#installation) for Python and device requirements. Choose the CPU or CUDA extra and use a separate virtual environment for each device.
4
+
5
+ For installation from a release wheel, replace the package name with the local path:
6
+
7
+ `python -m pip install './openocr_mlt-0.3.0-py3-none-any.whl[cpu]'`
8
+
9
+ For offline installation, supply a local wheel directory with all matching dependencies and add `--no-index --find-links ./wheelhouse`. The [model preparation instructions](MODELS.md) cover the separate model archive.
@@ -0,0 +1,5 @@
1
+ # Model Preparation
2
+
3
+ Use `openocr-mlt fetch-models` to download and verify the registered archive from its [pinned model repository revision](https://huggingface.co/ericleyu/openocr-mlt-models/resolve/7ef39160a893fc7e605e146c1544ba64c1a57f43/scriptmoe-medium-fp32-20261101-v1.tar.gz). The archive is 213,794,128 bytes with SHA256 `ee43e7358066ddf1286f61983024dc407808e350b1dd57eb986523eaf9417ad3`.
4
+
5
+ Use `openocr-mlt fetch-models --source ./scriptmoe-medium-fp32-20261101-v1.tar.gz --offline` to import a local copy. By default, the model cache is under `$XDG_CACHE_HOME/openocr-mlt` or `~/.cache/openocr-mlt`. The command returns the recognition and detector paths and checks all 40 files. See the [usage guide](../README.md#model-preparation) and [source notices](../THIRD_PARTY_NOTICES.md).
@@ -0,0 +1,5 @@
1
+ # Validation
2
+
3
+ The FP32 model and inference implementation in this release use the same pipeline evaluated with version 0.3.0.dev14. See the [evaluation summary](verification/fp32-v14-summary.json) for the historical TextMuSS and CC-OCR quality and performance results.
4
+
5
+ The [installation record](verification/acceptance-summary.json) describes the earlier 0.3.0.dev15 trial. It covers separate Linux x86_64 CPU/CUDA installations, model import, cache reuse, whole-page OCR and cropped recognition.
@@ -0,0 +1,48 @@
1
+ {
2
+ "version": "0.3.0.dev15",
3
+ "date": "2026-11-01",
4
+ "passed": true,
5
+ "public_release": false,
6
+ "public_model_url_configured": false,
7
+ "cpu_cuda_clean_installs": [
8
+ {
9
+ "kind": "cpu",
10
+ "python": "3.10.12 (main, Aug 31 2026, 10:18:17) [GCC 11.4.0]",
11
+ "packages": 13,
12
+ "version": "0.3.0.dev15",
13
+ "pip_check": true,
14
+ "model_files": 40,
15
+ "cache_reused": true,
16
+ "page_text": "Security",
17
+ "crop_text": "Security",
18
+ "actual_rec_device": "cpu",
19
+ "actual_det_device": "cpu",
20
+ "invalid_device_exit": 2
21
+ },
22
+ {
23
+ "kind": "cuda",
24
+ "python": "3.10.12 (main, Aug 31 2026, 10:18:17) [GCC 11.4.0]",
25
+ "packages": 19,
26
+ "version": "0.3.0.dev15",
27
+ "pip_check": true,
28
+ "model_files": 40,
29
+ "cache_reused": true,
30
+ "page_text": "Security",
31
+ "crop_text": "Security",
32
+ "actual_rec_device": "cuda:0",
33
+ "actual_det_device": "cuda:0",
34
+ "invalid_device_exit": 2
35
+ }
36
+ ],
37
+ "unit_boundary_tests": 12,
38
+ "https_test": "isolated transport/redirect tests; no public endpoint configured",
39
+ "sdist_runtime_files_identical": 31,
40
+ "hash_lock_dry_run_resolved": {
41
+ "cpu": 13,
42
+ "cuda": 19
43
+ },
44
+ "inference_modules_and_models_identical_to": "0.3.0.dev14",
45
+ "new_full_quality_runs": 0,
46
+ "new_performance_runs": 0,
47
+ "prior_evidence": "fp32-v14-summary.json"
48
+ }
@@ -0,0 +1,268 @@
1
+ {
2
+ "version": "0.3.0.dev14",
3
+ "date": "2026-11-01",
4
+ "recognition_model_id": "scriptmoe-onnx-fp32-v9",
5
+ "detector": "PP-OCRv6 medium ONNX",
6
+ "recognition": {
7
+ "cuda": {
8
+ "samples": 10899,
9
+ "failed": 0,
10
+ "skipped": 0,
11
+ "text_differences": 0,
12
+ "raw_text_differences": 0,
13
+ "termination_differences": 0,
14
+ "decoded_steps_differences": 0,
15
+ "filtered_exact_accuracy": 0.7975961097348381,
16
+ "max_score_difference": 8.195638656616211e-05,
17
+ "official_weighted_accuracy": 0.850812001101018,
18
+ "official_ten_group_macro": 0.8109283048360594,
19
+ "tested_package_version": "0.3.0.dev14"
20
+ },
21
+ "cpu": {
22
+ "samples": 10899,
23
+ "failed": 0,
24
+ "skipped": 0,
25
+ "text_differences": 1,
26
+ "raw_text_differences": 1,
27
+ "termination_differences": 0,
28
+ "decoded_steps_differences": 1,
29
+ "filtered_exact_accuracy": 0.7975043581979998,
30
+ "max_score_difference": 0.01205642976694632,
31
+ "official_weighted_accuracy": 0.850812001101018,
32
+ "official_ten_group_macro": 0.8109283048360594,
33
+ "tested_package_version": "0.3.0.dev13",
34
+ "applies_to": "0.3.0.dev14",
35
+ "cpu_code_and_models_identical": true,
36
+ "same_cpu_reference": "0.3.0.dev8",
37
+ "same_cpu_text_steps_eos_score_differences": 0,
38
+ "installed_current_version_regression_samples": 300
39
+ }
40
+ },
41
+ "cc_ocr": {
42
+ "dev": {
43
+ "images": 100,
44
+ "macro_f1": 0.7131863812825354,
45
+ "micro_f1": 0.8140047618306818
46
+ },
47
+ "eval": {
48
+ "images": 100,
49
+ "macro_f1": 0.7258491279221538,
50
+ "micro_f1": 0.7945454540507626
51
+ },
52
+ "images": 200,
53
+ "boxes": 1957,
54
+ "failed": 0,
55
+ "skipped": 0,
56
+ "page_text_differences": 0,
57
+ "raw_text_differences": 0,
58
+ "detection_differences": 0,
59
+ "decoding_contract_differences": 0
60
+ },
61
+ "performance": {
62
+ "cuda_whole_page": {
63
+ "rounds": 3,
64
+ "samples_per_backend": 24,
65
+ "pairs": [
66
+ {
67
+ "round": 1,
68
+ "torch_ms": 1124.4756288360804,
69
+ "onnx_ms": 921.554900589399,
70
+ "ratio": 1.2201938572698157
71
+ },
72
+ {
73
+ "round": 2,
74
+ "torch_ms": 983.9565325528383,
75
+ "onnx_ms": 913.2588730426505,
76
+ "ratio": 1.0774125076657055
77
+ },
78
+ {
79
+ "round": 3,
80
+ "torch_ms": 1038.3566328091547,
81
+ "onnx_ms": 953.9304067147896,
82
+ "ratio": 1.0885035485818277
83
+ }
84
+ ],
85
+ "mean_ms": {
86
+ "torch": 1048.9295980660245,
87
+ "onnx": 929.5813934489464
88
+ },
89
+ "round_mean_stdev_ms": {
90
+ "torch": 70.85368586606485,
91
+ "onnx": 21.490971442195402
92
+ },
93
+ "ratio": 1.128389192660441,
94
+ "text_differences": [],
95
+ "scope": "same medium+same8 CC-OCR eval pages+sameGPU,three alternating pairs; no broad full-CC speed claim"
96
+ },
97
+ "cpu_cropped_recognition": {
98
+ "rounds": 3,
99
+ "samples_per_backend": 60,
100
+ "pairs": [
101
+ {
102
+ "round": 1,
103
+ "torch_ms": 133.5505000781268,
104
+ "onnx_ms": 133.87302686460316,
105
+ "ratio": 0.9975908008205224
106
+ },
107
+ {
108
+ "round": 2,
109
+ "torch_ms": 180.9673246461898,
110
+ "onnx_ms": 178.97417438216507,
111
+ "ratio": 1.0111365244226171
112
+ },
113
+ {
114
+ "round": 3,
115
+ "torch_ms": 178.60913323238492,
116
+ "onnx_ms": 178.6530858837068,
117
+ "ratio": 0.9997539776539293
118
+ }
119
+ ],
120
+ "mean_ms": {
121
+ "torch": 164.37565265223384,
122
+ "onnx": 163.8334290434917
123
+ },
124
+ "ratio": 1.003309603002927,
125
+ "text_differences": [],
126
+ "scope": "cropped-recognition only, fixed20,three alternating pairs; not CPU whole-page or full-set performance"
127
+ }
128
+ },
129
+ "environment": {
130
+ "os": "Linux x86_64",
131
+ "glibc": "2.35",
132
+ "python": "3.10.12",
133
+ "onnxruntime": "1.20.0",
134
+ "numpy": "2.2.6",
135
+ "opencv_python_headless": "5.0.0.93",
136
+ "cpu": "Xeon Gold 6226R",
137
+ "gpu": "RTX 3090",
138
+ "driver": "575.57.08",
139
+ "cuda_runtime": "12.4.127",
140
+ "cudnn": "9.1.0.70"
141
+ },
142
+ "installation": {
143
+ "independent_cpu_cuda": true,
144
+ "torch": false,
145
+ "outside_source_directory": true,
146
+ "hash_locked_offline_install": true,
147
+ "sdist_runtime_files_equal": 29,
148
+ "model_prepared_ordinary_files": 40,
149
+ "persistent_real_ocr_examples": true,
150
+ "complete_offline_dependencies_in_zip": false
151
+ },
152
+ "wheel": {
153
+ "name": "openocr_mlt-0.3.0.dev14-py3-none-any.whl",
154
+ "bytes": 88248,
155
+ "sha256": "f5bcfbc81750b87b35ff0948459866f3c054cf6f8b2b1f29af99a2b32ebf0fea"
156
+ },
157
+ "sdist": {
158
+ "name": "openocr_mlt-0.3.0.dev14.tar.gz",
159
+ "bytes": 91909,
160
+ "sha256": "34aa38f1732e7d6d81712c80d7e9886ac718293b23bba3cb2500036876fad960"
161
+ },
162
+ "cpu_whole_page": {
163
+ "version": "0.3.0.dev14",
164
+ "scope": "CPU detection and CPU recognition, fixed nonrandom CC-OCR200,10 languages20 each; not full CC-OCR or detection Hmean",
165
+ "quality": {
166
+ "pages": 200,
167
+ "boxes": 1957,
168
+ "failed": 0,
169
+ "skipped": 0,
170
+ "crop_errors": 0,
171
+ "same_cpu_comparison": {
172
+ "passed": true,
173
+ "pages": 200,
174
+ "boxes": 1957,
175
+ "different_pages": 0,
176
+ "differences_by_page": {
177
+ "page_text": 0,
178
+ "detections": 0,
179
+ "raw_text": 0,
180
+ "filtered_text": 0,
181
+ "termination": 0,
182
+ "decoded_steps": 0,
183
+ "status": 0
184
+ },
185
+ "max_rec_score_difference": 1.7285346984863281e-06
186
+ },
187
+ "max_detection_score_difference": 0.0,
188
+ "metrics": {
189
+ "torch": {
190
+ "dev": {
191
+ "images": 100,
192
+ "macro_f1": 0.7131863812825354,
193
+ "micro_f1": 0.8140047618306818
194
+ },
195
+ "eval": {
196
+ "images": 100,
197
+ "macro_f1": 0.7258491279221538,
198
+ "micro_f1": 0.7945454540507626
199
+ }
200
+ },
201
+ "onnx": {
202
+ "dev": {
203
+ "images": 100,
204
+ "macro_f1": 0.7131863812825354,
205
+ "micro_f1": 0.8140047618306818
206
+ },
207
+ "eval": {
208
+ "images": 100,
209
+ "macro_f1": 0.7258491279221538,
210
+ "micro_f1": 0.7945454540507626
211
+ }
212
+ }
213
+ },
214
+ "cross_device_observation": {
215
+ "reference": "frozen CUDA, same fixed200",
216
+ "different_page_texts": 0
217
+ }
218
+ },
219
+ "performance": {
220
+ "rounds": 3,
221
+ "samples_per_backend": 24,
222
+ "pairs": [
223
+ {
224
+ "round": 1,
225
+ "torch_ms": 15015.680243377574,
226
+ "onnx_ms": 14303.62608062569,
227
+ "ratio": 1.0497813742290398
228
+ },
229
+ {
230
+ "round": 2,
231
+ "torch_ms": 15510.577811743133,
232
+ "onnx_ms": 15171.641253167763,
233
+ "ratio": 1.0223401379534072
234
+ },
235
+ {
236
+ "round": 3,
237
+ "torch_ms": 15150.97376552876,
238
+ "onnx_ms": 14863.175587030128,
239
+ "ratio": 1.0193631688472933
240
+ }
241
+ ],
242
+ "mean_ms": {
243
+ "torch": 15225.74394021649,
244
+ "onnx": 14779.48097360786
245
+ },
246
+ "ratio": 1.0301947657976307,
247
+ "all_output_comparisons_passed": true
248
+ },
249
+ "runtime_versions": {
250
+ "torch": {
251
+ "numpy": "2.2.6",
252
+ "Pillow": "12.3.0",
253
+ "opencv-python-headless": "5.0.0.93",
254
+ "onnxruntime": "1.20.0",
255
+ "torch": "2.5.1+cpu"
256
+ },
257
+ "onnx": {
258
+ "numpy": "2.2.6",
259
+ "Pillow": "12.3.0",
260
+ "opencv-python-headless": "5.0.0.93",
261
+ "onnxruntime": "1.20.0"
262
+ }
263
+ },
264
+ "frozen_wheel_sha256": "f5bcfbc81750b87b35ff0948459866f3c054cf6f8b2b1f29af99a2b32ebf0fea"
265
+ },
266
+ "documentation_updated": "2026-11-01",
267
+ "prior_frozen_evidence_date": "2026-10-31"
268
+ }
Binary file
Binary file
@@ -0,0 +1,9 @@
1
+ """Run complete page OCR with CPU or CUDA ONNX."""
2
+ import argparse,json
3
+ from openocr_mlt import OpenOCRMLT
4
+ p=argparse.ArgumentParser()
5
+ p.add_argument("--model-dir",required=True);p.add_argument("--det-model-dir",required=True)
6
+ p.add_argument("--image",required=True);p.add_argument("--device",default="cpu")
7
+ a=p.parse_args()
8
+ with OpenOCRMLT(model_dir=a.model_dir,det_model_dir=a.det_model_dir,device=a.device) as ocr:
9
+ print(json.dumps(ocr.ocr(a.image).to_dict(),ensure_ascii=False))
@@ -0,0 +1,13 @@
1
+ """Prepare the pinned models, then run one page."""
2
+ import argparse,json
3
+ from openocr_mlt import OpenOCRMLT,fetch_models
4
+ p=argparse.ArgumentParser()
5
+ p.add_argument("--bundle",help="Local bundle path or distributor-provided HTTPS URL")
6
+ p.add_argument("--cache-dir",default="./models-cache")
7
+ p.add_argument("--offline",action="store_true")
8
+ p.add_argument("--image",required=True)
9
+ p.add_argument("--device",default="cpu")
10
+ a=p.parse_args()
11
+ paths=fetch_models(source=a.bundle,cache_dir=a.cache_dir,offline=a.offline)
12
+ with OpenOCRMLT(model_dir=paths["recognition_dir"],det_model_dir=paths["detector_dir"],device=a.device) as engine:
13
+ print(json.dumps(engine.ocr(a.image).to_dict(),ensure_ascii=False))