Python3生成手寫(xiě)體數(shù)字方法
0.引言
平時(shí)上網(wǎng)干啥的基本上都會(huì)接觸驗(yàn)證碼,或者在機(jī)器學(xué)習(xí)學(xué)習(xí)過(guò)程中,大家或許會(huì)接觸過(guò)手寫(xiě)體識(shí)別/驗(yàn)證碼識(shí)別之類(lèi)問(wèn)題,會(huì)用到手寫(xiě)體的數(shù)據(jù)集;
自己嘗試寫(xiě)了一個(gè)生成手寫(xiě)體圖片的python程序,在此分享下生成單張 30*30像素的手寫(xiě)體數(shù)字1-9圖像 的一種實(shí)現(xiàn)方法;
我是利用random生成隨機(jī)數(shù)1-9,然后PIL寫(xiě)到圖像上,然后經(jīng)過(guò)旋轉(zhuǎn)、扭曲處理,得到“手寫(xiě)體”,這里沒(méi)有加干擾線和干擾點(diǎn);
得到的手寫(xiě)體數(shù)字圖像如圖1所示;
實(shí)現(xiàn)比較簡(jiǎn)單,用了PIL庫(kù),不需要額外安裝opencv啥的,有興趣可以自己試試。

圖1 生成的手寫(xiě)體數(shù)字1-9

圖2 利用generate_pngs.py寫(xiě)入到文件夾3的數(shù)字3圖像
如果你想生成手寫(xiě)體的字母/漢字也可以:


圖3 利用generate_single_png.py生成漢字的手寫(xiě)體
源碼上傳到了我的GitHub: https://github.com/coneypo/Generate_handwritten_number
1.設(shè)計(jì)流程

圖4 整體設(shè)計(jì)流程

圖5 生成的圖像經(jīng)過(guò)的處理
1.1 新建一個(gè)空白圖像img_50,尺寸大小為50*50
img_50_blank = Image.new('RGB', (50, 50), (255, 255, 255))
為什么我這里要先生成50*50的空白圖像?
因?yàn)閳D像背景(50*50像素的畫(huà)布)初始化的時(shí)候設(shè)置為白色(顏色數(shù)組(255, 255, 255)),而背景色之外的其實(shí)是黑色;
之后需要進(jìn)行旋轉(zhuǎn)處理,如果直接新建30*30像素的畫(huà)布,旋轉(zhuǎn)之后邊上會(huì)出現(xiàn)黑邊,如圖6所示;
所以我新建了一個(gè)50*50,然后旋轉(zhuǎn)之后從中間裁出來(lái)一個(gè)30*30的圖像出來(lái);

