放假了,终于可以继续可以静下心写一写OCR方面的东西。上次谈到文字的切割,今天打算总结一下我们怎么得到用于训练的文字数据集。如果是想训练一个手写体识别的模型,用一些前人收集好的手写文字集就好了,比如中科院的这些数据集。但是如果我们只是想要训练一个专门用于识别印刷汉字的模型,那么我们就需要各种印刷字体的训练集,那怎么获取呢?借助强大的图像库,自己生成就行了!

先捋一捋思路,生成文字集需要什么步骤:

  1. 确定你要生成多少字体,生成一个记录着汉字与label的对应表。
  2. 确定和收集需要用到的字体文件。
  3. 生成字体图像,存储在规定的目录下。
  4. 适当的数据增强。

第三步的生成字体图像最为重要,如果仅仅是生成很正规的文字,那么用这个正规文字集去训练模型,第一图像数目有点少,第二模型泛化能力比较差,所以我们需要对字体图像做大量的图像处理工作,以增大我们的印刷体文字数据集。

我总结了一下,我们可以做的一些图像增强工作有这些:

  1. 文字扭曲
  2. 背景噪声(椒盐)
  3. 文字位置(设置文字的中心点)
  4. 笔画粘连(膨胀来模拟)
  5. 笔画断裂(腐蚀来模拟)
  6. 文字倾斜(文字旋转)
  7. 多种字体

做完以上增强后,我们得到的数据集已经非常庞大了。

现在开始一步一步生成我们的3755个汉字的印刷体文字数据集。

一、生成汉字与label的对应表

这里的汉字、label映射表的生成我使用了pickel模块,借助它生成一个id:汉字的映射文件存储下来。
这里举个小例子说明怎么生成这个“汉字:id”映射表。

首先在一个txt文件里写入你想要的汉字,如果对汉字对应的ID没有要求的话,我们不妨使用该汉字的排位作为其ID,比如“一二三四五”中,五的ID就是00005。如此类推,把汉字读入内存,建立一个字典,把这个关系记录下来,再使用pickle.dump存入文件保存。

二、收集字体文件

字体文件上网收集就好了,但是值得注意的是,不是每一种字体都支持汉字,所以我们需要筛选出真正适合汉字生成的字体文件才可以。我一共使用了十三种汉字字体作为我们接下来汉字数据集用到的字体,具体如下图:

当然,如果需要进一步扩大数据集来增强训练得到的模型的泛化能力,可以花更多的时间去收集各类汉字字体,那么模型在面对各种字体时也能从容应对,给出准确的预测。

三、文字图像生成

首先是定义好输入参数,其中包括输出目录、字体目录、测试集大小、图像尺寸、图像旋转幅度等等。

def args_parse():#解析输入参数parser = argparse.ArgumentParser(description=description, formatter_class=RawTextHelpFormatter)parser.add_argument('--out_dir', dest='out_dir',default=None, required=True,help='write a caffe dir')parser.add_argument('--font_dir', dest='font_dir',default=None, required=True,help='font dir to to produce images')parser.add_argument('--test_ratio', dest='test_ratio',default=0.2, required=False,help='test dataset size')parser.add_argument('--width', dest='width',default=None, required=True,help='width')parser.add_argument('--height', dest='height',default=None, required=True,help='height')parser.add_argument('--no_crop', dest='no_crop',default=True, required=False,help='', action='store_true')parser.add_argument('--margin', dest='margin',default=0, required=False,help='', )parser.add_argument('--rotate', dest='rotate',default=0, required=False,help='max rotate degree 0-45')parser.add_argument('--rotate_step', dest='rotate_step',default=0, required=False,help='rotate step for the rotate angle')parser.add_argument('--need_aug', dest='need_aug',default=False, required=False,help='need data augmentation', action='store_true')   args = vars(parser.parse_args()) return args

接下来需要将我们第一步得到的对应表读入内存,因为这个表示ID到汉字的映射,我们在做一下转换,改成汉字到ID的映射,用于后面的字体生成。

#将汉字的label读入,得到(ID:汉字)的映射表label_dict
label_dict = get_label_dict()

char_list=[] # 汉字列表
value_list=[] # label列表
for (value,chars) in label_dict.items():
print (value,chars)
char_list.append(chars)
value_list.append(value)

# 合并成新的映射关系表:(汉字:ID)
lang_chars = dict(zip(char_list,value_list))
font_check = FontCheck(lang_chars)

