rusto 0.4.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rusto
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Pure Rust OCR library for Python based on RapidOCR, Indra OCR, and pure-Rust MNN (rusto-nn)
5
5
  Author-email: RustO Contributors <contact@rusto.dev>
6
6
  License-Expression: MIT
@@ -15,7 +15,7 @@ Official Python bindings for **RustO!** — a high-performance, pure-Rust OCR en
15
15
 
16
16
  ## Features
17
17
 
18
- - **Zero-Config Indra OCR**: `RustO.initialize()` automatically downloads and caches the default `indraocr` models on first run from Hugging Face Hub.
18
+ - **Zero-Config PP-OCRv6 Tiny Default**: `RustO.initialize()` automatically downloads and caches the default `ppocrv6-tiny` (FP32, Apache 2.0) models on first run from Hugging Face Hub.
19
19
  - **Context Manager Support**: Deterministic native resource cleanup with `with RustO.initialize() as engine:` or `engine.close()`.
20
20
  - **Full Document Pipeline**: Neural document unwarping (`with_auto_unwarp()`), page orientation detection (`with_orientation_detection()`), semantic layout analysis, and reading-order recovery (`with_layout_reading()`).
21
21
  - **Fluent Builder API**: Chain `.as_lines()`, `.as_words()`, `.as_spatial()`, `.as_tsv()`, or `.with_output(...)` into a single `.run()` terminal method.
@@ -32,7 +32,7 @@ pip install rusto
32
32
  ```python
33
33
  from rusto import RustO
34
34
 
35
- # Zero-config initialization (auto-downloads Indra OCR models from Hugging Face Hub if not cached)
35
+ # Zero-config initialization (auto-downloads PP-OCRv6 Tiny FP32 models from Hugging Face Hub if not cached)
36
36
  with RustO.initialize() as engine:
37
37
  result = (
38
38
  engine.detect_text("invoice.png")
@@ -110,7 +110,7 @@ Pre-download models into a target cache directory ahead of time (e.g., during Do
110
110
  ```python
111
111
  from rusto import download_model
112
112
 
113
- paths = download_model(model="indraocr", lang="latin", cache_dir="./models-cache")
113
+ paths = download_model(model="ppocrv6-tiny", cache_dir="./models-cache")
114
114
  ```
115
115
 
116
116
  ## Input Sources & Raw RGB Buffers
@@ -4,7 +4,7 @@ Official Python bindings for **RustO!** — a high-performance, pure-Rust OCR en
4
4
 
5
5
  ## Features
6
6
 
7
- - **Zero-Config Indra OCR**: `RustO.initialize()` automatically downloads and caches the default `indraocr` models on first run from Hugging Face Hub.
7
+ - **Zero-Config PP-OCRv6 Tiny Default**: `RustO.initialize()` automatically downloads and caches the default `ppocrv6-tiny` (FP32, Apache 2.0) models on first run from Hugging Face Hub.
8
8
  - **Context Manager Support**: Deterministic native resource cleanup with `with RustO.initialize() as engine:` or `engine.close()`.
9
9
  - **Full Document Pipeline**: Neural document unwarping (`with_auto_unwarp()`), page orientation detection (`with_orientation_detection()`), semantic layout analysis, and reading-order recovery (`with_layout_reading()`).
10
10
  - **Fluent Builder API**: Chain `.as_lines()`, `.as_words()`, `.as_spatial()`, `.as_tsv()`, or `.with_output(...)` into a single `.run()` terminal method.
@@ -21,7 +21,7 @@ pip install rusto
21
21
  ```python
22
22
  from rusto import RustO
23
23
 
24
- # Zero-config initialization (auto-downloads Indra OCR models from Hugging Face Hub if not cached)
24
+ # Zero-config initialization (auto-downloads PP-OCRv6 Tiny FP32 models from Hugging Face Hub if not cached)
25
25
  with RustO.initialize() as engine:
