PaddleOCR二次全流程——3.使用TextRender合成图片
1. 确定要合成的数据量参考PaddlePaddle的微信公众号文章:百家桨坛 | 第一期:OCR文字识别专题100问对于识别任务,需要保证识别字典中每个字符出现在不同场景的行文本图像中数目需要大于200张。例如:如果字典中有五个字,每个字都需要出现在200张图片以上,那么最少要求的图像数量应该在200-1000张之间,这样可以保证基本的识别效果我的场景是,英文26个,数字10个,同时还有一些标点
文章目录
1. 🛒确定要合成的数据量
参考PaddlePaddle的微信公众号文章:
百家桨坛 | 第一期:OCR文字识别专题100问
对于识别任务,需要保证识别字典中
每个字符
出现在不同场景
的行文本图像中数目需要大于200张。
例如:如果字典中有五个字,每个字都需要出现在200张图片以上,那么最少要求的图像数量应该在200-1000张之间,这样可以保证基本的识别效果
我的场景是,英文26个,数字10个,同时还有一些标点符号,- . x
等,差不多50个字符,那就是至少2w张图,反正越多越好。
另外,根据StyleText说明文档的应用案例部分
下面以金属表面英文数字识别和通用韩语识别两个场景为例,说明使用Style-Text合成数据,来提升文本识别效果的实际案例。下图给出了一些真实场景图像和合成图像的示例:
在添加上述合成数据进行训练后,识别模型的效果提升,如下表所示:
可以看到,对于这种本身图片就有些模糊的场景,准确率是不高的。
而且这里在识别 金属表面的
英文和数字
时,使用的合成数据是2w张。
所以确定对于英文、数字、字符这一场景,生成的图片数量是2w-2.5w。
2. 🧧TextRender
- TextRender2.0的版本,github链接,
我之前使用的博客记录,
PaddleOCR数字仪表识别——2(New). textrenderer使用及修改使之符合PaddleOCR数据标准。 - TextRender1.0版本,其实这个功能更加丰富,github链接,接下来的使用主要围绕这个1.0版本。
2.0 基于的环境
建议先在本地跑一下,跑通了,再去服务器上跑,以下内容均是在win10系统,py35环境下跑的。
2.1 安装
运行以下命令:
# 把repo下载下来,目录中会解压得到一个text_renderer的目录,建议复制一份,因为之后会改东西。。。
git clone https://github.com.cnpmjs.org/Sanster/text_renderer.git
# 安装依赖,切换到text_renderer目录下,安装依赖,我是用的是python3.5的环境,
# 觉得慢的话加上清华的镜像
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
2.2 使用
想不到搜索text_renderer使用,没搜到什么有用的,哎,小众领域的悲哀。
只有:
CV学习笔记(十八):文本数据集生成(text_renderer)
和CV学习笔记(十九):数据集拼接生成
还是靠自己吧。
2.2.0 测试
什么都不要改,直接在切换到text_renderer文件夹中,运行以下命令
python3 main.py
然后会输出类似这样的提示信息:
(py35) C:XXXX\ocr\SFTP\TextRenderer_copy>python main.py
Total fonts num: 1
Background num: 1
Loading corpus from: ./data/corpus
Loading chn corpus: 1/1
Total fonts num: 1
Background num: 1
Loading corpus from: ./data/corpus
Loading chn corpus: 1/1
Generate text images in ./output\default
Total fonts num: 1
Total fonts num: 1
Background num: 1
Background num: 1
Loading corpus from: ./data/corpus
Loading chn corpus: 1/1
Total fonts num: 1
Loading corpus from: ./data/corpus
Loading chn corpus: 1/1
Total fonts num: 1
Background num: 1
Total fonts num: 1
Background num: 1
Loading corpus from: ./data/corpus
Loading chn corpus: 1/1
Background num: 1
可以在output/default/
文件夹中看到输出的20张图像和对应的标签,产的还不错。感觉除了没有颜色,还挺像真实场景的。
2.2.1 帮助文档
使用python3 main.py --help
查看文档信息,翻译一波。或者直接去parse_args.py
文件中查看每个参数的意思(还有默认值)
--num_img NUM_IMG 要产生的图片的数量
--length LENGTH 每个图中的字数(中文)或者词语数量(英文),对于英文语料,默认长度是3
--clip_max_chars 训练一个CRNN模型时,一个图中最大字符数量应该小于最后一层CNN层的宽度
--img_height IMG_HEIGHT
--img_width IMG_WIDTH 如果是0,则输出图像会有不同的宽度
--chars_file CHARS_FILE 允许在图像中出现的字符集
--config_file CONFIG_FILE 设置渲染图像时的参数
--fonts_list FONTS_LIST 要是用的字体文件的路径
--bg_dir BG_DIR 背景图文件夹,对应于yaml配置文件
--corpus_dir CORPUS_DIR 当语料库模式为chn或eng时,图像上的文本将从语料库中随机选取。递归地查找语料库目录中的所有txt文件
-- corpus_mode {random,chn,eng,list} 不同的语料库类型有不同的Load/get_sample方法,random:随机从字符文件里选择字符,chn:从语料库里选择连续的字符,eng:从语料库中选择连续的词语,自带空格。
--output_dir OUTPUT_DIR 保存图像的目录
--tag TAG 输出图像被曝存在 output_dir/{tag}目录下
--debug 输出未被裁减的图像(cropped 裁剪的) 变换时中间过程的产物
--viz
--strict 检查生成图像时字体对字符的支持
--gpu 使用CUDA产生图像
--num_processes NUM_PROCESSED 产生图像时使用的进程数量。None的话,使用所有的CPU核数。
看完好像还是不太明晰,用起来就好了。看看源代码就好
2.2.2 配置文件
configs
文件夹中有两个文件,分别是default.yaml
和test.yaml
,配置文件中主要是一些文字特效,包括:透视变换
,随机裁剪
,弯曲
,light border 字边缘发亮(文字笔画外层有一层白色)
,Dark border(文字笔画外层有一层黑色)
,随机字符空白(大),字符间距变大
,随机字符空白(小),字符间距变小
,中间线(类似删除线)
,表格线
,下划线
,浮雕
,反色(颜色相反)
,blur模糊
,文本颜色
,线颜色
3. 👑使用自己的数据去产生图像
其实步骤很简单,我的目标是产生类似下面图的图像
3.1. 准备数据并放到对应的文件夹
主要包括背景图
,字体文件
,语料内容
,识别字符集
这四样。建议在操作前,复制一份text_render文件夹,因为要改其中的一些东西,如果改错了,还可以重来。。
3.1.1 背景
- 给的示例的背景图,尺寸是292x338,位深32,大小82.9KB
- 类似的,把自己的bg文件也放到
text_renderer\data\bg
文件夹中。(我放的图大小尺寸也和示例差不多,对应于上面两个图)
配置这个是因为运行中的参数:
--bg_dir='./data/bg
(这个就是默认参数,因为自己的bg图就是放在默认这里,所以这个参数到运行的时候可以不用写)
3.1.2 字体
自带的英文字体是:Hack-Regular.ttf
,可以把自己需要的字体放到text_renderer\data\fonts
文件夹中,我这里只关注英文,就放在eng
文件夹下了。类似这样:
可以双击ttf文件查看下字体形式,如果觉得默认自带的那个字体和自己的场景差距比较大,可以删掉。
将字体放入这个font\eng
文件夹之后,还需要在\data\fonts_list\eng.txt
这个文件中,写入刚刚那些字体文件的相对路径,类似:
配置这个是因为运行中的参数:
--fonts_list='./data/fonts_list/eng.txt
3.1.3 待识别字符集
由于我面对的场景中,除了英文和数字,还有一些额外的字符,比如[ , ] , ℃, °, μ, *, - . ,(, )
等。
所以只使用默认的\data\chars\eng.txt
这个字符集合文本文件是不够的,需要略加修改。这个文件的内容是:小写字母
+大写字母
+数字
+其他符号
,只需要改其他符号的部分就好。
+
-
²
%
℃
:
.
°
/
[
]
*
(
)
×
μ
θ
# 这是个空格
这里还有个空格的问题,示例中的Hello World
是自带空格的。这有可能是因为选择了英语模式的语料库造成的,但是后续试验发现,使用中文模式,只要英语字符集中写了空格,也是会产生空格的。
注意字体支持字符集问题
另外,熟悉字体的人应该都知道,有些字体只提供了部分字符,有些字符是无法显示的(比如matplotlib中,如果不额外明确中文字体,那么绘制出的图像中无法显示中文,会显示框框),可以使用check_font.py
这个脚本去检查某个字体到底有多少不支持的字符。
"""
这个脚本有三个参数,分别是:
1.--chars_file='./data/chars/eng.txt'
2. --font_dir='./data/fonts/eng' 字体文件夹
3. --delete'=False,(是否删除不完全支持字符集的字体)
"""
# 要用双引号,另外,使用方式是 参数 空格 值,不使用等号的方式
python tools/check_font.py --chars_file "data/chars/eng.txt" --font_dir "data/fonts/eng"
输出结果如下:
font: data/fonts/eng\YuGothR.ttc ,chars unsupported: 0
font: data/fonts/eng\segoeui.ttf ,chars unsupported: 1
font: data/fonts/eng\consola.ttf ,chars unsupported: 1
font: data/fonts/eng\Deng.ttf ,chars unsupported: 0
font: data/fonts/eng\msgothic.ttc ,chars unsupported: 0
3 fonts support all chars(79) in data/chars/eng.txt: ['data/fonts/eng\\YuGothR.ttc', 'data/fonts/eng\\Deng.ttf', 'data/fonts/eng\\msgothic.ttc']
这里并没有像示例一样,输出不支持的字符。。。
考虑分两批去生成,因为确定有一张图的字体就是consola
字体
3.1.4 关于语料
参数中规定了length
参数,这个参数用来控制生成图像中的字符个数,相关的参数是:
--corpus_dir CORPUS_DIR 当语料库模式为chn或eng时,图像上的文本将从语料库中随机选取。递归地查找语料库目录中的所有txt文件
-- corpus_mode {random,chn,eng,list} 不同的语料库类型有不同的Load/get_sample方法,random:随机从字符文件里选择字符,chn:从语料库里选择连续的字符,eng:从语料库中选择连续的词语,自带空格。
- 对应到数据文件夹中,
data\corpus\eng
或data\corpus\chn
这两个文件夹中都放了一个.txt
文件 - 如果
--corpus_mode
参数为chn
或者eng
,这就是从对应的.txt
文件(篇章段落)中选取连续的字符(英语就是连续的词语,自带空格) - 如果
--corpus_mode
参数为list
,则就是从list_corpus
中随机选择一项(一行为一项) - 如果
--corpus_mode
参数为random
,则就是从chars
文件中随机选择字符。
不得不吐槽一下,因为没有找到关于list使用方式的文档,想在github的issue上搜一搜,结果问的都是什么奇葩问题,吐了,🤐
实测一下:
python main.py
--corpus_dir "data/list_corpus"
--corpus_mode "list"
猜对了,这样运行,确实会使用list_corpus
中的文本文件中的每一项,来生成文本(和指定的文本长度无关,--length
参数无效),因为测试的list只有14项,所以会轮流使用其中的数据,产生不一样的效果。
3.1.5 放入自己的语料
文章/篇章级别的内容,放到data\corpus
文件夹中,这个文件夹中的所有txt
文件都会循环读入,然后从这些文章中随机连续选择--length
长度的字符,如果是中文,就是X个字符,如果是英文,就是X个单词(看自己需求去制造)
python main.py
--fonts_list "data/fonts_list/eng.txt"
--chars_file "data/chars/eng.txt"
--corpus_mode "chn"
这个产生的就是这些字体data/fonts_list/eng.txt
支持的字符data/chars/eng.txt
,在data\corpus
文件中的文本文件中选取连续的length
长度个字符(虽然都是英语,但是只要模式选中文,也可以达到选取连续个字符的效果)。就可以生成类似下面的效果:
虽然生成的字符看起来语义不明晰,而且没有空格。
在英语字符集中加入空格,重新生成,可以看到
另外,列表语料放入data\list_corpus\
,然后使用以下参数运行:
python main.py
--corpus_dir "data/list_corpus"
--corpus_mode "list"
经过计算,要产生2.4w张图片,每张图目前有5种特效。修改英语字符集(有空格和无空格)来从corpus中获取随机连续长度的文本,从list_corpus获取每一项文本。需要好好准备一下,用程序产一下list_corpus好了。
3.2. 修改配置文件
可以先看看default.yml
文件,可以发现,其中没有使用的特效:
# 颜色特效不可用
font_color:
enable: false
# 随机字符间距不可用(字符间距应该很容易和空格搞混)
random_space:
enable: false
# 曲线(我的场景里也遇不到这个,而且CTC并不擅长处理这种字符)
curve:
enable: false
# 随机裁剪文本高度(可能会导致字符显示不完整)
crop:
enable: false
使用了的特效(自己按照自己的需求改一改,尽量贴合实际场景),我的其实只有五个使用的特效
text_border:# 给文字的笔画加一层白色或者黑色,我的场景里有加白色的需求
……
3.3. 运行
这里我的图片其实是分批产生的,所以参数不同,运行了多次。结合我的场景,初步数据设计如下:
- 暂时不考虑识别空格,以英语语料,英语字符集,中文采样(连续采样length个字符)作为第一种产生图像的方式。
可能是由于文本篇章长度不够,所以在产数据的时候一直报错:ValueError: probabilities do not sum to 1
,但是并不影响生成的内容。。。
字符长度控制在5-12,其中,长度[5,9]的每个产生600张(相同文本内容可以有不同的样式),共计3k张,长度[10,12]的每个产生1200张,共计3600张,总共6600张。
实际操作中发现,当图片中文字长度为12时,就已经很多了,所以放弃了超过12的长度。
python main.py --fonts_list "data/fonts_list/eng.txt" --chars_file "data/chars/eng.txt" --corpus_mode "chn" --length "5" --num_img "600"
python main.py --fonts_list "data/fonts_list/eng.txt" --chars_file "data/chars/eng.txt" --corpus_mode "chn" --length "10" --num_img "1200"
- 列表语料,要产生1.8w张图,3-5个特效,语料列表长度要在6k左右
- 首先,以
LE9100S、ProcessNo.034、SP-V0546-R、*JAERGb0546-111*、Recipe No.[1-999]、1100×1300 Hori、Gap Tolerance(Calc)[μm]、CP1 Temp.Setting[℃]、3S-V2-130 4S-V3-40
这10个基本样式为基础,替换字母/数字,每个样式400种文字。- 其次,数字+字母+字符随机组合2k种,长度在10-15之间,随机选择。
- 在语料生成的图片中,发现图片的透视比较大,这里修改配置文件低一点。
最终列表语料有6558项
python main.py --corpus_dir "data/list_corpus" --corpus_mode "list" --num_img "18000"
运行过程中也是报错ValueError: probabilities do not sum to 1
,但是并不影响生成的内容。。
训练集就是1.8w+6600=2.46w张
- 另外要考虑训练集和测试集的问题,需要标记一部分那些真实图的数据,处理的时候都处理成灰度图即可。
参照其他项目的训练,基本上测试集和训练集是1:4(以前在机器学习scikit-learn的时候,test_train_split函数也经常是80%,20%)。
所有大约需要5k张测试集图片,这里可以放一部分真实数据进去。
3.4. 其他有趣的点
查看main.py
脚本,可以发现:
generate_img
函数中,可以看到base_name = '{:08d}'.format(img_index)
,保存图片时候,是8个数字restore_exist_labels
函数中,可以看到以下代码def restore_exist_labels(label_path): # 如果目标目录存在 labels.txt 则向该目录中追加图片 start_index = 0 if os.path.exists(label_path): start_index = len(utils.load_chars(label_path)) print('Generate more text images in %s. Start index %d' % (flags.save_dir, start_index)) else: print('Generate text images in %s' % flags.save_dir) return start_index
也就是说,可以多次产生,但是会自动追加。(我自己试了一下,连续运行两次示例代码,会产生40张图,序号和标签文件都是正确的)
此外,关于产生的两个标签文件,一个是labels.txt
,另一个是tmp_lables.txt
,存在两个文件是因为这里产生图片默认使用CPU的多进程,所以产生的顺序可能会不一致,所以先把图像名称和对应的标签(文本内容)都存起来,再排序,只存储标签即可(按需取用,有些人可能不需要这样的文件名)
4.常见文本合成图像工具比较
2021.4.12记录
名称及github链接 | star数目 | 最新更新时间 |
---|---|---|
TextRecognitionDataGenerator | 1.7K | 2020.5.10 |
SynthText | 1.6K | 3 months ago |
text_renderer1.0 | 941 | last month |
5. 其他
又遇到一个新的问题,单纯产数字,上面的重新来一遍。不太一样的点记录了一下:
检查字体不支持的字符情况:
(字符集改了,这里改一下,另外,注意,检查的时候是直接读取字体文件夹,而不是通过font_list.txt这个文件中的)
python tools/check_font.py --chars_file "data/chars/digit.txt" --font_dir "data/fonts/eng"
因为只有10个数字和一个小数点这11个字符,所以基本都支持,没什么问题。
语料问题
这次就不用文章/篇章级别的语料了,直接使用列表好了,需要产一下数字列表。
python main.py --corpus_dir "data/list_corpus/digitList.txt" --corpus_mode "list" --num_img "20"
# 注意,这里corpus_dir必须是目录,不能是具体的文件,不然会提示:
# Loading corpus from: data/list_corpus/digitList.txt Corpus not found.
# 注意,一定所有的值都要用双引号,用单引号会出错
# --output_dir 'output/train'的时候会产生 名为'output的文件夹,然后里面产生名为train'的文件夹
python main.py --fonts_list "data/fonts_list/eng.txt" --chars_file "data/chars/digit.txt" --corpus_mode "list" --num_img "8000" --corpus_dir "data/list_corpus" --img_width "128" --output_dir "output/test"
这里的重点就是语料列表的生成。
另外,这里还有一个问题,图片的长度是固定的,所以文字并不能填满整个图片背景,如下:
- 在
parse_args.py
中,可以看到有两个参数img_height
和img_width
这两个参数来控制图片的高度和宽度,一般要保证是32的倍数,因为之后的模型选择的是CRNN,要经过5次卷积,会缩放32倍。 - 测试了几次,发现对于5位数(整数位3位数,小数点1位数,小数点后两位),128的宽度比较合适。
- 此外,还可以注意到,对于不同的字体,小数点所占的位数确实不同,这个问题其实值得考虑。
修改标记文件格式
注意,生成的图片的标签文件格式是文件序号
,然后对应图片的标签
。
还是和之前一样,要修改成符合paddle训练的格式。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)