diff --git a/README_ch.md b/README_ch.md index f837980498c4b2e2c55a44192b7d3a2c7afb0ba9..e801ce561cb41aafb376f81a3016f0a6b838320d 100755 --- a/README_ch.md +++ b/README_ch.md @@ -71,6 +71,8 @@ PaddleOCR旨在打造一套丰富、领先、且实用的OCR工具库,助力 ## 《动手学OCR》电子书 - [《动手学OCR》电子书📚](./doc/doc_ch/ocr_book.md) +## 场景应用 +- PaddleOCR场景应用覆盖通用,制造、金融、交通行业的主要OCR垂类应用,在PP-OCR、PP-Structure的通用能力基础之上,以notebook的形式展示利用场景数据微调、模型优化方法、数据增广等内容,为开发者快速落地OCR应用提供示范与启发。详情可查看[README](./applications)。 ## 开源社区 diff --git "a/applications/PCB\345\255\227\347\254\246\350\257\206\345\210\253/PCB\345\255\227\347\254\246\350\257\206\345\210\253.md" "b/applications/PCB\345\255\227\347\254\246\350\257\206\345\210\253/PCB\345\255\227\347\254\246\350\257\206\345\210\253.md" index a5052e2897ab9f09a6ed7b747f9fa1198af2a8ab..ee13bacffdb65e6300a034531a527fdca4ed29f9 100644 --- "a/applications/PCB\345\255\227\347\254\246\350\257\206\345\210\253/PCB\345\255\227\347\254\246\350\257\206\345\210\253.md" +++ "b/applications/PCB\345\255\227\347\254\246\350\257\206\345\210\253/PCB\345\255\227\347\254\246\350\257\206\345\210\253.md" @@ -206,7 +206,11 @@ Eval.dataset.transforms.DetResizeForTest: 尺寸 limit_type: 'min' ``` -然后执行评估代码 +如需获取已训练模型,请扫码填写问卷,加入PaddleOCR官方交流群获取全部OCR垂类模型下载链接、《动手学OCR》电子书等全套OCR学习资料🎁 +
+ +
+将下载或训练完成的模型放置在对应目录下即可完成模型评估。 ```python diff --git a/applications/README.md b/applications/README.md new file mode 100644 index 0000000000000000000000000000000000000000..eba1e205dc13dd226066784659bdb6f353e776ca --- /dev/null +++ b/applications/README.md @@ -0,0 +1,41 @@ +# 场景应用 + +PaddleOCR场景应用覆盖通用,制造、金融、交通行业的主要OCR垂类应用,在PP-OCR、PP-Structure的通用能力基础之上,以notebook的形式展示利用场景数据微调、模型优化方法、数据增广等内容,为开发者快速落地OCR应用提供示范与启发。 + +> 如需下载全部垂类模型,可以扫描下方二维码,关注公众号填写问卷后,加入PaddleOCR官方交流群获取20G OCR学习大礼包(内含《动手学OCR》电子书、课程回放视频、前沿论文等重磅资料) + +
+ +
+ + +> 如果您是企业开发者且未在下述场景中找到合适的方案,可以填写[OCR应用合作调研问卷](https://paddle.wjx.cn/vj/QwF7GKw.aspx),免费与官方团队展开不同层次的合作,包括但不限于问题抽象、确定技术方案、项目答疑、共同研发等。如果您已经使用PaddleOCR落地项目,也可以填写此问卷,与飞桨平台共同宣传推广,提升企业技术品宣。期待您的提交! + +## 通用 + +| 类别 | 亮点 | 类别 | 亮点 | +| ------------------------------------------------- | -------- | ---------- | ------------ | +| [高精度中文识别模型SVTR](./高精度中文识别模型.md) | 新增模型 | 手写体识别 | 新增字形支持 | + +## 制造 + +| 类别 | 亮点 | 类别 | 亮点 | +| ------------------------------------------------------------ | ------------------------------ | ------------------------------------------- | -------------------- | +| [数码管识别](./光功率计数码管字符识别/光功率计数码管字符识别.md) | 数码管数据合成、漏识别调优 | 电表识别 | 大分辨率图像检测调优 | +| [液晶屏读数识别](./液晶屏读数识别.md) | 检测模型蒸馏、Serving部署 | [PCB文字识别](./PCB字符识别/PCB字符识别.md) | 小尺寸文本检测与识别 | +| [包装生产日期](./包装生产日期识别.md) | 点阵字符合成、过曝过暗文字识别 | 液晶屏缺陷检测 | 非文字字符识别 | + +## 金融 + +| 类别 | 亮点 | 类别 | 亮点 | +| ------------------------------ | ------------------------ | ------------ | --------------------- | +| [表单VQA](./多模态表单识别.md) | 多模态通用表单结构化提取 | 通用卡证识别 | 通用结构化提取 | +| 增值税发票 | 尽请期待 | 身份证识别 | 结构化提取、图像阴影 | +| 印章检测与识别 | 端到端弯曲文本识别 | 合同比对 | 密集文本检测、NLP串联 | + +## 交通 + +| 类别 | 亮点 | 类别 | 亮点 | +| ------------------------------- | ------------------------------ | ---------- | -------- | +| [车牌识别](./轻量级车牌识别.md) | 多角度图像、轻量模型、端侧部署 | 快递单识别 | 尽请期待 | +| 驾驶证/行驶证识别 | 尽请期待 | | | \ No newline at end of file diff --git a/applications/corpus/digital.txt "b/applications/\345\205\211\345\212\237\347\216\207\350\256\241\346\225\260\347\240\201\347\256\241\345\255\227\347\254\246\350\257\206\345\210\253/corpus/digital.txt" similarity index 100% rename from applications/corpus/digital.txt rename to "applications/\345\205\211\345\212\237\347\216\207\350\256\241\346\225\260\347\240\201\347\256\241\345\255\227\347\254\246\350\257\206\345\210\253/corpus/digital.txt" diff --git a/applications/fonts/DS-DIGI.TTF "b/applications/\345\205\211\345\212\237\347\216\207\350\256\241\346\225\260\347\240\201\347\256\241\345\255\227\347\254\246\350\257\206\345\210\253/fonts/DS-DIGI.TTF" similarity index 100% rename from applications/fonts/DS-DIGI.TTF rename to "applications/\345\205\211\345\212\237\347\216\207\350\256\241\346\225\260\347\240\201\347\256\241\345\255\227\347\254\246\350\257\206\345\210\253/fonts/DS-DIGI.TTF" diff --git a/applications/fonts/DS-DIGIB.TTF "b/applications/\345\205\211\345\212\237\347\216\207\350\256\241\346\225\260\347\240\201\347\256\241\345\255\227\347\254\246\350\257\206\345\210\253/fonts/DS-DIGIB.TTF" similarity index 100% rename from applications/fonts/DS-DIGIB.TTF rename to "applications/\345\205\211\345\212\237\347\216\207\350\256\241\346\225\260\347\240\201\347\256\241\345\255\227\347\254\246\350\257\206\345\210\253/fonts/DS-DIGIB.TTF" diff --git "a/applications/\345\205\211\345\212\237\347\216\207\350\256\241\346\225\260\347\240\201\347\256\241\345\255\227\347\254\246\350\257\206\345\210\253.md" "b/applications/\345\205\211\345\212\237\347\216\207\350\256\241\346\225\260\347\240\201\347\256\241\345\255\227\347\254\246\350\257\206\345\210\253/\345\205\211\345\212\237\347\216\207\350\256\241\346\225\260\347\240\201\347\256\241\345\255\227\347\254\246\350\257\206\345\210\253.md" similarity index 100% rename from "applications/\345\205\211\345\212\237\347\216\207\350\256\241\346\225\260\347\240\201\347\256\241\345\255\227\347\254\246\350\257\206\345\210\253.md" rename to "applications/\345\205\211\345\212\237\347\216\207\350\256\241\346\225\260\347\240\201\347\256\241\345\255\227\347\254\246\350\257\206\345\210\253/\345\205\211\345\212\237\347\216\207\350\256\241\346\225\260\347\240\201\347\256\241\345\255\227\347\254\246\350\257\206\345\210\253.md" diff --git "a/applications/\345\214\205\350\243\205\347\224\237\344\272\247\346\227\245\346\234\237\350\257\206\345\210\253.md" "b/applications/\345\214\205\350\243\205\347\224\237\344\272\247\346\227\245\346\234\237\350\257\206\345\210\253.md" new file mode 100644 index 0000000000000000000000000000000000000000..73c174c4f954d7cd348b57fc5d6b00f6c5dab64b --- /dev/null +++ "b/applications/\345\214\205\350\243\205\347\224\237\344\272\247\346\227\245\346\234\237\350\257\206\345\210\253.md" @@ -0,0 +1,685 @@ +# 一种基于PaddleOCR的产品包装生产日期识别模型 + +- [1. 项目介绍](#1-项目介绍) +- [2. 环境搭建](#2-环境搭建) +- [3. 数据准备](#3-数据准备) +- [4. 直接使用PP-OCRv3模型评估](#4-直接使用PPOCRv3模型评估) +- [5. 基于合成数据finetune](#5-基于合成数据finetune) + - [5.1 Text Renderer数据合成方法](#51-TextRenderer数据合成方法) + - [5.1.1 下载Text Renderer代码](#511-下载TextRenderer代码) + - [5.1.2 准备背景图片](#512-准备背景图片) + - [5.1.3 准备语料](#513-准备语料) + - [5.1.4 下载字体](#514-下载字体) + - [5.1.5 运行数据合成命令](#515-运行数据合成命令) + - [5.2 模型训练](#52-模型训练) +- [6. 基于真实数据finetune](#6-基于真实数据finetune) + - [6.1 python爬虫获取数据](#61-python爬虫获取数据) + - [6.2 数据挖掘](#62-数据挖掘) + - [6.3 模型训练](#63-模型训练) +- [7. 基于合成+真实数据finetune](#7-基于合成+真实数据finetune) + + +## 1. 项目介绍 + +产品包装生产日期是计算机视觉图像识别技术在工业场景中的一种应用。产品包装生产日期识别技术要求能够将产品生产日期从复杂背景中提取并识别出来,在物流管理、物资管理中得到广泛应用。 + +![](https://ai-studio-static-online.cdn.bcebos.com/d9e0533cc1df47ffa3bbe99de9e42639a3ebfa5bce834bafb1ca4574bf9db684) + + +- 项目难点 + +1. 没有训练数据 +2. 图像质量层次不齐: 角度倾斜、图片模糊、光照不足、过曝等问题严重 + +针对以上问题, 本例选用PP-OCRv3这一开源超轻量OCR系统进行包装产品生产日期识别系统的开发。直接使用PP-OCRv3进行评估的精度为62.99%。为提升识别精度,我们首先使用数据合成工具合成了3k数据,基于这部分数据进行finetune,识别精度提升至73.66%。由于合成数据与真实数据之间的分布存在差异,为进一步提升精度,我们使用网络爬虫配合数据挖掘策略得到了1k带标签的真实数据,基于真实数据finetune的精度为71.33%。最后,我们综合使用合成数据和真实数据进行finetune,将识别精度提升至86.99%。各策略的精度提升效果如下: + +| 策略 | 精度| +| :--------------- | :-------- | +| PP-OCRv3评估 | 62.99| +| 合成数据finetune | 73.66| +| 真实数据finetune | 71.33| +| 真实+合成数据finetune | 86.99| + +AIStudio项目链接: [一种基于PaddleOCR的包装生产日期识别方法](https://aistudio.baidu.com/aistudio/projectdetail/4287736) + +## 2. 环境搭建 + +本任务基于Aistudio完成, 具体环境如下: + +- 操作系统: Linux +- PaddlePaddle: 2.3 +- PaddleOCR: Release/2.5 +- text_renderer: master + +下载PaddlleOCR代码并安装依赖库: +```bash +git clone -b dygraph https://gitee.com/paddlepaddle/PaddleOCR + +# 安装依赖库 +cd PaddleOCR +pip install -r PaddleOCR/requirements.txt +``` + +## 3. 数据准备 + +本项目使用人工预标注的300张图像作为测试集。 + +部分数据示例如下: + +![](https://ai-studio-static-online.cdn.bcebos.com/39ff30e0ab0442579712255e6a9ea6b5271169c98e624e6eb2b8781f003bfea0) + + +标签文件格式如下: +```txt +数据路径 标签(中间以制表符分隔) +``` + +|数据集类型|数量| +|---|---| +|测试集| 300| + +数据集[下载链接](https://aistudio.baidu.com/aistudio/datasetdetail/149770),下载后可以通过下方命令解压: + +```bash +tar -xvf data.tar +mv data ${PaddleOCR_root} +``` + +数据解压后的文件结构如下: + +```shell +PaddleOCR +├── data +│ ├── mining_images # 挖掘的真实数据示例 +│ ├── mining_train.list # 挖掘的真实数据文件列表 +│ ├── render_images # 合成数据示例 +│ ├── render_train.list # 合成数据文件列表 +│ ├── val # 测试集数据 +│ └── val.list # 测试集数据文件列表 +| ├── bg # 合成数据所需背景图像 +│ └── corpus # 合成数据所需语料 +``` + +## 4. 直接使用PP-OCRv3模型评估 + +准备好测试数据后,可以使用PaddleOCR的PP-OCRv3模型进行识别。 + +- 下载预训练模型 + +首先需要下载PP-OCR v3中英文识别模型文件,下载链接可以在https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.5/doc/doc_ch/ppocr_introduction.md#6 获取,下载命令: + +```bash +cd ${PaddleOCR_root} +mkdir ckpt +wget -nc -P ckpt https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_rec_train.tar +pushd ckpt/ +tar -xvf ch_PP-OCRv3_rec_train.tar +popd +``` + +- 模型评估 + +使用以下命令进行PP-OCRv3评估: + +```bash +python tools/eval.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml \ + -o Global.checkpoints=ckpt/ch_PP-OCRv3_rec_train/best_accuracy \ + Eval.dataset.data_dir=./data \ + Eval.dataset.label_file_list=["./data/val.list"] + +``` + +其中各参数含义如下: + +```bash +-c: 指定使用的配置文件,ch_PP-OCRv3_rec_distillation.yml对应于OCRv3识别模型。 +-o: 覆盖配置文件中参数 +Global.checkpoints: 指定评估使用的模型文件路径 +Eval.dataset.data_dir: 指定评估数据集路径 +Eval.dataset.label_file_list: 指定评估数据集文件列表 +``` + +## 5. 基于合成数据finetune + +### 5.1 Text Renderer数据合成方法 + +#### 5.1.1 下载Text Renderer代码 + +首先从github或gitee下载Text Renderer代码,并安装相关依赖。 + +```bash +git clone https://gitee.com/wowowoll/text_renderer.git + +# 安装依赖库 +cd text_renderer +pip install -r requirements.txt +``` + +使用text renderer合成数据之前需要准备好背景图片、语料以及字体库,下面将逐一介绍各个步骤。 + +#### 5.1.2 准备背景图片 + +观察日常生活中常见的包装生产日期图片,我们可以发现其背景相对简单。为此我们可以从网上找一下图片,截取部分图像块作为背景图像。 + +本项目已准备了部分图像作为背景图片,在第3部分完成数据准备后,可以得到我们准备好的背景图像,示例如下: + +![](https://ai-studio-static-online.cdn.bcebos.com/456ae2acb27d4a94896c478812aee0bc3551c703d7bd40c9be4dc983c7b3fc8a) + + + +背景图像存放于如下位置: + +```shell +PaddleOCR +├── data +| ├── bg # 合成数据所需背景图像 +``` + +#### 5.1.3 准备语料 + +观察测试集生产日期图像,我们可以知道如下数据有如下特点: +1. 由年月日组成,中间可能以“/”、“-”、“:”、“.”或者空格间隔,也可能以汉字年月日分隔 +2. 有些生产日期包含在产品批号中,此时可能包含具体时间、英文字母或数字标识 + +基于以上两点,我们编写语料生成脚本: + +```python +import random +from random import choice +import os + +cropus_num = 2000 #设置语料数量 + +def get_cropus(f): + # 随机生成年份 + year = random.randint(0, 22) + # 随机生成月份 + month = random.randint(1, 12) + # 随机生成日期 + day_dict = {31: [1,3,5,7,8,10,12], 30: [4,6,9,11], 28: [2]} + for item in day_dict: + if month in day_dict[item]: + day = random.randint(0, item) + # 随机生成小时 + hours = random.randint(0, 24) + # 随机生成分钟 + minute = random.randint(0, 60) + # 随机生成秒数 + second = random.randint(0, 60) + + # 随机生成产品标识字符 + length = random.randint(0, 6) + file_id = [] + flag = 0 + my_dict = [i for i in range(48,58)] + [j for j in range(40, 42)] + [k for k in range(65,90)] # 大小写字母 + 括号 + + for i in range(1, length): + if flag: + if i == flag+2: #括号匹配 + file_id.append(')') + flag = 0 + continue + sel = choice(my_dict) + if sel == 41: + continue + if sel == 40: + if i == 1 or i > length-3: + continue + flag = i + my_ascii = chr(sel) + file_id.append(my_ascii) + file_id_str = ''.join(file_id) + + #随机生成产品标识字符 + file_id2 = random.randint(0, 9) + + rad = random.random() + if rad < 0.3: + f.write('20{:02d}{:02d}{:02d} {}'.format(year, month, day, file_id_str)) + elif 0.3 < rad < 0.5: + f.write('20{:02d}年{:02d}月{:02d}日'.format(year, month, day)) + elif 0.5 < rad < 0.7: + f.write('20{:02d}/{:02d}/{:02d}'.format(year, month, day)) + elif 0.7 < rad < 0.8: + f.write('20{:02d}-{:02d}-{:02d}'.format(year, month, day)) + elif 0.8 < rad < 0.9: + f.write('20{:02d}.{:02d}.{:02d}'.format(year, month, day)) + else: + f.write('{:02d}:{:02d}:{:02d} {:02d}'.format(hours, minute, second, file_id2)) + +if __name__ == "__main__": + file_path = '/home/aistudio/text_renderer/my_data/cropus' + if not os.path.exists(file_path): + os.makedirs(file_path) + file_name = os.path.join(file_path, 'books.txt') + f = open(file_name, 'w') + for i in range(cropus_num): + get_cropus(f) + if i < cropus_num-1: + f.write('\n') + + f.close() +``` + +本项目已准备了部分语料,在第3部分完成数据准备后,可以得到我们准备好的语料库,默认位置如下: + +```shell +PaddleOCR +├── data +│ └── corpus #合成数据所需语料 +``` + +#### 5.1.4 下载字体 + +观察包装生产日期,我们可以发现其使用的字体为点阵体。字体可以在如下网址下载: +https://www.fonts.net.cn/fonts-en/tag-dianzhen-1.html + +本项目已准备了部分字体,在第3部分完成数据准备后,可以得到我们准备好的字体,默认位置如下: + +```shell +PaddleOCR +├── data +│ └── fonts #合成数据所需字体 +``` + +下载好字体后,还需要在list文件中指定字体文件存放路径,脚本如下: + +```bash +cd text_renderer/my_data/ +touch fonts.list +ls /home/aistudio/PaddleOCR/data/fonts/* > fonts.list +``` + +#### 5.1.5 运行数据合成命令 + +完成数据准备后,my_data文件结构如下: + +```shell +my_data/ +├── cropus +│ └── books.txt #语料库 +├── eng.txt #字符列表 +└── fonts.list #字体列表 +``` + +在运行合成数据命令之前,还有两处细节需要手动修改: +1. 将默认配置文件`text_renderer/configs/default.yaml`中第9行enable的值设为`true`,即允许合成彩色图像。否则合成的都是灰度图。 + +```yaml + # color boundary is in R,G,B format + font_color: ++ enable: true #false +``` + +2. 将`text_renderer/textrenderer/renderer.py`第184行作如下修改,取消padding。否则图片两端会有一些空白。 + +```python +padding = random.randint(s_bbox_width // 10, s_bbox_width // 8) #修改前 +padding = 0 #修改后 +``` + +运行数据合成命令: + +```bash +cd /home/aistudio/text_renderer/ +python main.py --num_img=3000 \ + --fonts_list='./my_data/fonts.list' \ + --corpus_dir "./my_data/cropus" \ + --corpus_mode "list" \ + --bg_dir "/home/aistudio/PaddleOCR/data/bg/" \ + --img_width 0 +``` + +合成好的数据默认保存在`text_renderer/output`目录下,可进入该目录查看合成的数据。 + + +合成数据示例如下 +![](https://ai-studio-static-online.cdn.bcebos.com/d686a48d465a43d09fbee51924fdca42ee21c50e676646da8559fb9967b94185) + +数据合成好后,还需要生成如下格式的训练所需的标注文件, +``` +图像路径 标签 +``` + +使用如下脚本即可生成标注文件: + +```python +import random + +abspath = '/home/aistudio/text_renderer/output/default/' + +#标注文件生成路径 +fout = open('./render_train.list', 'w', encoding='utf-8') + +with open('./output/default/tmp_labels.txt','r') as f: + lines = f.readlines() + for item in lines: + label = item[9:] + filename = item[:8] + '.jpg' + fout.write(abspath + filename + '\t' + label) + + fout.close() +``` + +经过以上步骤,我们便完成了包装生产日期数据合成。 +数据位于`text_renderer/output`,标注文件位于`text_renderer/render_train.list`。 + +本项目提供了生成好的数据供大家体验,完成步骤3的数据准备后,可得数据路径位于: + +```shell +PaddleOCR +├── data +│ ├── render_images # 合成数据示例 +│ ├── render_train.list #合成数据文件列表 +``` + +### 5.2 模型训练 + +准备好合成数据后,我们可以使用以下命令,利用合成数据进行finetune: +```bash +cd ${PaddleOCR_root} +python tools/train.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml \ + -o Global.pretrained_model=./ckpt/ch_PP-OCRv3_rec_train/best_accuracy \ + Global.epoch_num=20 \ + Global.eval_batch_step='[0, 20]' \ + Train.dataset.data_dir=./data \ + Train.dataset.label_file_list=['./data/render_train.list'] \ + Train.loader.batch_size_per_card=64 \ + Eval.dataset.data_dir=./data \ + Eval.dataset.label_file_list=["./data/val.list"] \ + Eval.loader.batch_size_per_card=64 + +``` + +其中各参数含义如下: + +```txt +-c: 指定使用的配置文件,ch_PP-OCRv3_rec_distillation.yml对应于OCRv3识别模型。 +-o: 覆盖配置文件中参数 +Global.pretrained_model: 指定finetune使用的预训练模型 +Global.epoch_num: 指定训练的epoch数 +Global.eval_batch_step: 间隔多少step做一次评估 +Train.dataset.data_dir: 训练数据集路径 +Train.dataset.label_file_list: 训练集文件列表 +Train.loader.batch_size_per_card: 训练单卡batch size +Eval.dataset.data_dir: 评估数据集路径 +Eval.dataset.label_file_list: 评估数据集文件列表 +Eval.loader.batch_size_per_card: 评估单卡batch size +``` + +## 6. 基于真实数据finetune + + +使用合成数据finetune能提升我们模型的识别精度,但由于合成数据和真实数据之间的分布可能有一定差异,因此作用有限。为进一步提高识别精度,本节介绍如何挖掘真实数据进行模型finetune。 + +数据挖掘的整体思路如下: +1. 使用python爬虫从网上获取大量无标签数据 +2. 使用模型从大量无标签数据中构建出有效训练集 + +### 6.1 python爬虫获取数据 + +- 推荐使用[爬虫工具](https://github.com/Joeclinton1/google-images-download)获取无标签图片。 + +图片获取后,可按如下目录格式组织: + +```txt +sprider +├── file.list +├── data +│ ├── 00000.jpg +│ ├── 00001.jpg +... +``` + +### 6.2 数据挖掘 + +我们使用PaddleOCR对获取到的图片进行挖掘,具体步骤如下: +1. 使用 PP-OCRv3检测模型+svtr-tiny识别模型,对每张图片进行预测。 +2. 使用数据挖掘策略,得到有效图片。 +3. 将有效图片对应的图像区域和标签提取出来,构建训练集。 + + +首先下载预训练模型,PP-OCRv3检测模型下载链接:https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_det_infer.tar + +如需获取svtr-tiny高精度中文识别预训练模型,请扫码填写问卷,加入PaddleOCR官方交流群获取全部OCR垂类模型下载链接、《动手学OCR》电子书等全套OCR学习资料🎁 +
+ +
+ + +完成下载后,可将模型存储于如下位置: + +```shell +PaddleOCR +├── data +│ ├── rec_vit_sub_64_363_all/ # svtr_tiny高精度识别模型 +``` + +```bash +# 下载解压PP-OCRv3检测模型 +cd ${PaddleOCR_root} +wget -nc -P ckpt https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_det_infer.tar +pushd ckpt +tar -xvf ch_PP-OCRv3_det_infer.tar +popd ckpt +``` + +在使用PPOCRv3检测模型+svtr-tiny识别模型进行预测之前,有如下两处细节需要手动修改: +1. 将`tools/infer/predict_rec.py`中第110行`imgW`修改为`320` + +```python +#imgW = int((imgH * max_wh_ratio)) +imgW = 320 +``` + +2. 将`tools/infer/predict_system.py`第169行添加如下一行,将预测分数也写入结果文件中。 + +```python +"scores": rec_res[idx][1], +``` + +模型预测命令: +```bash +python tools/infer/predict_system.py \ + --image_dir="/home/aistudio/sprider/data" \ + --det_model_dir="./ckpt/ch_PP-OCRv3_det_infer/" \ + --rec_model_dir="/home/aistudio/PaddleOCR/data/rec_vit_sub_64_363_all/" \ + --rec_image_shape="3,32,320" +``` + +获得预测结果后,我们使用数据挖掘策略得到有效图片。具体挖掘策略如下: +1. 预测置信度高于95% +2. 识别结果包含字符‘20’,即年份 +3. 没有中文,或者有中文并且‘日’和'月'同时在识别结果中 + +```python +# 获取有效预测 + +import json +import re + +zh_pattern = re.compile(u'[\u4e00-\u9fa5]+') #正则表达式,筛选字符是否包含中文 + +file_path = '/home/aistudio/PaddleOCR/inference_results/system_results.txt' +out_path = '/home/aistudio/PaddleOCR/selected_results.txt' +f_out = open(out_path, 'w') + +with open(file_path, "r", encoding='utf-8') as fin: + lines = fin.readlines() + + +for line in lines: + flag = False + # 读取文件内容 + file_name, json_file = line.strip().split('\t') + preds = json.loads(json_file) + res = [] + for item in preds: + transcription = item['transcription'] #获取识别结果 + scores = item['scores'] #获取识别得分 + # 挖掘策略 + if scores > 0.95: + if '20' in transcription and len(transcription) > 4 and len(transcription) < 12: + word = transcription + if not(zh_pattern.search(word) and ('日' not in word or '月' not in word)): + flag = True + res.append(item) + save_pred = file_name + "\t" + json.dumps( + res, ensure_ascii=False) + "\n" + if flag ==True: + f_out.write(save_pred) + +f_out.close() +``` + +然后将有效预测对应的图像区域和标签提取出来,构建训练集。具体实现脚本如下: + +```python +import cv2 +import json +import numpy as np + +PATH = '/home/aistudio/PaddleOCR/inference_results/' #数据原始路径 +SAVE_PATH = '/home/aistudio/mining_images/' #裁剪后数据保存路径 +file_list = '/home/aistudio/PaddleOCR/selected_results.txt' #数据预测结果 +label_file = '/home/aistudio/mining_images/mining_train.list' #输出真实数据训练集标签list + +if not os.path.exists(SAVE_PATH): + os.mkdir(SAVE_PATH) + +f_label = open(label_file, 'w') + + +def get_rotate_crop_image(img, points): + """ + 根据检测结果points,从输入图像img中裁剪出相应的区域 + """ + assert len(points) == 4, "shape of points must be 4*2" + img_crop_width = int( + max( + np.linalg.norm(points[0] - points[1]), + np.linalg.norm(points[2] - points[3]))) + img_crop_height = int( + max( + np.linalg.norm(points[0] - points[3]), + np.linalg.norm(points[1] - points[2]))) + pts_std = np.float32([[0, 0], [img_crop_width, 0], + [img_crop_width, img_crop_height], + [0, img_crop_height]]) + M = cv2.getPerspectiveTransform(points, pts_std) + # 形变或倾斜,会做透视变换,reshape成矩形 + dst_img = cv2.warpPerspective( + img, + M, (img_crop_width, img_crop_height), + borderMode=cv2.BORDER_REPLICATE, + flags=cv2.INTER_CUBIC) + dst_img_height, dst_img_width = dst_img.shape[0:2] + if dst_img_height * 1.0 / dst_img_width >= 1.5: + dst_img = np.rot90(dst_img) + return dst_img + +def crop_and_get_filelist(file_list): + with open(file_list, "r", encoding='utf-8') as fin: + lines = fin.readlines() + + img_num = 0 + for line in lines: + img_name, json_file = line.strip().split('\t') + preds = json.loads(json_file) + for item in preds: + transcription = item['transcription'] + points = item['points'] + points = np.array(points).astype('float32') + #print('processing {}...'.format(img_name)) + + img = cv2.imread(PATH+img_name) + dst_img = get_rotate_crop_image(img, points) + h, w, c = dst_img.shape + newWidth = int((32. / h) * w) + newImg = cv2.resize(dst_img, (newWidth, 32)) + new_img_name = '{:05d}.jpg'.format(img_num) + cv2.imwrite(SAVE_PATH+new_img_name, dst_img) + f_label.write(SAVE_PATH+new_img_name+'\t'+transcription+'\n') + img_num += 1 + + +crop_and_get_filelist(file_list) +f_label.close() +``` + +### 6.3 模型训练 + +通过数据挖掘,我们得到了真实场景数据和对应的标签。接下来使用真实数据finetune,观察精度提升效果。 + + +利用真实数据进行finetune: + +```bash +cd ${PaddleOCR_root} +python tools/train.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml \ + -o Global.pretrained_model=./ckpt/ch_PP-OCRv3_rec_train/best_accuracy \ + Global.epoch_num=20 \ + Global.eval_batch_step='[0, 20]' \ + Train.dataset.data_dir=./data \ + Train.dataset.label_file_list=['./data/mining_train.list'] \ + Train.loader.batch_size_per_card=64 \ + Eval.dataset.data_dir=./data \ + Eval.dataset.label_file_list=["./data/val.list"] \ + Eval.loader.batch_size_per_card=64 +``` + +各参数含义参考第6部分合成数据finetune,只需要对训练数据路径做相应的修改: + +```txt +Train.dataset.data_dir: 训练数据集路径 +Train.dataset.label_file_list: 训练集文件列表 +``` + +示例使用我们提供的真实数据进行finetune,如想换成自己的数据,只需要相应的修改`Train.dataset.data_dir`和`Train.dataset.label_file_list`参数即可。 + +由于数据量不大,这里仅训练20个epoch即可。训练完成后,可以得到合成数据finetune后的精度为best acc=**71.33%**。 + +由于数量比较少,精度会比合成数据finetue的略低。 + + +## 7. 基于合成+真实数据finetune + +为进一步提升模型精度,我们结合使用合成数据和挖掘到的真实数据进行finetune。 + +利用合成+真实数据进行finetune,各参数含义参考第6部分合成数据finetune,只需要对训练数据路径做相应的修改: + +```txt +Train.dataset.data_dir: 训练数据集路径 +Train.dataset.label_file_list: 训练集文件列表 +``` + +生成训练list文件: +```bash +# 生成训练集文件list +cat /home/aistudio/PaddleOCR/data/render_train.list /home/aistudio/PaddleOCR/data/mining_train.list > /home/aistudio/PaddleOCR/data/render_mining_train.list +``` + +启动训练: +```bash +cd ${PaddleOCR_root} +python tools/train.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml \ + -o Global.pretrained_model=./ckpt/ch_PP-OCRv3_rec_train/best_accuracy \ + Global.epoch_num=40 \ + Global.eval_batch_step='[0, 20]' \ + Train.dataset.data_dir=./data \ + Train.dataset.label_file_list=['./data/render_mining_train.list'] \ + Train.loader.batch_size_per_card=64 \ + Eval.dataset.data_dir=./data \ + Eval.dataset.label_file_list=["./data/val.list"] \ + Eval.loader.batch_size_per_card=64 +``` + +示例使用我们提供的真实+合成数据进行finetune,如想换成自己的数据,只需要相应的修改Train.dataset.data_dir和Train.dataset.label_file_list参数即可。 + +由于数据量不大,这里仅训练40个epoch即可。训练完成后,可以得到合成数据finetune后的精度为best acc=**86.99%**。 + +可以看到,相较于原始PP-OCRv3的识别精度62.99%,使用合成数据+真实数据finetune后,识别精度能提升24%。 + +如需获取已训练模型,可以同样扫描上方二维码下载,将下载或训练完成的模型放置在对应目录下即可完成模型推理。 + +模型的推理部署方法可以参考repo文档: https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.5/deploy/README_ch.md diff --git "a/applications/\345\244\232\346\250\241\346\200\201\350\241\250\345\215\225\350\257\206\345\210\253.md" "b/applications/\345\244\232\346\250\241\346\200\201\350\241\250\345\215\225\350\257\206\345\210\253.md" index d47bbe77045d502d82a5a8d8b8eca685963e6380..471ca633c143635b1715f054efa6924c1d0a1eab 100644 --- "a/applications/\345\244\232\346\250\241\346\200\201\350\241\250\345\215\225\350\257\206\345\210\253.md" +++ "b/applications/\345\244\232\346\250\241\346\200\201\350\241\250\345\215\225\350\257\206\345\210\253.md" @@ -1,4 +1,33 @@ -# 1 项目说明 +# 多模态表单识别 +- [多模态表单识别](#多模态表单识别) + - [1 项目说明](#1-项目说明) + - [2 安装说明](#2-安装说明) + - [3 数据准备](#3-数据准备) + - [3.1 下载处理好的数据集](#31-下载处理好的数据集) + - [3.2 转换为PaddleOCR检测和识别格式](#32-转换为paddleocr检测和识别格式) + - [4 OCR](#4-ocr) + - [4.1 文本检测](#41-文本检测) + - [4.1.1 方案1:预训练模型](#411-方案1预训练模型) + - [4.1.2 方案2:XFUND数据集+fine-tune](#412-方案2xfund数据集fine-tune) + - [4.2 文本识别](#42-文本识别) + - [4.2.1 方案1:预训练模型](#421-方案1预训练模型) + - [4.2.2 方案2:XFUND数据集+finetune](#422-方案2xfund数据集finetune) + - [4.2.3 方案3:XFUND数据集+finetune+真实通用识别数据](#423-方案3xfund数据集finetune真实通用识别数据) + - [5 文档视觉问答(DOC-VQA)](#5-文档视觉问答doc-vqa) + - [5.1 SER](#51-ser) + - [5.1.1 模型训练](#511-模型训练) + - [5.1.2 模型评估](#512-模型评估) + - [5.1.3 模型预测](#513-模型预测) + - [5.2 RE](#52-re) + - [5.2.1 模型训练](#521-模型训练) + - [5.2.2 模型评估](#522-模型评估) + - [5.2.3 模型预测](#523-模型预测) + - [6 导出Excel](#6-导出excel) + - [获得模型](#获得模型) + - [更多资源](#更多资源) + - [参考链接](#参考链接) + +## 1 项目说明 计算机视觉在金融领域的应用覆盖文字识别、图像识别、视频识别等,其中文字识别(OCR)是金融领域中的核心AI能力,其应用覆盖客户服务、风险防控、运营管理等各项业务,针对的对象包括通用卡证票据识别(银行卡、身份证、营业执照等)、通用文本表格识别(印刷体、多语言、手写体等)以及一些金融特色票据凭证。通过因此如果能够在结构化信息提取时同时利用文字、页面布局等信息,便可增强不同版式下的泛化性。 @@ -16,39 +45,37 @@
图1 多模态表单识别流程图
-注:欢迎再AIStudio领取免费算力体验线上实训,项目链接: [多模态表单识别](https://aistudio.baidu.com/aistudio/projectdetail/3884375)(配备Tesla V100、A100等高级算力资源) +注:欢迎再AIStudio领取免费算力体验线上实训,项目链接: [多模态表单识别](https://aistudio.baidu.com/aistudio/projectdetail/3884375?contributionType=1) - - -# 2 安装说明 +## 2 安装说明 下载PaddleOCR源码,上述AIStudio项目中已经帮大家打包好的PaddleOCR(已经修改好配置文件),无需下载解压即可,只需安装依赖环境~ ```python -! unzip -q PaddleOCR.zip +unzip -q PaddleOCR.zip ``` ```python # 如仍需安装or安装更新,可以执行以下步骤 -# ! git clone https://github.com/PaddlePaddle/PaddleOCR.git -b dygraph -# ! git clone https://gitee.com/PaddlePaddle/PaddleOCR +# git clone https://github.com/PaddlePaddle/PaddleOCR.git -b dygraph +# git clone https://gitee.com/PaddlePaddle/PaddleOCR ``` ```python # 安装依赖包 -! pip install -U pip -! pip install -r /home/aistudio/PaddleOCR/requirements.txt -! pip install paddleocr +pip install -U pip +pip install -r /home/aistudio/PaddleOCR/requirements.txt +pip install paddleocr -! pip install yacs gnureadline paddlenlp==2.2.1 -! pip install xlsxwriter +pip install yacs gnureadline paddlenlp==2.2.1 +pip install xlsxwriter ``` -# 3 数据准备 +## 3 数据准备 这里使用[XFUN数据集](https://github.com/doc-analysis/XFUND)做为实验数据集。 XFUN数据集是微软提出的一个用于KIE任务的多语言数据集,共包含七个数据集,每个数据集包含149张训练集和50张验证集 @@ -59,7 +86,7 @@
图2 数据集样例,左中文,右法语
-## 3.1 下载处理好的数据集 +### 3.1 下载处理好的数据集 处理好的XFUND中文数据集下载地址:[https://paddleocr.bj.bcebos.com/dataset/XFUND.tar](https://paddleocr.bj.bcebos.com/dataset/XFUND.tar) ,可以运行如下指令完成中文数据集下载和解压。 @@ -69,13 +96,13 @@ ```python -! wget https://paddleocr.bj.bcebos.com/dataset/XFUND.tar -! tar -xf XFUND.tar +wget https://paddleocr.bj.bcebos.com/dataset/XFUND.tar +tar -xf XFUND.tar # XFUN其他数据集使用下面的代码进行转换 # 代码链接:https://github.com/PaddlePaddle/PaddleOCR/blob/release%2F2.4/ppstructure/vqa/helper/trans_xfun_data.py # %cd PaddleOCR -# !python3 ppstructure/vqa/tools/trans_xfun_data.py --ori_gt_path=path/to/json_path --output_path=path/to/save_path +# python3 ppstructure/vqa/tools/trans_xfun_data.py --ori_gt_path=path/to/json_path --output_path=path/to/save_path # %cd ../ ``` @@ -119,7 +146,7 @@ } ``` -## 3.2 转换为PaddleOCR检测和识别格式 +### 3.2 转换为PaddleOCR检测和识别格式 使用XFUND训练PaddleOCR检测和识别模型,需要将数据集格式改为训练需求的格式。 @@ -147,7 +174,7 @@ train_data/rec/train/word_002.jpg 用科技让复杂的世界更简单 ```python -! unzip -q /home/aistudio/data/data140302/XFUND_ori.zip -d /home/aistudio/data/data140302/ +unzip -q /home/aistudio/data/data140302/XFUND_ori.zip -d /home/aistudio/data/data140302/ ``` 已经提供转换脚本,执行如下代码即可转换成功: @@ -155,21 +182,20 @@ train_data/rec/train/word_002.jpg 用科技让复杂的世界更简单 ```python %cd /home/aistudio/ -! python trans_xfund_data.py +python trans_xfund_data.py ``` -# 4 OCR +## 4 OCR 选用飞桨OCR开发套件[PaddleOCR](https://github.com/PaddlePaddle/PaddleOCR/blob/dygraph/README_ch.md)中的PP-OCRv2模型进行文本检测和识别。PP-OCRv2在PP-OCR的基础上,进一步在5个方面重点优化,检测模型采用CML协同互学习知识蒸馏策略和CopyPaste数据增广策略;识别模型采用LCNet轻量级骨干网络、UDML 改进知识蒸馏策略和[Enhanced CTC loss](https://github.com/PaddlePaddle/PaddleOCR/blob/dygraph/doc/doc_ch/enhanced_ctc_loss.md)损失函数改进,进一步在推理速度和预测效果上取得明显提升。更多细节请参考PP-OCRv2[技术报告](https://arxiv.org/abs/2109.03144)。 - -## 4.1 文本检测 +### 4.1 文本检测 我们使用2种方案进行训练、评估: - **PP-OCRv2中英文超轻量检测预训练模型** - **XFUND数据集+fine-tune** -### **4.1.1 方案1:预训练模型** +#### 4.1.1 方案1:预训练模型 **1)下载预训练模型** @@ -195,8 +221,8 @@ PaddleOCR已经提供了PP-OCR系列模型,部分模型展示如下表所示 ```python %cd /home/aistudio/PaddleOCR/pretrain/ -! wget https://paddleocr.bj.bcebos.com/PP-OCRv2/chinese/ch_PP-OCRv2_det_distill_train.tar -! tar -xf ch_PP-OCRv2_det_distill_train.tar && rm -rf ch_PP-OCRv2_det_distill_train.tar +wget https://paddleocr.bj.bcebos.com/PP-OCRv2/chinese/ch_PP-OCRv2_det_distill_train.tar +tar -xf ch_PP-OCRv2_det_distill_train.tar && rm -rf ch_PP-OCRv2_det_distill_train.tar % cd .. ``` @@ -226,7 +252,7 @@ Eval.dataset.label_file_list:指向验证集标注文件 ```python %cd /home/aistudio/PaddleOCR -! python tools/eval.py \ +python tools/eval.py \ -c configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_distill.yml \ -o Global.checkpoints="./pretrain_models/ch_PP-OCRv2_det_distill_train/best_accuracy" ``` @@ -237,9 +263,9 @@ Eval.dataset.label_file_list:指向验证集标注文件 | -------- | -------- | | PP-OCRv2中英文超轻量检测预训练模型 | 77.26% | -使用文本检测预训练模型在XFUND验证集上评估,达到77%左右,充分说明ppocr提供的预训练模型有一定的泛化能力。 +使用文本检测预训练模型在XFUND验证集上评估,达到77%左右,充分说明ppocr提供的预训练模型具有泛化能力。 -### **4.1.2 方案2:XFUND数据集+fine-tune** +#### 4.1.2 方案2:XFUND数据集+fine-tune PaddleOCR提供的蒸馏预训练模型包含了多个模型的参数,我们提取Student模型的参数,在XFUND数据集上进行finetune,可以参考如下代码: @@ -281,7 +307,7 @@ Eval.dataset.transforms.DetResizeForTest:评估尺寸,添加如下参数 ```python -! CUDA_VISIBLE_DEVICES=0 python tools/train.py \ +CUDA_VISIBLE_DEVICES=0 python tools/train.py \ -c configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_student.yml ``` @@ -290,12 +316,18 @@ Eval.dataset.transforms.DetResizeForTest:评估尺寸,添加如下参数
图8 文本检测方案2-模型评估
-使用训练好的模型进行评估,更新模型路径`Global.checkpoints`,这里为大家提供训练好的模型`./pretrain/ch_db_mv3-student1600-finetune/best_accuracy`,[模型下载地址](https://paddleocr.bj.bcebos.com/fanliku/sheet_recognition/ch_db_mv3-student1600-finetune.zip) +使用训练好的模型进行评估,更新模型路径`Global.checkpoints`。如需获取已训练模型,请扫码填写问卷,加入PaddleOCR官方交流群获取全部OCR垂类模型下载链接、《动手学OCR》电子书等全套OCR学习资料🎁 + +
+ +
+ +将下载或训练完成的模型放置在对应目录下即可完成模型评估 ```python %cd /home/aistudio/PaddleOCR/ -! python tools/eval.py \ +python tools/eval.py \ -c configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_student.yml \ -o Global.checkpoints="pretrain/ch_db_mv3-student1600-finetune/best_accuracy" ``` @@ -305,7 +337,7 @@ Eval.dataset.transforms.DetResizeForTest:评估尺寸,添加如下参数 ```python %cd /home/aistudio/PaddleOCR/ -! python tools/eval.py \ +python tools/eval.py \ -c configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_student.yml \ -o Global.checkpoints="pretrain/ch_db_mv3-student1600/best_accuracy" ``` @@ -331,7 +363,7 @@ Eval.dataset.transforms.DetResizeForTest:评估尺寸,添加如下参数 # 加载配置文件`ch_PP-OCRv2_det_student.yml`,从`pretrain/ch_db_mv3-student1600-finetune`目录下加载`best_accuracy`模型 # inference模型保存在`./output/det_db_inference`目录下 %cd /home/aistudio/PaddleOCR/ -! python tools/export_model.py \ +python tools/export_model.py \ -c configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_student.yml \ -o Global.pretrained_model="pretrain/ch_db_mv3-student1600-finetune/best_accuracy" \ Global.save_inference_dir="./output/det_db_inference/" @@ -374,12 +406,11 @@ use_gpu:是否使用GPU | 方案 | hmeans | 结果分析 | | -------- | -------- | -------- | -| PP-OCRv2中英文超轻量检测预训练模型 | 77.26% | ppocr提供的预训练模型有一定的泛化能力 | +| PP-OCRv2中英文超轻量检测预训练模型 | 77.26% | ppocr提供的预训练模型有泛化能力 | | XFUND数据集 | 79.27% | | | XFUND数据集+finetune | 85.24% | finetune会提升垂类场景效果 | - -## 4.2 文本识别 +### 4.2 文本识别 我们分别使用如下3种方案进行训练、评估: @@ -387,8 +418,7 @@ use_gpu:是否使用GPU - XFUND数据集+fine-tune - XFUND数据集+fine-tune+真实通用识别数据 - -### **4.2.1 方案1:预训练模型** +#### 4.2.1 方案1:预训练模型 **1)下载预训练模型** @@ -401,8 +431,8 @@ use_gpu:是否使用GPU ```python %cd /home/aistudio/PaddleOCR/pretrain/ -! wget https://paddleocr.bj.bcebos.com/PP-OCRv2/chinese/ch_PP-OCRv2_rec_train.tar -! tar -xf ch_PP-OCRv2_rec_train.tar && rm -rf ch_PP-OCRv2_rec_train.tar +wget https://paddleocr.bj.bcebos.com/PP-OCRv2/chinese/ch_PP-OCRv2_rec_train.tar +tar -xf ch_PP-OCRv2_rec_train.tar && rm -rf ch_PP-OCRv2_rec_train.tar % cd .. ``` @@ -424,7 +454,7 @@ Eval.dataset.label_file_list:指向验证集标注文件 ```python %cd /home/aistudio/PaddleOCR -! CUDA_VISIBLE_DEVICES=0 python tools/eval.py \ +CUDA_VISIBLE_DEVICES=0 python tools/eval.py \ -c configs/rec/ch_PP-OCRv2/ch_PP-OCRv2_rec_distillation.yml \ -o Global.checkpoints=./pretrain/ch_PP-OCRv2_rec_train/best_accuracy ``` @@ -435,9 +465,9 @@ Eval.dataset.label_file_list:指向验证集标注文件 | -------- | -------- | | PP-OCRv2中英文超轻量识别预训练模型 | 67.48% | -使用文本预训练模型在XFUND验证集上评估,acc达到67%左右,充分说明ppocr提供的预训练模型有一定的泛化能力。 +使用文本预训练模型在XFUND验证集上评估,acc达到67%左右,充分说明ppocr提供的预训练模型具有泛化能力。 -### **4.2.2 方案2:XFUND数据集+finetune** +#### 4.2.2 方案2:XFUND数据集+finetune 同检测模型,我们提取Student模型的参数,在XFUND数据集上进行finetune,可以参考如下代码: @@ -474,11 +504,9 @@ Eval.dataset.label_file_list:指向验证集标注文件 ``` 执行如下命令启动训练: - - ```python %cd /home/aistudio/PaddleOCR/ -! CUDA_VISIBLE_DEVICES=0 python tools/train.py \ +CUDA_VISIBLE_DEVICES=0 python tools/train.py \ -c configs/rec/ch_PP-OCRv2/ch_PP-OCRv2_rec.yml ``` @@ -493,7 +521,7 @@ Eval.dataset.label_file_list:指向验证集标注文件 ```python %cd /home/aistudio/PaddleOCR/ -! CUDA_VISIBLE_DEVICES=0 python tools/eval.py \ +CUDA_VISIBLE_DEVICES=0 python tools/eval.py \ -c configs/rec/ch_PP-OCRv2/ch_PP-OCRv2_rec.yml \ -o Global.checkpoints=./pretrain/rec_mobile_pp-OCRv2-student-finetune/best_accuracy ``` @@ -506,7 +534,7 @@ Eval.dataset.label_file_list:指向验证集标注文件 使用XFUND数据集+finetune训练,在验证集上评估达到72%左右,说明 finetune会提升垂类场景效果。 -### **4.2.3 方案3:XFUND数据集+finetune+真实通用识别数据** +#### 4.2.3 方案3:XFUND数据集+finetune+真实通用识别数据 接着我们在上述`XFUND数据集+finetune`实验的基础上,添加真实通用识别数据,进一步提升识别效果。首先准备真实通用识别数据,并上传到AIStudio: @@ -528,7 +556,7 @@ Train.dataset.ratio_list:动态采样 ```python %cd /home/aistudio/PaddleOCR/ -! CUDA_VISIBLE_DEVICES=0 python tools/train.py \ +CUDA_VISIBLE_DEVICES=0 python tools/train.py \ -c configs/rec/ch_PP-OCRv2/ch_PP-OCRv2_rec.yml ``` @@ -538,11 +566,11 @@ Train.dataset.ratio_list:动态采样
图16 文本识别方案3-模型评估
-使用训练好的模型进行评估,更新模型路径`Global.checkpoints`,这里为大家提供训练好的模型`./pretrain/rec_mobile_pp-OCRv2-student-readldata/best_accuracy`,[模型下载地址](https://paddleocr.bj.bcebos.com/fanliku/sheet_recognition/rec_mobile_pp-OCRv2-student-realdata.zip) +使用训练好的模型进行评估,更新模型路径`Global.checkpoints`。 ```python -! CUDA_VISIBLE_DEVICES=0 python tools/eval.py \ +CUDA_VISIBLE_DEVICES=0 python tools/eval.py \ -c configs/rec/ch_PP-OCRv2/ch_PP-OCRv2_rec.yml \ -o Global.checkpoints=./pretrain/rec_mobile_pp-OCRv2-student-realdata/best_accuracy ``` @@ -580,7 +608,7 @@ Train.dataset.ratio_list:动态采样 ```python -! python tools/infer/predict_system.py \ +python tools/infer/predict_system.py \ --image_dir="./doc/vqa/input/zh_val_21.jpg" \ --det_model_dir="./output/det_db_inference/" \ --rec_model_dir="./output/rec_crnn_inference/" \ @@ -592,11 +620,11 @@ Train.dataset.ratio_list:动态采样 | 方案 | acc | 结果分析 | | -------- | -------- | -------- | -| PP-OCRv2中英文超轻量识别预训练模型 | 67.48% | ppocr提供的预训练模型有一定的泛化能力 | +| PP-OCRv2中英文超轻量识别预训练模型 | 67.48% | ppocr提供的预训练模型具有泛化能力 | | XFUND数据集+fine-tune |72.33% | finetune会提升垂类场景效果 | | XFUND数据集+fine-tune+真实通用识别数据 | 85.29% | 真实通用识别数据对于性能提升很有帮助 | -# 5 文档视觉问答(DOC-VQA) +## 5 文档视觉问答(DOC-VQA) VQA指视觉问答,主要针对图像内容进行提问和回答,DOC-VQA是VQA任务中的一种,DOC-VQA主要针对文本图像的文字内容提出问题。 @@ -608,14 +636,13 @@ PaddleOCR中DOC-VQA系列算法基于PaddleNLP自然语言处理算法库实现L ```python %cd pretrain #下载SER模型 -! wget https://paddleocr.bj.bcebos.com/pplayout/ser_LayoutXLM_xfun_zh.tar && tar -xvf ser_LayoutXLM_xfun_zh.tar +wget https://paddleocr.bj.bcebos.com/pplayout/ser_LayoutXLM_xfun_zh.tar && tar -xvf ser_LayoutXLM_xfun_zh.tar #下载RE模型 -! wget https://paddleocr.bj.bcebos.com/pplayout/re_LayoutXLM_xfun_zh.tar && tar -xvf re_LayoutXLM_xfun_zh.tar +wget https://paddleocr.bj.bcebos.com/pplayout/re_LayoutXLM_xfun_zh.tar && tar -xvf re_LayoutXLM_xfun_zh.tar %cd ../ ``` - -## 5.1 SER +### 5.1 SER SER: 语义实体识别 (Semantic Entity Recognition), 可以完成对图像中的文本识别与分类。 @@ -647,7 +674,7 @@ SER: 语义实体识别 (Semantic Entity Recognition), 可以完成对图像 ```python %cd /home/aistudio/PaddleOCR/ -! CUDA_VISIBLE_DEVICES=0 python tools/train.py -c configs/vqa/ser/layoutxlm.yml +CUDA_VISIBLE_DEVICES=0 python tools/train.py -c configs/vqa/ser/layoutxlm.yml ``` 最终会打印出`precision`, `recall`, `hmean`等指标。 在`./output/ser_layoutxlm/`文件夹中会保存训练日志,最优的模型和最新epoch的模型。 @@ -664,7 +691,7 @@ SER: 语义实体识别 (Semantic Entity Recognition), 可以完成对图像 ```python -! CUDA_VISIBLE_DEVICES=0 python tools/eval.py \ +CUDA_VISIBLE_DEVICES=0 python tools/eval.py \ -c configs/vqa/ser/layoutxlm.yml \ -o Architecture.Backbone.checkpoints=pretrain/ser_LayoutXLM_xfun_zh/ ``` @@ -684,7 +711,7 @@ SER: 语义实体识别 (Semantic Entity Recognition), 可以完成对图像 ```python -! CUDA_VISIBLE_DEVICES=0 python tools/infer_vqa_token_ser.py \ +CUDA_VISIBLE_DEVICES=0 python tools/infer_vqa_token_ser.py \ -c configs/vqa/ser/layoutxlm.yml \ -o Architecture.Backbone.checkpoints=pretrain/ser_LayoutXLM_xfun_zh/ \ Global.infer_img=doc/vqa/input/zh_val_42.jpg @@ -704,7 +731,7 @@ plt.figure(figsize=(48,24)) plt.imshow(img) ``` -## 5.2 RE +### 5.2 RE 基于 RE 任务,可以完成对图象中的文本内容的关系提取,如判断问题对(pair)。 @@ -729,7 +756,7 @@ plt.imshow(img) ```python -! CUDA_VISIBLE_DEVICES=0 python3 tools/train.py -c configs/vqa/re/layoutxlm.yml +CUDA_VISIBLE_DEVICES=0 python3 tools/train.py -c configs/vqa/re/layoutxlm.yml ``` 最终会打印出`precision`, `recall`, `hmean`等指标。 在`./output/re_layoutxlm/`文件夹中会保存训练日志,最优的模型和最新epoch的模型 @@ -744,7 +771,7 @@ plt.imshow(img) ```python -! CUDA_VISIBLE_DEVICES=0 python3 tools/eval.py \ +CUDA_VISIBLE_DEVICES=0 python3 tools/eval.py \ -c configs/vqa/re/layoutxlm.yml \ -o Architecture.Backbone.checkpoints=pretrain/re_LayoutXLM_xfun_zh/ ``` @@ -760,20 +787,14 @@ plt.imshow(img)
图26 RE-模型预测
- 使用OCR引擎 + SER + RE串联预测 - -使用如下命令即可完成OCR引擎 + SER + RE的串联预测, 以预训练SER和RE模型为例: - - +使用如下命令即可完成OCR引擎 + SER + RE的串联预测, 以预训练SER和RE模型为例, 最终会在config.Global.save_res_path字段所配置的目录下保存预测结果可视化图像以及预测结果文本文件,预测结果文本文件名为infer_results.txt。 - - ```python -%cd /home/aistudio/PaddleOCR -! CUDA_VISIBLE_DEVICES=0 python3 tools/infer_vqa_token_ser_re.py \ +cd /home/aistudio/PaddleOCR +CUDA_VISIBLE_DEVICES=0 python3 tools/infer_vqa_token_ser_re.py \ -c configs/vqa/re/layoutxlm.yml \ -o Architecture.Backbone.checkpoints=pretrain/re_LayoutXLM_xfun_zh/ \ Global.infer_img=test_imgs/ \ @@ -787,10 +808,9 @@ plt.imshow(img) test_imgs/t131.jpg {"政治面税": "群众", "性别": "男", "籍贯": "河北省邯郸市", "婚姻状况": "亏末婚口已婚口已娇", "通讯地址": "邯郸市阳光苑7号楼003", "民族": "汉族", "毕业院校": "河南工业大学", "户口性质": "口农村城镇", "户口地址": "河北省邯郸市", "联系电话": "13288888888", "健康状况": "健康", "姓名": "小六", "好高cm": "180", "出生年月": "1996年8月9日", "文化程度": "本科", "身份证号码": "458933777777777777"} ```` - +展示预测结果 ```python -# 展示预测结果 import cv2 from matplotlib import pyplot as plt %matplotlib inline @@ -800,7 +820,7 @@ plt.figure(figsize=(48,24)) plt.imshow(img) ``` -# 6 导出Excel +## 6 导出Excel
图27 导出Excel
@@ -859,7 +879,7 @@ with open('output/re/infer_results.txt', 'r', encoding='utf-8') as fin: workbook.close() ``` -# 更多资源 +## 更多资源 - 更多深度学习知识、产业案例、面试宝典等,请参考:[awesome-DeepLearning](https://github.com/paddlepaddle/awesome-DeepLearning) @@ -869,7 +889,7 @@ workbook.close() - 飞桨框架相关资料,请参考:[飞桨深度学习平台](https://www.paddlepaddle.org.cn/?fr=paddleEdu_aistudio) -# 参考链接 +## 参考链接 - LayoutXLM: Multimodal Pre-training for Multilingual Visually-rich Document Understanding, https://arxiv.org/pdf/2104.08836.pdf diff --git "a/applications/\346\266\262\346\231\266\345\261\217\350\257\273\346\225\260\350\257\206\345\210\253.md" "b/applications/\346\266\262\346\231\266\345\261\217\350\257\273\346\225\260\350\257\206\345\210\253.md" new file mode 100644 index 0000000000000000000000000000000000000000..ff2fb2cb4812f4f8366605b3c26af5b9aaaa290e --- /dev/null +++ "b/applications/\346\266\262\346\231\266\345\261\217\350\257\273\346\225\260\350\257\206\345\210\253.md" @@ -0,0 +1,616 @@ +# 基于PP-OCRv3的液晶屏读数识别 + +- [1. 项目背景及意义](#1-项目背景及意义) +- [2. 项目内容](#2-项目内容) +- [3. 安装环境](#3-安装环境) +- [4. 文字检测](#4-文字检测) + - [4.1 PP-OCRv3检测算法介绍](#41-PP-OCRv3检测算法介绍) + - [4.2 数据准备](#42-数据准备) + - [4.3 模型训练](#43-模型训练) + - [4.3.1 预训练模型直接评估](#431-预训练模型直接评估) + - [4.3.2 预训练模型直接finetune](#432-预训练模型直接finetune) + - [4.3.3 基于预训练模型Finetune_student模型](#433-基于预训练模型Finetune_student模型) + - [4.3.4 基于预训练模型Finetune_teacher模型](#434-基于预训练模型Finetune_teacher模型) + - [4.3.5 采用CML蒸馏进一步提升student模型精度](#435-采用CML蒸馏进一步提升student模型精度) + - [4.3.6 模型导出推理](#436-4.3.6-模型导出推理) +- [5. 文字识别](#5-文字识别) + - [5.1 PP-OCRv3识别算法介绍](#51-PP-OCRv3识别算法介绍) + - [5.2 数据准备](#52-数据准备) + - [5.3 模型训练](#53-模型训练) + - [5.4 模型导出推理](#54-模型导出推理) +- [6. 系统串联](#6-系统串联) + - [6.1 后处理](#61-后处理) +- [7. PaddleServing部署](#7-PaddleServing部署) + + +## 1. 项目背景及意义 +目前光学字符识别(OCR)技术在我们的生活当中被广泛使用,但是大多数模型在通用场景下的准确性还有待提高,针对于此我们借助飞桨提供的PaddleOCR套件较容易的实现了在垂类场景下的应用。 + +该项目以国家质量基础(NQI)为准绳,充分利用大数据、云计算、物联网等高新技术,构建覆盖计量端、实验室端、数据端和硬件端的完整计量解决方案,解决传统计量校准中存在的难题,拓宽计量检测服务体系和服务领域;解决无数传接口或数传接口不统一、不公开的计量设备,以及计量设备所处的环境比较恶劣,不适合人工读取数据。通过OCR技术实现远程计量,引领计量行业向智慧计量转型和发展。 + +## 2. 项目内容 +本项目基于PaddleOCR开源套件,以PP-OCRv3检测和识别模型为基础,针对液晶屏读数识别场景进行优化。 + +Aistudio项目链接:[OCR液晶屏读数识别](https://aistudio.baidu.com/aistudio/projectdetail/4080130) + +## 3. 安装环境 + +```python +# 首先git官方的PaddleOCR项目,安装需要的依赖 +# 第一次运行打开该注释 +# git clone https://gitee.com/PaddlePaddle/PaddleOCR.git +cd PaddleOCR +pip install -r requirements.txt +``` + +## 4. 文字检测 +文本检测的任务是定位出输入图像中的文字区域。近年来学术界关于文本检测的研究非常丰富,一类方法将文本检测视为目标检测中的一个特定场景,基于通用目标检测算法进行改进适配,如TextBoxes[1]基于一阶段目标检测器SSD[2]算法,调整目标框使之适合极端长宽比的文本行,CTPN[3]则是基于Faster RCNN[4]架构改进而来。但是文本检测与目标检测在目标信息以及任务本身上仍存在一些区别,如文本一般长宽比较大,往往呈“条状”,文本行之间可能比较密集,弯曲文本等,因此又衍生了很多专用于文本检测的算法。本项目基于PP-OCRv3算法进行优化。 + +### 4.1 PP-OCRv3检测算法介绍 +PP-OCRv3检测模型是对PP-OCRv2中的CML(Collaborative Mutual Learning) 协同互学习文本检测蒸馏策略进行了升级。如下图所示,CML的核心思想结合了①传统的Teacher指导Student的标准蒸馏与 ②Students网络之间的DML互学习,可以让Students网络互学习的同时,Teacher网络予以指导。PP-OCRv3分别针对教师模型和学生模型进行进一步效果优化。其中,在对教师模型优化时,提出了大感受野的PAN结构LK-PAN和引入了DML(Deep Mutual Learning)蒸馏策略;在对学生模型优化时,提出了残差注意力机制的FPN结构RSE-FPN。 +![](https://ai-studio-static-online.cdn.bcebos.com/c306b2f028364805a55494d435ab553a76cf5ae5dd3f4649a948ea9aeaeb28b8) + +详细优化策略描述请参考[PP-OCRv3优化策略](https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.5/doc/doc_ch/PP-OCRv3_introduction.md#2) + +### 4.2 数据准备 +[计量设备屏幕字符检测数据集](https://aistudio.baidu.com/aistudio/datasetdetail/127845)数据来源于实际项目中各种计量设备的数显屏,以及在网上搜集的一些其他数显屏,包含训练集755张,测试集355张。 + +```python +# 在PaddleOCR下创建新的文件夹train_data +mkdir train_data +# 下载数据集并解压到指定路径下 +unzip icdar2015.zip -d train_data +``` + +```python +# 随机查看文字检测数据集图片 +from PIL import Image +import matplotlib.pyplot as plt +import numpy as np +import os + + +train = './train_data/icdar2015/text_localization/test' +# 从指定目录中选取一张图片 +def get_one_image(train): + plt.figure() + files = os.listdir(train) + n = len(files) + ind = np.random.randint(0,n) + img_dir = os.path.join(train,files[ind]) + image = Image.open(img_dir) + plt.imshow(image) + plt.show() + image = image.resize([208, 208]) + +get_one_image(train) +``` +![det_png](https://ai-studio-static-online.cdn.bcebos.com/0639da09b774458096ae577e82b2c59e89ced6a00f55458f946997ab7472a4f8) + +### 4.3 模型训练 + +#### 4.3.1 预训练模型直接评估 +下载我们需要的PP-OCRv3检测预训练模型,更多选择请自行选择其他的[文字检测模型](https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.5/doc/doc_ch/models_list.md#1-%E6%96%87%E6%9C%AC%E6%A3%80%E6%B5%8B%E6%A8%A1%E5%9E%8B) + +```python +#使用该指令下载需要的预训练模型 +wget -P ./pretrained_models/ https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_det_distill_train.tar +# 解压预训练模型文件 +tar -xf ./pretrained_models/ch_PP-OCRv3_det_distill_train.tar -C pretrained_models +``` + +在训练之前,我们可以直接使用下面命令来评估预训练模型的效果: + +```python +# 评估预训练模型 +python tools/eval.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml -o Global.pretrained_model="./pretrained_models/ch_PP-OCRv3_det_distill_train/best_accuracy" +``` + +结果如下: + +| | 方案 |hmeans| +|---|---------------------------|---| +| 0 | PP-OCRv3中英文超轻量检测预训练模型直接预测 |47.5%| + +#### 4.3.2 预训练模型直接finetune +##### 修改配置文件 +我们使用configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml,主要修改训练轮数和学习率参相关参数,设置预训练模型路径,设置数据集路径。 另外,batch_size可根据自己机器显存大小进行调整。 具体修改如下几个地方: +``` +epoch:100 +save_epoch_step:10 +eval_batch_step:[0, 50] +save_model_dir: ./output/ch_PP-OCR_v3_det/ +pretrained_model: ./pretrained_models/ch_PP-OCRv3_det_distill_train/best_accuracy +learning_rate: 0.00025 +num_workers: 0 # 如果单卡训练,建议将Train和Eval的loader部分的num_workers设置为0,否则会出现`/dev/shm insufficient`的报错 +``` + +##### 开始训练 +使用我们上面修改的配置文件configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml,训练命令如下: + +```python +# 开始训练模型 +python tools/train.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml -o Global.pretrained_model=./pretrained_models/ch_PP-OCRv3_det_distill_train/best_accuracy +``` + +评估训练好的模型: + +```python +# 评估训练好的模型 +python tools/eval.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml -o Global.pretrained_model="./output/ch_PP-OCR_v3_det/best_accuracy" +``` + +结果如下: +| | 方案 |hmeans| +|---|---------------------------|---| +| 0 | PP-OCRv3中英文超轻量检测预训练模型直接预测 |47.5%| +| 1 | PP-OCRv3中英文超轻量检测预训练模型fintune |65.2%| + +#### 4.3.3 基于预训练模型Finetune_student模型 + +我们使用configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_student.yml,主要修改训练轮数和学习率参相关参数,设置预训练模型路径,设置数据集路径。 另外,batch_size可根据自己机器显存大小进行调整。 具体修改如下几个地方: +``` +epoch:100 +save_epoch_step:10 +eval_batch_step:[0, 50] +save_model_dir: ./output/ch_PP-OCR_v3_det_student/ +pretrained_model: ./pretrained_models/ch_PP-OCRv3_det_distill_train/student +learning_rate: 0.00025 +num_workers: 0 # 如果单卡训练,建议将Train和Eval的loader部分的num_workers设置为0,否则会出现`/dev/shm insufficient`的报错 +``` + +训练命令如下: + +```python +python tools/train.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_student.yml -o Global.pretrained_model=./pretrained_models/ch_PP-OCRv3_det_distill_train/student +``` + +评估训练好的模型: + +```python +# 评估训练好的模型 +python tools/eval.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_student.yml -o Global.pretrained_model="./output/ch_PP-OCR_v3_det_student/best_accuracy" +``` + +结果如下: +| | 方案 |hmeans| +|---|---------------------------|---| +| 0 | PP-OCRv3中英文超轻量检测预训练模型直接预测 |47.5%| +| 1 | PP-OCRv3中英文超轻量检测预训练模型fintune |65.2%| +| 2 | PP-OCRv3中英文超轻量检测预训练模型fintune学生模型 |80.0%| + +#### 4.3.4 基于预训练模型Finetune_teacher模型 + +首先需要从提供的预训练模型best_accuracy.pdparams中提取teacher参数,组合成适合dml训练的初始化模型,提取代码如下: + +```python +cd ./pretrained_models/ +# transform teacher params in best_accuracy.pdparams into teacher_dml.paramers +import paddle + +# load pretrained model +all_params = paddle.load("ch_PP-OCRv3_det_distill_train/best_accuracy.pdparams") +# print(all_params.keys()) + +# keep teacher params +t_params = {key[len("Teacher."):]: all_params[key] for key in all_params if "Teacher." in key} + +# print(t_params.keys()) + +s_params = {"Student." + key: t_params[key] for key in t_params} +s2_params = {"Student2." + key: t_params[key] for key in t_params} +s_params = {**s_params, **s2_params} +# print(s_params.keys()) + +paddle.save(s_params, "ch_PP-OCRv3_det_distill_train/teacher_dml.pdparams") + +``` + +我们使用configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_dml.yml,主要修改训练轮数和学习率参相关参数,设置预训练模型路径,设置数据集路径。 另外,batch_size可根据自己机器显存大小进行调整。 具体修改如下几个地方: +``` +epoch:100 +save_epoch_step:10 +eval_batch_step:[0, 50] +save_model_dir: ./output/ch_PP-OCR_v3_det_teacher/ +pretrained_model: ./pretrained_models/ch_PP-OCRv3_det_distill_train/teacher_dml +learning_rate: 0.00025 +num_workers: 0 # 如果单卡训练,建议将Train和Eval的loader部分的num_workers设置为0,否则会出现`/dev/shm insufficient`的报错 +``` + +训练命令如下: + +```python +python tools/train.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_dml.yml -o Global.pretrained_model=./pretrained_models/ch_PP-OCRv3_det_distill_train/teacher_dml +``` + +评估训练好的模型: + +```python +# 评估训练好的模型 +python tools/eval.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_dml.yml -o Global.pretrained_model="./output/ch_PP-OCR_v3_det_teacher/best_accuracy" +``` + +结果如下: +| | 方案 |hmeans| +|---|---------------------------|---| +| 0 | PP-OCRv3中英文超轻量检测预训练模型直接预测 |47.5%| +| 1 | PP-OCRv3中英文超轻量检测预训练模型fintune |65.2%| +| 2 | PP-OCRv3中英文超轻量检测预训练模型fintune学生模型 |80.0%| +| 3 | PP-OCRv3中英文超轻量检测预训练模型fintune教师模型 |84.8%| + +#### 4.3.5 采用CML蒸馏进一步提升student模型精度 + +需要从4.3.3和4.3.4训练得到的best_accuracy.pdparams中提取各自代表student和teacher的参数,组合成适合cml训练的初始化模型,提取代码如下: + +```python +# transform teacher params and student parameters into cml model +import paddle + +all_params = paddle.load("./pretrained_models/ch_PP-OCRv3_det_distill_train/best_accuracy.pdparams") +# print(all_params.keys()) + +t_params = paddle.load("./output/ch_PP-OCR_v3_det_teacher/best_accuracy.pdparams") +# print(t_params.keys()) + +s_params = paddle.load("./output/ch_PP-OCR_v3_det_student/best_accuracy.pdparams") +# print(s_params.keys()) + +for key in all_params: + # teacher is OK + if "Teacher." in key: + new_key = key.replace("Teacher", "Student") + #print("{} >> {}\n".format(key, new_key)) + assert all_params[key].shape == t_params[new_key].shape + all_params[key] = t_params[new_key] + + if "Student." in key: + new_key = key.replace("Student.", "") + #print("{} >> {}\n".format(key, new_key)) + assert all_params[key].shape == s_params[new_key].shape + all_params[key] = s_params[new_key] + + if "Student2." in key: + new_key = key.replace("Student2.", "") + print("{} >> {}\n".format(key, new_key)) + assert all_params[key].shape == s_params[new_key].shape + all_params[key] = s_params[new_key] + +paddle.save(all_params, "./pretrained_models/ch_PP-OCRv3_det_distill_train/teacher_cml_student.pdparams") +``` + +训练命令如下: + +```python +python tools/train.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml -o Global.pretrained_model=./pretrained_models/ch_PP-OCRv3_det_distill_train/teacher_cml_student Global.save_model_dir=./output/ch_PP-OCR_v3_det_finetune/ +``` + +评估训练好的模型: + +```python +# 评估训练好的模型 +python tools/eval.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml -o Global.pretrained_model="./output/ch_PP-OCR_v3_det_finetune/best_accuracy" +``` + +结果如下: +| | 方案 |hmeans| +|---|---------------------------|---| +| 0 | PP-OCRv3中英文超轻量检测预训练模型直接预测 |47.5%| +| 1 | PP-OCRv3中英文超轻量检测预训练模型fintune |65.2%| +| 2 | PP-OCRv3中英文超轻量检测预训练模型fintune学生模型 |80.0%| +| 3 | PP-OCRv3中英文超轻量检测预训练模型fintune教师模型 |84.8%| +| 4 | 基于2和3训练好的模型fintune |82.7%| + +如需获取已训练模型,请扫码填写问卷,加入PaddleOCR官方交流群获取全部OCR垂类模型下载链接、《动手学OCR》电子书等全套OCR学习资料🎁 +
+ +
+将下载或训练完成的模型放置在对应目录下即可完成模型推理。 + +#### 4.3.6 模型导出推理 +训练完成后,可以将训练模型转换成inference模型。inference 模型会额外保存模型的结构信息,在预测部署、加速推理上性能优越,灵活方便,适合于实际系统集成。 +##### 4.3.6.1 模型导出 +导出命令如下: + +```python +# 转化为推理模型 +python tools/export_model.py \ +-c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_cml.yml \ +-o Global.pretrained_model=./output/ch_PP-OCR_v3_det_finetune/best_accuracy \ +-o Global.save_inference_dir="./inference/det_ppocrv3" + +``` + +##### 4.3.6.2 模型推理 +导出模型后,可以使用如下命令进行推理预测: + +```python +# 推理预测 +python tools/infer/predict_det.py --image_dir="train_data/icdar2015/text_localization/test/1.jpg" --det_model_dir="./inference/det_ppocrv3/Student" +``` + +## 5. 文字识别 +文本识别的任务是识别出图像中的文字内容,一般输入来自于文本检测得到的文本框截取出的图像文字区域。文本识别一般可以根据待识别文本形状分为规则文本识别和不规则文本识别两大类。规则文本主要指印刷字体、扫描文本等,文本大致处在水平线位置;不规则文本往往不在水平位置,存在弯曲、遮挡、模糊等问题。不规则文本场景具有很大的挑战性,也是目前文本识别领域的主要研究方向。本项目基于PP-OCRv3算法进行优化。 + +### 5.1 PP-OCRv3识别算法介绍 +PP-OCRv3的识别模块是基于文本识别算法[SVTR](https://arxiv.org/abs/2205.00159)优化。SVTR不再采用RNN结构,通过引入Transformers结构更加有效地挖掘文本行图像的上下文信息,从而提升文本识别能力。如下图所示,PP-OCRv3采用了6个优化策略。 +![](https://ai-studio-static-online.cdn.bcebos.com/d4f5344b5b854d50be738671598a89a45689c6704c4d481fb904dd7cf72f2a1a) + +优化策略汇总如下: +* SVTR_LCNet:轻量级文本识别网络 +* GTC:Attention指导CTC训练策略 +* TextConAug:挖掘文字上下文信息的数据增广策略 +* TextRotNet:自监督的预训练模型 +* UDML:联合互学习策略 +* UIM:无标注数据挖掘方案 + +详细优化策略描述请参考[PP-OCRv3优化策略](https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.5/doc/doc_ch/PP-OCRv3_introduction.md#3-%E8%AF%86%E5%88%AB%E4%BC%98%E5%8C%96) + +### 5.2 数据准备 +[计量设备屏幕字符识别数据集](https://aistudio.baidu.com/aistudio/datasetdetail/128714)数据来源于实际项目中各种计量设备的数显屏,以及在网上搜集的一些其他数显屏,包含训练集19912张,测试集4099张。 + +```python +# 解压下载的数据集到指定路径下 +unzip ic15_data.zip -d train_data +``` + +```python +# 随机查看文字检测数据集图片 +from PIL import Image +import matplotlib.pyplot as plt +import numpy as np +import os + +train = './train_data/ic15_data/train' +# 从指定目录中选取一张图片 +def get_one_image(train): + plt.figure() + files = os.listdir(train) + n = len(files) + ind = np.random.randint(0,n) + img_dir = os.path.join(train,files[ind]) + image = Image.open(img_dir) + plt.imshow(image) + plt.show() + image = image.resize([208, 208]) + +get_one_image(train) +``` + +![rec_png](https://ai-studio-static-online.cdn.bcebos.com/3de0d475c69746d0a184029001ef07c85fd68816d66d4beaa10e6ef60030f9b4) + +### 5.3 模型训练 +#### 下载预训练模型 +下载我们需要的PP-OCRv3识别预训练模型,更多选择请自行选择其他的[文字识别模型](https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.5/doc/doc_ch/models_list.md#2-%E6%96%87%E6%9C%AC%E8%AF%86%E5%88%AB%E6%A8%A1%E5%9E%8B) + +```python +# 使用该指令下载需要的预训练模型 +wget -P ./pretrained_models/ https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_rec_train.tar +# 解压预训练模型文件 +tar -xf ./pretrained_models/ch_PP-OCRv3_rec_train.tar -C pretrained_models +``` + +#### 修改配置文件 +我们使用configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml,主要修改训练轮数和学习率参相关参数,设置预训练模型路径,设置数据集路径。 另外,batch_size可根据自己机器显存大小进行调整。 具体修改如下几个地方: +``` + epoch_num: 100 # 训练epoch数 + save_model_dir: ./output/ch_PP-OCR_v3_rec + save_epoch_step: 10 + eval_batch_step: [0, 100] # 评估间隔,每隔100step评估一次 + cal_metric_during_train: true + pretrained_model: ./pretrained_models/ch_PP-OCRv3_rec_train/best_accuracy # 预训练模型路径 + character_dict_path: ppocr/utils/ppocr_keys_v1.txt + use_space_char: true # 使用空格 + + lr: + name: Cosine # 修改学习率衰减策略为Cosine + learning_rate: 0.0002 # 修改fine-tune的学习率 + warmup_epoch: 2 # 修改warmup轮数 + +Train: + dataset: + name: SimpleDataSet + data_dir: ./train_data/ic15_data/ # 训练集图片路径 + ext_op_transform_idx: 1 + label_file_list: + - ./train_data/ic15_data/rec_gt_train.txt # 训练集标签 + ratio_list: + - 1.0 + loader: + shuffle: true + batch_size_per_card: 64 + drop_last: true + num_workers: 4 +Eval: + dataset: + name: SimpleDataSet + data_dir: ./train_data/ic15_data/ # 测试集图片路径 + label_file_list: + - ./train_data/ic15_data/rec_gt_test.txt # 测试集标签 + ratio_list: + - 1.0 + loader: + shuffle: false + drop_last: false + batch_size_per_card: 64 + num_workers: 4 +``` + +在训练之前,我们可以直接使用下面命令来评估预训练模型的效果: + +```python +# 评估预训练模型 +python tools/eval.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml -o Global.pretrained_model="./pretrained_models/ch_PP-OCRv3_rec_train/best_accuracy" +``` + +结果如下: +| | 方案 |accuracy| +|---|---------------------------|---| +| 0 | PP-OCRv3中英文超轻量识别预训练模型直接预测 |70.4%| + +#### 开始训练 +我们使用上面修改好的配置文件configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml,预训练模型,数据集路径,学习率,训练轮数等都已经设置完毕后,可以使用下面命令开始训练。 + +```python +# 开始训练识别模型 +python tools/train.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml +``` + +训练完成后,可以对训练模型中最好的进行测试,评估命令如下: + +```python +# 评估finetune效果 +python tools/eval.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml -o Global.checkpoints="./output/ch_PP-OCR_v3_rec/best_accuracy" +``` + +结果如下: +| | 方案 |accuracy| +|---|---------------------------|---| +| 0 | PP-OCRv3中英文超轻量识别预训练模型直接预测 |70.4%| +| 1 | PP-OCRv3中英文超轻量识别预训练模型finetune |82.2%| + +如需获取已训练模型,请扫码填写问卷,加入PaddleOCR官方交流群获取全部OCR垂类模型下载链接、《动手学OCR》电子书等全套OCR学习资料🎁 +
+ +
+将下载或训练完成的模型放置在对应目录下即可完成模型推理。 + +### 5.4 模型导出推理 +训练完成后,可以将训练模型转换成inference模型。inference 模型会额外保存模型的结构信息,在预测部署、加速推理上性能优越,灵活方便,适合于实际系统集成。 +#### 模型导出 +导出命令如下: + +```python +# 转化为推理模型 +python tools/export_model.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml -o Global.pretrained_model="./output/ch_PP-OCR_v3_rec/best_accuracy" Global.save_inference_dir="./inference/rec_ppocrv3/" +``` + +#### 模型推理 +导出模型后,可以使用如下命令进行推理预测 + +```python +# 推理预测 +python tools/infer/predict_rec.py --image_dir="train_data/ic15_data/test/1_crop_0.jpg" --rec_model_dir="./inference/rec_ppocrv3/Student" +``` + +## 6. 系统串联 +我们将上面训练好的检测和识别模型进行系统串联测试,命令如下: + +```python +#串联测试 +python3 tools/infer/predict_system.py --image_dir="./train_data/icdar2015/text_localization/test/142.jpg" --det_model_dir="./inference/det_ppocrv3/Student" --rec_model_dir="./inference/rec_ppocrv3/Student" +``` + +测试结果保存在`./inference_results/`目录下,可以用下面代码进行可视化 + +```python +%cd /home/aistudio/PaddleOCR +# 显示结果 +import matplotlib.pyplot as plt +from PIL import Image +img_path= "./inference_results/142.jpg" +img = Image.open(img_path) +plt.figure("test_img", figsize=(30,30)) +plt.imshow(img) +plt.show() +``` + +![sys_res_png](https://ai-studio-static-online.cdn.bcebos.com/901ab741cb46441ebec510b37e63b9d8d1b7c95f63cc4e5e8757f35179ae6373) + +### 6.1 后处理 +如果需要获取key-value信息,可以基于启发式的规则,将识别结果与关键字库进行匹配;如果匹配上了,则取该字段为key, 后面一个字段为value。 + +```python +def postprocess(rec_res): + keys = ["型号", "厂家", "版本号", "检定校准分类", "计量器具编号", "烟尘流量", + "累积体积", "烟气温度", "动压", "静压", "时间", "试验台编号", "预测流速", + "全压", "烟温", "流速", "工况流量", "标杆流量", "烟尘直读嘴", "烟尘采样嘴", + "大气压", "计前温度", "计前压力", "干球温度", "湿球温度", "流量", "含湿量"] + key_value = [] + if len(rec_res) > 1: + for i in range(len(rec_res) - 1): + rec_str, _ = rec_res[i] + for key in keys: + if rec_str in key: + key_value.append([rec_str, rec_res[i + 1][0]]) + break + return key_value +key_value = postprocess(filter_rec_res) +``` + +## 7. PaddleServing部署 +首先需要安装PaddleServing部署相关的环境 + +```python +python -m pip install paddle-serving-server-gpu +python -m pip install paddle_serving_client +python -m pip install paddle-serving-app +``` + +### 7.1 转化检测模型 + +```python +cd deploy/pdserving/ +python -m paddle_serving_client.convert --dirname ../../inference/det_ppocrv3/Student/ \ + --model_filename inference.pdmodel \ + --params_filename inference.pdiparams \ + --serving_server ./ppocr_det_v3_serving/ \ + --serving_client ./ppocr_det_v3_client/ +``` + +### 7.2 转化识别模型 + +```python +python -m paddle_serving_client.convert --dirname ../../inference/rec_ppocrv3/Student \ + --model_filename inference.pdmodel \ + --params_filename inference.pdiparams \ + --serving_server ./ppocr_rec_v3_serving/ \ + --serving_client ./ppocr_rec_v3_client/ +``` + + +### 7.3 启动服务 +首先可以将后处理代码加入到web_service.py中,具体修改如下: +``` +# 代码153行后面增加下面代码 +def _postprocess(rec_res): + keys = ["型号", "厂家", "版本号", "检定校准分类", "计量器具编号", "烟尘流量", + "累积体积", "烟气温度", "动压", "静压", "时间", "试验台编号", "预测流速", + "全压", "烟温", "流速", "工况流量", "标杆流量", "烟尘直读嘴", "烟尘采样嘴", + "大气压", "计前温度", "计前压力", "干球温度", "湿球温度", "流量", "含湿量"] + key_value = [] + if len(rec_res) > 1: + for i in range(len(rec_res) - 1): + rec_str, _ = rec_res[i] + for key in keys: + if rec_str in key: + key_value.append([rec_str, rec_res[i + 1][0]]) + break + return key_value +key_value = _postprocess(rec_list) +res = {"result": str(key_value)} +# res = {"result": str(result_list)} +``` + +启动服务端 +```python +python web_service.py 2>&1 >log.txt +``` + +### 7.4 发送请求 +然后再开启一个新的终端,运行下面的客户端代码 + +```python +python pipeline_http_client.py --image_dir ../../train_data/icdar2015/text_localization/test/142.jpg +``` + +可以获取到最终的key-value结果: +``` +大气压, 100.07kPa +干球温度, 0000℃ +计前温度, 0000℃ +湿球温度, 0000℃ +计前压力, -0000kPa +流量, 00.0L/min +静压, 00000kPa +含湿量, 00.0 % +``` diff --git a/configs/kie/kie_unet_sdmgr.yml b/configs/kie/kie_unet_sdmgr.yml index a6968aaa3aa7a717a848416efc5ccc567f774b4d..da2e4fda504fcbff280788b0baf6d803cf75fe4b 100644 --- a/configs/kie/kie_unet_sdmgr.yml +++ b/configs/kie/kie_unet_sdmgr.yml @@ -17,7 +17,7 @@ Global: checkpoints: save_inference_dir: use_visualdl: False - class_path: ./train_data/wildreceipt/class_list.txt + class_path: &class_path ./train_data/wildreceipt/class_list.txt infer_img: ./train_data/wildreceipt/1.txt save_res_path: ./output/sdmgr_kie/predicts_kie.txt img_scale: [ 1024, 512 ] @@ -72,6 +72,7 @@ Train: order: 'hwc' - KieLabelEncode: # Class handling label character_dict_path: ./train_data/wildreceipt/dict.txt + class_path: *class_path - KieResize: - ToCHWImage: - KeepKeys: @@ -88,7 +89,6 @@ Eval: data_dir: ./train_data/wildreceipt label_file_list: - ./train_data/wildreceipt/wildreceipt_test.txt - # - /paddle/data/PaddleOCR/train_data/wildreceipt/1.txt transforms: - DecodeImage: # load image img_mode: RGB diff --git a/configs/vqa/ser/layoutlm.yml b/configs/vqa/ser/layoutlm.yml index 87131170c9daabd8553269b900893ac26fb32bc8..47ab093e1fce5cb38a75409eb1d9ac67c6426ba4 100644 --- a/configs/vqa/ser/layoutlm.yml +++ b/configs/vqa/ser/layoutlm.yml @@ -43,7 +43,7 @@ Optimizer: PostProcess: name: VQASerTokenLayoutLMPostProcess - class_path: &class_path ppstructure/vqa/labels/labels_ser.txt + class_path: &class_path train_data/XFUND/class_list_xfun.txt Metric: name: VQASerTokenMetric @@ -54,7 +54,7 @@ Train: name: SimpleDataSet data_dir: train_data/XFUND/zh_train/image label_file_list: - - train_data/XFUND/zh_train/xfun_normalize_train.json + - train_data/XFUND/zh_train/train.json transforms: - DecodeImage: # load image img_mode: RGB @@ -89,7 +89,7 @@ Eval: name: SimpleDataSet data_dir: train_data/XFUND/zh_val/image label_file_list: - - train_data/XFUND/zh_val/xfun_normalize_val.json + - train_data/XFUND/zh_val/val.json transforms: - DecodeImage: # load image img_mode: RGB diff --git a/configs/vqa/ser/layoutlmv2.yml b/configs/vqa/ser/layoutlmv2.yml index 33406252b31adf4175d7ea2f57772b0faf33cdab..d6a9c03e5ec9683a3a6423ed22a98f361769541f 100644 --- a/configs/vqa/ser/layoutlmv2.yml +++ b/configs/vqa/ser/layoutlmv2.yml @@ -44,7 +44,7 @@ Optimizer: PostProcess: name: VQASerTokenLayoutLMPostProcess - class_path: &class_path ppstructure/vqa/labels/labels_ser.txt + class_path: &class_path train_data/XFUND/class_list_xfun.txt Metric: name: VQASerTokenMetric @@ -55,7 +55,7 @@ Train: name: SimpleDataSet data_dir: train_data/XFUND/zh_train/image label_file_list: - - train_data/XFUND/zh_train/xfun_normalize_train.json + - train_data/XFUND/zh_train/train.json transforms: - DecodeImage: # load image img_mode: RGB @@ -90,7 +90,7 @@ Eval: name: SimpleDataSet data_dir: train_data/XFUND/zh_val/image label_file_list: - - train_data/XFUND/zh_val/xfun_normalize_val.json + - train_data/XFUND/zh_val/val.json transforms: - DecodeImage: # load image img_mode: RGB diff --git a/configs/vqa/ser/layoutxlm.yml b/configs/vqa/ser/layoutxlm.yml index eb1cca5a215dd65ef9c302441d05b482f2622a79..3686989ccf7481a28584fd49c3969c1a69cd04d4 100644 --- a/configs/vqa/ser/layoutxlm.yml +++ b/configs/vqa/ser/layoutxlm.yml @@ -11,7 +11,7 @@ Global: save_inference_dir: use_visualdl: False seed: 2022 - infer_img: doc/vqa/input/zh_val_42.jpg + infer_img: ppstructure/docs/vqa/input/zh_val_42.jpg save_res_path: ./output/ser Architecture: @@ -54,7 +54,7 @@ Train: name: SimpleDataSet data_dir: train_data/XFUND/zh_train/image label_file_list: - - train_data/XFUND/zh_train/xfun_normalize_train.json + - train_data/XFUND/zh_train/train.json ratio_list: [ 1.0 ] transforms: - DecodeImage: # load image @@ -90,7 +90,7 @@ Eval: name: SimpleDataSet data_dir: train_data/XFUND/zh_val/image label_file_list: - - train_data/XFUND/zh_val/xfun_normalize_val.json + - train_data/XFUND/zh_val/val.json transforms: - DecodeImage: # load image img_mode: RGB diff --git a/doc/doc_ch/algorithm_overview.md b/doc/doc_ch/algorithm_overview.md index eb81e4cd6dae2542dd07d0e25fe543419f798c9b..ef96f6ec122594afd115b333ffc18fb836253b79 100755 --- a/doc/doc_ch/algorithm_overview.md +++ b/doc/doc_ch/algorithm_overview.md @@ -86,8 +86,9 @@ |SAR|Resnet31| 87.20% | rec_r31_sar | [训练模型](https://paddleocr.bj.bcebos.com/dygraph_v2.1/rec/rec_r31_sar_train.tar) | |SEED|Aster_Resnet| 85.35% | rec_resnet_stn_bilstm_att | [训练模型](https://paddleocr.bj.bcebos.com/dygraph_v2.1/rec/rec_resnet_stn_bilstm_att.tar) | |SVTR|SVTR-Tiny| 89.25% | rec_svtr_tiny_none_ctc_en | [训练模型](https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/rec_svtr_tiny_none_ctc_en_train.tar) | -|ViTSTR|ViTSTR| 79.82% | rec_vitstr_none_ce_en | [训练模型](https://paddleocr.bj.bcebos.com/rec_vitstr_none_ce_train.tar) | -|ABINet|Resnet45| 90.75% | rec_r45_abinet_en | [训练模型](https://paddleocr.bj.bcebos.com/rec_r45_abinet_train.tar) | +|ViTSTR|ViTSTR| 79.82% | rec_vitstr_none_ce | [训练模型](https://paddleocr.bj.bcebos.com/rec_vitstr_none_ce_train.tar) | +|ABINet|Resnet45| 90.75% | rec_r45_abinet | [训练模型](https://paddleocr.bj.bcebos.com/rec_r45_abinet_train.tar) | + diff --git a/doc/doc_ch/application.md b/doc/doc_ch/application.md new file mode 100644 index 0000000000000000000000000000000000000000..5135dfac10b0b49d1c91eb07202bd3929c7bbb7c --- /dev/null +++ b/doc/doc_ch/application.md @@ -0,0 +1,41 @@ +# 场景应用 + +PaddleOCR场景应用覆盖通用,制造、金融、交通行业的主要OCR垂类应用,在PP-OCR、PP-Structure的通用能力基础之上,以notebook的形式展示利用场景数据微调、模型优化方法、数据增广等内容,为开发者快速落地OCR应用提供示范与启发。 + +> 如需下载全部垂类模型,可以扫描下方二维码,关注公众号填写问卷后,加入PaddleOCR官方交流群获取20G OCR学习大礼包(内含《动手学OCR》电子书、课程回放视频、前沿论文等重磅资料) + +
+ +
+ + +> 如果您是企业开发者且未在下述场景中找到合适的方案,可以填写[OCR应用合作调研问卷](https://paddle.wjx.cn/vj/QwF7GKw.aspx),免费与官方团队展开不同层次的合作,包括但不限于问题抽象、确定技术方案、项目答疑、共同研发等。如果您已经使用PaddleOCR落地项目,也可以填写此问卷,与飞桨平台共同宣传推广,提升企业技术品宣。期待您的提交! + +## 通用 + +| 类别 | 亮点 | 类别 | 亮点 | +| ---------------------- | -------- | ---------- | ------------ | +| 高精度中文识别模型SVTR | 新增模型 | 手写体识别 | 新增字形支持 | + +## 制造 + +| 类别 | 亮点 | 类别 | 亮点 | +| -------------- | ------------------------------ | -------------- | -------------------- | +| 数码管识别 | 数码管数据合成、漏识别调优 | 电表识别 | 大分辨率图像检测调优 | +| 液晶屏读数识别 | 检测模型蒸馏、Serving部署 | PCB文字识别 | 小尺寸文本检测与识别 | +| 包装生产日期 | 点阵字符合成、过曝过暗文字识别 | 液晶屏缺陷检测 | 非文字形态识别 | + +## 金融 + +| 类别 | 亮点 | 类别 | 亮点 | +| -------------- | ------------------------ | ------------ | --------------------- | +| 表单VQA | 多模态通用表单结构化提取 | 通用卡证识别 | 通用结构化提取 | +| 增值税发票 | 尽请期待 | 身份证识别 | 结构化提取、图像阴影 | +| 印章检测与识别 | 端到端弯曲文本识别 | 合同比对 | 密集文本检测、NLP串联 | + +## 交通 + +| 类别 | 亮点 | 类别 | 亮点 | +| ----------------- | ------------------------------ | ---------- | -------- | +| 车牌识别 | 多角度图像、轻量模型、端侧部署 | 快递单识别 | 尽请期待 | +| 驾驶证/行驶证识别 | 尽请期待 | | | \ No newline at end of file diff --git a/doc/doc_en/algorithm_overview_en.md b/doc/doc_en/algorithm_overview_en.md index 28aca7c0d171008156104fbcc786707538fd49ef..bc96cdf2351f10454441e20d319e485019bbec00 100755 --- a/doc/doc_en/algorithm_overview_en.md +++ b/doc/doc_en/algorithm_overview_en.md @@ -85,8 +85,9 @@ Refer to [DTRB](https://arxiv.org/abs/1904.01906), the training and evaluation r |SAR|Resnet31| 87.20% | rec_r31_sar | [trained model](https://paddleocr.bj.bcebos.com/dygraph_v2.1/rec/rec_r31_sar_train.tar) | |SEED|Aster_Resnet| 85.35% | rec_resnet_stn_bilstm_att | [trained model](https://paddleocr.bj.bcebos.com/dygraph_v2.1/rec/rec_resnet_stn_bilstm_att.tar) | |SVTR|SVTR-Tiny| 89.25% | rec_svtr_tiny_none_ctc_en | [trained model](https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/rec_svtr_tiny_none_ctc_en_train.tar) | -|ViTSTR|ViTSTR| 79.82% | rec_vitstr_none_ce_en | [trained model](https://paddleocr.bj.bcebos.com/rec_vitstr_none_none_train.tar) | -|ABINet|Resnet45| 90.75% | rec_r45_abinet_en | [trained model](https://paddleocr.bj.bcebos.com/rec_r45_abinet_train.tar) | +|ViTSTR|ViTSTR| 79.82% | rec_vitstr_none_ce | [trained model](https://paddleocr.bj.bcebos.com/rec_vitstr_none_none_train.tar) | +|ABINet|Resnet45| 90.75% | rec_r45_abinet | [trained model](https://paddleocr.bj.bcebos.com/rec_r45_abinet_train.tar) | + diff --git a/ppocr/data/imaug/__init__.py b/ppocr/data/imaug/__init__.py index 63dfda91f8d0eb200d3c635fda43670039375784..8d8f4e1ffab75473315425c030a1576e19d46e24 100644 --- a/ppocr/data/imaug/__init__.py +++ b/ppocr/data/imaug/__init__.py @@ -23,9 +23,10 @@ from .random_crop_data import EastRandomCropData, RandomCropImgMask from .make_pse_gt import MakePseGt -from .rec_img_aug import RecAug, RecConAug, RecResizeImg, ClsResizeImg, \ - SRNRecResizeImg, GrayRecResizeImg, SARRecResizeImg, PRENResizeImg, \ - ABINetRecResizeImg, SVTRRecResizeImg, ABINetRecAug + +from .rec_img_aug import BaseDataAugmentation, RecAug, RecConAug, RecResizeImg, ClsResizeImg, \ + SRNRecResizeImg, GrayRecResizeImg, SARRecResizeImg, PRENResizeImg, \ + ABINetRecResizeImg, SVTRRecResizeImg, ABINetRecAug from .ssl_img_aug import SSLRotateResize from .randaugment import RandAugment from .copy_paste import CopyPaste diff --git a/ppocr/data/imaug/label_ops.py b/ppocr/data/imaug/label_ops.py index 312d6dc9ad25bfa73aa9009f932fe6f3d3ca7644..c95b326224515c43b03f90ee51c809006399dfff 100644 --- a/ppocr/data/imaug/label_ops.py +++ b/ppocr/data/imaug/label_ops.py @@ -259,15 +259,26 @@ class E2ELabelEncodeTrain(object): class KieLabelEncode(object): - def __init__(self, character_dict_path, norm=10, directed=False, **kwargs): + def __init__(self, + character_dict_path, + class_path, + norm=10, + directed=False, + **kwargs): super(KieLabelEncode, self).__init__() self.dict = dict({'': 0}) + self.label2classid_map = dict() with open(character_dict_path, 'r', encoding='utf-8') as fr: idx = 1 for line in fr: char = line.strip() self.dict[char] = idx idx += 1 + with open(class_path, "r") as fin: + lines = fin.readlines() + for idx, line in enumerate(lines): + line = line.strip("\n") + self.label2classid_map[line] = idx self.norm = norm self.directed = directed @@ -408,7 +419,7 @@ class KieLabelEncode(object): text_ind = [self.dict[c] for c in text if c in self.dict] text_inds.append(text_ind) if 'label' in ann.keys(): - labels.append(ann['label']) + labels.append(self.label2classid_map[ann['label']]) elif 'key_cls' in ann.keys(): labels.append(ann['key_cls']) else: @@ -876,15 +887,16 @@ class VQATokenLabelEncode(object): for info in ocr_info: if train_re: # for re - if len(info["text"]) == 0: + if len(info["transcription"]) == 0: empty_entity.add(info["id"]) continue id2label[info["id"]] = info["label"] relations.extend([tuple(sorted(l)) for l in info["linking"]]) # smooth_box + info["bbox"] = self.trans_poly_to_bbox(info["points"]) bbox = self._smooth_box(info["bbox"], height, width) - text = info["text"] + text = info["transcription"] encode_res = self.tokenizer.encode( text, pad_to_max_seq_len=False, return_attention_mask=True) @@ -900,7 +912,7 @@ class VQATokenLabelEncode(object): label = info['label'] gt_label = self._parse_label(label, encode_res) - # construct entities for re +# construct entities for re if train_re: if gt_label[0] != self.label2id_map["O"]: entity_id_to_index_map[info["id"]] = len(entities) @@ -944,29 +956,29 @@ class VQATokenLabelEncode(object): data['entity_id_to_index_map'] = entity_id_to_index_map return data - def _load_ocr_info(self, data): - def trans_poly_to_bbox(poly): - x1 = np.min([p[0] for p in poly]) - x2 = np.max([p[0] for p in poly]) - y1 = np.min([p[1] for p in poly]) - y2 = np.max([p[1] for p in poly]) - return [x1, y1, x2, y2] + def trans_poly_to_bbox(self, poly): + x1 = np.min([p[0] for p in poly]) + x2 = np.max([p[0] for p in poly]) + y1 = np.min([p[1] for p in poly]) + y2 = np.max([p[1] for p in poly]) + return [x1, y1, x2, y2] + def _load_ocr_info(self, data): if self.infer_mode: ocr_result = self.ocr_engine.ocr(data['image'], cls=False) ocr_info = [] for res in ocr_result: ocr_info.append({ - "text": res[1][0], - "bbox": trans_poly_to_bbox(res[0]), - "poly": res[0], + "transcription": res[1][0], + "bbox": self.trans_poly_to_bbox(res[0]), + "points": res[0], }) return ocr_info else: info = data['label'] # read text info info_dict = json.loads(info) - return info_dict["ocr_info"] + return info_dict def _smooth_box(self, bbox, height, width): bbox[0] = int(bbox[0] * 1000.0 / width) @@ -977,7 +989,7 @@ class VQATokenLabelEncode(object): def _parse_label(self, label, encode_res): gt_label = [] - if label.lower() == "other": + if label.lower() in ["other", "others", "ignore"]: gt_label.extend([0] * len(encode_res["input_ids"])) else: gt_label.append(self.label2id_map[("b-" + label).upper()]) diff --git a/ppocr/data/imaug/vqa/__init__.py b/ppocr/data/imaug/vqa/__init__.py index a5025e7985198e7ee40d6c92d8e1814eb1797032..bde175115536a3f644750260082204fe5f10dc05 100644 --- a/ppocr/data/imaug/vqa/__init__.py +++ b/ppocr/data/imaug/vqa/__init__.py @@ -13,7 +13,12 @@ # limitations under the License. from .token import VQATokenPad, VQASerTokenChunk, VQAReTokenChunk, VQAReTokenRelation +from .augment import DistortBBox __all__ = [ - 'VQATokenPad', 'VQASerTokenChunk', 'VQAReTokenChunk', 'VQAReTokenRelation' + 'VQATokenPad', + 'VQASerTokenChunk', + 'VQAReTokenChunk', + 'VQAReTokenRelation', + 'DistortBBox', ] diff --git a/ppocr/data/imaug/vqa/augment.py b/ppocr/data/imaug/vqa/augment.py new file mode 100644 index 0000000000000000000000000000000000000000..fcdc9685e9855c3a2d8e9f6f5add270f95f15a6c --- /dev/null +++ b/ppocr/data/imaug/vqa/augment.py @@ -0,0 +1,37 @@ +# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys +import numpy as np +import random + + +class DistortBBox: + def __init__(self, prob=0.5, max_scale=1, **kwargs): + """Random distort bbox + """ + self.prob = prob + self.max_scale = max_scale + + def __call__(self, data): + if random.random() > self.prob: + return data + bbox = np.array(data['bbox']) + rnd_scale = (np.random.rand(*bbox.shape) - 0.5) * 2 * self.max_scale + bbox = np.round(bbox + rnd_scale).astype(bbox.dtype) + data['bbox'] = np.clip(data['bbox'], 0, 1000) + data['bbox'] = bbox.tolist() + sys.stdout.flush() + return data diff --git a/ppocr/utils/utility.py b/ppocr/utils/utility.py index 4a25ff8b2fa182faaf4f4ce8909c9ec2e9b55ccc..b881fcab20bc5ca076a0002bd72349768c7d881a 100755 --- a/ppocr/utils/utility.py +++ b/ppocr/utils/utility.py @@ -91,18 +91,19 @@ def check_and_read_gif(img_path): def load_vqa_bio_label_maps(label_map_path): with open(label_map_path, "r", encoding='utf-8') as fin: lines = fin.readlines() - lines = [line.strip() for line in lines] - if "O" not in lines: - lines.insert(0, "O") - labels = [] - for line in lines: - if line == "O": - labels.append("O") - else: - labels.append("B-" + line) - labels.append("I-" + line) - label2id_map = {label: idx for idx, label in enumerate(labels)} - id2label_map = {idx: label for idx, label in enumerate(labels)} + old_lines = [line.strip() for line in lines] + lines = ["O"] + for line in old_lines: + # "O" has already been in lines + if line.upper() in ["OTHER", "OTHERS", "IGNORE"]: + continue + lines.append(line) + labels = ["O"] + for line in lines[1:]: + labels.append("B-" + line) + labels.append("I-" + line) + label2id_map = {label.upper(): idx for idx, label in enumerate(labels)} + id2label_map = {idx: label.upper() for idx, label in enumerate(labels)} return label2id_map, id2label_map diff --git a/ppocr/utils/visual.py b/ppocr/utils/visual.py index 7a8c1674a74f89299de59f7cd120b4577a7499d8..235eb572a3975b4446ae2f2c9ad9c8558d5c5ad8 100644 --- a/ppocr/utils/visual.py +++ b/ppocr/utils/visual.py @@ -19,7 +19,7 @@ from PIL import Image, ImageDraw, ImageFont def draw_ser_results(image, ocr_results, font_path="doc/fonts/simfang.ttf", - font_size=18): + font_size=14): np.random.seed(2021) color = (np.random.permutation(range(255)), np.random.permutation(range(255)), @@ -40,9 +40,15 @@ def draw_ser_results(image, if ocr_info["pred_id"] not in color_map: continue color = color_map[ocr_info["pred_id"]] - text = "{}: {}".format(ocr_info["pred"], ocr_info["text"]) + text = "{}: {}".format(ocr_info["pred"], ocr_info["transcription"]) - draw_box_txt(ocr_info["bbox"], text, draw, font, font_size, color) + if "bbox" in ocr_info: + # draw with ocr engine + bbox = ocr_info["bbox"] + else: + # draw with ocr groundtruth + bbox = trans_poly_to_bbox(ocr_info["points"]) + draw_box_txt(bbox, text, draw, font, font_size, color) img_new = Image.blend(image, img_new, 0.5) return np.array(img_new) @@ -62,6 +68,14 @@ def draw_box_txt(bbox, text, draw, font, font_size, color): draw.text((bbox[0][0] + 1, start_y), text, fill=(255, 255, 255), font=font) +def trans_poly_to_bbox(poly): + x1 = np.min([p[0] for p in poly]) + x2 = np.max([p[0] for p in poly]) + y1 = np.min([p[1] for p in poly]) + y2 = np.max([p[1] for p in poly]) + return [x1, y1, x2, y2] + + def draw_re_results(image, result, font_path="doc/fonts/simfang.ttf", @@ -80,10 +94,10 @@ def draw_re_results(image, color_line = (0, 255, 0) for ocr_info_head, ocr_info_tail in result: - draw_box_txt(ocr_info_head["bbox"], ocr_info_head["text"], draw, font, - font_size, color_head) - draw_box_txt(ocr_info_tail["bbox"], ocr_info_tail["text"], draw, font, - font_size, color_tail) + draw_box_txt(ocr_info_head["bbox"], ocr_info_head["transcription"], + draw, font, font_size, color_head) + draw_box_txt(ocr_info_tail["bbox"], ocr_info_tail["transcription"], + draw, font, font_size, color_tail) center_head = ( (ocr_info_head['bbox'][0] + ocr_info_head['bbox'][2]) // 2, diff --git a/ppstructure/docs/kie.md b/ppstructure/docs/kie.md index 35498b33478d1010fd2548dfcb8586b4710723a1..315dd9f7bafa6b6160489eab330e8d278b2d119d 100644 --- a/ppstructure/docs/kie.md +++ b/ppstructure/docs/kie.md @@ -16,7 +16,7 @@ SDMGR是一个关键信息提取算法,将每个检测到的文本区域分类 训练和测试的数据采用wildreceipt数据集,通过如下指令下载数据集: ``` -wget https://paddleocr.bj.bcebos.com/dygraph_v2.1/kie/wildreceipt.tar && tar xf wildreceipt.tar +wget https://paddleocr.bj.bcebos.com/ppstructure/dataset/wildreceipt.tar && tar xf wildreceipt.tar ``` 执行预测: diff --git a/ppstructure/docs/kie_en.md b/ppstructure/docs/kie_en.md index 1fe38b0b399e9290526dafa5409673dc87026db7..7b3752223dd765e780d56d146c90bd0f892aac7b 100644 --- a/ppstructure/docs/kie_en.md +++ b/ppstructure/docs/kie_en.md @@ -15,7 +15,7 @@ This section provides a tutorial example on how to quickly use, train, and evalu [Wildreceipt dataset](https://paperswithcode.com/dataset/wildreceipt) is used for this tutorial. It contains 1765 photos, with 25 classes, and 50000 text boxes, which can be downloaded by wget: ```shell -wget https://paddleocr.bj.bcebos.com/dygraph_v2.1/kie/wildreceipt.tar && tar xf wildreceipt.tar +wget https://paddleocr.bj.bcebos.com/ppstructure/dataset/wildreceipt.tar && tar xf wildreceipt.tar ``` Download the pretrained model and predict the result: diff --git a/ppstructure/vqa/README.md b/ppstructure/vqa/README.md index e3a10671ddb6494eb15073e7ac007aa1e8e6a32a..711ffa313865cd5a210143819cd4604dc28ef4f4 100644 --- a/ppstructure/vqa/README.md +++ b/ppstructure/vqa/README.md @@ -125,13 +125,13 @@ If you want to experience the prediction process directly, you can download the * Download the processed dataset -The download address of the processed XFUND Chinese dataset: [https://paddleocr.bj.bcebos.com/dataset/XFUND.tar](https://paddleocr.bj.bcebos.com/dataset/XFUND.tar). +The download address of the processed XFUND Chinese dataset: [link](https://paddleocr.bj.bcebos.com/ppstructure/dataset/XFUND.tar). Download and unzip the dataset, and place the dataset in the current directory after unzipping. ```shell -wget https://paddleocr.bj.bcebos.com/dataset/XFUND.tar +wget https://paddleocr.bj.bcebos.com/ppstructure/dataset/XFUND.tar ```` * Convert the dataset diff --git a/ppstructure/vqa/README_ch.md b/ppstructure/vqa/README_ch.md index b677dc07bce6c1a752d753b6a1c538b4d3f99271..297ba64f82e70eafd4a0b1fee0764899799219ad 100644 --- a/ppstructure/vqa/README_ch.md +++ b/ppstructure/vqa/README_ch.md @@ -122,13 +122,13 @@ python3 -m pip install -r ppstructure/vqa/requirements.txt * 下载处理好的数据集 -处理好的XFUND中文数据集下载地址:[https://paddleocr.bj.bcebos.com/dataset/XFUND.tar](https://paddleocr.bj.bcebos.com/dataset/XFUND.tar)。 +处理好的XFUND中文数据集下载地址:[链接](https://paddleocr.bj.bcebos.com/ppstructure/dataset/XFUND.tar)。 下载并解压该数据集,解压后将数据集放置在当前目录下。 ```shell -wget https://paddleocr.bj.bcebos.com/dataset/XFUND.tar +wget https://paddleocr.bj.bcebos.com/ppstructure/dataset/XFUND.tar ``` * 转换数据集 diff --git a/ppstructure/vqa/labels/labels_ser.txt b/ppstructure/vqa/labels/labels_ser.txt deleted file mode 100644 index 508e48112412f62538baf0c78bcf99ec8945196e..0000000000000000000000000000000000000000 --- a/ppstructure/vqa/labels/labels_ser.txt +++ /dev/null @@ -1,3 +0,0 @@ -QUESTION -ANSWER -HEADER diff --git a/ppstructure/vqa/tools/trans_xfun_data.py b/ppstructure/vqa/tools/trans_xfun_data.py index 93ec98163c6cec96ec93399c1d41524200ddc499..11d221bea40367f091b3e09dde42e87f2217a617 100644 --- a/ppstructure/vqa/tools/trans_xfun_data.py +++ b/ppstructure/vqa/tools/trans_xfun_data.py @@ -21,26 +21,22 @@ def transfer_xfun_data(json_path=None, output_file=None): json_info = json.loads(lines[0]) documents = json_info["documents"] - label_info = {} with open(output_file, "w", encoding='utf-8') as fout: for idx, document in enumerate(documents): + label_info = [] img_info = document["img"] document = document["document"] image_path = img_info["fname"] - label_info["height"] = img_info["height"] - label_info["width"] = img_info["width"] - - label_info["ocr_info"] = [] - for doc in document: - label_info["ocr_info"].append({ - "text": doc["text"], + x1, y1, x2, y2 = doc["box"] + points = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]] + label_info.append({ + "transcription": doc["text"], "label": doc["label"], - "bbox": doc["box"], + "points": points, "id": doc["id"], - "linking": doc["linking"], - "words": doc["words"] + "linking": doc["linking"] }) fout.write(image_path + "\t" + json.dumps( diff --git a/tools/infer_kie.py b/tools/infer_kie.py index 0cb0b8702cbd7ea74a7b7fcff69122731578a1bd..346e2e0aeeee695ab49577b6b13dcc058150df1a 100755 --- a/tools/infer_kie.py +++ b/tools/infer_kie.py @@ -39,13 +39,12 @@ import time def read_class_list(filepath): - dict = {} + ret = {} with open(filepath, "r") as f: lines = f.readlines() - for line in lines: - key, value = line.split(" ") - dict[key] = value.rstrip() - return dict + for idx, line in enumerate(lines): + ret[idx] = line.strip("\n") + return ret def draw_kie_result(batch, node, idx_to_cls, count): @@ -71,7 +70,7 @@ def draw_kie_result(batch, node, idx_to_cls, count): x_min = int(min([point[0] for point in new_box])) y_min = int(min([point[1] for point in new_box])) - pred_label = str(node_pred_label[i]) + pred_label = node_pred_label[i] if pred_label in idx_to_cls: pred_label = idx_to_cls[pred_label] pred_score = '{:.2f}'.format(node_pred_score[i]) @@ -109,8 +108,7 @@ def main(): save_res_path = config['Global']['save_res_path'] class_path = config['Global']['class_path'] idx_to_cls = read_class_list(class_path) - if not os.path.exists(os.path.dirname(save_res_path)): - os.makedirs(os.path.dirname(save_res_path)) + os.makedirs(os.path.dirname(save_res_path), exist_ok=True) model.eval() diff --git a/tools/infer_vqa_token_ser.py b/tools/infer_vqa_token_ser.py index 83ed72b392e627c161903c3945f57be0abfabc2b..39ada64a99847a910158b74672c89398ba08f032 100755 --- a/tools/infer_vqa_token_ser.py +++ b/tools/infer_vqa_token_ser.py @@ -86,15 +86,16 @@ class SerPredictor(object): ] transforms.append(op) - global_config['infer_mode'] = True + if config["Global"].get("infer_mode", None) is None: + global_config['infer_mode'] = True self.ops = create_operators(config['Eval']['dataset']['transforms'], global_config) self.model.eval() - def __call__(self, img_path): - with open(img_path, 'rb') as f: + def __call__(self, data): + with open(data["img_path"], 'rb') as f: img = f.read() - data = {'image': img} + data["image"] = img batch = transform(data, self.ops) batch = to_tensor(batch) preds = self.model(batch) @@ -112,20 +113,35 @@ if __name__ == '__main__': ser_engine = SerPredictor(config) - infer_imgs = get_image_file_list(config['Global']['infer_img']) + if config["Global"].get("infer_mode", None) is False: + data_dir = config['Eval']['dataset']['data_dir'] + with open(config['Global']['infer_img'], "rb") as f: + infer_imgs = f.readlines() + else: + infer_imgs = get_image_file_list(config['Global']['infer_img']) + with open( os.path.join(config['Global']['save_res_path'], "infer_results.txt"), "w", encoding='utf-8') as fout: - for idx, img_path in enumerate(infer_imgs): + for idx, info in enumerate(infer_imgs): + if config["Global"].get("infer_mode", None) is False: + data_line = info.decode('utf-8') + substr = data_line.strip("\n").split("\t") + img_path = os.path.join(data_dir, substr[0]) + data = {'img_path': img_path, 'label': substr[1]} + else: + img_path = info + data = {'img_path': img_path} + save_img_path = os.path.join( config['Global']['save_res_path'], os.path.splitext(os.path.basename(img_path))[0] + "_ser.jpg") logger.info("process: [{}/{}], save result to {}".format( idx, len(infer_imgs), save_img_path)) - result, _ = ser_engine(img_path) + result, _ = ser_engine(data) result = result[0] fout.write(img_path + "\t" + json.dumps( { diff --git a/tools/program.py b/tools/program.py index aa3ba82c44d6afba725a8059dc7f8cae41947b3d..f598feb7e87d2f04a30d8ac27c5d2442f90eaf80 100755 --- a/tools/program.py +++ b/tools/program.py @@ -576,8 +576,8 @@ def preprocess(is_train=False): assert alg in [ 'EAST', 'DB', 'SAST', 'Rosetta', 'CRNN', 'STARNet', 'RARE', 'SRN', 'CLS', 'PGNet', 'Distillation', 'NRTR', 'TableAttn', 'SAR', 'PSE', - 'SEED', 'SDMGR', 'LayoutXLM', 'LayoutLM', 'PREN', 'FCE', 'SVTR', - 'ViTSTR', 'ABINet' + 'SEED', 'SDMGR', 'LayoutXLM', 'LayoutLM', 'LayoutLMv2', 'PREN', 'FCE', + 'SVTR', 'ViTSTR', 'ABINet' ] if use_xpu: