From f0ee6b122e9f6b4ccc0cd96032f93cd43721543f Mon Sep 17 00:00:00 2001 From: sml2h3 Date: Mon, 6 May 2024 11:43:42 +0800 Subject: [PATCH] =?UTF-8?q?1.5.1=20=E6=96=B0=E5=A2=9Eocr=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E6=A6=82=E7=8E=87=E8=A1=A8=E8=BF=94=E5=9B=9E=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E7=BB=93=E6=9E=9C=E9=99=90=E5=AE=9A=EF=BC=8C?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E6=95=B4=E7=90=86=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 459 +++++++++++++++++++++++++--------------- ddddocr/README.md | 459 +++++++++++++++++++++++++--------------- ddddocr/__init__.py | 100 ++++++++- ddddocr/common.onnx | Bin 54088521 -> 54088400 bytes ddddocr/common_old.onnx | Bin 13606198 -> 13606051 bytes ddddocr/logo.png | Bin 0 -> 29382 bytes logo.png | Bin 0 -> 29382 bytes setup.py | 5 +- 8 files changed, 667 insertions(+), 356 deletions(-) create mode 100644 ddddocr/logo.png create mode 100644 logo.png diff --git a/README.md b/README.md index e8cc23a..3c1459f 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,237 @@ -![header.png](https://cdn.wenanzhe.com/img/68747470733a2f2f7a332e617831782e636f6d2f323032312f30372f30322f5236496832382e6a7067.jfif) -# 带带弟弟OCR通用验证码离线本地识别SDK免费开源版 -# 当前版本为1.4.11 +# DdddOcr 带带弟弟OCR通用验证码离线本地识别SDK免费开源版 -## 下一版本更新计划,重新训练 中英数识别模型,即将支持自定义划定输出范围,如纯英文/纯数字/部分英文+部分数字/特殊符号/中文/各种混合等等,如果有已经标注好的数据也可以发送邮件(sml2h3@gmail.com)与我分享 +DdddOcr,其由作者与kerlomz共同合作完成,通过大批量生成随机数据后进行深度网络训练,本身并非针对任何一家验证码厂商而制作,本库使用效果完全靠玄学,可能可以识别,可能不能识别。 -## 1.4.11 更新时间2024.01.10 +DdddOcr、最简依赖的理念,尽量减少用户的配置和使用成本,希望给每一位测试者带来舒适的体验 -新增对透明黑色png格式图片得识别支持: `classification` 方法 新增 `png_fix` 参数,默认为False +项目地址: [点我传送](https://github.com/sml2h3/ddddocr) -## 1.4.3更新内容 + -本次升级的主要原因为,[dddd_trainer](https://github.com/sml2h3/dddd_trainer) 的开源进行适配,使[dddd_trainer](https://github.com/sml2h3/dddd_trainer) 训练出的模型可以直接无缝导入到ddddocr里面来使用 +[![Contributors][contributors-shield]][contributors-url] +[![Forks][forks-shield]][forks-url] +[![Stargazers][stars-shield]][stars-url] +[![Issues][issues-shield]][issues-url] +[![MIT License][license-shield]][license-url] -### 支持使用ddddocr调用 [dddd_trainer](https://github.com/sml2h3/dddd_trainer) 训练后的自定义模型 + +
-[dddd_trainer](https://github.com/sml2h3/dddd_trainer) 训练后会在models目录里导出charsets.json和onnx模型 +

+ + Logo + +

+ 一个容易使用的通用验证码识别python库 +
+ 探索本项目的文档 » +
+
+ · + 报告Bug + · + 提出新特性 +

-如下所示,import_onnx_path为onnx所在地址,charsets_path为onnx所在地址 -```python -import ddddocr +

-ocr = ddddocr.DdddOcr(det=False, ocr=False, import_onnx_path="myproject_0.984375_139_13000_2022-02-26-15-34-13.onnx", charsets_path="charsets.json") + +## 目录 -with open('888e28774f815b01e871d474e5c84ff2.jpg', 'rb') as f: - image_bytes = f.read() +- [上手指南](#上手指南) + - [环境支持](#环境支持) + - [安装步骤](#安装步骤) +- [文件目录说明](#文件目录说明) +- [项目底层支持](#项目底层支持) +- [使用文档](#使用文档) + - [基础ocr识别能力](#基础ocr识别能力) + - [目标检测能力](#目标检测能力) + - [滑块检测](#滑块检测) + - [OCR概率输出](#OCR概率输出) + - [自定义OCR训练模型导入](#自定义OCR训练模型导入) +- [版本控制](#版本控制) +- [作者](#作者) +- [鸣谢](#鸣谢) -res = ocr.classification(image_bytes) -print(res) +### 上手指南 + +###### 环境支持 + + + +| 系统 | CPU | GPU | 最大支持py版本 | 备注 | +|------------------|-----|------|----------|--------------------------------------------------------------------| +| Windows 64位 | √ | √ | 3.11 | 部分版本windows需要安装vc运行库 | +| Windows 32位 | × | × | - | | +| Linux 64 / ARM64 | √ | √ | 3.11 | | +| Linux 32 | × | × | - | | +| Macos X64 | √ | √ | 3.11 | M1/M2/M3...芯片参考#67 | + +###### **安装步骤** + +**i. 从pypi安装** +```sh +pip install ddddocr +``` + +**ii. 从源码安装** +```sh +git clone https://github.com/sml2h3/ddddocr.git +cd ddddocr +python setup.py +``` + +**请勿直接在ddddocr项目的根目录内直接import ddddocr**,请确保你的开发项目目录名称不为ddddocr,此为基础常识。 + +### 文件目录说明 +eg: + +``` +ddddocr +├── MANIFEST.in +├── LICENSE +├── README.md +├── /ddddocr/ +│ │── __init__.py 主代码库文件 +│ │── common.onnx 新ocr模型 +│ │── common_det.onnx 目标检测模型 +│ │── common_old.onnx 老ocr模型 +│ │── logo.png +│ │── README.md +│ │── requirements.txt +├── logo.png +└── setup.py ``` -# 捐赠 (如果项目有帮助到您,可以选择捐赠一些费用用于ddddocr的后续版本维护,本项目长期维护) +### 项目底层支持 - ![Test](https://cdn.wenanzhe.com/img/zhifubao.jpg!/scale/30) - ![Test](https://cdn.wenanzhe.com/img/weixin.jpg!/scale/30) +本项目基于[dddd_trainer](https://github.com/sml2h3/dddd_trainer) 训练所得,训练底层框架位pytorch,ddddocr推理底层抵赖于[onnxruntime](https://pypi.org/project/onnxruntime/),故本项目的最大兼容性与python版本支持主要取决于[onnxruntime](https://pypi.org/project/onnxruntime/)。 -# 赞助合作商 +### 使用文档 -| | 赞助合作商 | 推荐理由 | -|------------------------------------------------------------|------------|--------------------------------------------------------------------------------------------------| -| ![YesCaptcha](https://cdn.wenanzhe.com/img/yescaptcha.png) | [YesCaptcha](https://yescaptcha.com/i/NSwk7i) | 谷歌reCaptcha验证码 / hCaptcha验证码 / funCaptcha验证码商业级识别接口 [点我](https://yescaptcha.com/i/NSwk7i) 直达VIP4 | -| ![Malenia](https://cdn.wenanzhe.com/img/malenia.png!/scale/50) | [Malenia](https://malenia.iinti.cn/malenia-doc/) | Malenia企业级代理IP网关平台/代理IP分销软件 | +##### i. 基础ocr识别能力 -# 1.4.0版本更新内容 +主要用于识别单行文字,即文字部分占据图片的主体部分,例如常见的英数验证码等,本项目可以对中文、英文(随机大小写or通过设置结果范围圈定大小写)、数字以及部分特殊字符。 - 本次更新新增了两种滑块识别算法,算法非深度神经网络实现,仅使用opencv和PIL完成。 +```python +# example.py +import ddddocr - ## 算法1 - 小滑块为单独的png图片,背景是透明图,如下图 +ocr = ddddocr.DdddOcr() - ![Test](https://cdn.wenanzhe.com/img/b.png) +image = open("example.jpg", "rb").read() +result = ocr.classification(image) +print(result) +``` - 然后背景为带小滑块坑位的,如下图 - - ![Test](https://cdn.wenanzhe.com/img/a.png) +本库内置有两套ocr模型,默认情况下不会自动切换,需要在初始化ddddocr的时候通过参数进行切换 - ```python +```python +# example.py +import ddddocr + +ocr = ddddocr.DdddOcr(beta=True) # 切换为第二套ocr模型 + +image = open("example.jpg", "rb").read() +result = ocr.classification(image) +print(result) +``` + +**提示** +对于部分透明黑色png格式图片得识别支持: `classification` 方法 使用 `png_fix` 参数,默认为False + +```python + ocr.classification(image, png_fix=True) +``` + +**注意** + +之前发现很多人喜欢在每次ocr识别的时候都重新初始化ddddocr,即每次都执行```ocr = ddddocr.DdddOcr()```,这是错误的,通常来说只需要初始化一次即可,因为每次初始化和初始化后的第一次识别速度都非常慢 + + +**参考例图** + +包括且不限于以下图片 + +captcha +captcha +captcha +captcha +captcha +captcha +
+captcha +captcha +captcha +captcha +captcha +captcha + +##### ii. 目标检测能力 + +主要用于快速检测出图像中可能的目标主体位置,由于被检测出的目标不一定为文字,所以本功能仅提供目标的bbox位置 **(在⽬标检测⾥,我们通常使⽤bbox(bounding box,缩写是 bbox)来描述⽬标位置。bbox是⼀个矩形框,可以由矩形左上⻆的 x 和 y 轴坐标与右下⻆的 x 和 y 轴坐标确定)** + +如果使用过程中无需调用ocr功能,可以在初始化时通过传参`ocr=False`关闭ocr功能,开启目标检测需要传入参数`det=True` + +```python +import ddddocr +import cv2 + +det = ddddocr.DdddOcr(det=True) + +with open("test.jpg", 'rb') as f: + image = f.read() + +bboxes = det.detection(image) +print(bboxes) + +im = cv2.imread("test.jpg") + +for bbox in bboxes: + x1, y1, x2, y2 = bbox + im = cv2.rectangle(im, (x1, y1), (x2, y2), color=(0, 0, 255), thickness=2) + +cv2.imwrite("result.jpg", im) + +``` + + + +**参考例图** + +包括且不限于以下图片 + +captcha +captcha +captcha +captcha +
+captcha +captcha +captcha + +##### Ⅲ. 滑块检测 + +本项目的滑块检测功能并非AI识别实现,均为opencv内置算法实现。可能对于截图党用户没那么友好~,如果使用过程中无需调用ocr功能或目标检测功能,可以在初始化时通过传参`ocr=False`关闭ocr功能或`det=False`来关闭目标检测功能 + +本功能内置两套算法实现,适用于两种不同情况,具体请参考以下说明 + +**a.算法1** + +算法1原理是通过滑块图像的边缘在背景图中计算找到相对应的坑位,可以分别获取到滑块图和背景图,滑块图为透明背景图 + +滑块图 + +captcha + +背景图 + +captcha + +```python det = ddddocr.DdddOcr(det=False, ocr=False) with open('target.png', 'rb') as f: @@ -70,7 +244,10 @@ print(res) print(res) ``` - *提示:如果小图无过多背景部分,则可以添加simple_target参数, 通常为jpg或者bmp格式的图片* + 由于滑块图可能存在透明边框的问题,导致计算结果不一定准确,需要自行估算滑块图透明边框的宽度用于修正得出的bbox + + *提示:如果滑块无过多背景部分,则可以添加simple_target参数, 通常为jpg或者bmp格式的图片* + ```python slide = ddddocr.DdddOcr(det=False, ocr=False) @@ -84,16 +261,20 @@ print(res) print(res) ``` - ## 算法2 - 一张图为带坑位的原图,如下图 - ![Test](https://cdn.wenanzhe.com/img/bg.jpg) +**a.算法2** - 一张图为原图,如下图 - - ![Test](https://cdn.wenanzhe.com/img/fullpage.jpg) +算法2是通过比较两张图的不同之处进行判断滑块目标坑位的位置 - ```python +参考图a,带有目标坑位阴影的全图 + +captcha + +参考图b,全图 + +captcha + +```python slide = ddddocr.DdddOcr(det=False, ocr=False) with open('bg.jpg', 'rb') as f: @@ -109,165 +290,97 @@ print(res) print(res) ``` - ## 更新内容2 - 添加全局ocr关闭参数,初始化时传入 +##### Ⅳ. OCR概率输出 - `dddd = ddddocr.DdddOcr(ocr=False)` +为了提供更灵活的ocr结果控制与范围限定,项目支持对ocr结果进行范围限定。 - 则为关闭ocr功能,如果det = True,则会自动关闭ocr +可以通过在调用`classification`方法的时候传参`probability=True`,此时`classification`方法将返回全字符表的概率 +当然也可以通过`set_ranges`方法设置输出字符范围来限定返回的结果。 +Ⅰ. `set_ranges` 方法限定返回字符返回 -# 1.3.1版本更新内容 +本方法接受1个参数,如果输入为int类型为内置的字符集限制,string类型则为自定义的字符集 - 想必很多做验证码的新手,一定头疼碰到点选类型的图像,做样本费时费力,神经网络不会写,训练设备太昂贵,模型效果又不好。 +如果为int类型,请参考下表 - 市场上常见的点选类验证码图片如下图所示 +| 参数值 | 意义 | +|-----|-----------------------------------| +| 0 | 纯整数0-9 | +| 1 | 纯小写英文a-z | +| 2 | 纯大写英文A-Z | +| 3 | 小写英文a-z + 大写英文A-Z | +| 4 | 小写英文a-z + 整数0-9 | +| 5 | 大写英文A-Z + 整数0-9 | +| 6 | 小写英文a-z + 大写英文A-Z + 整数0-9 | +| 7 | 默认字符库 - 小写英文a-z - 大写英文A-Z - 整数0-9 | - - ![Test](https://cdn.wenanzhe.com/img/0446fe794381489f90719d5e0506f2da.jpg) - - ![Test](https://cdn.wenanzhe.com/img/6175e944c1dc408a89aabe4f7fc07fca.jpg) - - ![Test](https://cdn.wenanzhe.com/img/20211226135747.png) - - ![Test](https://cdn.wenanzhe.com/img/f34390d4911c45ce9058dc2e7e9d847a.jpg) - - 那么今天,他来了,ddddocr带着重磅更新大摇大摆的走来了。 -# 简介 - ddddocr是由sml2h3开发的专为验证码厂商进行对自家新版本验证码难易强度进行验证的一个python库,其由作者与kerlomz共同合作完成,通过大批量生成随机数据后进行深度网络训练,本身并非针对任何一家验证码厂商而制作,本库使用效果完全靠玄学,可能可以识别,可能不能识别。 - - ddddocr奉行着开箱即用、最简依赖的理念,尽量减少用户的配置和使用成本,希望给每一位测试者带来舒适的体验 - -项目地址: [点我传送](https://github.com/sml2h3/ddddocr) - -# 更新说明 - - 本次更新其实分为两部分,其中有一部分是在1.2.0版本就已经更新了,但是在这里还是有必要提一下的。 - -## 第一部分 OCR识别部分 - - 在1.2.0开始,ddddocr的识别部分进行了一次beta更新,主要更新在于网络结构主体的升级,其训练数据并没有发生过多的改变,所以理论上在识别结果上,原先可能识别效果的很好的图形在1.2.0上有一小部分概率会有一定程度的下降,也有可能原本识别不好的图形在1.2.0之后效果却变得特别好。 - 测试代码: - +如果为string类型请传入一段不包含空格的文本,其中的每个字符均为一个待选词 +如:`"0123456789+-x/=""` ```python import ddddocr ocr = ddddocr.DdddOcr() -with open("test.jpg", 'rb') as f: - image = f.read() +image = open("test.jpg", "rb").read() +ocr.set_ranges("0123456789+-x/=") +result = ocr.classification(image, probability=True) +s = "" +for i in result['probability']: + s += result['charsets'][i.index(max(i))] -res = ocr.classification(image) -print(res) -``` -通过在初始化ddddocr的时候使用beta参数即可快速切换新模型 - -```python -import ddddocr - -ocr = ddddocr.DdddOcr(beta=True) - -with open("test.jpg", 'rb') as f: - image = f.read() - -res = ocr.classification(image) -print(res) -``` - - OCR部分应该已经有很多人做了测试,在这里就放一部分网友的测试图片。 - - ![Test](https://cdn.wenanzhe.com/img/20210715211733855.png) - ![Test](https://cdn.wenanzhe.com/img/78b7f57d-371d-4b65-afb2-d19608ae1892.png) - ![Test](https://cdn.wenanzhe.com/img/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20211226142305.png) - ![Test](https://cdn.wenanzhe.com/img/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20211226142325.png) - ![Test](https://cdn.wenanzhe.com/img/2AMLyA_fd83e1f1800e829033417ae6dd0e0ae0.png) - ![Test](https://cdn.wenanzhe.com/img/aabd_181ae81dd5526b8b89f987d1179266ce.jpg) - ![Test](https://cdn.wenanzhe.com/img/2bghz_b504e9f9de1ed7070102d21c6481e0cf.png) - ![Test](https://cdn.wenanzhe.com/img/0000_z4ecc2p65rxc610x.jpg) - ![Test](https://cdn.wenanzhe.com/img/2acd_0586b6b36858a4e8a9939db8a7ec07b7.jpg) - ![Test](https://cdn.wenanzhe.com/img/2a8r_79074e311d573d31e1630978fe04b990.jpg) - ![Test](https://cdn.wenanzhe.com/img/aftf_C2vHZlk8540y3qAmCM.bmp) - ![Test](https://cdn.wenanzhe.com/img/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20211226144057.png) -等等更多图片等你测试哟~ - -## 第二部分 目标检测部分 - 在本次1.3.0的更新中,目标检测部分隆重登场! - 目标检测部分同样也是由大量随机合成数据训练而成,对于现在已有的点选验证码图片或者未知的验证码图片都有可能具备一定的识别能力,适用于文字点选和图标点选。 - 简单来说,对于点选类的验证码,可以快速的检测出图片上的文字或者图标。 - - -```python -import ddddocr -import cv2 - -det = ddddocr.DdddOcr(det=True) - -with open("test.jpg", 'rb') as f: - image = f.read() - -poses = det.detection(image) -print(poses) - -im = cv2.imread("test.jpg") - -for box in poses: - x1, y1, x2, y2 = box - im = cv2.rectangle(im, (x1, y1), (x2, y2), color=(0, 0, 255), thickness=2) - -cv2.imwrite("result.jpg", im) +print(s) ``` -举些例子: +##### Ⅴ. 自定义OCR训练模型导入 - ![Test](https://cdn.wenanzhe.com/img/page1_1.jpg) - ![Test](https://cdn.wenanzhe.com/img/page1_2.jpg) - ![Test](https://cdn.wenanzhe.com/img/page1_3.jpg) - ![Test](https://cdn.wenanzhe.com/img/page1_4.jpg) - ![Test](https://cdn.wenanzhe.com/img/result.jpg) - ![Test](https://cdn.wenanzhe.com/img/result2.jpg) - ![Test](https://cdn.wenanzhe.com/img/result4.jpg) +本项目支持导入来自于 [dddd_trainer](https://github.com/sml2h3/dddd_trainer) 进行自定义训练后的模型,参考导入代码为 -以上只是目前我能找到的点选验证码图片,做了一个简单的测试。 +```python +import ddddocr -# 安装 +ocr = ddddocr.DdddOcr(det=False, ocr=False, import_onnx_path="myproject_0.984375_139_13000_2022-02-26-15-34-13.onnx", charsets_path="charsets.json") -## 环境支持 +with open('test.jpg', 'rb') as f: + image_bytes = f.read() -`python <= 3.9` +res = ocr.classification(image_bytes) +print(res) -`Windows/Linux/Macos..` +``` -暂时不支持Macbook M1(X),M1(X)用户需要自己编译onnxruntime才可以使用 +### 版本控制 -## 安装命令 +该项目使用Git进行版本管理。您可以在repository参看当前可用版本。 -`pip install ddddocr` +### 作者 -以上命令将自动安装符合自己电脑环境的最新ddddocr - -## 拓展 一键部署ddddocr api,支持docker部署 - -[github](https://github.com/sml2h3/ocr_api_server) - -[gitee](https://gitee.com/fkgeek/ocr_api_server) - -## 爬虫框架推荐 - -[feapder](https://github.com/Boris-code/feapder) - -[crawlab](https://github.com/crawlab-team/crawlab) - -# 交流群 (个人微信太懒了不一定会通过) +sml2h3@gamil.com - ![六群链接](https://cdn.wenanzhe.com/img/group.jpg!/scale/50) - ![Test](https://cdn.wenanzhe.com/img/mmqrcode1640418911274.png!/scale/50) +wechat + + *好友数过多不一定通过,有问题可以在issue进行交流* + +### 版权说明 + +该项目签署了MIT 授权许可,详情请参阅 [LICENSE](https://github.com/sml2h3/ddddocr/blob/master/LICENSE) + + + +[your-project-path]:sml2h3/ddddocr +[contributors-shield]: https://img.shields.io/github/contributors/sml2h3/ddddocr?style=flat-square +[contributors-url]: https://github.com/shaojintian/Best_README_template/graphs/contributors +[forks-shield]: https://img.shields.io/github/forks/sml2h3/ddddocr?style=flat-square +[forks-url]: https://github.com/shaojintian/Best_README_template/network/members +[stars-shield]: https://img.shields.io/github/stars/sml2h3/ddddocr?style=flat-square +[stars-url]: https://github.com/shaojintian/Best_README_template/stargazers +[issues-shield]: https://img.shields.io/github/issues/sml2h3/ddddocr?style=flat-square +[issues-url]: https://img.shields.io/github/issues/sml2h3/ddddocr.svg +[license-shield]: https://img.shields.io/github/license/sml2h3/ddddocr?style=flat-square +[license-url]: https://github.com/sml2h3/ddddocr/blob/master/LICENSE + -# 主要贡献者 - -## Star 历史 -[![Star History Chart](https://api.star-history.com/svg?repos=sml2h3/ddddocr&type=Date)](https://star-history.com/#sml2h3/ddddocr&Date) diff --git a/ddddocr/README.md b/ddddocr/README.md index e8cc23a..3c1459f 100644 --- a/ddddocr/README.md +++ b/ddddocr/README.md @@ -1,63 +1,237 @@ -![header.png](https://cdn.wenanzhe.com/img/68747470733a2f2f7a332e617831782e636f6d2f323032312f30372f30322f5236496832382e6a7067.jfif) -# 带带弟弟OCR通用验证码离线本地识别SDK免费开源版 -# 当前版本为1.4.11 +# DdddOcr 带带弟弟OCR通用验证码离线本地识别SDK免费开源版 -## 下一版本更新计划,重新训练 中英数识别模型,即将支持自定义划定输出范围,如纯英文/纯数字/部分英文+部分数字/特殊符号/中文/各种混合等等,如果有已经标注好的数据也可以发送邮件(sml2h3@gmail.com)与我分享 +DdddOcr,其由作者与kerlomz共同合作完成,通过大批量生成随机数据后进行深度网络训练,本身并非针对任何一家验证码厂商而制作,本库使用效果完全靠玄学,可能可以识别,可能不能识别。 -## 1.4.11 更新时间2024.01.10 +DdddOcr、最简依赖的理念,尽量减少用户的配置和使用成本,希望给每一位测试者带来舒适的体验 -新增对透明黑色png格式图片得识别支持: `classification` 方法 新增 `png_fix` 参数,默认为False +项目地址: [点我传送](https://github.com/sml2h3/ddddocr) -## 1.4.3更新内容 + -本次升级的主要原因为,[dddd_trainer](https://github.com/sml2h3/dddd_trainer) 的开源进行适配,使[dddd_trainer](https://github.com/sml2h3/dddd_trainer) 训练出的模型可以直接无缝导入到ddddocr里面来使用 +[![Contributors][contributors-shield]][contributors-url] +[![Forks][forks-shield]][forks-url] +[![Stargazers][stars-shield]][stars-url] +[![Issues][issues-shield]][issues-url] +[![MIT License][license-shield]][license-url] -### 支持使用ddddocr调用 [dddd_trainer](https://github.com/sml2h3/dddd_trainer) 训练后的自定义模型 + +
-[dddd_trainer](https://github.com/sml2h3/dddd_trainer) 训练后会在models目录里导出charsets.json和onnx模型 +

+ + Logo + +

+ 一个容易使用的通用验证码识别python库 +
+ 探索本项目的文档 » +
+
+ · + 报告Bug + · + 提出新特性 +

-如下所示,import_onnx_path为onnx所在地址,charsets_path为onnx所在地址 -```python -import ddddocr +

-ocr = ddddocr.DdddOcr(det=False, ocr=False, import_onnx_path="myproject_0.984375_139_13000_2022-02-26-15-34-13.onnx", charsets_path="charsets.json") + +## 目录 -with open('888e28774f815b01e871d474e5c84ff2.jpg', 'rb') as f: - image_bytes = f.read() +- [上手指南](#上手指南) + - [环境支持](#环境支持) + - [安装步骤](#安装步骤) +- [文件目录说明](#文件目录说明) +- [项目底层支持](#项目底层支持) +- [使用文档](#使用文档) + - [基础ocr识别能力](#基础ocr识别能力) + - [目标检测能力](#目标检测能力) + - [滑块检测](#滑块检测) + - [OCR概率输出](#OCR概率输出) + - [自定义OCR训练模型导入](#自定义OCR训练模型导入) +- [版本控制](#版本控制) +- [作者](#作者) +- [鸣谢](#鸣谢) -res = ocr.classification(image_bytes) -print(res) +### 上手指南 + +###### 环境支持 + + + +| 系统 | CPU | GPU | 最大支持py版本 | 备注 | +|------------------|-----|------|----------|--------------------------------------------------------------------| +| Windows 64位 | √ | √ | 3.11 | 部分版本windows需要安装vc运行库 | +| Windows 32位 | × | × | - | | +| Linux 64 / ARM64 | √ | √ | 3.11 | | +| Linux 32 | × | × | - | | +| Macos X64 | √ | √ | 3.11 | M1/M2/M3...芯片参考#67 | + +###### **安装步骤** + +**i. 从pypi安装** +```sh +pip install ddddocr +``` + +**ii. 从源码安装** +```sh +git clone https://github.com/sml2h3/ddddocr.git +cd ddddocr +python setup.py +``` + +**请勿直接在ddddocr项目的根目录内直接import ddddocr**,请确保你的开发项目目录名称不为ddddocr,此为基础常识。 + +### 文件目录说明 +eg: + +``` +ddddocr +├── MANIFEST.in +├── LICENSE +├── README.md +├── /ddddocr/ +│ │── __init__.py 主代码库文件 +│ │── common.onnx 新ocr模型 +│ │── common_det.onnx 目标检测模型 +│ │── common_old.onnx 老ocr模型 +│ │── logo.png +│ │── README.md +│ │── requirements.txt +├── logo.png +└── setup.py ``` -# 捐赠 (如果项目有帮助到您,可以选择捐赠一些费用用于ddddocr的后续版本维护,本项目长期维护) +### 项目底层支持 - ![Test](https://cdn.wenanzhe.com/img/zhifubao.jpg!/scale/30) - ![Test](https://cdn.wenanzhe.com/img/weixin.jpg!/scale/30) +本项目基于[dddd_trainer](https://github.com/sml2h3/dddd_trainer) 训练所得,训练底层框架位pytorch,ddddocr推理底层抵赖于[onnxruntime](https://pypi.org/project/onnxruntime/),故本项目的最大兼容性与python版本支持主要取决于[onnxruntime](https://pypi.org/project/onnxruntime/)。 -# 赞助合作商 +### 使用文档 -| | 赞助合作商 | 推荐理由 | -|------------------------------------------------------------|------------|--------------------------------------------------------------------------------------------------| -| ![YesCaptcha](https://cdn.wenanzhe.com/img/yescaptcha.png) | [YesCaptcha](https://yescaptcha.com/i/NSwk7i) | 谷歌reCaptcha验证码 / hCaptcha验证码 / funCaptcha验证码商业级识别接口 [点我](https://yescaptcha.com/i/NSwk7i) 直达VIP4 | -| ![Malenia](https://cdn.wenanzhe.com/img/malenia.png!/scale/50) | [Malenia](https://malenia.iinti.cn/malenia-doc/) | Malenia企业级代理IP网关平台/代理IP分销软件 | +##### i. 基础ocr识别能力 -# 1.4.0版本更新内容 +主要用于识别单行文字,即文字部分占据图片的主体部分,例如常见的英数验证码等,本项目可以对中文、英文(随机大小写or通过设置结果范围圈定大小写)、数字以及部分特殊字符。 - 本次更新新增了两种滑块识别算法,算法非深度神经网络实现,仅使用opencv和PIL完成。 +```python +# example.py +import ddddocr - ## 算法1 - 小滑块为单独的png图片,背景是透明图,如下图 +ocr = ddddocr.DdddOcr() - ![Test](https://cdn.wenanzhe.com/img/b.png) +image = open("example.jpg", "rb").read() +result = ocr.classification(image) +print(result) +``` - 然后背景为带小滑块坑位的,如下图 - - ![Test](https://cdn.wenanzhe.com/img/a.png) +本库内置有两套ocr模型,默认情况下不会自动切换,需要在初始化ddddocr的时候通过参数进行切换 - ```python +```python +# example.py +import ddddocr + +ocr = ddddocr.DdddOcr(beta=True) # 切换为第二套ocr模型 + +image = open("example.jpg", "rb").read() +result = ocr.classification(image) +print(result) +``` + +**提示** +对于部分透明黑色png格式图片得识别支持: `classification` 方法 使用 `png_fix` 参数,默认为False + +```python + ocr.classification(image, png_fix=True) +``` + +**注意** + +之前发现很多人喜欢在每次ocr识别的时候都重新初始化ddddocr,即每次都执行```ocr = ddddocr.DdddOcr()```,这是错误的,通常来说只需要初始化一次即可,因为每次初始化和初始化后的第一次识别速度都非常慢 + + +**参考例图** + +包括且不限于以下图片 + +captcha +captcha +captcha +captcha +captcha +captcha +
+captcha +captcha +captcha +captcha +captcha +captcha + +##### ii. 目标检测能力 + +主要用于快速检测出图像中可能的目标主体位置,由于被检测出的目标不一定为文字,所以本功能仅提供目标的bbox位置 **(在⽬标检测⾥,我们通常使⽤bbox(bounding box,缩写是 bbox)来描述⽬标位置。bbox是⼀个矩形框,可以由矩形左上⻆的 x 和 y 轴坐标与右下⻆的 x 和 y 轴坐标确定)** + +如果使用过程中无需调用ocr功能,可以在初始化时通过传参`ocr=False`关闭ocr功能,开启目标检测需要传入参数`det=True` + +```python +import ddddocr +import cv2 + +det = ddddocr.DdddOcr(det=True) + +with open("test.jpg", 'rb') as f: + image = f.read() + +bboxes = det.detection(image) +print(bboxes) + +im = cv2.imread("test.jpg") + +for bbox in bboxes: + x1, y1, x2, y2 = bbox + im = cv2.rectangle(im, (x1, y1), (x2, y2), color=(0, 0, 255), thickness=2) + +cv2.imwrite("result.jpg", im) + +``` + + + +**参考例图** + +包括且不限于以下图片 + +captcha +captcha +captcha +captcha +
+captcha +captcha +captcha + +##### Ⅲ. 滑块检测 + +本项目的滑块检测功能并非AI识别实现,均为opencv内置算法实现。可能对于截图党用户没那么友好~,如果使用过程中无需调用ocr功能或目标检测功能,可以在初始化时通过传参`ocr=False`关闭ocr功能或`det=False`来关闭目标检测功能 + +本功能内置两套算法实现,适用于两种不同情况,具体请参考以下说明 + +**a.算法1** + +算法1原理是通过滑块图像的边缘在背景图中计算找到相对应的坑位,可以分别获取到滑块图和背景图,滑块图为透明背景图 + +滑块图 + +captcha + +背景图 + +captcha + +```python det = ddddocr.DdddOcr(det=False, ocr=False) with open('target.png', 'rb') as f: @@ -70,7 +244,10 @@ print(res) print(res) ``` - *提示:如果小图无过多背景部分,则可以添加simple_target参数, 通常为jpg或者bmp格式的图片* + 由于滑块图可能存在透明边框的问题,导致计算结果不一定准确,需要自行估算滑块图透明边框的宽度用于修正得出的bbox + + *提示:如果滑块无过多背景部分,则可以添加simple_target参数, 通常为jpg或者bmp格式的图片* + ```python slide = ddddocr.DdddOcr(det=False, ocr=False) @@ -84,16 +261,20 @@ print(res) print(res) ``` - ## 算法2 - 一张图为带坑位的原图,如下图 - ![Test](https://cdn.wenanzhe.com/img/bg.jpg) +**a.算法2** - 一张图为原图,如下图 - - ![Test](https://cdn.wenanzhe.com/img/fullpage.jpg) +算法2是通过比较两张图的不同之处进行判断滑块目标坑位的位置 - ```python +参考图a,带有目标坑位阴影的全图 + +captcha + +参考图b,全图 + +captcha + +```python slide = ddddocr.DdddOcr(det=False, ocr=False) with open('bg.jpg', 'rb') as f: @@ -109,165 +290,97 @@ print(res) print(res) ``` - ## 更新内容2 - 添加全局ocr关闭参数,初始化时传入 +##### Ⅳ. OCR概率输出 - `dddd = ddddocr.DdddOcr(ocr=False)` +为了提供更灵活的ocr结果控制与范围限定,项目支持对ocr结果进行范围限定。 - 则为关闭ocr功能,如果det = True,则会自动关闭ocr +可以通过在调用`classification`方法的时候传参`probability=True`,此时`classification`方法将返回全字符表的概率 +当然也可以通过`set_ranges`方法设置输出字符范围来限定返回的结果。 +Ⅰ. `set_ranges` 方法限定返回字符返回 -# 1.3.1版本更新内容 +本方法接受1个参数,如果输入为int类型为内置的字符集限制,string类型则为自定义的字符集 - 想必很多做验证码的新手,一定头疼碰到点选类型的图像,做样本费时费力,神经网络不会写,训练设备太昂贵,模型效果又不好。 +如果为int类型,请参考下表 - 市场上常见的点选类验证码图片如下图所示 +| 参数值 | 意义 | +|-----|-----------------------------------| +| 0 | 纯整数0-9 | +| 1 | 纯小写英文a-z | +| 2 | 纯大写英文A-Z | +| 3 | 小写英文a-z + 大写英文A-Z | +| 4 | 小写英文a-z + 整数0-9 | +| 5 | 大写英文A-Z + 整数0-9 | +| 6 | 小写英文a-z + 大写英文A-Z + 整数0-9 | +| 7 | 默认字符库 - 小写英文a-z - 大写英文A-Z - 整数0-9 | - - ![Test](https://cdn.wenanzhe.com/img/0446fe794381489f90719d5e0506f2da.jpg) - - ![Test](https://cdn.wenanzhe.com/img/6175e944c1dc408a89aabe4f7fc07fca.jpg) - - ![Test](https://cdn.wenanzhe.com/img/20211226135747.png) - - ![Test](https://cdn.wenanzhe.com/img/f34390d4911c45ce9058dc2e7e9d847a.jpg) - - 那么今天,他来了,ddddocr带着重磅更新大摇大摆的走来了。 -# 简介 - ddddocr是由sml2h3开发的专为验证码厂商进行对自家新版本验证码难易强度进行验证的一个python库,其由作者与kerlomz共同合作完成,通过大批量生成随机数据后进行深度网络训练,本身并非针对任何一家验证码厂商而制作,本库使用效果完全靠玄学,可能可以识别,可能不能识别。 - - ddddocr奉行着开箱即用、最简依赖的理念,尽量减少用户的配置和使用成本,希望给每一位测试者带来舒适的体验 - -项目地址: [点我传送](https://github.com/sml2h3/ddddocr) - -# 更新说明 - - 本次更新其实分为两部分,其中有一部分是在1.2.0版本就已经更新了,但是在这里还是有必要提一下的。 - -## 第一部分 OCR识别部分 - - 在1.2.0开始,ddddocr的识别部分进行了一次beta更新,主要更新在于网络结构主体的升级,其训练数据并没有发生过多的改变,所以理论上在识别结果上,原先可能识别效果的很好的图形在1.2.0上有一小部分概率会有一定程度的下降,也有可能原本识别不好的图形在1.2.0之后效果却变得特别好。 - 测试代码: - +如果为string类型请传入一段不包含空格的文本,其中的每个字符均为一个待选词 +如:`"0123456789+-x/=""` ```python import ddddocr ocr = ddddocr.DdddOcr() -with open("test.jpg", 'rb') as f: - image = f.read() +image = open("test.jpg", "rb").read() +ocr.set_ranges("0123456789+-x/=") +result = ocr.classification(image, probability=True) +s = "" +for i in result['probability']: + s += result['charsets'][i.index(max(i))] -res = ocr.classification(image) -print(res) -``` -通过在初始化ddddocr的时候使用beta参数即可快速切换新模型 - -```python -import ddddocr - -ocr = ddddocr.DdddOcr(beta=True) - -with open("test.jpg", 'rb') as f: - image = f.read() - -res = ocr.classification(image) -print(res) -``` - - OCR部分应该已经有很多人做了测试,在这里就放一部分网友的测试图片。 - - ![Test](https://cdn.wenanzhe.com/img/20210715211733855.png) - ![Test](https://cdn.wenanzhe.com/img/78b7f57d-371d-4b65-afb2-d19608ae1892.png) - ![Test](https://cdn.wenanzhe.com/img/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20211226142305.png) - ![Test](https://cdn.wenanzhe.com/img/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20211226142325.png) - ![Test](https://cdn.wenanzhe.com/img/2AMLyA_fd83e1f1800e829033417ae6dd0e0ae0.png) - ![Test](https://cdn.wenanzhe.com/img/aabd_181ae81dd5526b8b89f987d1179266ce.jpg) - ![Test](https://cdn.wenanzhe.com/img/2bghz_b504e9f9de1ed7070102d21c6481e0cf.png) - ![Test](https://cdn.wenanzhe.com/img/0000_z4ecc2p65rxc610x.jpg) - ![Test](https://cdn.wenanzhe.com/img/2acd_0586b6b36858a4e8a9939db8a7ec07b7.jpg) - ![Test](https://cdn.wenanzhe.com/img/2a8r_79074e311d573d31e1630978fe04b990.jpg) - ![Test](https://cdn.wenanzhe.com/img/aftf_C2vHZlk8540y3qAmCM.bmp) - ![Test](https://cdn.wenanzhe.com/img/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20211226144057.png) -等等更多图片等你测试哟~ - -## 第二部分 目标检测部分 - 在本次1.3.0的更新中,目标检测部分隆重登场! - 目标检测部分同样也是由大量随机合成数据训练而成,对于现在已有的点选验证码图片或者未知的验证码图片都有可能具备一定的识别能力,适用于文字点选和图标点选。 - 简单来说,对于点选类的验证码,可以快速的检测出图片上的文字或者图标。 - - -```python -import ddddocr -import cv2 - -det = ddddocr.DdddOcr(det=True) - -with open("test.jpg", 'rb') as f: - image = f.read() - -poses = det.detection(image) -print(poses) - -im = cv2.imread("test.jpg") - -for box in poses: - x1, y1, x2, y2 = box - im = cv2.rectangle(im, (x1, y1), (x2, y2), color=(0, 0, 255), thickness=2) - -cv2.imwrite("result.jpg", im) +print(s) ``` -举些例子: +##### Ⅴ. 自定义OCR训练模型导入 - ![Test](https://cdn.wenanzhe.com/img/page1_1.jpg) - ![Test](https://cdn.wenanzhe.com/img/page1_2.jpg) - ![Test](https://cdn.wenanzhe.com/img/page1_3.jpg) - ![Test](https://cdn.wenanzhe.com/img/page1_4.jpg) - ![Test](https://cdn.wenanzhe.com/img/result.jpg) - ![Test](https://cdn.wenanzhe.com/img/result2.jpg) - ![Test](https://cdn.wenanzhe.com/img/result4.jpg) +本项目支持导入来自于 [dddd_trainer](https://github.com/sml2h3/dddd_trainer) 进行自定义训练后的模型,参考导入代码为 -以上只是目前我能找到的点选验证码图片,做了一个简单的测试。 +```python +import ddddocr -# 安装 +ocr = ddddocr.DdddOcr(det=False, ocr=False, import_onnx_path="myproject_0.984375_139_13000_2022-02-26-15-34-13.onnx", charsets_path="charsets.json") -## 环境支持 +with open('test.jpg', 'rb') as f: + image_bytes = f.read() -`python <= 3.9` +res = ocr.classification(image_bytes) +print(res) -`Windows/Linux/Macos..` +``` -暂时不支持Macbook M1(X),M1(X)用户需要自己编译onnxruntime才可以使用 +### 版本控制 -## 安装命令 +该项目使用Git进行版本管理。您可以在repository参看当前可用版本。 -`pip install ddddocr` +### 作者 -以上命令将自动安装符合自己电脑环境的最新ddddocr - -## 拓展 一键部署ddddocr api,支持docker部署 - -[github](https://github.com/sml2h3/ocr_api_server) - -[gitee](https://gitee.com/fkgeek/ocr_api_server) - -## 爬虫框架推荐 - -[feapder](https://github.com/Boris-code/feapder) - -[crawlab](https://github.com/crawlab-team/crawlab) - -# 交流群 (个人微信太懒了不一定会通过) +sml2h3@gamil.com - ![六群链接](https://cdn.wenanzhe.com/img/group.jpg!/scale/50) - ![Test](https://cdn.wenanzhe.com/img/mmqrcode1640418911274.png!/scale/50) +wechat + + *好友数过多不一定通过,有问题可以在issue进行交流* + +### 版权说明 + +该项目签署了MIT 授权许可,详情请参阅 [LICENSE](https://github.com/sml2h3/ddddocr/blob/master/LICENSE) + + + +[your-project-path]:sml2h3/ddddocr +[contributors-shield]: https://img.shields.io/github/contributors/sml2h3/ddddocr?style=flat-square +[contributors-url]: https://github.com/shaojintian/Best_README_template/graphs/contributors +[forks-shield]: https://img.shields.io/github/forks/sml2h3/ddddocr?style=flat-square +[forks-url]: https://github.com/shaojintian/Best_README_template/network/members +[stars-shield]: https://img.shields.io/github/stars/sml2h3/ddddocr?style=flat-square +[stars-url]: https://github.com/shaojintian/Best_README_template/stargazers +[issues-shield]: https://img.shields.io/github/issues/sml2h3/ddddocr?style=flat-square +[issues-url]: https://img.shields.io/github/issues/sml2h3/ddddocr.svg +[license-shield]: https://img.shields.io/github/license/sml2h3/ddddocr?style=flat-square +[license-url]: https://github.com/sml2h3/ddddocr/blob/master/LICENSE + -# 主要贡献者 - -## Star 历史 -[![Star History Chart](https://api.star-history.com/svg?repos=sml2h3/ddddocr&type=Date)](https://star-history.com/#sml2h3/ddddocr&Date) diff --git a/ddddocr/__init__.py b/ddddocr/__init__.py index 28c27d6..f10e7fd 100644 --- a/ddddocr/__init__.py +++ b/ddddocr/__init__.py @@ -51,6 +51,7 @@ class DdddOcr(object): self.use_import_onnx = False self.__word = False self.__resize = [] + self.__charset_range = [] self.__channel = 1 if import_onnx_path != "": det = False @@ -2552,7 +2553,47 @@ class DdddOcr(object): return [] return result - def classification(self, img, png_fix: bool = False): + def set_ranges(self, charset_range: int | str): + if isinstance(charset_range, int): + if charset_range == 0: + # 数字 + self.__charset_range = list("0123456789") + elif charset_range == 1: + # 小写英文 + self.__charset_range = list("abcdefghijklmnopqrstuvwxyz") + elif charset_range == 2: + # 大写英文 + self.__charset_range = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + elif charset_range == 3: + # 混合英文 + self.__charset_range = list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + elif charset_range == 4: + # 小写英文+数字 + self.__charset_range = list("abcdefghijklmnopqrstuvwxyz") + list( + "0123456789") + elif charset_range == 5: + # 大写英文+数字 + self.__charset_range = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + list( + "0123456789") + elif charset_range == 6: + # 混合大小写+数字 + self.__charset_range = list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + list( + "0123456789") + elif charset_range == 7: + # 除去英文,数字 + delete_range = list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + list("0123456789") + self.__charset_range = [item for item in self.__charset if item not in delete_range] + elif isinstance(charset_range, str): + charset_range_list = list(charset_range) + self.__charset_range = charset_range_list + else: + raise TypeError("暂时不支持该类型数据的输入") + + # 去重 + self.__charset_range = list(set(self.__charset_range)) + [""] + + + def classification(self, img, png_fix: bool = False, probability=False): if self.det: raise TypeError("当前识别类型为目标检测") if not isinstance(img, (bytes, str, pathlib.PurePath, Image.Image)): @@ -2601,19 +2642,62 @@ class DdddOcr(object): result = [] last_item = 0 + if self.__word: for item in ort_outs[1]: result.append(self.__charset[item]) else: - for item in ort_outs[0][0]: - if item == last_item: - continue + if not self.use_import_onnx: + # 概率输出仅限于使用官方模型 + if probability: + ort_outs = ort_outs[0] + ort_outs = np.exp(ort_outs) / np.sum(np.exp(ort_outs)) + ort_outs_sum = np.sum(ort_outs, axis=2) + ort_outs_probability = np.empty_like(ort_outs) + for i in range(ort_outs.shape[0]): + ort_outs_probability[i] = ort_outs[i] / ort_outs_sum[i] + ort_outs_probability = np.squeeze(ort_outs_probability).tolist() + result = {} + if len(self.__charset_range) == 0: + # 返回全部 + result['charsets'] = self.__charset + result['probability'] = ort_outs_probability + else: + result['charsets'] = self.__charset_range + probability_result_index = [] + for item in self.__charset_range: + if item in self.__charset: + probability_result_index.append(self.__charset.index(item)) + else: + # 未知字符 + probability_result_index.append(-1) + probability_result = [] + for item in ort_outs_probability: + probability_result.append([item[i] if i != -1 else -1 for i in probability_result_index ]) + result['probability'] = probability_result + return result else: - last_item = item - if item != 0: - result.append(self.__charset[item]) + last_item = 0 + argmax_result = np.squeeze(np.argmax(ort_outs[0], axis=2)) + for item in argmax_result: + if item == last_item: + continue + else: + last_item = item + if item != 0: + result.append(self.__charset[item]) + return ''.join(result) - return ''.join(result) + else: + last_item = 0 + for item in ort_outs[0][0]: + if item == last_item: + continue + else: + last_item = item + if item != 0: + result.append(self.__charset[item]) + return ''.join(result) def detection(self, img_bytes: bytes = None, img_base64: str = None): if not self.det: diff --git a/ddddocr/common.onnx b/ddddocr/common.onnx index c615405ce8fd4c30ff3fc4c32b46888209cba0af..3f215d86742d5bbc6269939befddcf175a36b041 100644 GIT binary patch delta 3937 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 Rq=%D2;b2@WGq3b-@E;65sH*?~ delta 1018 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