26
26
  result = (
27
27
  engine.detect_text("invoice.png")
@@ -99,7 +99,7 @@ Pre-download models into a target cache directory ahead of time (e.g., during Do
99
99
  ```python
100
100
  from rusto import download_model
101
101
 
102
- paths = download_model(model="indraocr", lang="latin", cache_dir="./models-cache")
102
+ paths = download_model(model="ppocrv6-tiny", cache_dir="./models-cache")
103
103
  ```
104
104
 
105
105
  ## Input Sources & Raw RGB Buffers
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rusto"
7
- version = "0.4.0"
7
+ version = "0.4.1"
8
8
  description = "Pure Rust OCR library for Python based on RapidOCR, Indra OCR, and pure-Rust MNN (rusto-nn)"
9
9
  readme = "README.md"
10
10
  authors = [{ name = "RustO Contributors", email = "contact@rusto.dev" }]
@@ -1,11 +1,11 @@
1
1
  """RustO - High performance, pure-Rust OCR engine for Python.
2
2
 
3
- RustO provides fast, memory-safe text extraction powered by Indra OCR and PaddleOCR
4
- architectures on the pure-Rust RTen deep learning inference engine.
3
+ RustO provides fast, memory-safe text extraction powered by PaddleOCR and Indra OCR
4
+ architectures on pure-Rust MNN (`rusto-nn`) and RTen (`rusto-rten`) inference engines.
5
5
 
6
6
  Quick Start:
7
7
  >>> from rusto import RustO, InitializeConfig
8
- >>> engine = RustO.initialize(InitializeConfig.indra())
8
+ >>> engine = RustO.initialize()
9
9
  >>> result = engine.detect_text("receipt.jpg").with_auto_unwarp().with_layout_reading().as_lines().run()
10
10
  >>> if result.is_STRUCTURED():
11
11
  ... for item in result.results:
@@ -169,14 +169,24 @@ class PreRecognitionOptions(_GeneratedPreRecognitionOptions):
169
169
 
170
170
 
171
171
  class RecognitionRunOptions(_GeneratedRecognitionRunOptions):
172
- """Phase 5: Recognition inference options."""
172
+ """Phase 5: Recognition inference options.
173
+
174
+ Args:
175
+ batch_size: Recognition batch size (number of text crops processed concurrently).
176
+ workers: Number of parallel worker threads for recognition inference (`None` = auto).
177
+ max_workers: Maximum worker thread count clamp for recognition inference (`None` = engine default 8).
178
+ """
173
179
  def __init__(
174
180
  self,
175
181
  *,
176
182
  batch_size: int | None = None,
183
+ workers: int | None = None,
184
+ max_workers: int | None = None,
177
185
  ):
178
186
  super().__init__(
179
187
  batch_size=batch_size,
188
+ workers=workers,
189
+ max_workers=max_workers,
180
190
  )
181
191
 
182
192
 
@@ -220,19 +230,32 @@ class DetectionConfig(_GeneratedDetectionConfig):
220
230
 
221
231
 
222
232
  class RecognitionConfig(_GeneratedRecognitionConfig):
223
- """Text recognition model configuration."""
233
+ """Text recognition model configuration.
234
+
235
+ Args:
236
+ enabled: Whether recognition stage is enabled.
237
+ model_path: Filesystem path to recognition model (.rten or .onnx).
238
+ dict_path: Filesystem path to dictionary vocabulary file (.txt).
239
+ rec_batch_num: Recognition batch size. Default: 1.
240
+ rec_workers: Number of parallel worker threads for recognition (`None` = auto).
241
+ max_rec_workers: Maximum worker thread count clamp (`None` = engine default 8).
242
+ """
224
243
  def __init__(
225
244
  self,
226
245
  enabled: bool | None = None,
227
246
  model_path: str | None = None,
228
247
  dict_path: str | None = None,
229
248
  rec_batch_num: int | None = None,
249
+ rec_workers: int | None = None,
250
+ max_rec_workers: int | None = None,
230
251
  ):
231
252
  super().__init__(
232
253
  enabled=enabled,
233
254
  model_path=model_path,
234
255
  dict_path=dict_path,
235
256
  rec_batch_num=rec_batch_num,
257
+ rec_workers=rec_workers,
258
+ max_rec_workers=max_rec_workers,
236
259
  )
237
260
 
238
261
 
@@ -443,7 +466,7 @@ class InitializeConfig(_GeneratedInitializeConfig):
443
466
  @classmethod
444
467
  def for_model(
445
468
  cls,
446
- model: str | None = "indraocr",
469
+ model: str | None = "ppocrv6-tiny",
447
470
  lang: str | None = None,
448
471
  model_path: str | None = None,
449
472
  ) -> InitializeConfig:
@@ -660,12 +683,18 @@ class DetectTextBuilder:
660
683
  options: RecognitionRunOptions | None = None,
661
684
  *,
662
685
  batch_size: int | None = None,
686
+ workers: int | None = None,
687
+ max_workers: int | None = None,
663
688
  ) -> DetectTextBuilder:
664
689
  """Configure recognition inference options."""
665
690
  if options is not None:
666
691
  self._recognition = options
667
692
  else:
668
- self._recognition = RecognitionRunOptions(batch_size=batch_size)
693
+ self._recognition = RecognitionRunOptions(
694
+ batch_size=batch_size,
695
+ workers=workers,
696
+ max_workers=max_workers,
697
+ )
669
698
  return self
670
699
 
671
700
  def with_post_recognition(
Binary file
@@ -1372,22 +1372,62 @@ class _UniffiFfiConverterOptionalTypeDetectionConfig(_UniffiConverterRustBuffer)
1372
1372
  else:
1373
1373
  raise InternalError("Unexpected flag byte for optional type")
1374
1374
 
1375
+ class _UniffiFfiConverterUInt32(_UniffiConverterPrimitiveInt):
1376
+ CLASS_NAME = "u32"
1377
+ VALUE_MIN = 0
1378
+ VALUE_MAX = 2**32
1379
+
1380
+ @staticmethod
1381
+ def read(buf):
1382
+ return buf.read_u32()
1383
+
1384
+ @staticmethod
1385
+ def write(value, buf):
1386
+ buf.write_u32(value)
1387
+
1388
+ class _UniffiFfiConverterOptionalUInt32(_UniffiConverterRustBuffer):
1389
+ @classmethod
1390
+ def check_lower(cls, value):
1391
+ if value is not None:
1392
+ _UniffiFfiConverterUInt32.check_lower(value)
1393
+
1394
+ @classmethod
1395
+ def write(cls, value, buf):
1396
+ if value is None:
1397
+ buf.write_u8(0)
1398
+ return
1399
+
1400
+ buf.write_u8(1)
1401
+ _UniffiFfiConverterUInt32.write(value, buf)
1402
+
1403
+ @classmethod
1404
+ def read(cls, buf):
1405
+ flag = buf.read_u8()
1406
+ if flag == 0:
1407
+ return None
1408
+ elif flag == 1:
1409
+ return _UniffiFfiConverterUInt32.read(buf)
1410
+ else:
1411
+ raise InternalError("Unexpected flag byte for optional type")
1412
+
1375
1413
  @dataclass
1376
1414
  class RecognitionConfig:
1377
1415
  """
1378
1416
  Text recognition model configuration.
1379
1417
  """
1380
- def __init__(self, *, enabled:typing.Optional[bool], model_path:typing.Optional[str], dict_path:typing.Optional[str], rec_batch_num:typing.Optional[int]):
1418
+ def __init__(self, *, enabled:typing.Optional[bool], model_path:typing.Optional[str], dict_path:typing.Optional[str], rec_batch_num:typing.Optional[int], rec_workers:typing.Optional[int], max_rec_workers:typing.Optional[int]):
1381
1419
  self.enabled = enabled
1382
1420
  self.model_path = model_path
1383
1421
  self.dict_path = dict_path
1384
1422
  self.rec_batch_num = rec_batch_num
1423
+ self.rec_workers = rec_workers
1424
+ self.max_rec_workers = max_rec_workers
1385
1425
 
1386
1426
 
1387
1427
 
1388
1428
 
1389
1429
  def __str__(self):
1390
- return "RecognitionConfig(enabled={}, model_path={}, dict_path={}, rec_batch_num={})".format(self.enabled, self.model_path, self.dict_path, self.rec_batch_num)
1430
+ return "RecognitionConfig(enabled={}, model_path={}, dict_path={}, rec_batch_num={}, rec_workers={}, max_rec_workers={})".format(self.enabled, self.model_path, self.dict_path, self.rec_batch_num, self.rec_workers, self.max_rec_workers)
1391
1431
  def __eq__(self, other):
1392
1432
  if self.enabled != other.enabled:
1393
1433
  return False
@@ -1397,6 +1437,10 @@ class RecognitionConfig:
1397
1437
  return False
1398
1438
  if self.rec_batch_num != other.rec_batch_num:
1399
1439
  return False
1440
+ if self.rec_workers != other.rec_workers:
1441
+ return False
1442
+ if self.max_rec_workers != other.max_rec_workers:
1443
+ return False
1400
1444
  return True
1401
1445
 
1402
1446
  class _UniffiFfiConverterTypeRecognitionConfig(_UniffiConverterRustBuffer):
@@ -1407,6 +1451,8 @@ class _UniffiFfiConverterTypeRecognitionConfig(_UniffiConverterRustBuffer):
1407
1451
  model_path=_UniffiFfiConverterOptionalString.read(buf),
1408
1452
  dict_path=_UniffiFfiConverterOptionalString.read(buf),
1409
1453
  rec_batch_num=_UniffiFfiConverterOptionalInt32.read(buf),
1454
+ rec_workers=_UniffiFfiConverterOptionalUInt32.read(buf),
1455
+ max_rec_workers=_UniffiFfiConverterOptionalUInt32.read(buf),
1410
1456
  )
1411
1457
 
1412
1458
  @staticmethod
@@ -1415,6 +1461,8 @@ class _UniffiFfiConverterTypeRecognitionConfig(_UniffiConverterRustBuffer):
1415
1461
  _UniffiFfiConverterOptionalString.check_lower(value.model_path)
1416
1462
  _UniffiFfiConverterOptionalString.check_lower(value.dict_path)
1417
1463
  _UniffiFfiConverterOptionalInt32.check_lower(value.rec_batch_num)
1464
+ _UniffiFfiConverterOptionalUInt32.check_lower(value.rec_workers)
1465
+ _UniffiFfiConverterOptionalUInt32.check_lower(value.max_rec_workers)
1418
1466
 
1419
1467
  @staticmethod
1420
1468
  def write(value, buf):
@@ -1422,6 +1470,8 @@ class _UniffiFfiConverterTypeRecognitionConfig(_UniffiConverterRustBuffer):
1422
1470
  _UniffiFfiConverterOptionalString.write(value.model_path, buf)
1423
1471
  _UniffiFfiConverterOptionalString.write(value.dict_path, buf)
1424
1472
  _UniffiFfiConverterOptionalInt32.write(value.rec_batch_num, buf)
1473
+ _UniffiFfiConverterOptionalUInt32.write(value.rec_workers, buf)
1474
+ _UniffiFfiConverterOptionalUInt32.write(value.max_rec_workers, buf)
1425
1475
 
1426
1476
  class _UniffiFfiConverterOptionalTypeRecognitionConfig(_UniffiConverterRustBuffer):
1427
1477
  @classmethod
@@ -1871,44 +1921,6 @@ class _UniffiFfiConverterTypeOutputGranularity(_UniffiConverterRustBuffer):
1871
1921
 
1872
1922
 
1873
1923
 
1874
- class _UniffiFfiConverterUInt32(_UniffiConverterPrimitiveInt):
1875
- CLASS_NAME = "u32"
1876
- VALUE_MIN = 0
1877
- VALUE_MAX = 2**32
1878
-
1879
- @staticmethod
1880
- def read(buf):
1881
- return buf.read_u32()
1882
-
1883
- @staticmethod
1884
- def write(value, buf):
1885
- buf.write_u32(value)
1886
-
1887
- class _UniffiFfiConverterOptionalUInt32(_UniffiConverterRustBuffer):
1888
- @classmethod
1889
- def check_lower(cls, value):
1890
- if value is not None:
1891
- _UniffiFfiConverterUInt32.check_lower(value)
1892
-
1893
- @classmethod
1894
- def write(cls, value, buf):
1895
- if value is None:
1896
- buf.write_u8(0)
1897
- return
1898
-
1899
- buf.write_u8(1)
1900
- _UniffiFfiConverterUInt32.write(value, buf)
1901
-
1902
- @classmethod
1903
- def read(cls, buf):
1904
- flag = buf.read_u8()
1905
- if flag == 0:
1906
- return None
1907
- elif flag == 1:
1908
- return _UniffiFfiConverterUInt32.read(buf)
1909
- else:
1910
- raise InternalError("Unexpected flag byte for optional type")
1911
-
1912
1924
  @dataclass
1913
1925
  class PreDetectionOptions:
1914
1926
  """
@@ -2191,17 +2203,23 @@ class RecognitionRunOptions:
2191
2203
  """
2192
2204
  Text recognition inference parameters.
2193
2205
  """
2194
- def __init__(self, *, batch_size:typing.Optional[int]):
2206
+ def __init__(self, *, batch_size:typing.Optional[int], workers:typing.Optional[int], max_workers:typing.Optional[int]):
2195
2207
  self.batch_size = batch_size
2208
+ self.workers = workers
2209
+ self.max_workers = max_workers
2196
2210
 
2197
2211
 
2198
2212
 
2199
2213
 
2200
2214
  def __str__(self):
2201
- return "RecognitionRunOptions(batch_size={})".format(self.batch_size)
2215
+ return "RecognitionRunOptions(batch_size={}, workers={}, max_workers={})".format(self.batch_size, self.workers, self.max_workers)
2202
2216
  def __eq__(self, other):
2203
2217
  if self.batch_size != other.batch_size:
2204
2218
  return False
2219
+ if self.workers != other.workers:
2220
+ return False
2221
+ if self.max_workers != other.max_workers:
2222
+ return False
2205
2223
  return True
2206
2224
 
2207
2225
  class _UniffiFfiConverterTypeRecognitionRunOptions(_UniffiConverterRustBuffer):
@@ -2209,15 +2227,21 @@ class _UniffiFfiConverterTypeRecognitionRunOptions(_UniffiConverterRustBuffer):
2209
2227
  def read(buf):
2210
2228
  return RecognitionRunOptions(
2211
2229
  batch_size=_UniffiFfiConverterOptionalInt32.read(buf),
2230
+ workers=_UniffiFfiConverterOptionalUInt32.read(buf),
2231
+ max_workers=_UniffiFfiConverterOptionalUInt32.read(buf),
2212
2232
  )
2213
2233
 
2214
2234
  @staticmethod
2215
2235
  def check_lower(value):
2216
2236
  _UniffiFfiConverterOptionalInt32.check_lower(value.batch_size)
2237
+ _UniffiFfiConverterOptionalUInt32.check_lower(value.workers)
2238
+ _UniffiFfiConverterOptionalUInt32.check_lower(value.max_workers)
2217
2239
 
2218
2240
  @staticmethod
2219
2241
  def write(value, buf):
2220
2242
  _UniffiFfiConverterOptionalInt32.write(value.batch_size, buf)
2243
+ _UniffiFfiConverterOptionalUInt32.write(value.workers, buf)
2244
+ _UniffiFfiConverterOptionalUInt32.write(value.max_workers, buf)
2221
2245
 
2222
2246
  class _UniffiFfiConverterOptionalTypeRecognitionRunOptions(_UniffiConverterRustBuffer):
2223
2247
  @classmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rusto
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Pure Rust OCR library for Python based on RapidOCR, Indra OCR, and pure-Rust MNN (rusto-nn)
5
5
  Author-email: RustO Contributors <contact@rusto.dev>
6
6
  License-Expression: MIT
@@ -15,7 +15,7 @@ Official Python bindings for **RustO!** — a high-performance, pure-Rust OCR en
15
15
 
16
16
  ## Features
17
17
 
18
- - **Zero-Config Indra OCR**: `RustO.initialize()` automatically downloads and caches the default `indraocr` models on first run from Hugging Face Hub.
18
+ - **Zero-Config PP-OCRv6 Tiny Default**: `RustO.initialize()` automatically downloads and caches the default `ppocrv6-tiny` (FP32, Apache 2.0) models on first run from Hugging Face Hub.
19
19
  - **Context Manager Support**: Deterministic native resource cleanup with `with RustO.initialize() as engine:` or `engine.close()`.
20
20
  - **Full Document Pipeline**: Neural document unwarping (`with_auto_unwarp()`), page orientation detection (`with_orientation_detection()`), semantic layout analysis, and reading-order recovery (`with_layout_reading()`).
21
21
  - **Fluent Builder API**: Chain `.as_lines()`, `.as_words()`, `.as_spatial()`, `.as_tsv()`, or `.with_output(...)` into a single `.run()` terminal method.
@@ -32,7 +32,7 @@ pip install rusto
32
32
  ```python
33
33
  from rusto import RustO
34
34
 
35
- # Zero-config initialization (auto-downloads Indra OCR models from Hugging Face Hub if not cached)
35
+ # Zero-config initialization (auto-downloads PP-OCRv6 Tiny FP32 models from Hugging Face Hub if not cached)
36
36
  with RustO.initialize() as engine:
37
37
  result = (
38
38
  engine.detect_text("invoice.png")
@@ -110,7 +110,7 @@ Pre-download models into a target cache directory ahead of time (e.g., during Do
110
110
  ```python
111
111
  from rusto import download_model
112
112
 
113
- paths = download_model(model="indraocr", lang="latin", cache_dir="./models-cache")
113
+ paths = download_model(model="ppocrv6-tiny", cache_dir="./models-cache")
114
114
  ```
115
115
 
116
116
  ## Input Sources & Raw RGB Buffers
@@ -2,6 +2,7 @@ import unittest
2
2
  import sys
3
3
  import os
4
4
  import json
5
+ import re
5
6
  from pathlib import Path
6
7
 
7
8
  sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
@@ -133,7 +134,10 @@ class TestRustOPythonBinding(unittest.TestCase):
133
134
  self.assertTrue(callable(RustO.download_model))
134
135
  self.assertTrue(callable(version))
135
136
  self.assertTrue(callable(is_ready))
136
- self.assertEqual(version(), "0.4.0")
137
+ pyproject = Path(__file__).resolve().parents[1] / "pyproject.toml"
138
+ m = re.search(r'^version\s*=\s*"([^"]+)"', pyproject.read_text(encoding="utf-8"), re.MULTILINE)
139
+ self.assertIsNotNone(m)
140
+ self.assertEqual(version(), m.group(1))
137
141
 
138
142
  def test_chronam_ad_page_with_layout(self):
139
143
  root = Path(__file__).resolve().parents[3]
@@ -158,7 +162,7 @@ class TestRustOPythonBinding(unittest.TestCase):
158
162
  )
159
163
  res = engine.detect_text(ImageSource.PATH(str(img_path)), opts)
160
164
  self.assertTrue(res.is_STRUCTURED())
161
- self.assertEqual(len(res.results), 249)
165
+ self.assertEqual(len(res.results), 243)
162
166
  self.assertEqual(res.results[0].text, "All Orders in our")
163
167
  self.assertIsNotNone(res.results[0].region_id)
164
168
  self.assertIsNotNone(res.results[0].layout_type)
@@ -186,7 +190,7 @@ class TestRustOPythonBinding(unittest.TestCase):
186
190
  engine.detect_text(str(img_path))
187
191
  .with_layout_reading(True)
188
192
  .with_pre_detection(descreen_strength=0.1)
189
- .with_recognition(batch_size=6)
193
+ .with_recognition(batch_size=6, workers=2, max_workers=4)
190
194
  .with_post_recognition(score_threshold=0.5)
191
195
  .as_lines()
192
196
  )
Binary file
File without changes
File without changes
File without changes