最近做了一个汉字验证码识别的项目,该项目最大的难点就是数据集的准备。在自己没有数据集的情况下选择了自动生成,不过自己生成的数据集训练出来的模型,只在用自己方法生成的数据上表现比较好,但是在真实数据集上表现比较差,也算是自己为自己踩了一格坑。不过自己生成数据集的经历,还是学到了很多知识。并且该方法产生的数据集,可以产生无限的大量数据,可以供后面一些模型的测试,提供了很好的资源,今天我也把产生数据集的方法分享给大家。
一、问题的产生背景和自己生成数据集的意义
我作的项目是破解验证码,没错,是不是感觉有点像黑客。哈哈,也是自己以为,其实业界人会觉得很low。破解验证码的目的是为了网络爬虫准备,在爬数据的过程中会遇到跳出验证吗的问题。影响到机器的自动爬,需要输进去验证码,这样我的工作就有了意义。但是,遇到一个很大的问题,我没数据,我没数据。所以有了两个思路,第一个思虑就是爬验证码,然后在标注训练。不过开始想了想真是特别夸张,要多少人力,汉字那么多分类我要多少数据。因此产生了第二个想法,自己生成数据,这样谁也管不住老子产生多少数据了。
二、真实数据样例
数据样本分析:
背景色相同,有四个干扰线,颜色不同。字体经过自己子产生数据过程中的对比是书宋,有干扰点,分布均匀。这不是明显机器自动生成的数据嘛,更加坚信了我产生数据的想法。
三、数据的生成过程
我就写一下步骤吧:
1、产生与真实数据相同的背景
2、产生干扰点,你们会想为什么这么早产生干扰点,因为我是用改变像素点的方法产生,我遍历整个图的像素,根据自己的设定随机改变一些像素值,就会产生了干扰点,而对后面汉字产生没有影响。(经过实验比较好的方法)
3、用drawimage的方法在背景上生成汉字,汉字我用的是shusong.ttf
4、旋转字体,字体写上以后就是整张图旋转了,但是旋转以后背景图的四个角是改变的。因为背景是有层次感的,导致旋转后四个角遮挡不聊了,漏出黑色。
5、这一步大家应该知道,把四个角的黑色三角变成白色。网上很多方法,先对图片根据像素值对其进行截取,只留下汉字所在的方框,然后在放在另外一个背景下,让背景统一。不过博主有一个另外简单想法,就是根据三通道值进行判断哪一部分是黑色三角,如果三通道值都是0,则代表是。但是这种方法应该最前面字体产生有个要求,黑色字体的像素不能是0,要是从1开始,这样对视觉没啥影响吧。
6、旋转以后,我们就要对图片产生干扰线。随机生成四条线,宽度和汉字的相同,颜色随机产生。
7、批量产生,我把常用汉字存到一个字典里,然后产生某个汉字的图片,都放在汉字所对应的key值文件夹下,这样我在训练的时候,根据访问文件夹就知道样本的标签啦。
四、代码分享和分析
from PIL import Imageimport matplotlib.pyplot as pltfrom PIL import ImageFontfrom PIL import ImageDrawimport tensorflow as tfimport numpy as npimport randomimport osimport cv2import skimage.utilfrom code7 import ImageCharclass Creatimage(object): def __init__(self, font=ImageFont.truetype('fangzheng_shusong.ttf', 30), img_r=Image.new("RGB", (50, 50), (255, 255, 255)), size=(50, 50), chars="作", image_arr=np.zeros((256, 256, 3), np.uint8), value=0, count=0, image_list = [[255,240,245],[185,211,238],[187,255,255]]): self.font = font self.img_r = img_r self.image_arr = image_arr self.size = size self.char = chars self.value = value self.count = count self.image_list=image_list def addnoise(self): self.img_r = np.array(self.img_r) for i in range(50): for j in range(50): if i%2 ==0 and j%2==0: list =self.image_list[random.randint(0,2)] self.img_r[i][j][0]=list[0] self.img_r[i][j][1]=list[1] self.img_r[i][j][2]=list[2] def drawText(self): self.img_r = Image.fromarray(self.img_r) draw = ImageDraw.Draw(self.img_r) draw.text((15, 20), self.char, self.randRGB(), font=self.font) del draw def rotate(self): self.img_r = self.img_r.rotate(random.randint(0,90), expand=0) def randLine(self, num): draw = ImageDraw.Draw(self.img_r) lineColor = self.randRGB() for i in range(0, num): draw.line([self.randPoint(), self.randPoint()], lineColor, 2) del draw def randPoint(self): (width, height) = self.size return (random.randint(0, width), random.randint(0, height)) def randRGB(self): return (random.randint(1, 255), random.randint(1, 255), random.randint(1, 255)) def changeIm(self): self.image_arr = np.array(self.img_r) for i in range(50): for j in range(50): if self.image_arr[i][j][0]==0 and self.image_arr[i][j][0]==0 and self.image_arr[i][j][0]==0: self.image_arr[i][j][0]=255 self.image_arr[i][j][1]=255 self.image_arr[i][j][2]=255 if i % 2 == 0 and j % 2 == 0: list = self.image_list[random.randint(0, 2)] self.image_arr[i][j][0] = list[0] self.image_arr[i][j][1] = list[1] self.image_arr[i][j][2] = list[2] # print(self.image_arr) #im=np.array(img_r) #imshow(im) def printimage(self): plt.imshow(self.image_arr) plt.show() def createImage(self, num): self.addnoise() self.drawText() self.rotate() self.randLine(num) self.changeIm() self.printimage() def saveImage(self): out_dir = os.path.expanduser("./dateset") train_images_dir = os.path.join(out_dir, "train") if not os.path.isdir(train_images_dir): os.makedirs(train_images_dir) char_dir = os.path.join(train_images_dir, "%0.5d" % self.value) path_image = os.path.join(char_dir, "%d.jpeg" % self.count) if not os.path.isdir(char_dir): os.makedirs(char_dir) cv2.imwrite(path_image, self.image_arr) im = Image.fromarray(self.image_arr) im.save(path_image)if __name__ == "__main__": ic = Creatimage() ic.createImage(4) ic.saveImage()
六、产生数据的样例
七、总结
良心说我感觉很像了,也是我呕心沥血,用了浑身解数的模仿,加各种技巧产生的。分享给大家,批量生产的源码在我的github上(https://github.com/machine-lv/Production-verification-code)