mathcraft-ocr 0.2.7__tar.gz → 0.2.9__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.
Potentially problematic release.
This version of mathcraft-ocr might be problematic. Click here for more details.
- {mathcraft_ocr-0.2.7/mathcraft_ocr.egg-info → mathcraft_ocr-0.2.9}/PKG-INFO +60 -13
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/README_MATHCRAFT_OCR.md +48 -5
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/__init__.py +7 -1
- mathcraft_ocr-0.2.9/mathcraft_ocr/adapters/common.py +129 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/adapters/text_recognizer.py +13 -56
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/cli.py +7 -4
- mathcraft_ocr-0.2.9/mathcraft_ocr/devices.py +360 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/doctor.py +4 -2
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/hardware.py +28 -27
- mathcraft_ocr-0.2.9/mathcraft_ocr/providers.py +279 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/runtime.py +12 -3
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/serialization.py +8 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/worker.py +4 -1
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9/mathcraft_ocr.egg-info}/PKG-INFO +60 -13
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr.egg-info/SOURCES.txt +1 -0
- mathcraft_ocr-0.2.9/mathcraft_ocr.egg-info/requires.txt +33 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/pyproject.toml +15 -8
- mathcraft_ocr-0.2.7/mathcraft_ocr/adapters/common.py +0 -54
- mathcraft_ocr-0.2.7/mathcraft_ocr/providers.py +0 -89
- mathcraft_ocr-0.2.7/mathcraft_ocr.egg-info/requires.txt +0 -30
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/LICENSE +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/MANIFEST.in +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/__main__.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/adapters/__init__.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/adapters/formula_detector.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/adapters/formula_recognizer.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/adapters/text_detector.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/api.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/cache.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/debug_blocks.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/downloader.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/error_patterns.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/errors.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/formula_lines.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/image.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/latex_alignment.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/latex_quality.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/layout.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/manifest.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/manifests/models.v1.json +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/profiles.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr/results.py +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr.egg-info/dependency_links.txt +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr.egg-info/entry_points.txt +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/mathcraft_ocr.egg-info/top_level.txt +0 -0
- {mathcraft_ocr-0.2.7 → mathcraft_ocr-0.2.9}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mathcraft-ocr
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: ONNX-only OCR runtime for mathematical documents
|
|
5
5
|
Author: SakuraMathcraft
|
|
6
6
|
License-Expression: GPL-3.0-only
|
|
@@ -21,21 +21,25 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Classifier: Topic :: Scientific/Engineering :: Image Recognition
|
|
23
23
|
Classifier: Topic :: Text Processing
|
|
24
|
-
Requires-Python:
|
|
24
|
+
Requires-Python: <3.14,>=3.10
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
License-File: LICENSE
|
|
27
|
-
Requires-Dist: numpy<3,>=
|
|
28
|
-
Requires-Dist: pillow<12
|
|
27
|
+
Requires-Dist: numpy<3,>=2
|
|
28
|
+
Requires-Dist: pillow<13,>=12
|
|
29
29
|
Requires-Dist: opencv-python<5,>=4.10
|
|
30
30
|
Requires-Dist: rapidocr==3.5.0
|
|
31
31
|
Requires-Dist: transformers==4.55.4
|
|
32
32
|
Requires-Dist: tokenizers==0.21.4
|
|
33
33
|
Provides-Extra: cpu
|
|
34
|
-
Requires-Dist: onnxruntime<1.
|
|
35
|
-
Requires-Dist: onnxruntime<1.26,>=1.20; python_version >= "3.13" and extra == "cpu"
|
|
34
|
+
Requires-Dist: onnxruntime<1.30,>=1.20; extra == "cpu"
|
|
36
35
|
Provides-Extra: gpu
|
|
37
|
-
Requires-Dist: onnxruntime-gpu<1.
|
|
38
|
-
|
|
36
|
+
Requires-Dist: onnxruntime-gpu<1.30,>=1.27; extra == "gpu"
|
|
37
|
+
Provides-Extra: gpu-cu12
|
|
38
|
+
Requires-Dist: onnxruntime-gpu<1.27,>=1.21; extra == "gpu-cu12"
|
|
39
|
+
Provides-Extra: directml
|
|
40
|
+
Requires-Dist: onnxruntime-directml<1.30,>=1.20; platform_system == "Windows" and extra == "directml"
|
|
41
|
+
Provides-Extra: openvino
|
|
42
|
+
Requires-Dist: onnxruntime-openvino<1.30,>=1.20; (platform_system == "Windows" or platform_system == "Linux") and extra == "openvino"
|
|
39
43
|
Provides-Extra: dev
|
|
40
44
|
Requires-Dist: ruff>=0.8; extra == "dev"
|
|
41
45
|
Requires-Dist: build>=1.2; extra == "dev"
|
|
@@ -76,9 +80,27 @@ GPU backend:
|
|
|
76
80
|
pip install "mathcraft-ocr[gpu]"
|
|
77
81
|
```
|
|
78
82
|
|
|
79
|
-
|
|
83
|
+
CUDA 12 backend:
|
|
80
84
|
|
|
81
|
-
|
|
85
|
+
```powershell
|
|
86
|
+
pip install "mathcraft-ocr[gpu-cu12]"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Windows DirectML backend:
|
|
90
|
+
|
|
91
|
+
```powershell
|
|
92
|
+
pip install "mathcraft-ocr[directml]"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Intel OpenVINO backend on Windows or Linux:
|
|
96
|
+
|
|
97
|
+
```powershell
|
|
98
|
+
pip install "mathcraft-ocr[openvino]"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Install only one backend extra in a clean environment. The CPU, CUDA/TensorRT, DirectML, and OpenVINO ONNX Runtime distributions must not be mixed in the same environment. The standard `onnxruntime` macOS wheel includes CoreML EP support.
|
|
102
|
+
|
|
103
|
+
LaTeXSnipper's dependency wizard selects the ONNX Runtime GPU line from the detected CUDA toolkit: CUDA 11.x uses ONNX Runtime 1.20 from the official CUDA 11 feed, CUDA 12.x uses ONNX Runtime 1.21-1.26 from PyPI, and CUDA 13.x uses ONNX Runtime 1.27-1.29 from PyPI. The static `mathcraft-ocr[gpu]` extra follows the current PyPI default (CUDA 13 and Python 3.11+); use `gpu-cu12` for CUDA 12. CUDA 11 installations must also select the official CUDA 11 package feed, so LaTeXSnipper's dependency wizard is recommended for that configuration.
|
|
82
104
|
|
|
83
105
|
## Quick Start
|
|
84
106
|
|
|
@@ -190,11 +212,36 @@ Model artifacts are downloaded from the MathCraft-Models release assets declared
|
|
|
190
212
|
|
|
191
213
|
`provider_preference` accepts:
|
|
192
214
|
|
|
193
|
-
- `auto`: prefer CUDA
|
|
215
|
+
- `auto`: prefer CUDA, then a platform accelerator, and finally CPU; TensorRT remains explicit because of its first-use engine build cost.
|
|
194
216
|
- `cpu`: force CPU.
|
|
195
|
-
- `gpu`: request
|
|
217
|
+
- `gpu`: request the first available supported accelerator.
|
|
218
|
+
- `cuda`: request CUDA.
|
|
219
|
+
- `tensorrt` or `trt`: request TensorRT, followed by CUDA and CPU for unsupported nodes.
|
|
220
|
+
- `directml` or `dml`: request DirectML.
|
|
221
|
+
- `coreml`: request CoreML.
|
|
222
|
+
- `openvino`: request OpenVINO.
|
|
223
|
+
|
|
224
|
+
Advanced callers can pass an explicit ordered provider list and provider options:
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
runtime = MathCraftRuntime(
|
|
228
|
+
providers=[
|
|
229
|
+
(
|
|
230
|
+
"TensorrtExecutionProvider",
|
|
231
|
+
{
|
|
232
|
+
"device_id": 0,
|
|
233
|
+
"trt_engine_cache_enable": True,
|
|
234
|
+
"trt_engine_cache_path": "./trt-cache",
|
|
235
|
+
},
|
|
236
|
+
),
|
|
237
|
+
("CUDAExecutionProvider", {"device_id": 0}),
|
|
238
|
+
]
|
|
239
|
+
)
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`auto` deliberately prefers CUDA over TensorRT because TensorRT can spend substantial time building an engine on first use. Select `tensorrt` explicitly when its native runtime is installed; enable its engine cache for repeated startup performance.
|
|
196
243
|
|
|
197
|
-
The actual provider is available on results through the `provider` field.
|
|
244
|
+
The actual provider is available on recognition results through the `provider` field. Doctor and warmup reports also expose `device_id`, `device_name`, `device_uuid`, and `device_verified` under `provider_info`. GPU sessions bind the reported `device_id` explicitly; `device_verified` becomes true only after the runtime confirms the device used by initialized inference sessions.
|
|
198
245
|
|
|
199
246
|
## Development
|
|
200
247
|
|
|
@@ -30,9 +30,27 @@ GPU backend:
|
|
|
30
30
|
pip install "mathcraft-ocr[gpu]"
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
CUDA 12 backend:
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
```powershell
|
|
36
|
+
pip install "mathcraft-ocr[gpu-cu12]"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Windows DirectML backend:
|
|
40
|
+
|
|
41
|
+
```powershell
|
|
42
|
+
pip install "mathcraft-ocr[directml]"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Intel OpenVINO backend on Windows or Linux:
|
|
46
|
+
|
|
47
|
+
```powershell
|
|
48
|
+
pip install "mathcraft-ocr[openvino]"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Install only one backend extra in a clean environment. The CPU, CUDA/TensorRT, DirectML, and OpenVINO ONNX Runtime distributions must not be mixed in the same environment. The standard `onnxruntime` macOS wheel includes CoreML EP support.
|
|
52
|
+
|
|
53
|
+
LaTeXSnipper's dependency wizard selects the ONNX Runtime GPU line from the detected CUDA toolkit: CUDA 11.x uses ONNX Runtime 1.20 from the official CUDA 11 feed, CUDA 12.x uses ONNX Runtime 1.21-1.26 from PyPI, and CUDA 13.x uses ONNX Runtime 1.27-1.29 from PyPI. The static `mathcraft-ocr[gpu]` extra follows the current PyPI default (CUDA 13 and Python 3.11+); use `gpu-cu12` for CUDA 12. CUDA 11 installations must also select the official CUDA 11 package feed, so LaTeXSnipper's dependency wizard is recommended for that configuration.
|
|
36
54
|
|
|
37
55
|
## Quick Start
|
|
38
56
|
|
|
@@ -144,11 +162,36 @@ Model artifacts are downloaded from the MathCraft-Models release assets declared
|
|
|
144
162
|
|
|
145
163
|
`provider_preference` accepts:
|
|
146
164
|
|
|
147
|
-
- `auto`: prefer CUDA
|
|
165
|
+
- `auto`: prefer CUDA, then a platform accelerator, and finally CPU; TensorRT remains explicit because of its first-use engine build cost.
|
|
148
166
|
- `cpu`: force CPU.
|
|
149
|
-
- `gpu`: request
|
|
167
|
+
- `gpu`: request the first available supported accelerator.
|
|
168
|
+
- `cuda`: request CUDA.
|
|
169
|
+
- `tensorrt` or `trt`: request TensorRT, followed by CUDA and CPU for unsupported nodes.
|
|
170
|
+
- `directml` or `dml`: request DirectML.
|
|
171
|
+
- `coreml`: request CoreML.
|
|
172
|
+
- `openvino`: request OpenVINO.
|
|
173
|
+
|
|
174
|
+
Advanced callers can pass an explicit ordered provider list and provider options:
|
|
150
175
|
|
|
151
|
-
|
|
176
|
+
```python
|
|
177
|
+
runtime = MathCraftRuntime(
|
|
178
|
+
providers=[
|
|
179
|
+
(
|
|
180
|
+
"TensorrtExecutionProvider",
|
|
181
|
+
{
|
|
182
|
+
"device_id": 0,
|
|
183
|
+
"trt_engine_cache_enable": True,
|
|
184
|
+
"trt_engine_cache_path": "./trt-cache",
|
|
185
|
+
},
|
|
186
|
+
),
|
|
187
|
+
("CUDAExecutionProvider", {"device_id": 0}),
|
|
188
|
+
]
|
|
189
|
+
)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`auto` deliberately prefers CUDA over TensorRT because TensorRT can spend substantial time building an engine on first use. Select `tensorrt` explicitly when its native runtime is installed; enable its engine cache for repeated startup performance.
|
|
193
|
+
|
|
194
|
+
The actual provider is available on recognition results through the `provider` field. Doctor and warmup reports also expose `device_id`, `device_name`, `device_uuid`, and `device_verified` under `provider_info`. GPU sessions bind the reported `device_id` explicitly; `device_verified` becomes true only after the runtime confirms the device used by initialized inference sessions.
|
|
152
195
|
|
|
153
196
|
## Development
|
|
154
197
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
__version__ = "0.2.
|
|
5
|
+
__version__ = "0.2.9"
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"DoctorReport",
|
|
@@ -12,6 +12,8 @@ __all__ = [
|
|
|
12
12
|
"MathCraftRuntime",
|
|
13
13
|
"MixedRecognitionResult",
|
|
14
14
|
"OCRRegion",
|
|
15
|
+
"ProviderInfo",
|
|
16
|
+
"ProviderSpec",
|
|
15
17
|
"__version__",
|
|
16
18
|
"run_doctor",
|
|
17
19
|
]
|
|
@@ -32,6 +34,10 @@ def __getattr__(name: str) -> object:
|
|
|
32
34
|
from . import doctor
|
|
33
35
|
|
|
34
36
|
return getattr(doctor, name)
|
|
37
|
+
if name in {"ProviderInfo", "ProviderSpec"}:
|
|
38
|
+
from . import providers
|
|
39
|
+
|
|
40
|
+
return getattr(providers, name)
|
|
35
41
|
if name == "MathCraftError":
|
|
36
42
|
from .errors import MathCraftError
|
|
37
43
|
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
from functools import lru_cache
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from ..providers import ACCELERATOR_PROVIDER_NAMES, ProviderInfo
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
ProviderOptions = tuple[tuple[str, str], ...]
|
|
13
|
+
ProviderConfig = tuple[str, ProviderOptions]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _ort():
|
|
17
|
+
return importlib.import_module("onnxruntime")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def session_providers(provider_info: ProviderInfo) -> list[str | tuple[str, dict[str, object]]]:
|
|
21
|
+
if provider_info.provider_specs:
|
|
22
|
+
return [spec.as_ort_provider() for spec in provider_info.provider_specs]
|
|
23
|
+
available = list(provider_info.available_providers)
|
|
24
|
+
active = provider_info.active_provider
|
|
25
|
+
if active and active in ACCELERATOR_PROVIDER_NAMES and "CPUExecutionProvider" in available:
|
|
26
|
+
return [(active, {"device_id": int(provider_info.device_id or 0)}), "CPUExecutionProvider"]
|
|
27
|
+
if active and active in ACCELERATOR_PROVIDER_NAMES:
|
|
28
|
+
return [(active, {"device_id": int(provider_info.device_id or 0)})]
|
|
29
|
+
if "CPUExecutionProvider" in available:
|
|
30
|
+
return ["CPUExecutionProvider"]
|
|
31
|
+
return available
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create_session(model_path: str | Path, provider_info: ProviderInfo):
|
|
35
|
+
model_path = str(Path(model_path).resolve())
|
|
36
|
+
provider_config = _freeze_provider_config(session_providers(provider_info))
|
|
37
|
+
session = _create_session_cached(model_path, provider_config)
|
|
38
|
+
enforce_session_provider(session, provider_info)
|
|
39
|
+
return session
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@lru_cache(maxsize=16)
|
|
43
|
+
def _create_session_cached(model_path: str, providers: tuple[ProviderConfig, ...]):
|
|
44
|
+
ort = _ort()
|
|
45
|
+
configured = [
|
|
46
|
+
(name, {key: value for key, value in options}) if options else name
|
|
47
|
+
for name, options in providers
|
|
48
|
+
]
|
|
49
|
+
kwargs = {
|
|
50
|
+
"providers": configured,
|
|
51
|
+
"enable_fallback": False,
|
|
52
|
+
}
|
|
53
|
+
session_options = _session_options(ort, providers)
|
|
54
|
+
if session_options is not None:
|
|
55
|
+
kwargs["sess_options"] = session_options
|
|
56
|
+
return ort.InferenceSession(model_path, **kwargs)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _session_options(ort, providers: tuple[ProviderConfig, ...]):
|
|
60
|
+
create_options = getattr(ort, "SessionOptions", None)
|
|
61
|
+
if not callable(create_options):
|
|
62
|
+
return None
|
|
63
|
+
session_options = create_options()
|
|
64
|
+
session_options.log_severity_level = 4
|
|
65
|
+
names = {name for name, _options in providers}
|
|
66
|
+
if "DmlExecutionProvider" not in names:
|
|
67
|
+
return session_options
|
|
68
|
+
session_options.enable_mem_pattern = False
|
|
69
|
+
execution_mode = getattr(getattr(ort, "ExecutionMode", None), "ORT_SEQUENTIAL", None)
|
|
70
|
+
if execution_mode is not None:
|
|
71
|
+
session_options.execution_mode = execution_mode
|
|
72
|
+
return session_options
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _freeze_provider_config(
|
|
76
|
+
providers: list[str | tuple[str, dict[str, object]]],
|
|
77
|
+
) -> tuple[ProviderConfig, ...]:
|
|
78
|
+
frozen: list[ProviderConfig] = []
|
|
79
|
+
for item in providers:
|
|
80
|
+
if isinstance(item, str):
|
|
81
|
+
frozen.append((item, ()))
|
|
82
|
+
continue
|
|
83
|
+
name, options = item
|
|
84
|
+
frozen.append((name, tuple(sorted((str(key), str(value)) for key, value in options.items()))))
|
|
85
|
+
return tuple(frozen)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def enforce_session_provider(session, provider_info: ProviderInfo) -> None:
|
|
89
|
+
validate_session_provider(
|
|
90
|
+
session,
|
|
91
|
+
str(provider_info.active_provider or ""),
|
|
92
|
+
int(provider_info.device_id or 0),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def validate_session_provider(
|
|
97
|
+
session,
|
|
98
|
+
active_provider: str,
|
|
99
|
+
device_id: int = 0,
|
|
100
|
+
*,
|
|
101
|
+
runtime_name: str = "ONNX Runtime",
|
|
102
|
+
) -> None:
|
|
103
|
+
actual = list(session.get_providers() or [])
|
|
104
|
+
active = str(active_provider or "")
|
|
105
|
+
if not actual or actual[0] != active:
|
|
106
|
+
provider_kind = "ONNX accelerator provider" if active in ACCELERATOR_PROVIDER_NAMES else "ONNX provider"
|
|
107
|
+
raise RuntimeError(
|
|
108
|
+
f"requested {provider_kind} {active or '<none>'}, "
|
|
109
|
+
f"but {runtime_name} session providers are {actual}"
|
|
110
|
+
)
|
|
111
|
+
if active in {"CUDAExecutionProvider", "TensorrtExecutionProvider", "DmlExecutionProvider"}:
|
|
112
|
+
get_options = getattr(session, "get_provider_options", None)
|
|
113
|
+
if callable(get_options):
|
|
114
|
+
options_by_provider = get_options() or {}
|
|
115
|
+
active_options = options_by_provider.get(active, {}) if isinstance(options_by_provider, dict) else {}
|
|
116
|
+
actual_device_id = active_options.get("device_id") if isinstance(active_options, dict) else None
|
|
117
|
+
if actual_device_id not in (None, "") and int(actual_device_id) != int(device_id):
|
|
118
|
+
raise RuntimeError(
|
|
119
|
+
f"requested ONNX provider {active} device_id={int(device_id)}, "
|
|
120
|
+
f"but {runtime_name} session uses device_id={actual_device_id}"
|
|
121
|
+
)
|
|
122
|
+
disable_fallback = getattr(session, "disable_fallback", None)
|
|
123
|
+
if not callable(disable_fallback):
|
|
124
|
+
raise RuntimeError(f"{runtime_name} session cannot disable provider fallback")
|
|
125
|
+
disable_fallback()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def clear_session_cache() -> None:
|
|
129
|
+
_create_session_cached.cache_clear()
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import logging
|
|
5
6
|
from functools import lru_cache
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
@@ -10,6 +11,12 @@ from rapidocr import EngineType, LangRec, ModelType, OCRVersion
|
|
|
10
11
|
from rapidocr.ch_ppocr_rec import TextRecInput, TextRecognizer
|
|
11
12
|
from rapidocr.utils.typings import TaskType
|
|
12
13
|
|
|
14
|
+
from .common import create_session
|
|
15
|
+
from ..providers import ProviderInfo
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
logging.getLogger("RapidOCR").setLevel(logging.WARNING)
|
|
19
|
+
|
|
13
20
|
|
|
14
21
|
class _Config(dict):
|
|
15
22
|
def __init__(self, *args, **kwargs):
|
|
@@ -54,23 +61,13 @@ def recognize_pp_text_lines(
|
|
|
54
61
|
|
|
55
62
|
def _create_pp_text_recognizer(model_dir: Path, provider_info) -> TextRecognizer:
|
|
56
63
|
model_dir = model_dir.resolve()
|
|
57
|
-
|
|
58
|
-
use_cuda = active_provider in {"CUDAExecutionProvider", "TensorrtExecutionProvider"}
|
|
59
|
-
use_dml = active_provider == "DmlExecutionProvider"
|
|
60
|
-
return _create_pp_text_recognizer_cached(
|
|
61
|
-
str(model_dir),
|
|
62
|
-
active_provider,
|
|
63
|
-
use_cuda,
|
|
64
|
-
use_dml,
|
|
65
|
-
)
|
|
64
|
+
return _create_pp_text_recognizer_cached(str(model_dir), provider_info)
|
|
66
65
|
|
|
67
66
|
|
|
68
67
|
@lru_cache(maxsize=8)
|
|
69
68
|
def _create_pp_text_recognizer_cached(
|
|
70
69
|
model_dir: str,
|
|
71
|
-
|
|
72
|
-
use_cuda: bool,
|
|
73
|
-
use_dml: bool,
|
|
70
|
+
provider_info: ProviderInfo,
|
|
74
71
|
) -> TextRecognizer:
|
|
75
72
|
model_dir = Path(model_dir)
|
|
76
73
|
model_candidates = sorted(model_dir.glob("**/*rec*.onnx"))
|
|
@@ -84,6 +81,7 @@ def _create_pp_text_recognizer_cached(
|
|
|
84
81
|
is_server = "server" in model_name or "server" in model_dir.name
|
|
85
82
|
is_v5 = "v5" in model_name or "v5" in model_dir.name
|
|
86
83
|
is_english = dict_path.name == "en_dict.txt"
|
|
84
|
+
session = create_session(model_path, provider_info)
|
|
87
85
|
config = _Config({
|
|
88
86
|
"engine_type": EngineType.ONNXRUNTIME,
|
|
89
87
|
"lang_type": LangRec.EN if is_english else LangRec.CH,
|
|
@@ -96,51 +94,10 @@ def _create_pp_text_recognizer_cached(
|
|
|
96
94
|
"rec_img_shape": [3, 48, 320],
|
|
97
95
|
"rec_batch_num": 6,
|
|
98
96
|
"font_path": None,
|
|
99
|
-
"
|
|
100
|
-
|
|
101
|
-
"inter_op_num_threads": -1,
|
|
102
|
-
"enable_cpu_mem_arena": False,
|
|
103
|
-
"cpu_ep_cfg": {"arena_extend_strategy": "kSameAsRequested"},
|
|
104
|
-
"use_cuda": use_cuda,
|
|
105
|
-
"cuda_ep_cfg": {
|
|
106
|
-
"device_id": 0,
|
|
107
|
-
"arena_extend_strategy": "kNextPowerOfTwo",
|
|
108
|
-
"cudnn_conv_algo_search": "EXHAUSTIVE",
|
|
109
|
-
"do_copy_in_default_stream": True,
|
|
110
|
-
},
|
|
111
|
-
"use_dml": use_dml,
|
|
112
|
-
"dm_ep_cfg": None,
|
|
113
|
-
"use_cann": False,
|
|
114
|
-
"cann_ep_cfg": {
|
|
115
|
-
"device_id": 0,
|
|
116
|
-
"arena_extend_strategy": "kNextPowerOfTwo",
|
|
117
|
-
"npu_mem_limit": 21474836480,
|
|
118
|
-
"op_select_impl_mode": "high_performance",
|
|
119
|
-
"optypelist_for_implmode": "Gelu",
|
|
120
|
-
"enable_cann_graph": True,
|
|
121
|
-
},
|
|
122
|
-
},
|
|
97
|
+
"session": session,
|
|
98
|
+
"engine_cfg": {},
|
|
123
99
|
})
|
|
124
|
-
|
|
125
|
-
_enforce_strict_provider(recognizer, active_provider)
|
|
126
|
-
return recognizer
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def _enforce_strict_provider(recognizer: TextRecognizer, active_provider: str) -> None:
|
|
130
|
-
engine = getattr(recognizer, "session", None)
|
|
131
|
-
session = getattr(engine, "session", None)
|
|
132
|
-
if session is None:
|
|
133
|
-
raise RuntimeError("RapidOCR did not expose its ONNX Runtime session")
|
|
134
|
-
actual = list(session.get_providers() or [])
|
|
135
|
-
if not active_provider or not actual or actual[0] != active_provider:
|
|
136
|
-
raise RuntimeError(
|
|
137
|
-
f"requested ONNX provider {active_provider or '<none>'}, "
|
|
138
|
-
f"but RapidOCR session providers are {actual}"
|
|
139
|
-
)
|
|
140
|
-
disable_fallback = getattr(session, "disable_fallback", None)
|
|
141
|
-
if not callable(disable_fallback):
|
|
142
|
-
raise RuntimeError("RapidOCR ONNX Runtime session cannot disable provider fallback")
|
|
143
|
-
disable_fallback()
|
|
100
|
+
return TextRecognizer(config)
|
|
144
101
|
|
|
145
102
|
|
|
146
103
|
def clear_text_recognizer_cache() -> None:
|
|
@@ -16,6 +16,9 @@ from .serialization import (
|
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
_PROVIDER_HELP = "auto, cpu, gpu, cuda, tensorrt, directml, coreml, or openvino"
|
|
20
|
+
|
|
21
|
+
|
|
19
22
|
def _resolve_ocr_output_path(args: argparse.Namespace) -> Path | None:
|
|
20
23
|
output = str(getattr(args, "output", "") or "").strip()
|
|
21
24
|
output_dir = str(getattr(args, "output_dir", "") or "").strip()
|
|
@@ -45,22 +48,22 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
45
48
|
models_sub.add_parser("check")
|
|
46
49
|
|
|
47
50
|
doctor = sub.add_parser("doctor")
|
|
48
|
-
doctor.add_argument("--provider", default="auto")
|
|
51
|
+
doctor.add_argument("--provider", default="auto", help=_PROVIDER_HELP)
|
|
49
52
|
|
|
50
53
|
warmup = sub.add_parser("warmup")
|
|
51
54
|
warmup.add_argument("--profile", default="formula")
|
|
52
|
-
warmup.add_argument("--provider", default="auto")
|
|
55
|
+
warmup.add_argument("--provider", default="auto", help=_PROVIDER_HELP)
|
|
53
56
|
|
|
54
57
|
ocr = sub.add_parser("ocr")
|
|
55
58
|
ocr.add_argument("image")
|
|
56
59
|
ocr.add_argument("--profile", choices=("formula", "text", "mixed"), default="mixed")
|
|
57
|
-
ocr.add_argument("--provider", default="auto")
|
|
60
|
+
ocr.add_argument("--provider", default="auto", help=_PROVIDER_HELP)
|
|
58
61
|
ocr.add_argument("--output", "-o", default="")
|
|
59
62
|
ocr.add_argument("--output-dir", default="")
|
|
60
63
|
ocr.add_argument("--json", action="store_true", dest="as_json")
|
|
61
64
|
|
|
62
65
|
worker = sub.add_parser("worker")
|
|
63
|
-
worker.add_argument("--provider", default="auto")
|
|
66
|
+
worker.add_argument("--provider", default="auto", help=_PROVIDER_HELP)
|
|
64
67
|
return parser
|
|
65
68
|
|
|
66
69
|
|