cnocr 2.3.2.3__tar.gz → 2.3.3__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.
- {cnocr-2.3.2.3 → cnocr-2.3.3}/PKG-INFO +32 -15
- {cnocr-2.3.2.3 → cnocr-2.3.3}/README.md +30 -13
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/__init__.py +4 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/__version__.py +1 -1
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/cli.py +23 -3
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/cn_ocr.py +31 -5
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/ppocr/consts.py +16 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/ppocr/rapid_recognizer.py +106 -6
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/utils.py +57 -9
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr.egg-info/PKG-INFO +32 -15
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr.egg-info/requires.txt +2 -2
- {cnocr-2.3.2.3 → cnocr-2.3.3}/setup.py +3 -3
- {cnocr-2.3.2.3 → cnocr-2.3.3}/LICENSE +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/app.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/classification/__init__.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/classification/dataset.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/classification/image_classifier.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/clf_cli.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/consts.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/data_utils/__init__.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/data_utils/aug.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/data_utils/block_shuffle.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/data_utils/transforms.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/data_utils/utils.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/dataset.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/dataset_utils.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/gradio_app.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/gradio_app2.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/label_cn.txt +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/label_number.txt +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/line_split.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/lr_scheduler.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/models/__init__.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/models/ctc.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/models/densenet.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/models/mobilenet.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/models/ocr_model.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/ppocr/__init__.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/ppocr/postprocess/__init__.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/ppocr/postprocess/rec_postprocess.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/ppocr/pp_recognizer.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/ppocr/utility.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/ppocr/utils/__init__.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/ppocr/utils/chinese_cht_dict.txt +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/ppocr/utils/en_dict.txt +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/ppocr/utils/ppocr_keys_v1.txt +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/recognizer.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/serve.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr/trainer.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr.egg-info/SOURCES.txt +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr.egg-info/dependency_links.txt +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr.egg-info/entry_points.txt +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr.egg-info/not-zip-safe +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/cnocr.egg-info/top_level.txt +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/setup.cfg +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/tests/test_cnocr.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/tests/test_dataset.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/tests/test_models.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/tests/test_ppocr.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/tests/test_pytorch.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/tests/test_trainer.py +0 -0
- {cnocr-2.3.2.3 → cnocr-2.3.3}/tests/test_transforms.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cnocr
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.3
|
|
4
4
|
Summary: Python3 package for Chinese/English OCR, with small pretrained models
|
|
5
5
|
Home-page: https://github.com/breezedeus/cnocr
|
|
6
6
|
Author: breezedeus
|
|
@@ -16,11 +16,11 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
16
16
|
Classifier: Programming Language :: Python
|
|
17
17
|
Classifier: Programming Language :: Python :: Implementation
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.8
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.9
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
23
22
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
Provides-Extra: ort-cpu
|
|
26
26
|
Provides-Extra: ort-gpu
|
|
@@ -69,6 +69,17 @@ License-File: LICENSE
|
|
|
69
69
|
---
|
|
70
70
|
</div>
|
|
71
71
|
|
|
72
|
+
### Update 2026.07.04:发布 V2.3.3
|
|
73
|
+
|
|
74
|
+
主要变更:
|
|
75
|
+
|
|
76
|
+
* 基于 RapidOCR 支持 PP-OCRv6 多语种 OCR 模型
|
|
77
|
+
* 新增支持 PP-OCRv6 识别模型:`multi_PP-OCRv6_tiny`、`multi_PP-OCRv6`、`multi_PP-OCRv6_small` 和 `multi_PP-OCRv6_medium`
|
|
78
|
+
* 通过 CnSTD 新增支持 PP-OCRv6 检测模型:`multi_PP-OCRv6_det_tiny`、`multi_PP-OCRv6_det_small` 和 `multi_PP-OCRv6_det_medium`
|
|
79
|
+
* `CnOcr` 新增 `rec_lang_type` 参数,可为 RapidOCR v6 识别模型指定语言类型
|
|
80
|
+
* CLI 新增 `--rec-lang-type` 和 `--det-lang-type`,可为 RapidOCR v6 模型指定语言类型
|
|
81
|
+
|
|
82
|
+
|
|
72
83
|
### Update 2025.06.26:发布 V2.3.2
|
|
73
84
|
|
|
74
85
|
主要变更:
|
|
@@ -192,13 +203,13 @@ print(out)
|
|
|
192
203
|
|
|
193
204
|
### 竖排文字识别
|
|
194
205
|
|
|
195
|
-
采用来自 [**PaddleOCR**](https://github.com/PaddlePaddle/PaddleOCR)(之后简称 **ppocr
|
|
206
|
+
采用来自 [**PaddleOCR**](https://github.com/PaddlePaddle/PaddleOCR)(之后简称 **ppocr**)的 PP-OCRv6 多语种识别模型 `rec_model_name='multi_PP-OCRv6'` 进行识别。
|
|
196
207
|
|
|
197
208
|
```python
|
|
198
209
|
from cnocr import CnOcr
|
|
199
210
|
|
|
200
211
|
img_fp = './docs/examples/shupai.png'
|
|
201
|
-
ocr = CnOcr(rec_model_name='
|
|
212
|
+
ocr = CnOcr(rec_model_name='multi_PP-OCRv6')
|
|
202
213
|
out = ocr.ocr(img_fp)
|
|
203
214
|
|
|
204
215
|
print(out)
|
|
@@ -233,31 +244,27 @@ print(out)
|
|
|
233
244
|
|
|
234
245
|
### 繁体中文识别
|
|
235
246
|
|
|
236
|
-
采用来自ppocr
|
|
247
|
+
采用来自 ppocr 的 PP-OCRv6 多语种识别模型,并通过 `rec_lang_type='chinese_cht'` 指定繁体中文进行识别。
|
|
237
248
|
|
|
238
249
|
```python
|
|
239
250
|
from cnocr import CnOcr
|
|
240
251
|
|
|
241
252
|
img_fp = './docs/examples/fanti.jpg'
|
|
242
|
-
ocr = CnOcr(rec_model_name='
|
|
253
|
+
ocr = CnOcr(rec_model_name='multi_PP-OCRv6', rec_lang_type='chinese_cht')
|
|
243
254
|
out = ocr.ocr(img_fp)
|
|
244
255
|
|
|
245
256
|
print(out)
|
|
246
257
|
```
|
|
247
258
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
* 识别精度一般,不是很好;
|
|
251
|
-
|
|
252
|
-
* 除了繁体字,对标点、英文、数字的识别都不好;
|
|
253
|
-
|
|
254
|
-
* 此模型不支持竖排文字的识别。
|
|
259
|
+
`multi_PP-OCRv6` 是 `multi_PP-OCRv6_small` 的别名;`chinese_cht` 是繁体中文对应的 `lang_type`。
|
|
255
260
|
|
|
256
261
|
识别结果:
|
|
257
262
|
<div align="center">
|
|
258
263
|
<img src="./docs/predict-outputs/fanti.jpg-result.jpg" alt="繁体中文识别" width="700px"/>
|
|
259
264
|
</div>
|
|
260
265
|
|
|
266
|
+
注:上图中的识别结果来自 V3 模型;V6 模型的识别效果已经有显著增强。
|
|
267
|
+
|
|
261
268
|
|
|
262
269
|
### 单行文字的图片识别
|
|
263
270
|
|
|
@@ -331,7 +338,7 @@ $ pip install cnocr[ort-cpu] -i https://mirrors.aliyun.com/pypi/simple
|
|
|
331
338
|
|
|
332
339
|
> **Note**
|
|
333
340
|
>
|
|
334
|
-
> 请使用 **
|
|
341
|
+
> 请使用 **Python 3.8 或更高版本**。
|
|
335
342
|
|
|
336
343
|
更多说明可见 [安装文档](https://cnocr.readthedocs.io/zh-cn/stable/install/)。
|
|
337
344
|
|
|
@@ -428,13 +435,18 @@ print(ocr_out)
|
|
|
428
435
|
| db_mobilenet_v3_small | √ | X | cnocr | 7.9 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
429
436
|
| db_resnet34 | √ | X | cnocr | 86 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
430
437
|
| db_resnet18 | √ | X | cnocr | 47 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
438
|
+
| multi_PP-OCRv6_det_tiny | X | √ | ppocr | 1.7 M | 多语种(不含日文) | √ |
|
|
439
|
+
| multi_PP-OCRv6_det_small | X | √ | ppocr | 9.5 M | 多语种 | √ |
|
|
440
|
+
| multi_PP-OCRv6_det_medium | X | √ | ppocr | 59 M | 多语种 | √ |
|
|
431
441
|
| ch_PP-OCRv5_det | X | √ | ppocr | 4.6 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
432
|
-
| ch_PP-OCRv5_det_server | X | √ | ppocr | 84 M
|
|
442
|
+
| ch_PP-OCRv5_det_server | X | √ | ppocr | 84 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
433
443
|
| ch_PP-OCRv4_det | X | √ | ppocr | 4.5 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
434
444
|
| ch_PP-OCRv4_det_server | X | √ | ppocr | 108 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
435
445
|
| ch_PP-OCRv3_det | X | √ | ppocr | 2.3 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
436
446
|
| **en_PP-OCRv3_det** | X | √ | ppocr | 2.3 M | **英文**、数字 | √ |
|
|
437
447
|
|
|
448
|
+
PP-OCRv6 的 `multi_PP-OCRv6_det_small` 和 `multi_PP-OCRv6_det_medium` 支持的 `lang_type` 包括:`ch`, `chinese_cht`, `en`, `japan`, `af`, `az`, `bs`, `ca`, `cs`, `cy`, `da`, `de`, `es`, `et`, `eu`, `fi`, `fr`, `ga`, `gl`, `hr`, `hu`, `id`, `is`, `it`, `ku`, `la`, `lb`, `lt`, `lv`, `mi`, `ms`, `mt`, `nl`, `no`, `oc`, `pl`, `pt`, `qu`, `rm`, `ro`, `rs_latin`, `sk`, `sl`, `sq`, `sv`, `sw`, `tl`, `tr`, `uz`, `vi`, `french`, `german`;`multi_PP-OCRv6_det_tiny` 不支持 `japan`。`multi` 是模型族名称,不是可传入的 `lang_type`。
|
|
449
|
+
|
|
438
450
|
|
|
439
451
|
|
|
440
452
|
### 可使用的识别模型
|
|
@@ -469,6 +481,9 @@ print(ocr_out)
|
|
|
469
481
|
| **number-densenet_lite_136-fc** 🆕 | √ | √ | cnocr | 2.7 M | **纯数字**(仅包含 `0~9` 十个数字) | X |
|
|
470
482
|
| **number-densenet_lite_136-gru** 🆕 <br /> ([星球会员](https://t.zsxq.com/FEYZRJQ)专享) | √ | √ | cnocr | 5.5 M | **纯数字**(仅包含 `0~9` 十个数字) | X |
|
|
471
483
|
| **number-densenet_lite_666-gru_large** 🆕 <br />(购买链接:[B站](https://mall.bilibili.com/neul-next/detailuniversal/detail.html?isMerchant=1&page=detailuniversal_detail&saleType=10&itemsId=11884155&loadingShow=1&noTitleBar=1&msource=merchant_share)、[Lemon Squeezy](https://ocr.lemonsqueezy.com/)) | √ | √ | cnocr | 55 M | **纯数字**(仅包含 `0~9` 十个数字) | X |
|
|
484
|
+
| multi_PP-OCRv6_tiny | X | √ | ppocr | 4.3 M | 多语种(不含日文) | √ |
|
|
485
|
+
| multi_PP-OCRv6 / multi_PP-OCRv6_small | X | √ | ppocr | 20 M | 多语种 | √ |
|
|
486
|
+
| multi_PP-OCRv6_medium | X | √ | ppocr | 73 M | 多语种 | √ |
|
|
472
487
|
| ch_PP-OCRv5 | X | √ | ppocr | 16 M | 简体中文、英文、数字 | √ |
|
|
473
488
|
| ch_PP-OCRv5_server | X | √ | ppocr | 81 M | 简体中文、英文、数字 | √ |
|
|
474
489
|
| ch_PP-OCRv4 | X | √ | ppocr | 10 M | 简体中文、英文、数字 | √ |
|
|
@@ -484,6 +499,8 @@ print(ocr_out)
|
|
|
484
499
|
| latin_PP-OCRv3 | X | √ | ppocr | 8.6 M | **拉丁文**、英文、数字 | √ |
|
|
485
500
|
| arabic_PP-OCRv3 | X | √ | ppocr | 8.6 M | **阿拉伯文**、英文、数字 | √ |
|
|
486
501
|
|
|
502
|
+
PP-OCRv6 的 `multi_PP-OCRv6_small`、`multi_PP-OCRv6_medium` 支持的 `lang_type` 包括:`ch`, `chinese_cht`, `en`, `japan`, `af`, `az`, `bs`, `ca`, `cs`, `cy`, `da`, `de`, `es`, `et`, `eu`, `fi`, `fr`, `ga`, `gl`, `hr`, `hu`, `id`, `is`, `it`, `ku`, `la`, `lb`, `lt`, `lv`, `mi`, `ms`, `mt`, `nl`, `no`, `oc`, `pl`, `pt`, `qu`, `rm`, `ro`, `rs_latin`, `sk`, `sl`, `sq`, `sv`, `sw`, `tl`, `tr`, `uz`, `vi`, `french`, `german`;`multi_PP-OCRv6_tiny` 不支持 `japan`。`multi_PP-OCRv6` 是 `multi_PP-OCRv6_small` 的别名;`multi` 是模型族名称,不是可传入的 `lang_type`。
|
|
503
|
+
|
|
487
504
|
|
|
488
505
|
|
|
489
506
|
## 未来工作
|
|
@@ -39,6 +39,17 @@
|
|
|
39
39
|
---
|
|
40
40
|
</div>
|
|
41
41
|
|
|
42
|
+
### Update 2026.07.04:发布 V2.3.3
|
|
43
|
+
|
|
44
|
+
主要变更:
|
|
45
|
+
|
|
46
|
+
* 基于 RapidOCR 支持 PP-OCRv6 多语种 OCR 模型
|
|
47
|
+
* 新增支持 PP-OCRv6 识别模型:`multi_PP-OCRv6_tiny`、`multi_PP-OCRv6`、`multi_PP-OCRv6_small` 和 `multi_PP-OCRv6_medium`
|
|
48
|
+
* 通过 CnSTD 新增支持 PP-OCRv6 检测模型:`multi_PP-OCRv6_det_tiny`、`multi_PP-OCRv6_det_small` 和 `multi_PP-OCRv6_det_medium`
|
|
49
|
+
* `CnOcr` 新增 `rec_lang_type` 参数,可为 RapidOCR v6 识别模型指定语言类型
|
|
50
|
+
* CLI 新增 `--rec-lang-type` 和 `--det-lang-type`,可为 RapidOCR v6 模型指定语言类型
|
|
51
|
+
|
|
52
|
+
|
|
42
53
|
### Update 2025.06.26:发布 V2.3.2
|
|
43
54
|
|
|
44
55
|
主要变更:
|
|
@@ -162,13 +173,13 @@ print(out)
|
|
|
162
173
|
|
|
163
174
|
### 竖排文字识别
|
|
164
175
|
|
|
165
|
-
采用来自 [**PaddleOCR**](https://github.com/PaddlePaddle/PaddleOCR)(之后简称 **ppocr
|
|
176
|
+
采用来自 [**PaddleOCR**](https://github.com/PaddlePaddle/PaddleOCR)(之后简称 **ppocr**)的 PP-OCRv6 多语种识别模型 `rec_model_name='multi_PP-OCRv6'` 进行识别。
|
|
166
177
|
|
|
167
178
|
```python
|
|
168
179
|
from cnocr import CnOcr
|
|
169
180
|
|
|
170
181
|
img_fp = './docs/examples/shupai.png'
|
|
171
|
-
ocr = CnOcr(rec_model_name='
|
|
182
|
+
ocr = CnOcr(rec_model_name='multi_PP-OCRv6')
|
|
172
183
|
out = ocr.ocr(img_fp)
|
|
173
184
|
|
|
174
185
|
print(out)
|
|
@@ -203,31 +214,27 @@ print(out)
|
|
|
203
214
|
|
|
204
215
|
### 繁体中文识别
|
|
205
216
|
|
|
206
|
-
采用来自ppocr
|
|
217
|
+
采用来自 ppocr 的 PP-OCRv6 多语种识别模型,并通过 `rec_lang_type='chinese_cht'` 指定繁体中文进行识别。
|
|
207
218
|
|
|
208
219
|
```python
|
|
209
220
|
from cnocr import CnOcr
|
|
210
221
|
|
|
211
222
|
img_fp = './docs/examples/fanti.jpg'
|
|
212
|
-
ocr = CnOcr(rec_model_name='
|
|
223
|
+
ocr = CnOcr(rec_model_name='multi_PP-OCRv6', rec_lang_type='chinese_cht')
|
|
213
224
|
out = ocr.ocr(img_fp)
|
|
214
225
|
|
|
215
226
|
print(out)
|
|
216
227
|
```
|
|
217
228
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
* 识别精度一般,不是很好;
|
|
221
|
-
|
|
222
|
-
* 除了繁体字,对标点、英文、数字的识别都不好;
|
|
223
|
-
|
|
224
|
-
* 此模型不支持竖排文字的识别。
|
|
229
|
+
`multi_PP-OCRv6` 是 `multi_PP-OCRv6_small` 的别名;`chinese_cht` 是繁体中文对应的 `lang_type`。
|
|
225
230
|
|
|
226
231
|
识别结果:
|
|
227
232
|
<div align="center">
|
|
228
233
|
<img src="./docs/predict-outputs/fanti.jpg-result.jpg" alt="繁体中文识别" width="700px"/>
|
|
229
234
|
</div>
|
|
230
235
|
|
|
236
|
+
注:上图中的识别结果来自 V3 模型;V6 模型的识别效果已经有显著增强。
|
|
237
|
+
|
|
231
238
|
|
|
232
239
|
### 单行文字的图片识别
|
|
233
240
|
|
|
@@ -301,7 +308,7 @@ $ pip install cnocr[ort-cpu] -i https://mirrors.aliyun.com/pypi/simple
|
|
|
301
308
|
|
|
302
309
|
> **Note**
|
|
303
310
|
>
|
|
304
|
-
> 请使用 **
|
|
311
|
+
> 请使用 **Python 3.8 或更高版本**。
|
|
305
312
|
|
|
306
313
|
更多说明可见 [安装文档](https://cnocr.readthedocs.io/zh-cn/stable/install/)。
|
|
307
314
|
|
|
@@ -398,13 +405,18 @@ print(ocr_out)
|
|
|
398
405
|
| db_mobilenet_v3_small | √ | X | cnocr | 7.9 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
399
406
|
| db_resnet34 | √ | X | cnocr | 86 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
400
407
|
| db_resnet18 | √ | X | cnocr | 47 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
408
|
+
| multi_PP-OCRv6_det_tiny | X | √ | ppocr | 1.7 M | 多语种(不含日文) | √ |
|
|
409
|
+
| multi_PP-OCRv6_det_small | X | √ | ppocr | 9.5 M | 多语种 | √ |
|
|
410
|
+
| multi_PP-OCRv6_det_medium | X | √ | ppocr | 59 M | 多语种 | √ |
|
|
401
411
|
| ch_PP-OCRv5_det | X | √ | ppocr | 4.6 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
402
|
-
| ch_PP-OCRv5_det_server | X | √ | ppocr | 84 M
|
|
412
|
+
| ch_PP-OCRv5_det_server | X | √ | ppocr | 84 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
403
413
|
| ch_PP-OCRv4_det | X | √ | ppocr | 4.5 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
404
414
|
| ch_PP-OCRv4_det_server | X | √ | ppocr | 108 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
405
415
|
| ch_PP-OCRv3_det | X | √ | ppocr | 2.3 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
406
416
|
| **en_PP-OCRv3_det** | X | √ | ppocr | 2.3 M | **英文**、数字 | √ |
|
|
407
417
|
|
|
418
|
+
PP-OCRv6 的 `multi_PP-OCRv6_det_small` 和 `multi_PP-OCRv6_det_medium` 支持的 `lang_type` 包括:`ch`, `chinese_cht`, `en`, `japan`, `af`, `az`, `bs`, `ca`, `cs`, `cy`, `da`, `de`, `es`, `et`, `eu`, `fi`, `fr`, `ga`, `gl`, `hr`, `hu`, `id`, `is`, `it`, `ku`, `la`, `lb`, `lt`, `lv`, `mi`, `ms`, `mt`, `nl`, `no`, `oc`, `pl`, `pt`, `qu`, `rm`, `ro`, `rs_latin`, `sk`, `sl`, `sq`, `sv`, `sw`, `tl`, `tr`, `uz`, `vi`, `french`, `german`;`multi_PP-OCRv6_det_tiny` 不支持 `japan`。`multi` 是模型族名称,不是可传入的 `lang_type`。
|
|
419
|
+
|
|
408
420
|
|
|
409
421
|
|
|
410
422
|
### 可使用的识别模型
|
|
@@ -439,6 +451,9 @@ print(ocr_out)
|
|
|
439
451
|
| **number-densenet_lite_136-fc** 🆕 | √ | √ | cnocr | 2.7 M | **纯数字**(仅包含 `0~9` 十个数字) | X |
|
|
440
452
|
| **number-densenet_lite_136-gru** 🆕 <br /> ([星球会员](https://t.zsxq.com/FEYZRJQ)专享) | √ | √ | cnocr | 5.5 M | **纯数字**(仅包含 `0~9` 十个数字) | X |
|
|
441
453
|
| **number-densenet_lite_666-gru_large** 🆕 <br />(购买链接:[B站](https://mall.bilibili.com/neul-next/detailuniversal/detail.html?isMerchant=1&page=detailuniversal_detail&saleType=10&itemsId=11884155&loadingShow=1&noTitleBar=1&msource=merchant_share)、[Lemon Squeezy](https://ocr.lemonsqueezy.com/)) | √ | √ | cnocr | 55 M | **纯数字**(仅包含 `0~9` 十个数字) | X |
|
|
454
|
+
| multi_PP-OCRv6_tiny | X | √ | ppocr | 4.3 M | 多语种(不含日文) | √ |
|
|
455
|
+
| multi_PP-OCRv6 / multi_PP-OCRv6_small | X | √ | ppocr | 20 M | 多语种 | √ |
|
|
456
|
+
| multi_PP-OCRv6_medium | X | √ | ppocr | 73 M | 多语种 | √ |
|
|
442
457
|
| ch_PP-OCRv5 | X | √ | ppocr | 16 M | 简体中文、英文、数字 | √ |
|
|
443
458
|
| ch_PP-OCRv5_server | X | √ | ppocr | 81 M | 简体中文、英文、数字 | √ |
|
|
444
459
|
| ch_PP-OCRv4 | X | √ | ppocr | 10 M | 简体中文、英文、数字 | √ |
|
|
@@ -454,6 +469,8 @@ print(ocr_out)
|
|
|
454
469
|
| latin_PP-OCRv3 | X | √ | ppocr | 8.6 M | **拉丁文**、英文、数字 | √ |
|
|
455
470
|
| arabic_PP-OCRv3 | X | √ | ppocr | 8.6 M | **阿拉伯文**、英文、数字 | √ |
|
|
456
471
|
|
|
472
|
+
PP-OCRv6 的 `multi_PP-OCRv6_small`、`multi_PP-OCRv6_medium` 支持的 `lang_type` 包括:`ch`, `chinese_cht`, `en`, `japan`, `af`, `az`, `bs`, `ca`, `cs`, `cy`, `da`, `de`, `es`, `et`, `eu`, `fi`, `fr`, `ga`, `gl`, `hr`, `hu`, `id`, `is`, `it`, `ku`, `la`, `lb`, `lt`, `lv`, `mi`, `ms`, `mt`, `nl`, `no`, `oc`, `pl`, `pt`, `qu`, `rm`, `ro`, `rs_latin`, `sk`, `sl`, `sq`, `sv`, `sw`, `tl`, `tr`, `uz`, `vi`, `french`, `german`;`multi_PP-OCRv6_tiny` 不支持 `japan`。`multi_PP-OCRv6` 是 `multi_PP-OCRv6_small` 的别名;`multi` 是模型族名称,不是可传入的 `lang_type`。
|
|
473
|
+
|
|
457
474
|
|
|
458
475
|
|
|
459
476
|
## 未来工作
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
# specific language governing permissions and limitations
|
|
18
18
|
# under the License.
|
|
19
19
|
|
|
20
|
+
import logging
|
|
21
|
+
|
|
22
|
+
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
|
23
|
+
|
|
20
24
|
from cnstd.consts import AVAILABLE_MODELS as DET_AVAILABLE_MODELS
|
|
21
25
|
from cnstd.utils import pil_to_numpy
|
|
22
26
|
|
|
@@ -54,6 +54,7 @@ _CONTEXT_SETTINGS = {"help_option_names": ['-h', '--help']}
|
|
|
54
54
|
logger = set_logger(log_level=logging.INFO)
|
|
55
55
|
|
|
56
56
|
DEFAULT_MODEL_NAME = 'densenet_lite_136-gru'
|
|
57
|
+
DEFAULT_DET_MODEL_NAME = 'multi_PP-OCRv6_det_small'
|
|
57
58
|
LEGAL_MODEL_NAMES = {
|
|
58
59
|
enc_name + '-' + dec_name
|
|
59
60
|
for enc_name in ENCODER_CONFIGS.keys()
|
|
@@ -211,12 +212,18 @@ def visualize_example(example, fp_prefix):
|
|
|
211
212
|
default=None,
|
|
212
213
|
help='识别模型使用的词表。默认取值为 `None` 表示使用系统设定的词表',
|
|
213
214
|
)
|
|
215
|
+
@click.option(
|
|
216
|
+
'--rec-lang-type',
|
|
217
|
+
type=str,
|
|
218
|
+
default=None,
|
|
219
|
+
help='RapidOCR识别模型的语言类型;PP-OCRv6支持如 ch、en、japan、french、german 等。默认值为 `None`',
|
|
220
|
+
)
|
|
214
221
|
@click.option(
|
|
215
222
|
'-d',
|
|
216
223
|
'--det-model-name',
|
|
217
224
|
type=str,
|
|
218
|
-
default=
|
|
219
|
-
help='检测模型名称。默认值为
|
|
225
|
+
default=DEFAULT_DET_MODEL_NAME,
|
|
226
|
+
help='检测模型名称。默认值为 %s' % DEFAULT_DET_MODEL_NAME,
|
|
220
227
|
)
|
|
221
228
|
@click.option(
|
|
222
229
|
'--det-model-backend',
|
|
@@ -224,6 +231,12 @@ def visualize_example(example, fp_prefix):
|
|
|
224
231
|
default='onnx',
|
|
225
232
|
help='检测模型类型。默认值为 `onnx`',
|
|
226
233
|
)
|
|
234
|
+
@click.option(
|
|
235
|
+
'--det-lang-type',
|
|
236
|
+
type=str,
|
|
237
|
+
default=None,
|
|
238
|
+
help='RapidOCR检测模型的语言类型;PP-OCRv6支持如 ch、en、japan、french、german 等。默认值为 `None`',
|
|
239
|
+
)
|
|
227
240
|
@click.option(
|
|
228
241
|
'--det-resized-shape', type=int, default=768, help='检测模型输入图像尺寸。默认值为 768',
|
|
229
242
|
)
|
|
@@ -264,8 +277,10 @@ def predict(
|
|
|
264
277
|
rec_model_name,
|
|
265
278
|
rec_model_backend,
|
|
266
279
|
rec_vocab_fp,
|
|
280
|
+
rec_lang_type,
|
|
267
281
|
det_model_name,
|
|
268
282
|
det_model_backend,
|
|
283
|
+
det_lang_type,
|
|
269
284
|
det_resized_shape,
|
|
270
285
|
pretrained_model_fp,
|
|
271
286
|
context,
|
|
@@ -295,6 +310,10 @@ def predict(
|
|
|
295
310
|
if len(fp_list) == 0:
|
|
296
311
|
raise ValueError(f'No image is found from "{img_file_or_dir}".')
|
|
297
312
|
|
|
313
|
+
det_more_configs = {}
|
|
314
|
+
if det_lang_type is not None:
|
|
315
|
+
det_more_configs['lang_type'] = det_lang_type
|
|
316
|
+
|
|
298
317
|
ocr = CnOcr(
|
|
299
318
|
rec_model_name=rec_model_name,
|
|
300
319
|
rec_model_backend=rec_model_backend,
|
|
@@ -302,8 +321,9 @@ def predict(
|
|
|
302
321
|
det_model_name=det_model_name,
|
|
303
322
|
det_model_backend=det_model_backend,
|
|
304
323
|
rec_model_fp=pretrained_model_fp,
|
|
324
|
+
rec_lang_type=rec_lang_type,
|
|
325
|
+
det_more_configs=det_more_configs,
|
|
305
326
|
context=context,
|
|
306
|
-
# det_more_configs={'rotated_bbox': False},
|
|
307
327
|
)
|
|
308
328
|
ocr_func = ocr.ocr_for_single_line if single_line else ocr.ocr
|
|
309
329
|
ocr_kwargs = {}
|
|
@@ -27,12 +27,16 @@ from pathlib import Path
|
|
|
27
27
|
import numpy as np
|
|
28
28
|
import torch
|
|
29
29
|
from PIL import Image
|
|
30
|
+
from rapidocr import LangRec
|
|
30
31
|
from cnstd.consts import AVAILABLE_MODELS as DET_AVAILABLE_MODELS
|
|
31
32
|
from cnstd import CnStd
|
|
32
33
|
from cnstd.utils import data_dir as det_data_dir
|
|
33
34
|
|
|
34
35
|
from .consts import AVAILABLE_MODELS as REC_AVAILABLE_MODELS
|
|
35
|
-
from .utils import
|
|
36
|
+
from .utils import (
|
|
37
|
+
data_dir,
|
|
38
|
+
read_img,
|
|
39
|
+
)
|
|
36
40
|
from .line_split import line_split
|
|
37
41
|
from .recognizer import Recognizer
|
|
38
42
|
from .ppocr import PPRecognizer, RapidRecognizer, PP_SPACE
|
|
@@ -64,12 +68,13 @@ class CnOcr(object):
|
|
|
64
68
|
self,
|
|
65
69
|
rec_model_name: str = 'densenet_lite_136-gru',
|
|
66
70
|
*,
|
|
67
|
-
det_model_name: str = '
|
|
71
|
+
det_model_name: str = 'multi_PP-OCRv6_det_small',
|
|
68
72
|
cand_alphabet: Optional[Union[Collection, str]] = None,
|
|
69
73
|
context: str = 'cpu', # ['cpu', 'gpu', 'cuda']
|
|
70
74
|
rec_model_fp: Optional[str] = None,
|
|
71
75
|
rec_model_backend: str = 'onnx', # ['pytorch', 'onnx']
|
|
72
76
|
rec_vocab_fp: Optional[Union[str, Path]] = None,
|
|
77
|
+
rec_lang_type: Optional[Union[str, LangRec]] = None,
|
|
73
78
|
rec_more_configs: Optional[Dict[str, Any]] = None,
|
|
74
79
|
rec_root: Union[str, Path] = data_dir(),
|
|
75
80
|
det_model_fp: Optional[str] = None,
|
|
@@ -83,7 +88,7 @@ class CnOcr(object):
|
|
|
83
88
|
|
|
84
89
|
Args:
|
|
85
90
|
rec_model_name (str): 识别模型名称。默认为 `densenet_lite_136-gru`
|
|
86
|
-
det_model_name (str): 检测模型名称。默认为 `
|
|
91
|
+
det_model_name (str): 检测模型名称。默认为 `multi_PP-OCRv6_det_small`
|
|
87
92
|
cand_alphabet (Optional[Union[Collection, str]]): 待识别字符所在的候选集合。默认为 `None`,表示不限定识别字符范围
|
|
88
93
|
context (str): 'cpu', or 'gpu'。表明预测时是使用CPU还是GPU。默认为 `cpu`。
|
|
89
94
|
此参数仅在 `model_backend=='pytorch'` 时有效。
|
|
@@ -92,6 +97,8 @@ class CnOcr(object):
|
|
|
92
97
|
同样的模型,ONNX 版本的预测速度一般是 PyTorch 版本的2倍左右。默认为 'onnx'。
|
|
93
98
|
rec_vocab_fp (Optional[Union[str, Path]]): 识别字符集合的文件路径,即 `label_cn.txt` 文件路径。取值为 `None` 表示使用系统设定的词表。
|
|
94
99
|
若训练的自有模型更改了字符集,看通过此参数传入新的字符集文件路径。
|
|
100
|
+
rec_lang_type (Optional[Union[str, LangRec]]): RapidOCR识别模型语言类型。PP-OCRv6
|
|
101
|
+
需传入具体语言,如 `ch`、`en`、`japan`、`french` 等;默认为中文。
|
|
95
102
|
rec_more_configs (Optional[Dict[str, Any]]): 识别模型初始化时传入的其他参数。
|
|
96
103
|
rec_root (Union[str, Path]): 识别模型文件所在的根目录。
|
|
97
104
|
Linux/Mac下默认值为 `~/.cnocr`,表示模型文件所处文件夹类似 `~/.cnocr/2.3/densenet_lite_136-gru`。
|
|
@@ -101,7 +108,7 @@ class CnOcr(object):
|
|
|
101
108
|
同样的模型,ONNX 版本的预测速度一般是 PyTorch 版本的2倍左右。默认为 'onnx'。
|
|
102
109
|
det_more_configs (Optional[Dict[str, Any]]): 识别模型初始化时传入的其他参数。
|
|
103
110
|
det_root: 检测模型文件所在的根目录。
|
|
104
|
-
Linux/Mac下默认值为 `~/.cnstd`,表示模型文件所处文件夹类似 `~/.cnstd/1.2/
|
|
111
|
+
Linux/Mac下默认值为 `~/.cnstd`,表示模型文件所处文件夹类似 `~/.cnstd/1.2/ppocr/multi_PP-OCRv6_det_small`
|
|
105
112
|
Windows下默认值为 `C:/Users/<username>/AppData/Roaming/cnstd`。
|
|
106
113
|
**kwargs: 目前未被使用。
|
|
107
114
|
|
|
@@ -152,7 +159,14 @@ class CnOcr(object):
|
|
|
152
159
|
'%s is not supported currently' % ((rec_model_name, rec_model_backend),)
|
|
153
160
|
)
|
|
154
161
|
|
|
155
|
-
rec_more_configs = rec_more_configs or
|
|
162
|
+
rec_more_configs = dict(rec_more_configs or {})
|
|
163
|
+
if rec_lang_type is not None:
|
|
164
|
+
if 'lang_type' in rec_more_configs:
|
|
165
|
+
logger.warning(
|
|
166
|
+
'both `rec_lang_type` and `rec_more_configs["lang_type"]` are set; '
|
|
167
|
+
'`rec_lang_type` will be used'
|
|
168
|
+
)
|
|
169
|
+
rec_more_configs['lang_type'] = rec_lang_type
|
|
156
170
|
self.rec_model = rec_cls(
|
|
157
171
|
model_name=rec_model_name,
|
|
158
172
|
model_backend=rec_model_backend,
|
|
@@ -163,6 +177,18 @@ class CnOcr(object):
|
|
|
163
177
|
vocab_fp=rec_vocab_fp,
|
|
164
178
|
**rec_more_configs,
|
|
165
179
|
)
|
|
180
|
+
rec_effective_lang_type = getattr(self.rec_model, '_lang_type', None)
|
|
181
|
+
rec_effective_lang_type = getattr(
|
|
182
|
+
rec_effective_lang_type, 'value', rec_effective_lang_type
|
|
183
|
+
)
|
|
184
|
+
logger.info(
|
|
185
|
+
'use rec model: name=%s, backend=%s, lang_type=%s, fp=%s',
|
|
186
|
+
rec_model_name,
|
|
187
|
+
rec_model_backend,
|
|
188
|
+
rec_effective_lang_type,
|
|
189
|
+
getattr(self.rec_model, '_model_fp', rec_model_fp),
|
|
190
|
+
extra={'log_color': 'green'},
|
|
191
|
+
)
|
|
166
192
|
|
|
167
193
|
self.det_model = None
|
|
168
194
|
if det_model_name in DET_MODLE_NAMES:
|
|
@@ -81,6 +81,22 @@ MODEL_LABELS_FILE_DICT = {
|
|
|
81
81
|
"recognizer": "RapidRecognizer",
|
|
82
82
|
"repo": "breezedeus/cnocr-ppocr-ch_PP-OCRv5_server",
|
|
83
83
|
},
|
|
84
|
+
("multi_PP-OCRv6_tiny", "onnx"): {
|
|
85
|
+
"recognizer": "RapidRecognizer",
|
|
86
|
+
"repo": "breezedeus/cnocr-ppocr-multi_PP-OCRv6_tiny",
|
|
87
|
+
},
|
|
88
|
+
("multi_PP-OCRv6", "onnx"): {
|
|
89
|
+
"recognizer": "RapidRecognizer",
|
|
90
|
+
"repo": "breezedeus/cnocr-ppocr-multi_PP-OCRv6",
|
|
91
|
+
},
|
|
92
|
+
("multi_PP-OCRv6_small", "onnx"): {
|
|
93
|
+
"recognizer": "RapidRecognizer",
|
|
94
|
+
"repo": "breezedeus/cnocr-ppocr-multi_PP-OCRv6_small",
|
|
95
|
+
},
|
|
96
|
+
("multi_PP-OCRv6_medium", "onnx"): {
|
|
97
|
+
"recognizer": "RapidRecognizer",
|
|
98
|
+
"repo": "breezedeus/cnocr-ppocr-multi_PP-OCRv6_medium",
|
|
99
|
+
},
|
|
84
100
|
}
|
|
85
101
|
|
|
86
102
|
PP_SPACE = "ppocr"
|
|
@@ -7,10 +7,12 @@ import os
|
|
|
7
7
|
import logging
|
|
8
8
|
from typing import Union, Optional, List, Tuple
|
|
9
9
|
from pathlib import Path
|
|
10
|
+
from copy import deepcopy
|
|
10
11
|
|
|
11
12
|
import numpy as np
|
|
12
13
|
from rapidocr import EngineType, LangRec, ModelType, OCRVersion
|
|
13
14
|
from rapidocr.utils.typings import TaskType
|
|
15
|
+
from rapidocr.utils.model_resolver import resolve_model_key
|
|
14
16
|
from rapidocr.ch_ppocr_rec import TextRecognizer, TextRecInput
|
|
15
17
|
from cnstd.utils import prepare_model_files
|
|
16
18
|
|
|
@@ -32,6 +34,8 @@ class Config(dict):
|
|
|
32
34
|
"task_type": TaskType.REC,
|
|
33
35
|
"model_path": None,
|
|
34
36
|
"model_dir": None,
|
|
37
|
+
"model_root_dir": None,
|
|
38
|
+
"font_path": None,
|
|
35
39
|
"rec_keys_path": None,
|
|
36
40
|
"rec_img_shape": [3, 48, 320],
|
|
37
41
|
"rec_batch_num": 6,
|
|
@@ -88,6 +92,7 @@ class RapidRecognizer(Recognizer):
|
|
|
88
92
|
root: Union[str, Path] = data_dir(),
|
|
89
93
|
context: str = "cpu", # ['cpu', 'gpu']
|
|
90
94
|
rec_image_shape: str = "3, 48, 320",
|
|
95
|
+
lang_type: Optional[Union[str, LangRec]] = None,
|
|
91
96
|
**kwargs
|
|
92
97
|
):
|
|
93
98
|
"""
|
|
@@ -99,16 +104,24 @@ class RapidRecognizer(Recognizer):
|
|
|
99
104
|
root (Union[str, Path]): 模型文件所在的根目录
|
|
100
105
|
context (str): 使用的设备。默认为 `cpu`,可选 `gpu`
|
|
101
106
|
rec_image_shape (str): 输入图片尺寸,无需更改使用默认值即可。默认值:`"3, 48, 320"`
|
|
107
|
+
lang_type (Optional[Union[str, LangRec]]): RapidOCR识别模型语言类型。PP-OCRv6
|
|
108
|
+
需传入具体语言,如 `ch`、`en`、`japan`、`french` 等;默认为中文。
|
|
102
109
|
**kwargs: 其他参数
|
|
103
110
|
"""
|
|
104
111
|
self.rec_image_shape = [int(v) for v in rec_image_shape.split(",")]
|
|
105
112
|
self._model_name = model_name
|
|
106
113
|
self._model_backend = "onnx"
|
|
107
114
|
use_gpu = context.lower() not in ("cpu", "mps")
|
|
115
|
+
model_type = self._get_model_type(model_name)
|
|
116
|
+
ocr_version = self._get_ocr_version(model_name)
|
|
117
|
+
lang_type = self._get_lang_type(model_name, model_type, lang_type)
|
|
118
|
+
self._model_type = model_type
|
|
119
|
+
self._ocr_version = ocr_version
|
|
120
|
+
self._lang_type = lang_type
|
|
108
121
|
|
|
109
122
|
self._assert_and_prepare_model_files(model_fp, root)
|
|
110
123
|
|
|
111
|
-
config = Config.DEFAULT_CFG
|
|
124
|
+
config = deepcopy(Config.DEFAULT_CFG)
|
|
112
125
|
## add custom font path
|
|
113
126
|
if 'font_path' in kwargs:
|
|
114
127
|
config['font_path'] = kwargs['font_path']
|
|
@@ -117,24 +130,111 @@ class RapidRecognizer(Recognizer):
|
|
|
117
130
|
config["engine_cfg"].update(kwargs["engine_cfg"])
|
|
118
131
|
config["rec_img_shape"] = self.rec_image_shape
|
|
119
132
|
config["model_path"] = self._model_fp
|
|
133
|
+
config["model_root_dir"] = self._model_dir
|
|
120
134
|
# 从 model_name 中获取 model_type 和 ocr_version
|
|
121
|
-
config["model_type"] =
|
|
122
|
-
config["ocr_version"] =
|
|
135
|
+
config["model_type"] = model_type
|
|
136
|
+
config["ocr_version"] = ocr_version
|
|
137
|
+
config["lang_type"] = lang_type
|
|
123
138
|
|
|
124
139
|
config = Config(config)
|
|
125
140
|
self.recognizer = TextRecognizer(config)
|
|
126
141
|
|
|
142
|
+
@staticmethod
|
|
143
|
+
def _get_ocr_version(model_name: str):
|
|
144
|
+
if "v6" in model_name:
|
|
145
|
+
if not hasattr(OCRVersion, "PPOCRV6"):
|
|
146
|
+
raise RuntimeError(
|
|
147
|
+
"PP-OCRv6 models require rapidocr>=3.9.0. "
|
|
148
|
+
"Please upgrade rapidocr to use this model."
|
|
149
|
+
)
|
|
150
|
+
return OCRVersion.PPOCRV6
|
|
151
|
+
if "v5" in model_name:
|
|
152
|
+
return OCRVersion.PPOCRV5
|
|
153
|
+
return OCRVersion.PPOCRV4
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
def _get_model_type(model_name: str):
|
|
157
|
+
if "server" in model_name:
|
|
158
|
+
return ModelType.SERVER
|
|
159
|
+
for model_type in ("tiny", "small", "medium"):
|
|
160
|
+
if model_type in model_name:
|
|
161
|
+
if not hasattr(ModelType, model_type.upper()):
|
|
162
|
+
raise RuntimeError(
|
|
163
|
+
"PP-OCRv6 models require rapidocr>=3.9.0. "
|
|
164
|
+
"Please upgrade rapidocr to use this model."
|
|
165
|
+
)
|
|
166
|
+
return getattr(ModelType, model_type.upper())
|
|
167
|
+
if "v6" in model_name:
|
|
168
|
+
if not hasattr(ModelType, "SMALL"):
|
|
169
|
+
raise RuntimeError(
|
|
170
|
+
"PP-OCRv6 models require rapidocr>=3.9.0. "
|
|
171
|
+
"Please upgrade rapidocr to use this model."
|
|
172
|
+
)
|
|
173
|
+
return ModelType.SMALL
|
|
174
|
+
return ModelType.MOBILE
|
|
175
|
+
|
|
176
|
+
@classmethod
|
|
177
|
+
def _get_model_file_name(cls, model_name: str):
|
|
178
|
+
if "v6" in model_name:
|
|
179
|
+
model_type = cls._get_model_type(model_name).value
|
|
180
|
+
return f"PP-OCRv6_rec_{model_type}.onnx"
|
|
181
|
+
return "%s_rec_infer.onnx" % model_name
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
def _get_lang_type(model_name: str, model_type: ModelType, lang_type=None):
|
|
185
|
+
# RapidOCR's PP-OCRv6 model files are named "multi_*", but its
|
|
186
|
+
# resolver expects a concrete language and maps it to the multi model.
|
|
187
|
+
if lang_type is None:
|
|
188
|
+
return LangRec.CH
|
|
189
|
+
|
|
190
|
+
normalized = lang_type.value if hasattr(lang_type, "value") else str(lang_type)
|
|
191
|
+
normalized = normalized.strip().lower()
|
|
192
|
+
if "v6" in model_name and normalized == "multi":
|
|
193
|
+
raise ValueError(
|
|
194
|
+
"PP-OCRv6 requires a concrete lang_type such as 'ch' or 'en'; "
|
|
195
|
+
"'multi' is the model family name, not a valid v6 lang_type."
|
|
196
|
+
)
|
|
197
|
+
if "v6" in model_name:
|
|
198
|
+
resolve_model_key(
|
|
199
|
+
TaskType.REC, OCRVersion.PPOCRV6, lang_type, model_type
|
|
200
|
+
)
|
|
201
|
+
return lang_type
|
|
202
|
+
|
|
127
203
|
def _assert_and_prepare_model_files(self, model_fp, root):
|
|
128
204
|
if model_fp is not None and not os.path.isfile(model_fp):
|
|
129
205
|
raise FileNotFoundError("can not find model file %s" % model_fp)
|
|
130
206
|
|
|
207
|
+
root = os.path.join(root, MODEL_VERSION)
|
|
208
|
+
self._model_dir = os.path.join(root, PP_SPACE, self._model_name)
|
|
209
|
+
|
|
131
210
|
if model_fp is not None:
|
|
132
211
|
self._model_fp = model_fp
|
|
133
212
|
return
|
|
134
213
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
214
|
+
if "v6" in self._model_name:
|
|
215
|
+
if (self._model_name, self._model_backend) not in AVAILABLE_MODELS:
|
|
216
|
+
raise NotImplementedError(
|
|
217
|
+
"%s is not a downloadable model"
|
|
218
|
+
% ((self._model_name, self._model_backend),)
|
|
219
|
+
)
|
|
220
|
+
remote_repo = AVAILABLE_MODELS.get_value(
|
|
221
|
+
self._model_name, self._model_backend, "repo"
|
|
222
|
+
)
|
|
223
|
+
if remote_repo is None:
|
|
224
|
+
raise RuntimeError(
|
|
225
|
+
"no remote repo is configured for model %s"
|
|
226
|
+
% ((self._model_name, self._model_backend),)
|
|
227
|
+
)
|
|
228
|
+
model_fp = os.path.join(
|
|
229
|
+
self._model_dir, self._get_model_file_name(self._model_name)
|
|
230
|
+
)
|
|
231
|
+
self._model_fp = str(prepare_model_files(model_fp, remote_repo))
|
|
232
|
+
logger.info("use model: %s" % self._model_fp)
|
|
233
|
+
return
|
|
234
|
+
|
|
235
|
+
model_fp = os.path.join(
|
|
236
|
+
self._model_dir, self._get_model_file_name(self._model_name)
|
|
237
|
+
)
|
|
138
238
|
if not os.path.isfile(model_fp):
|
|
139
239
|
logger.warning("can not find model file %s" % model_fp)
|
|
140
240
|
if (self._model_name, self._model_backend) not in AVAILABLE_MODELS:
|
|
@@ -42,11 +42,53 @@ from .consts import (
|
|
|
42
42
|
IMG_STANDARD_HEIGHT,
|
|
43
43
|
)
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
LOG_FMT = '[%(levelname)s] %(asctime)s [%(package_name)s] %(filename)s:%(lineno)d: %(message)s'
|
|
46
|
+
GREEN = '\033[32m'
|
|
47
|
+
RESET = '\033[0m'
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _package_label(logger_name):
|
|
51
|
+
if logger_name.startswith('cnocr'):
|
|
52
|
+
return 'CnOCR'
|
|
53
|
+
if logger_name.startswith('cnstd'):
|
|
54
|
+
return 'CnSTD'
|
|
55
|
+
if logger_name.startswith('RapidOCR'):
|
|
56
|
+
return 'RapidOCR'
|
|
57
|
+
return logger_name.split('.', maxsplit=1)[0]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class PackageFormatter(logging.Formatter):
|
|
61
|
+
def format(self, record):
|
|
62
|
+
record.package_name = _package_label(record.name)
|
|
63
|
+
return super().format(record)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ColoredFormatter(PackageFormatter):
|
|
67
|
+
def format(self, record):
|
|
68
|
+
msg = super().format(record)
|
|
69
|
+
if getattr(record, 'log_color', None) == 'green':
|
|
70
|
+
return GREEN + msg + RESET
|
|
71
|
+
return msg
|
|
46
72
|
|
|
47
73
|
logger = logging.getLogger(__name__)
|
|
48
74
|
|
|
49
75
|
|
|
76
|
+
def set_rapidocr_logger_level(log_level=logging.WARNING):
|
|
77
|
+
logging.getLogger('RapidOCR').setLevel(log_level)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _console_handler():
|
|
81
|
+
console_handler = logging.StreamHandler()
|
|
82
|
+
console_handler.setFormatter(ColoredFormatter(LOG_FMT))
|
|
83
|
+
return console_handler
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def get_logger(name=__name__, log_level=logging.INFO):
|
|
87
|
+
logger = logging.getLogger(name)
|
|
88
|
+
logger.setLevel(log_level)
|
|
89
|
+
return logger
|
|
90
|
+
|
|
91
|
+
|
|
50
92
|
def set_logger(log_file=None, log_level=logging.INFO, log_file_level=logging.NOTSET):
|
|
51
93
|
"""
|
|
52
94
|
Example:
|
|
@@ -54,14 +96,18 @@ def set_logger(log_file=None, log_level=logging.INFO, log_file_level=logging.NOT
|
|
|
54
96
|
>>> logger.info("abc'")
|
|
55
97
|
"""
|
|
56
98
|
global logger
|
|
57
|
-
log_format =
|
|
58
|
-
logging.basicConfig(format=fmt)
|
|
99
|
+
log_format = PackageFormatter(LOG_FMT)
|
|
59
100
|
logging.captureWarnings(True)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
101
|
+
set_rapidocr_logger_level()
|
|
102
|
+
|
|
103
|
+
root_logger = logging.getLogger()
|
|
104
|
+
root_logger.setLevel(log_level)
|
|
105
|
+
root_logger.handlers = [_console_handler()]
|
|
106
|
+
for package_name in ('cnocr', 'cnstd'):
|
|
107
|
+
package_logger = logging.getLogger(package_name)
|
|
108
|
+
package_logger.setLevel(logging.NOTSET)
|
|
109
|
+
package_logger.propagate = True
|
|
110
|
+
|
|
65
111
|
if log_file and log_file != '':
|
|
66
112
|
if not Path(log_file).parent.exists():
|
|
67
113
|
os.makedirs(Path(log_file).parent)
|
|
@@ -70,7 +116,9 @@ def set_logger(log_file=None, log_level=logging.INFO, log_file_level=logging.NOT
|
|
|
70
116
|
file_handler = logging.FileHandler(log_file)
|
|
71
117
|
file_handler.setLevel(log_file_level)
|
|
72
118
|
file_handler.setFormatter(log_format)
|
|
73
|
-
|
|
119
|
+
root_logger.addHandler(file_handler)
|
|
120
|
+
logger = logging.getLogger(__name__)
|
|
121
|
+
logger.setLevel(log_level)
|
|
74
122
|
return logger
|
|
75
123
|
|
|
76
124
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cnocr
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.3
|
|
4
4
|
Summary: Python3 package for Chinese/English OCR, with small pretrained models
|
|
5
5
|
Home-page: https://github.com/breezedeus/cnocr
|
|
6
6
|
Author: breezedeus
|
|
@@ -16,11 +16,11 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
16
16
|
Classifier: Programming Language :: Python
|
|
17
17
|
Classifier: Programming Language :: Python :: Implementation
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.8
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.9
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
23
22
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
Provides-Extra: ort-cpu
|
|
26
26
|
Provides-Extra: ort-gpu
|
|
@@ -69,6 +69,17 @@ License-File: LICENSE
|
|
|
69
69
|
---
|
|
70
70
|
</div>
|
|
71
71
|
|
|
72
|
+
### Update 2026.07.04:发布 V2.3.3
|
|
73
|
+
|
|
74
|
+
主要变更:
|
|
75
|
+
|
|
76
|
+
* 基于 RapidOCR 支持 PP-OCRv6 多语种 OCR 模型
|
|
77
|
+
* 新增支持 PP-OCRv6 识别模型:`multi_PP-OCRv6_tiny`、`multi_PP-OCRv6`、`multi_PP-OCRv6_small` 和 `multi_PP-OCRv6_medium`
|
|
78
|
+
* 通过 CnSTD 新增支持 PP-OCRv6 检测模型:`multi_PP-OCRv6_det_tiny`、`multi_PP-OCRv6_det_small` 和 `multi_PP-OCRv6_det_medium`
|
|
79
|
+
* `CnOcr` 新增 `rec_lang_type` 参数,可为 RapidOCR v6 识别模型指定语言类型
|
|
80
|
+
* CLI 新增 `--rec-lang-type` 和 `--det-lang-type`,可为 RapidOCR v6 模型指定语言类型
|
|
81
|
+
|
|
82
|
+
|
|
72
83
|
### Update 2025.06.26:发布 V2.3.2
|
|
73
84
|
|
|
74
85
|
主要变更:
|
|
@@ -192,13 +203,13 @@ print(out)
|
|
|
192
203
|
|
|
193
204
|
### 竖排文字识别
|
|
194
205
|
|
|
195
|
-
采用来自 [**PaddleOCR**](https://github.com/PaddlePaddle/PaddleOCR)(之后简称 **ppocr
|
|
206
|
+
采用来自 [**PaddleOCR**](https://github.com/PaddlePaddle/PaddleOCR)(之后简称 **ppocr**)的 PP-OCRv6 多语种识别模型 `rec_model_name='multi_PP-OCRv6'` 进行识别。
|
|
196
207
|
|
|
197
208
|
```python
|
|
198
209
|
from cnocr import CnOcr
|
|
199
210
|
|
|
200
211
|
img_fp = './docs/examples/shupai.png'
|
|
201
|
-
ocr = CnOcr(rec_model_name='
|
|
212
|
+
ocr = CnOcr(rec_model_name='multi_PP-OCRv6')
|
|
202
213
|
out = ocr.ocr(img_fp)
|
|
203
214
|
|
|
204
215
|
print(out)
|
|
@@ -233,31 +244,27 @@ print(out)
|
|
|
233
244
|
|
|
234
245
|
### 繁体中文识别
|
|
235
246
|
|
|
236
|
-
采用来自ppocr
|
|
247
|
+
采用来自 ppocr 的 PP-OCRv6 多语种识别模型,并通过 `rec_lang_type='chinese_cht'` 指定繁体中文进行识别。
|
|
237
248
|
|
|
238
249
|
```python
|
|
239
250
|
from cnocr import CnOcr
|
|
240
251
|
|
|
241
252
|
img_fp = './docs/examples/fanti.jpg'
|
|
242
|
-
ocr = CnOcr(rec_model_name='
|
|
253
|
+
ocr = CnOcr(rec_model_name='multi_PP-OCRv6', rec_lang_type='chinese_cht')
|
|
243
254
|
out = ocr.ocr(img_fp)
|
|
244
255
|
|
|
245
256
|
print(out)
|
|
246
257
|
```
|
|
247
258
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
* 识别精度一般,不是很好;
|
|
251
|
-
|
|
252
|
-
* 除了繁体字,对标点、英文、数字的识别都不好;
|
|
253
|
-
|
|
254
|
-
* 此模型不支持竖排文字的识别。
|
|
259
|
+
`multi_PP-OCRv6` 是 `multi_PP-OCRv6_small` 的别名;`chinese_cht` 是繁体中文对应的 `lang_type`。
|
|
255
260
|
|
|
256
261
|
识别结果:
|
|
257
262
|
<div align="center">
|
|
258
263
|
<img src="./docs/predict-outputs/fanti.jpg-result.jpg" alt="繁体中文识别" width="700px"/>
|
|
259
264
|
</div>
|
|
260
265
|
|
|
266
|
+
注:上图中的识别结果来自 V3 模型;V6 模型的识别效果已经有显著增强。
|
|
267
|
+
|
|
261
268
|
|
|
262
269
|
### 单行文字的图片识别
|
|
263
270
|
|
|
@@ -331,7 +338,7 @@ $ pip install cnocr[ort-cpu] -i https://mirrors.aliyun.com/pypi/simple
|
|
|
331
338
|
|
|
332
339
|
> **Note**
|
|
333
340
|
>
|
|
334
|
-
> 请使用 **
|
|
341
|
+
> 请使用 **Python 3.8 或更高版本**。
|
|
335
342
|
|
|
336
343
|
更多说明可见 [安装文档](https://cnocr.readthedocs.io/zh-cn/stable/install/)。
|
|
337
344
|
|
|
@@ -428,13 +435,18 @@ print(ocr_out)
|
|
|
428
435
|
| db_mobilenet_v3_small | √ | X | cnocr | 7.9 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
429
436
|
| db_resnet34 | √ | X | cnocr | 86 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
430
437
|
| db_resnet18 | √ | X | cnocr | 47 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
438
|
+
| multi_PP-OCRv6_det_tiny | X | √ | ppocr | 1.7 M | 多语种(不含日文) | √ |
|
|
439
|
+
| multi_PP-OCRv6_det_small | X | √ | ppocr | 9.5 M | 多语种 | √ |
|
|
440
|
+
| multi_PP-OCRv6_det_medium | X | √ | ppocr | 59 M | 多语种 | √ |
|
|
431
441
|
| ch_PP-OCRv5_det | X | √ | ppocr | 4.6 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
432
|
-
| ch_PP-OCRv5_det_server | X | √ | ppocr | 84 M
|
|
442
|
+
| ch_PP-OCRv5_det_server | X | √ | ppocr | 84 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
433
443
|
| ch_PP-OCRv4_det | X | √ | ppocr | 4.5 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
434
444
|
| ch_PP-OCRv4_det_server | X | √ | ppocr | 108 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
435
445
|
| ch_PP-OCRv3_det | X | √ | ppocr | 2.3 M | 简体中文、繁体中文、英文、数字 | √ |
|
|
436
446
|
| **en_PP-OCRv3_det** | X | √ | ppocr | 2.3 M | **英文**、数字 | √ |
|
|
437
447
|
|
|
448
|
+
PP-OCRv6 的 `multi_PP-OCRv6_det_small` 和 `multi_PP-OCRv6_det_medium` 支持的 `lang_type` 包括:`ch`, `chinese_cht`, `en`, `japan`, `af`, `az`, `bs`, `ca`, `cs`, `cy`, `da`, `de`, `es`, `et`, `eu`, `fi`, `fr`, `ga`, `gl`, `hr`, `hu`, `id`, `is`, `it`, `ku`, `la`, `lb`, `lt`, `lv`, `mi`, `ms`, `mt`, `nl`, `no`, `oc`, `pl`, `pt`, `qu`, `rm`, `ro`, `rs_latin`, `sk`, `sl`, `sq`, `sv`, `sw`, `tl`, `tr`, `uz`, `vi`, `french`, `german`;`multi_PP-OCRv6_det_tiny` 不支持 `japan`。`multi` 是模型族名称,不是可传入的 `lang_type`。
|
|
449
|
+
|
|
438
450
|
|
|
439
451
|
|
|
440
452
|
### 可使用的识别模型
|
|
@@ -469,6 +481,9 @@ print(ocr_out)
|
|
|
469
481
|
| **number-densenet_lite_136-fc** 🆕 | √ | √ | cnocr | 2.7 M | **纯数字**(仅包含 `0~9` 十个数字) | X |
|
|
470
482
|
| **number-densenet_lite_136-gru** 🆕 <br /> ([星球会员](https://t.zsxq.com/FEYZRJQ)专享) | √ | √ | cnocr | 5.5 M | **纯数字**(仅包含 `0~9` 十个数字) | X |
|
|
471
483
|
| **number-densenet_lite_666-gru_large** 🆕 <br />(购买链接:[B站](https://mall.bilibili.com/neul-next/detailuniversal/detail.html?isMerchant=1&page=detailuniversal_detail&saleType=10&itemsId=11884155&loadingShow=1&noTitleBar=1&msource=merchant_share)、[Lemon Squeezy](https://ocr.lemonsqueezy.com/)) | √ | √ | cnocr | 55 M | **纯数字**(仅包含 `0~9` 十个数字) | X |
|
|
484
|
+
| multi_PP-OCRv6_tiny | X | √ | ppocr | 4.3 M | 多语种(不含日文) | √ |
|
|
485
|
+
| multi_PP-OCRv6 / multi_PP-OCRv6_small | X | √ | ppocr | 20 M | 多语种 | √ |
|
|
486
|
+
| multi_PP-OCRv6_medium | X | √ | ppocr | 73 M | 多语种 | √ |
|
|
472
487
|
| ch_PP-OCRv5 | X | √ | ppocr | 16 M | 简体中文、英文、数字 | √ |
|
|
473
488
|
| ch_PP-OCRv5_server | X | √ | ppocr | 81 M | 简体中文、英文、数字 | √ |
|
|
474
489
|
| ch_PP-OCRv4 | X | √ | ppocr | 10 M | 简体中文、英文、数字 | √ |
|
|
@@ -484,6 +499,8 @@ print(ocr_out)
|
|
|
484
499
|
| latin_PP-OCRv3 | X | √ | ppocr | 8.6 M | **拉丁文**、英文、数字 | √ |
|
|
485
500
|
| arabic_PP-OCRv3 | X | √ | ppocr | 8.6 M | **阿拉伯文**、英文、数字 | √ |
|
|
486
501
|
|
|
502
|
+
PP-OCRv6 的 `multi_PP-OCRv6_small`、`multi_PP-OCRv6_medium` 支持的 `lang_type` 包括:`ch`, `chinese_cht`, `en`, `japan`, `af`, `az`, `bs`, `ca`, `cs`, `cy`, `da`, `de`, `es`, `et`, `eu`, `fi`, `fr`, `ga`, `gl`, `hr`, `hu`, `id`, `is`, `it`, `ku`, `la`, `lb`, `lt`, `lv`, `mi`, `ms`, `mt`, `nl`, `no`, `oc`, `pl`, `pt`, `qu`, `rm`, `ro`, `rs_latin`, `sk`, `sl`, `sq`, `sv`, `sw`, `tl`, `tr`, `uz`, `vi`, `french`, `german`;`multi_PP-OCRv6_tiny` 不支持 `japan`。`multi_PP-OCRv6` 是 `multi_PP-OCRv6_small` 的别名;`multi` 是模型族名称,不是可传入的 `lang_type`。
|
|
503
|
+
|
|
487
504
|
|
|
488
505
|
|
|
489
506
|
## 未来工作
|
|
@@ -47,8 +47,8 @@ required = [
|
|
|
47
47
|
"torchmetrics",
|
|
48
48
|
"pillow>=5.3.0",
|
|
49
49
|
"onnx",
|
|
50
|
-
"cnstd>=1.2.
|
|
51
|
-
"rapidocr>=3.
|
|
50
|
+
"cnstd>=1.2.8",
|
|
51
|
+
"rapidocr>=3.9.1",
|
|
52
52
|
]
|
|
53
53
|
extras_require = {
|
|
54
54
|
"ort-cpu": ["onnxruntime"],
|
|
@@ -91,6 +91,7 @@ setup(
|
|
|
91
91
|
entry_points=entry_points,
|
|
92
92
|
install_requires=required,
|
|
93
93
|
extras_require=extras_require,
|
|
94
|
+
python_requires='>=3.8',
|
|
94
95
|
zip_safe=False,
|
|
95
96
|
classifiers=[
|
|
96
97
|
'Development Status :: 4 - Beta',
|
|
@@ -100,7 +101,6 @@ setup(
|
|
|
100
101
|
'Programming Language :: Python',
|
|
101
102
|
'Programming Language :: Python :: Implementation',
|
|
102
103
|
'Programming Language :: Python :: 3',
|
|
103
|
-
'Programming Language :: Python :: 3.7',
|
|
104
104
|
'Programming Language :: Python :: 3.8',
|
|
105
105
|
'Programming Language :: Python :: 3.9',
|
|
106
106
|
'Programming Language :: Python :: 3.10',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|