圖6 直接用30*30像素的畫(huà)布寫(xiě)字旋轉(zhuǎn)(會(huì)出現(xiàn)黑邊)
1.2 利用PIL在圖像上寫(xiě)文字
利用PIL的ImageDraw,創(chuàng)建畫(huà)筆,然后利用draw.text在指定位置寫(xiě)字;
xy=(18,11)是從圖像左上角開(kāi)始的坐標(biāo),取值自己根據(jù)需求調(diào)整;
# 創(chuàng)建畫(huà)筆
draw = ImageDraw.Draw(img_50_blank)
# 生成隨機(jī)數(shù)1-9
num = str(random.randint(1, 9))
# 設(shè)置字體,這里選取字體大小25
font = ImageFont.truetype('simsun.ttc', 20)
# xy是左上角開(kāi)始的位置坐標(biāo)
draw.text(xy=(18, 11), font=font, text=num, fill=(0, 0, 0))
1.3 將圖像隨機(jī)旋轉(zhuǎn)一定角度
利用 rotate(angel) 進(jìn)行旋轉(zhuǎn)圖像,angel取的是度數(shù),這里讓它隨機(jī)旋轉(zhuǎn)-10到+10度:
# 隨機(jī)旋轉(zhuǎn)-10-10角度 random_angle = random.randint(-10, 10) img_50_rotated = img_50_blank.rotate(random_angle)
1.4 圖像扭曲
這里是生成“手寫(xiě)體”數(shù)字的核心步驟,一個(gè)正常的圖像經(jīng)過(guò)扭曲之后就可以得到想要的驗(yàn)證碼了:
# 圖形扭曲參數(shù)
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]
# 創(chuàng)建扭曲
img_50_transformed = img_50_rotated.transform((50, 50), Image.PERSPECTIVE, params)
2.py源碼介紹
2.1 generate_folders_1to9.py
因?yàn)槲覀円獙⒅付ǖ膱D像分類(lèi)放入指定文件夾,所以我們需要先在項(xiàng)目目錄下面新建9個(gè)文件夾:
(當(dāng)然你也可以自己新建,新建9個(gè)文件夾工作量還不大,但是如果要生成的驗(yàn)證碼包含英文字母那就比較多了,大寫(xiě)A-Z共24個(gè)+小寫(xiě)a-z共24個(gè)+數(shù)字1-9共9個(gè)=57個(gè)子文件夾)
# 2018-01-9
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# generate_folders_1to9.py
# 在目錄下生成用來(lái)存放數(shù)字1-9的9個(gè)文件夾,分別用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 自動(dòng)生成的用來(lái)存放指定圖像的文件夾
2.2 generate_pngs.py
根據(jù)給定隨機(jī)次數(shù)samples, 生成samples個(gè)手寫(xiě)體數(shù)字1-9,然后存放到本地文件夾1-9生成數(shù)據(jù)集;
在49行可以修改生成圖像的大小,我這里取的是30*30像素;
# 2018-01-9
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# generate_pngs.py
# 生成手寫(xiě)體數(shù)字
import random
from PIL import Image, ImageDraw, ImageFilter, ImageFont
random.seed(3)
# 生成單張扭曲的數(shù)字圖像
def generate_single():
# 先繪制一個(gè)50*50的空?qǐng)D像
img_50_blank = Image.new('RGB', (50, 50), (255, 255, 255))
# 創(chuàng)建畫(huà)筆
draw = ImageDraw.Draw(img_50_blank)
# 生成隨機(jī)數(shù)1-9
num = str(random.randint(1, 9))
# 設(shè)置字體,這里選取字體大小25
font = ImageFont.truetype('simsun.ttc', 20)
# xy是左上角開(kāi)始的位置坐標(biāo)
draw.text(xy=(18, 11), font=font, text=num, fill=(0, 0, 0))
# 隨機(jī)旋轉(zhuǎn)-10-10角度
random_angle = random.randint(-10, 10)
img_50_rotated = img_50_blank.rotate(random_angle)
# 圖形扭曲參數(shù)
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]
# 創(chuàng)建扭曲
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/"
# 生成手寫(xiě)體數(shù)字1-9存入指定文件夾1-9
# 用cnt_num[1]-cnt_num[9]來(lái)計(jì)數(shù)數(shù)字1-9生成的個(gè)數(shù),方便之后進(jìn)行命名
cnt_num = []
for i in range(10):
cnt_num.append(0)
# 生成次數(shù)
samples = 200
for m in range(1, samples+1):
# 調(diào)用生成圖像文件函數(shù)
img, generate_num = generate_single()
# 取灰度
imgray = img.convert('1')
# 計(jì)數(shù)生成的數(shù)字1-9的個(gè)數(shù),用來(lái)命名圖像文件
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, 你就可以生成指定大小的數(shù)據(jù)集;
2.3 generate_single_png.py
更改27行的char=" "(可以是數(shù)字/字母/漢字),生成單張手寫(xiě)體扭曲圖像:
# 2018-01-9
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# generate_single_png.py
# 生成手寫(xiě)體數(shù)字/字母/漢字
import random
from PIL import Image, ImageDraw, ImageFilter, ImageFont
random.seed(3)
# 生成單張扭曲的數(shù)字圖像
def generate_single():
# 先繪制一個(gè)50*50的空?qǐng)D像
img_50_blank = Image.new('RGB', (50, 50), (255, 255, 255))
# 創(chuàng)建畫(huà)筆
draw = ImageDraw.Draw(img_50_blank)
# 設(shè)置字體,這里選取字體大小25
font = ImageFont.truetype('simsun.ttc', 20)
# xy是左上角開(kāi)始的位置坐標(biāo)
# text是你想要顯示的內(nèi)容,數(shù)字/字母/漢字
char ="呵"
draw.text(xy=(12, 11), font=font, text=char, fill=(0, 0, 0))
# 隨機(jī)旋轉(zhuǎn)-10-10角度
random_angle = random.randint(-10, 10)
img_50_rotated = img_50_blank.rotate(random_angle)
# 圖形扭曲參數(shù)
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]
# 創(chuàng)建扭曲
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/"
# 調(diào)用生成圖像文件函數(shù)
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中的所有圖片:
# 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.總結(jié)
自己動(dòng)手豐衣足食,有興趣可以自己做手寫(xiě)體數(shù)字?jǐn)?shù)據(jù)集,字母和漢字的數(shù)據(jù)集稍加修改也可以做;
# GitHub: https://github.com/coneypo/Generate_handwritten_number
相關(guān)文章
pandas 相關(guān)性和正態(tài)性分析的實(shí)踐
當(dāng)我們談?wù)撜龖B(tài)性(Normality)和相關(guān)性(Correlation)時(shí),我們實(shí)際上在嘗試?yán)斫鈹?shù)據(jù)的分布模式和不同變量之間的關(guān)系,本文就來(lái)介紹一下pandas 相關(guān)性和正態(tài)性的實(shí)踐,感興趣的可以了解一下2024-07-07
用Python做個(gè)自動(dòng)化彈鋼琴腳本實(shí)現(xiàn)天空之城彈奏
突然靈機(jī)一動(dòng),能不能用Python自動(dòng)化腳本彈奏一曲美妙的鋼琴曲呢?今天就一起帶大家如何用Python實(shí)現(xiàn)自動(dòng)化彈出一首《天空之城》有需要的朋友可以借鑒參考下2021-09-09
Python3 pip3 list 出現(xiàn) DEPRECATION 警告的解決方法
今天小編就為大家分享一篇Python3 pip3 list 出現(xiàn) DEPRECATION 警告的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-02-02
對(duì)python中的for循環(huán)和range內(nèi)置函數(shù)詳解
下面小編就為大家分享一篇對(duì)python中的for循環(huán)和range內(nèi)置函數(shù)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
python學(xué)習(xí)開(kāi)發(fā)mock接口
這篇文章主要為大家詳細(xì)介紹了python學(xué)習(xí)開(kāi)發(fā)mock接口的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04