我们对旋转的角度存储到列表中,旋转角度的范围是[-rotate,rotate].

if rotate < 0:roate = - rotate

if rotate > 0 and rotate <= 45:
all_rotate_angles = []
for i in range(0, rotate+1, rotate_step):
all_rotate_angles.append(i)
for i in range(-rotate, 0, rotate_step):
all_rotate_angles.append(i)
#print(all_rotate_angles)

现在说一下字体图像是怎么生成的,首先我们使用的工具是PIL。PIL里面有很好用的汉字生成函数,我们用这个函数再结合我们提供的字体文件,就可以生成我们想要的数字化的汉字了。我们先设定好我们生成的字体颜色为黑底白色,字体尺寸由输入参数来动态设定。

# 生成字体图像
class Font2Image(object):
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>,width, height,need_crop, margin)</span></span>:<span class="hljs-keyword">self</span>.width = width<span class="hljs-keyword">self</span>.height = height<span class="hljs-keyword">self</span>.need_crop = need_crop<span class="hljs-keyword">self</span>.margin = margin<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">do</span><span class="hljs-params">(<span class="hljs-keyword">self</span>, font_path, char, rotate=<span class="hljs-number">0</span>)</span></span>:find_image_bbox = FindImageBBox()<span class="hljs-comment"># 黑色背景</span>img = Image.new(<span class="hljs-string">"RGB"</span>, (<span class="hljs-keyword">self</span>.width, <span class="hljs-keyword">self</span>.height), <span class="hljs-string">"black"</span>)draw = ImageDraw.Draw(img)font = ImageFont.truetype(font_path, int(<span class="hljs-keyword">self</span>.width * <span class="hljs-number">0</span>.<span class="hljs-number">7</span>),)<span class="hljs-comment"># 白色字体</span>draw.text((<span class="hljs-number">0</span>, <span class="hljs-number">0</span>), char, (<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>),font=font)<span class="hljs-keyword">if</span> rotate != <span class="hljs-number">0</span>:img = img.rotate(rotate)data = list(img.getdata())sum_val = <span class="hljs-number">0</span><span class="hljs-keyword">for</span> i_data <span class="hljs-keyword">in</span> <span class="hljs-symbol">data:</span>sum_val += sum(i_data)<span class="hljs-keyword">if</span> sum_val &gt; <span class="hljs-number">2</span>:np_img = np.asarray(data, dtype=<span class="hljs-string">'uint8'</span>)np_img = np_img[<span class="hljs-symbol">:</span>, <span class="hljs-number">0</span>]np_img = np_img.reshape((<span class="hljs-keyword">self</span>.height, <span class="hljs-keyword">self</span>.width))cropped_box = find_image_bbox.<span class="hljs-keyword">do</span>(np_img)left, upper, right, lower = cropped_boxnp_img = np_img[<span class="hljs-symbol">upper:</span> lower + <span class="hljs-number">1</span>, <span class="hljs-symbol">left:</span> right + <span class="hljs-number">1</span>]<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">self</span>.<span class="hljs-symbol">need_crop:</span>preprocess_resize_keep_ratio_fill_bg = \PreprocessResizeKeepRatioFillBG(<span class="hljs-keyword">self</span>.width, <span class="hljs-keyword">self</span>.height,fill_bg=False,margin=<span class="hljs-keyword">self</span>.margin)np_img = preprocess_resize_keep_ratio_fill_bg.<span class="hljs-keyword">do</span>(np_img)<span class="hljs-comment"># cv2.imwrite(path_img, np_img)</span><span class="hljs-keyword">return</span> np_img<span class="hljs-symbol">else:</span>print(<span class="hljs-string">"img doesn't exist."</span>)</code></pre>

我们写两个循环,外层循环是汉字列表,内层循环是字体列表,对于每个汉字会得到一个image_list列表,里面存储着这个汉字的所有图像。

for (char, value) in lang_chars.items():  # 外层循环是字image_list = []print (char,value)#char_dir = os.path.join(images_dir, "%0.5d" % value)for j, verified_font_path in enumerate(verified_font_paths):    # 内层循环是字体   if rotate == 0:image = font2image.do(verified_font_path, char)image_list.append(image)else:for k in all_rotate_angles: image = font2image.do(verified_font_path, char, rotate=k)image_list.append(image)

