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

2024-03-03 18:48

本文主要是介绍OCR技术3-大批量生成文字训练集,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

如果是想训练一个手写体识别的模型,用一些前人收集好的手写文字集就好了,比如中科院的这些数据集。但是如果我们只是想要训练一个专门用于识别印刷汉字的模型,那么我们就需要各种印刷字体的训练集,那怎么获取呢?借助强大的图像库,自己生成就行了!

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

  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 = - rotateif 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):def __init__(self,width, height,need_crop, margin):self.width = widthself.height = heightself.need_crop = need_cropself.margin = margindef do(self, font_path, char, rotate=0):find_image_bbox = FindImageBBox()# 黑色背景img = Image.new("RGB", (self.width, self.height), "black")draw = ImageDraw.Draw(img)font = ImageFont.truetype(font_path, int(self.width * 0.7),)# 白色字体draw.text((0, 0), char, (255, 255, 255),font=font)if rotate != 0:img = img.rotate(rotate)data = list(img.getdata())sum_val = 0for i_data in data:sum_val += sum(i_data)if sum_val > 2:np_img = np.asarray(data, dtype='uint8')np_img = np_img[:, 0]np_img = np_img.reshape((self.height, self.width))cropped_box = find_image_bbox.do(np_img)left, upper, right, lower = cropped_boxnp_img = np_img[upper: lower + 1, left: right + 1]if not self.need_crop:preprocess_resize_keep_ratio_fill_bg = \PreprocessResizeKeepRatioFillBG(self.width, self.height,fill_bg=False,margin=self.margin)np_img = preprocess_resize_keep_ratio_fill_bg.do(np_img)# cv2.imwrite(path_img, np_img)return np_imgelse:print("img doesn't exist.")

我们写两个循环,外层循环是汉字列表,内层循环是字体列表,对于每个汉字会得到一个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)if not os.path.isdir(char_dir):os.makedirs(char_dir)path_image = os.path.join(char_dir,"%d.png" % count)cv2.imwrite(path_image,img)count += 1

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

 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

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

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

原文地址:http://www.cnblogs.com/skyfsm/p/8436820.html

 

欢迎扫码关注我的微信公众号

 

这篇关于OCR技术3-大批量生成文字训练集的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/770552

相关文章

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

Java利用JSONPath操作JSON数据的技术指南

《Java利用JSONPath操作JSON数据的技术指南》JSONPath是一种强大的工具,用于查询和操作JSON数据,类似于SQL的语法,它为处理复杂的JSON数据结构提供了简单且高效... 目录1、简述2、什么是 jsONPath?3、Java 示例3.1 基本查询3.2 过滤查询3.3 递归搜索3.4

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

java中使用POI生成Excel并导出过程

《java中使用POI生成Excel并导出过程》:本文主要介绍java中使用POI生成Excel并导出过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录需求说明及实现方式需求完成通用代码版本1版本2结果展示type参数为atype参数为b总结注:本文章中代码均为

在java中如何将inputStream对象转换为File对象(不生成本地文件)

《在java中如何将inputStream对象转换为File对象(不生成本地文件)》:本文主要介绍在java中如何将inputStream对象转换为File对象(不生成本地文件),具有很好的参考价... 目录需求说明问题解决总结需求说明在后端中通过POI生成Excel文件流,将输出流(outputStre

C/C++随机数生成的五种方法

《C/C++随机数生成的五种方法》C++作为一种古老的编程语言,其随机数生成的方法已经经历了多次的变革,早期的C++版本使用的是rand()函数和RAND_MAX常量,这种方法虽然简单,但并不总是提供... 目录C/C++ 随机数生成方法1. 使用 rand() 和 srand()2. 使用 <random

Flask 验证码自动生成的实现示例

《Flask验证码自动生成的实现示例》本文主要介绍了Flask验证码自动生成的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习... 目录生成图片以及结果处理验证码蓝图html页面展示想必验证码大家都有所了解,但是可以自己定义图片验证码

Python如何在Word中生成多种不同类型的图表

《Python如何在Word中生成多种不同类型的图表》Word文档中插入图表不仅能直观呈现数据,还能提升文档的可读性和专业性,本文将介绍如何使用Python在Word文档中创建和自定义各种图表,需要的... 目录在Word中创建柱形图在Word中创建条形图在Word中创建折线图在Word中创建饼图在Word