0.引言
平时上网干啥的基本上都会接触验证码,或者在机器学习学习过程中,大家或许会接触过手写体识别/验证码识别之类问题,会用到手写体的数据集;
自己尝试写了一个生成手写体图片的python程序,在此分享下生成单张 30*30像素的手写体数字1-9图像 的一种实现方法;
我是利用random生成随机数1-9,然后PIL写到图像上,然后经过旋转、扭曲处理,得到“手写体”,这里没有加干扰线和干扰点;
得到的手写体数字图像如图1所示;
实现比较简单,用了PIL库,不需要额外安装opencv啥的,有兴趣可以自己试试。
图1 生成的手写体数字1-9
图2 利用generate_pngs.py写入到文件夹3的数字3图像
如果你想生成手写体的字母/汉字也可以:
图3 利用generate_single_png.py生成汉字的手写体
源码上传到了我的GitHub: https://github.com/coneypo/Generate_handwritten_number
1.设计流程
图4 整体设计流程
图5 生成的图像经过的处理
1.1 新建一个空白图像img_50,尺寸大小为50*50
1
|
img_50_blank = Image.new( 'RGB' , ( 50 , 50 ), ( 255 , 255 , 255 ))
|
为什么我这里要先生成50*50的空白图像?
因为图像背景(50*50像素的画布)初始化的时候设置为白色(颜色数组(255, 255, 255)),而背景色之外的其实是黑色;
之后需要进行旋转处理,如果直接新建30*30像素的画布,旋转之后边上会出现黑边,如图6所示;
所以我新建了一个50*50,然后旋转之后从中间裁出来一个30*30的图像出来;
图6 直接用30*30像素的画布写字旋转(会出现黑边)
1.2 利用PIL在图像上写文字
利用PIL的ImageDraw,创建画笔,然后利用draw.text在指定位置写字;
xy=(18,11)是从图像左上角开始的坐标,取值自己根据需求调整;
1
2
3
4
5
6
7
8
9
10
11
|
# 创建画笔
draw = ImageDraw.Draw(img_50_blank)
# 生成随机数1-9
num = str (random.randint( 1 , 9 ))
# 设置字体,这里选取字体大小25
font = ImageFont.truetype( 'simsun.ttc' , 20 )
# xy是左上角开始的位置坐标
draw.text(xy = ( 18 , 11 ), font = font, text = num, fill = ( 0 , 0 , 0 ))
|
1.3 将图像随机旋转一定角度
利用 rotate(angel) 进行旋转图像,angel取的是度数,这里让它随机旋转-10到+10度:
1
2
3
|
# 随机旋转-10-10角度
random_angle = random.randint( - 10 , 10 )
img_50_rotated = img_50_blank.rotate(random_angle)
|
1.4 图像扭曲
这里是生成“手写体”数字的核心步骤,一个正常的图像经过扭曲之后就可以得到想要的验证码了:
1
2
3
4
5
6
7
8
9
10
11
12
|
# 图形扭曲参数
params = [ 1 - float (random.randint( 1 , 2 )) / 100 ,
0 ,
0 ,
0 ,
1 - float (random.randint( 1 , 10 )) / 100 ,
float (random.randint( 1 , 2 )) / 500 ,
0.001 ,
float (random.randint( 1 , 2 )) / 500 ]
# 创建扭曲
img_50_transformed = img_50_rotated.transform(( 50 , 50 ), Image.PERSPECTIVE, params)
|
2.py源码介绍
2.1 generate_folders_1to9.py
因为我们要将指定的图像分类放入指定文件夹,所以我们需要先在项目目录下面新建9个文件夹:
(当然你也可以自己新建,新建9个文件夹工作量还不大,但是如果要生成的验证码包含英文字母那就比较多了,大写A-Z共24个+小写a-z共24个+数字1-9共9个=57个子文件夹)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# 2018-01-9
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# generate_folders_1to9.py
# 在目录下生成用来存放数字1-9的9个文件夹,分别用1-9命名
import os
path_folders = "F:/***/P_generate_handwritten_number/data_pngs/"
# 1-9
for i in range ( 49 , 58 ):
if (os.path.isdir(path_folders + chr (i))):
pass
else :
# print(i,": ",path_1+chr(i))
# 生成目录
os.mkdir(path_folders + chr (i))
|
图7 自动生成的用来存放指定图像的文件夹
2.2 generate_pngs.py
根据给定随机次数samples, 生成samples个手写体数字1-9,然后存放到本地文件夹1-9生成数据集;
在49行可以修改生成图像的大小,我这里取的是30*30像素;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
# 2018-01-9
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# generate_pngs.py
# 生成手写体数字
import random
from PIL import Image, ImageDraw, ImageFilter, ImageFont
random.seed( 3 )
# 生成单张扭曲的数字图像
def generate_single():
# 先绘制一个50*50的空图像
img_50_blank = Image.new( 'RGB' , ( 50 , 50 ), ( 255 , 255 , 255 ))
# 创建画笔
draw = ImageDraw.Draw(img_50_blank)
# 生成随机数1-9
num = str (random.randint( 1 , 9 ))
# 设置字体,这里选取字体大小25
font = ImageFont.truetype( 'simsun.ttc' , 20 )
# xy是左上角开始的位置坐标
draw.text(xy = ( 18 , 11 ), font = font, text = num, fill = ( 0 , 0 , 0 ))
# 随机旋转-10-10角度
random_angle = random.randint( - 10 , 10 )
img_50_rotated = img_50_blank.rotate(random_angle)
# 图形扭曲参数
params = [ 1 - float (random.randint( 1 , 2 )) / 100 ,
0 ,
0 ,
0 ,
1 - float (random.randint( 1 , 10 )) / 100 ,
float (random.randint( 1 , 2 )) / 500 ,
0.001 ,
float (random.randint( 1 , 2 )) / 500 ]
# 创建扭曲
img_50_transformed = img_50_rotated.transform(( 50 , 50 ), Image.PERSPECTIVE, params)
# 生成新的30*30空白图像,(在此处可以更改生成的图像大小)
img_30 = img_50_transformed.crop([ 10 , 10 , 40 , 40 ])
return img_30, num
path_pic = "F:/***/P_generate_handwritten_number/data_pngs/"
# 生成手写体数字1-9存入指定文件夹1-9
# 用cnt_num[1]-cnt_num[9]来计数数字1-9生成的个数,方便之后进行命名
cnt_num = []
for i in range ( 10 ):
cnt_num.append( 0 )
# 生成次数
samples = 200
for m in range ( 1 , samples + 1 ):
# 调用生成图像文件函数
img, generate_num = generate_single()
# 取灰度
imgray = img.convert( '1' )
# 计数生成的数字1-9的个数,用来命名图像文件
for j in range ( 1 , 10 ):
if (generate_num = = str (j)):
cnt_num[j] = cnt_num[j] + 1
# 路径如 "F:/code/***/P_generate_handwritten_number/data_pngs/1/1_231.png"
# 输出显示路径
print (path_pic + str (j) + "/" + str (j) + "_" + str (cnt_num[j]) + ".png" )
# 将图像保存在指定文件夹中
imgray.save(path_pic + str (j) + "/" + str (j) + "_" + str (cnt_num[j]) + ".png" )
# 输出显示1-9的分布
print ( "\n" , "生成的1-9的分布:" )
for k in range ( 9 ):
print (k + 1 , ":" , cnt_num[k + 1 ], "张" )
|
output
D:\***\anaconda\python.exe F:/***/P_generate_handwritten_number/generate_pngs.py
F:/***/P_generate_handwritten_number/data_pngs/4/4_1.png
F:/***/P_generate_handwritten_number/data_pngs/1/1_1.png
F:/***/P_generate_handwritten_number/data_pngs/8/8_1.png
F:/***/P_generate_handwritten_number/data_pngs/3/3_1.png
F:/***/P_generate_handwritten_number/data_pngs/1/1_2.png
...生成的1-9的分布:
: 25 张
: 17 张
: 21 张
: 19 张
: 20 张
: 22 张
: 25 张
: 24 张
: 27 张
修改 generate_pngs.py中的samples, 你就可以生成指定大小的数据集;
2.3 generate_single_png.py
更改27行的char=" "(可以是数字/字母/汉字),生成单张手写体扭曲图像:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
# 2018-01-9
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# generate_single_png.py
# 生成手写体数字/字母/汉字
import random
from PIL import Image, ImageDraw, ImageFilter, ImageFont
random.seed( 3 )
# 生成单张扭曲的数字图像
def generate_single():
# 先绘制一个50*50的空图像
img_50_blank = Image.new( 'RGB' , ( 50 , 50 ), ( 255 , 255 , 255 ))
# 创建画笔
draw = ImageDraw.Draw(img_50_blank)
# 设置字体,这里选取字体大小25
font = ImageFont.truetype( 'simsun.ttc' , 20 )
# xy是左上角开始的位置坐标
# text是你想要显示的内容,数字/字母/汉字
char = "呵"
draw.text(xy = ( 12 , 11 ), font = font, text = char, fill = ( 0 , 0 , 0 ))
# 随机旋转-10-10角度
random_angle = random.randint( - 10 , 10 )
img_50_rotated = img_50_blank.rotate(random_angle)
# 图形扭曲参数
params = [ 1 - float (random.randint( 1 , 2 )) / 100 ,
0 ,
0 ,
0 ,
1 - float (random.randint( 1 , 10 )) / 100 ,
float (random.randint( 1 , 2 )) / 500 ,
0.001 ,
float (random.randint( 1 , 2 )) / 500 ]
# 创建扭曲
img_50_transformed = img_50_rotated.transform(( 50 , 50 ), Image.PERSPECTIVE, params)
# 生成新的30*30空白图像
img_30 = img_50_transformed.crop([ 10 , 10 , 40 , 40 ])
return img_30, char
path_pic = "F:/code/python/P_generate_handwritten_number/"
# 调用生成图像文件函数
img, generated_char = generate_single()
imgray = img.convert( '1' )
print (path_pic + "test.png" )
# 将图像保存在指定文件夹中
imgray.save(path_pic + "test.png" )
|
2.4 del_pngs.py
删除指定目录下子文件夹1-9中的所有图片:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# 2018-01-9
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# del_pngs.py
# 删除路径下生成的图像文件
import os
path_pic = "F:/***/P_generate_handwritten_number/data_pngs/"
#删除路径下的图片
def del_pic():
for i in range ( 1 , 10 ):
# print(path_png+chr(i))
namedir = os.listdir(path_pic + str (i))
for tmppng in namedir:
if ( tmppng in namedir):
# print(tmppng)
os.remove(path_pic + str (i) + "/" + tmppng)
del_pic()
|
3.总结
自己动手丰衣足食,有兴趣可以自己做手写体数字数据集,字母和汉字的数据集稍加修改也可以做;
# GitHub: https://github.com/coneypo/Generate_handwritten_number
原文链接:https://www.cnblogs.com/AdaminXie/p/8379749.html