我们将image_list中图像按照比例分为训练集和测试集存储。

        test_num = len(image_list) * test_ratiorandom.shuffle(image_list)  # 图像列表打乱count = 0for i in range(len(image_list)):img = image_list[i]#print(img.shape)if count < test_num :char_dir = os.path.join(test_images_dir, "%0.5d" % value)else:char_dir = os.path.join(train_images_dir, "%0.5d" % value)
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> <span class="hljs-built_in">os</span>.<span class="hljs-built_in">path</span>.isdir(char_dir):<span class="hljs-built_in">os</span>.makedirs(char_dir)path_image = <span class="hljs-built_in">os</span>.<span class="hljs-built_in">path</span>.join(char_dir,<span class="hljs-string">"%d.png"</span> % count)cv2.imwrite(path_image,img)count += <span class="hljs-number">1</span></code></pre>

写好代码后,我们执行如下指令,开始生成印刷体文字汉字集。

 python gen_printed_char.py --out_dir ./dataset --font_dir ./chinese_fonts --width 30 --height 30 --margin 4 --rotate 30 --rotate_step 1

解析一下上述指令的附属参数:

  1. --out_dir 表示生成的汉字图像的存储目录
  2. --font_dir 表示放置汉字字体文件的路径
  3. --width --height 表示生成图像的高度和宽度
  4. --margin 表示字体与边缘的间隔
  5. --rotate 表示字体旋转的范围,[-rotate,rotate]
  6. --rotate_step 表示每次旋转的间隔

生成这么一个3755个汉字的数据集的所需的时间还是很久的,估计接近一个小时。其实这个生成过程可以用多线程、多进程并行加速,但是考虑到这种文字数据集只需生成一次就好,所以就没做这方面的优化了。数据集生成完我们可以发现,在dataset文件夹下得到train和test两个文件夹,train和test文件夹下都有3755个子文件夹,分别存储着生成的3755个汉字对应的图像,每个子文件的名字就是该汉字对应的id。随便选择一个train文件夹下的一个子文件夹打开,可以看到所获得的汉字图像,一共634个。

dataset下自动生成测试集和训练集

测试集和训练集下都有3755个子文件夹,用于存储每个汉字的图像。

生成出来的汉字图像

额外的图像增强

第三步生成的汉字图像是最基本的数据集,它所做的图像处理仅有旋转这么一项,如果我们想在数据增强上再做多点东西,想必我们最终训练出来的OCR模型的性能会更加优秀。我们使用opencv来完成我们定制的汉字图像增强任务。

因为生成的图像比较小,仅仅是30*30,如果对这么小的图像加噪声或者形态学处理,得到的字体图像会很糟糕,所以我们在做数据增强时,把图片尺寸适当增加,比如设置为100×100,再进行相应的数据增强,效果会更好。

噪点增加

def add_noise(cls,img):for i in range(20): #添加点噪声temp_x = np.random.randint(0,img.shape[0])temp_y = np.random.randint(0,img.shape[1])img[temp_x][temp_y] = 255return img

适当腐蚀

def add_erode(cls,img):kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3, 3))    img = cv2.erode(img,kernel) return img

适当膨胀

def add_dilate(cls,img):kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3, 3))    img = cv2.dilate(img,kernel) return img

然后做随机扰动

def do(self,img_list=[]):aug_list= copy.deepcopy(img_list)for i in range(len(img_list)):im = img_list[i]if self.noise and random.random()<0.5:im = self.add_noise(im)if self.dilate and random.random()<0.25:im = self.add_dilate(im)if self.erode and random.random()<0.25:im = self.add_erode(im)    aug_list.append(im)return aug_list

输入指令

python gen_printed_char.py --out_dir ./dataset2 --font_dir ./chinese_fonts --width 100 --height 100 --margin 10 --rotate 30 --rotate_step 1 --need_aug

使用这种生成的图像如下图所示,第一数据集扩大了两倍,第二图像的丰富性进一步提高,效果还是明显的。当然,如果要获得最好的效果,还需要调一下里面的参数,这里就不再详细说明了。

至此,我们所需的印刷体汉字数据集已经成功生成完毕,下一步要做的就是利用这些数据集设计一个卷积神经网络做文字识别了!完整的代码可以在我的github获取。

【OCR三】大批量生成文字训练集(转)相关推荐

  1. 【OCR技术系列之三】大批量生成文字训练集

    放假了,终于可以继续可以静下心写一写OCR方面的东西.上次谈到文字的切割,今天打算总结一下我们怎么得到用于训练的文字数据集.如果是想训练一个手写体识别的模型,用一些前人收集好的手写文字集就好了,比如中 ...

  2. OCR技术(大批量生成文字训练集)

    如果是想训练一个手写体识别的模型,用一些前人收集好的手写文字集就好了,比如中科院的这些数据集.但是如果我们只是想要训练一个专门用于识别印刷汉字的模型,那么我们就需要各种印刷字体的训练集,那怎么获取呢? ...

  3. 文字识别(四)--大批量生成文字训练集

    转自:https://www.cnblogs.com/skyfsm/p/8436820.html 上次谈到文字的切割,今天打算总结一下我们怎么得到用于训练的文字数据集.如果是想训练一个手写体识别的模型 ...

  4. 【OCR技术】大批量生成文字训练集

    向AI转型的程序员都关注了这个号

  5. OCR技术3-大批量生成文字训练集

    如果是想训练一个手写体识别的模型,用一些前人收集好的手写文字集就好了,比如中科院的这些数据集.但是如果我们只是想要训练一个专门用于识别印刷汉字的模型,那么我们就需要各种印刷字体的训练集,那怎么获取呢? ...

  6. 【OCR技术】大批量构造中文文字训练集

    向AI转型的程序员都关注了这个号??? 大数据挖掘DT数据分析  公众号: datadw 本文全部代码 github 地址 在公众号 datadw 里 回复 训练集  即可获取. 放假了,终于可以继续 ...

  7. yolov3的训练(五)darknet的VOC测试集和训练集以及训练前准备

    VOC测试集和训练集     ################################## 同学们,这个系列的文件不要直接就跟着我操作了,因为这个是踩坑的记录,不是教程,我只是将整个流程记录下 ...

  8. 使用AI工具如何生成文字图片?其实可以这样生成

    在最近几年,自媒体的风头逐渐兴起,许多人都会选择当一个自媒体博主来当作自己的副业.当我们在进行内容创作的时候,难免会遇到一些需要给文章进行配图的情况,但有时我们会对图片的内容完全没有头绪,其实我们可以 ...

  9. 字节最新文本生成图像AI,训练集里居然没有一张带文字描述的图片?!

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 丰色 发自 凹非寺 量子位 | 公众号 QbitAI 一个文本-图像 ...

最新文章

  1. [转] Freemarker的常用技巧总结
  2. 11. Android框架和工具之 Logger(调试代码)
  3. Visual C# 2008+SQL Server 2005 数据库与网络开发――3.2.4 匿名类型
  4. 我知道关于马云的故事,他不成功都难
  5. SprinBoot整合SSM之添加
  6. 有什么工具或应用可以帮助找到适合搭配一种颜色的另一种颜色?
  7. 基于python爬虫的论文标题_Python3实现爬取简书首页文章标题和文章链接的方法【测试可用】...
  8. SQL server 创建数据库代码,非常详细
  9. 编写c#程序,修改文件后缀名
  10. Java基于SSH技术的毕业设计管理系统
  11. 精灵图(雪碧图)一招解决定位难问题,再也不用猜位置或者ps测距离了
  12. 简单JAVA版本淘宝客程序上线
  13. 用python画卡通人物的画法_教你绘制扁平化风格的卡通人物肖像
  14. docker中各ID之间的关系和计算(二)-layerID-diffID-chainID-cacheID的计算
  15. MMDetection理解
  16. 狂神JUC——CAS
  17. 企航软件工作室网站正式采用开源blog系统
  18. ios-deploy 安装与使用
  19. 计算机网络基础知识点三
  20. $timeformat $time

热门文章

  1. 时序分析基本概念介绍——SDC概述
  2. javaweb入门笔记(5)-cookie和session
  3. fastjson转换json格式数据为ListHashMap转换异常问题
  4. ISE使用中RAM IP核配置及ram测试(两种测试)
  5. nginx+keepalived构建主备负载均衡代理服务器
  6. PropertyGrid仿VS的属性事件窗口
  7. createwindow 和 dialogbox的区别
  8. 云南大学软件测试,软件测试大赛云南省省赛在软件学院如期举行
  9. 从四个角度揭密Windows Server 2008技术
  10. 对象当前正在其他地方使用_2019 为什么我们还会继续使用 PHP ?