實戰七:手把手教你用TensorFlow進行驗證碼識別(上)
目錄
- 準備模型開發環境
- 生成驗證碼數據集
- 輸入與輸出數據處理
- 模型結構設計
- 模型損失函數設計
- 模型訓練過程分析
- 模型部署與效果演示
一、準備模型開發環境
1.第三方依賴包
pip install Pillow captcha pydot flask
2.Pillow(PUL Fork)
PIL(Python Imaging Library)
爲Python解釋器添加了圖像處理功能。但是,在2009年發佈1.17版本之後,社區便停止更新和維護。
Pillow
是由Alex Clark及社區貢獻者一起開發和維護的一款分叉自PIL
的圖像工具庫。至今,社區依然非常活躍,Pillow
仍在快速迭代。
Pillow
提供廣泛的文件格式支持,高效的內部表示和相當強大的圖像處理功能。核心圖像庫旨在快速訪問以幾種基本像素格式存儲的數據,它應該爲一般的圖像處理工具提供堅實的基礎。
3.catpcha
Catpcha
是一個生成圖像和音頻驗證碼的開源工具庫
from captcha.image import ImageCaptcha
from captcha.audio import AudioCaptcha
image = ImageCaptcha(fonts=["/path/A.ttf","/path/B.ttf")
data = image.generate("1234")
image.write("1234","out.png")
audio = AudioCaptcha(voicedir="/path/voices")
data = audio.generate("1234")
audio.write("1234","out.wav")
4.pydot
pydot
是用純Python
實現的GraphViz
接口,支持使用GraphViz
解析和存儲DOT
語言(graph description language).其主要依賴pyparsing
和GraphViz
這兩個工具庫
pyparsing
:僅用於加載DOT
文件,在pydot
安裝期間自動安裝。
Graphviz
:將圖形渲染爲PDF,PNG,SVG等格式文件,需獨立安裝。
5.flask
flask
是一個基於Werkzeug和jinja2開發的Python Web應用程序框架,遵從BSD
開源協議。它以一種簡約的方式實現了框架核心,又保留了擴展性。
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello,World!"
二、生成驗證碼數據集
1.驗證碼(catpcha)簡介
全自動區分計算機和人類對的公開圖靈測試,俗稱驗證碼,是一種區分用戶是計算機或人的公共全自動程序
。在captcha測試中,作爲服務器的計算機會自動生成一個問題由用戶來解答。這個問題可以由計算機生成並評判,但是必須只有人類才能解答。由於計算機無法解答captcha的問題,所以回答出問題的用戶就可以被認爲是人類。
一種常用的captcha測試是讓用戶輸入一個扭曲變形的圖片上所顯示的文字或數字
,扭曲變形式爲了避免被光學字符識別(OCR)之類的計算機程序自動識別出圖片上的數字而失去效果。由於這個測試是由計算機來考人類,而不是標準圖靈測試中那樣由人類來考計算機,人們有時稱captcha是一種反向圖靈測試
。
2.驗證碼的演進
3.驗證碼的生成
a.引入第三方包
from captcha.image import ImageCaptcha
import random
import numpy as np
import tensorflow.gfile as gfile
import matplotlib.pyplot as plt
import PIL.Image as Image
b.定義常量和字符集
NUMBER = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
LOWERCASE = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
'v', 'w', 'x', 'y', 'z']
UPPERCASE = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z']
CAPTCHA_CHARSET = NUMBER # 驗證碼字符集
CAPTCHA_LEN = 4 # 驗證碼長度
CAPTCHA_HEIGHT = 60 # 驗證碼高度
CAPTCHA_WIDTH = 160 # 驗證碼寬度
TRAIN_DATASET_SIZE = 5000 # 驗證碼數據集大小
TEST_DATASET_SIZE = 1000 # 驗證碼測試集大小
TRAIN_DATA_DIR = "./train-data/" # 驗證碼數據集目錄
TEST_DATA_DIR = "./test-data/" # 驗證碼測試集目錄
c.生成隨機字符的方法
def gen_random_text(charset=CAPTCHA_CHARSET,length=CAPTCHA_LEN):
text = [random.choice(charset) for _ in range(length)]
return "".join(text)
d.創建並保存驗證碼數據集的方法
def create_captcha_dataset(size=100,data_dir="./data/",height=60,width=160,image_format=".png"):
# 如果保存驗證碼圖像,先清空data_dir目錄
if gfile.Exists(data_dir):
gfile.DeleteRecursively(data_dir)
gfile.MakeDirs(data_dir)
# 創建ImageCaptcha實例captcha
captcha = ImageCaptcha(width=width,height=height)
for _ in range(size):
# 生成隨機的驗證碼字符
text = gen_random_text(CAPTCHA_CHARSET,CAPTCHA_LEN)
captcha.write(text,data_dir + text + image_format)
return None
e.創建並保存訓練集及測試集
# 創建並保存訓練集
create_captcha_dataset(TRAIN_DATASET_SIZE,TRAIN_DATA_DIR)
# 創建並保存測試集
create_captcha_dataset(TEST_DATASET_SIZE,TEST_DATA_DIR)
f.生成並返回驗證碼數據集的方法
def gen_captcha_dataset(size=100,height=60,width=160,image_font=".png"):
# 創建ImageCaptcha實例captcha
captcha = ImageCaptcha(width=width,height=height)
# 創建圖像和文本數組
images,texts = [None]*size,[None]*size
for i in range(size):
# 生成隨機的驗證碼字符
texts[i] = gen_random_text(CAPTCHA_CHARSET,CAPTCHA_LEN)
# 使用PIL.Image.open() 識別新生成的驗證碼圖像
# 然後,將圖像轉換爲形如(CAPTCHA_WIDTH,CAPTCHA_HEIGHT,3)的numpy數組
images[i] = np.array(Image.open(captcha.generate(texts[i])))
return images,texts
g.生成100張驗證碼圖像和字符
# 生成並返回圖像和標籤
images,texts = gen_captcha_dataset()
# 展示
plt.figure()
for i in range(20):
plt.subplot(5,4,i+1) # 繪製前20個驗證碼,以5行4列子圖形式展示
plt.tight_layout() # 自動適配子圖尺寸
plt.imshow(images[i])
plt.title("Label:{}".format(texts[i])) # 設置標籤爲子圖標題
plt.xticks([]) # 刪除x軸標記
plt.yticks([]) # 刪除y軸標記
plt.show()
三、輸入與輸出數據處理
1.輸入數據處理
2.輸出數據處理
3.數據處理
a.引入第三方包
from PIL import Image
from keras import backend as K
import random
import glob
import numpy as np
import tensorflow.gfile as gfile
import matplotlib.pyplot as plt
b.定義超參數和字符集
NUMBER = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
LOWERCASE = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
'v', 'w', 'x', 'y', 'z']
UPPERCASE = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z']
CAPTCHA_CHARSET = NUMBER # 驗證碼字符集
CAPTCHA_LEN = 4 # 驗證碼長度
CAPTCHA_HEIGHT = 60 # 驗證碼高度
CAPTCHA_WIDTH = 160 # 驗證碼寬度
TRAIN_DATA_DIR = '.\\train-data\\' # 驗證碼數據集目錄
c.讀取訓練集前100張圖片,並通過文件名解析驗證碼(標籤)
image = []
text = []
count = 0
for filename in glob.glob(TRAIN_DATA_DIR + "*.png"):
image.append(np.array(Image.open(filename)))
text.append(filename.lstrip(TRAIN_DATA_DIR).rstrip(".png"))
count += 1
if count >= 100:
break
d.數據可視化
plt.figure()
for i in range(20):
plt.subplot(5,4,i+1) # 繪製前20個驗證碼,以5行4列子圖形式展示
plt.tight_layout() # 自動適配子圖尺寸
plt.imshow(image[i])
plt.title("Label:{}".format(text[i])) # 設置標籤爲子圖標題
plt.xticks([]) # 刪除x軸標記
plt.yticks([]) # 刪除y軸標記
plt.show()
image = np.array(image,dtype=np.float32)
print(image.shape,type(image))
(100, 60, 160, 3) <class 'numpy.ndarray'>
e.將RGB驗證碼圖像轉成灰度圖
# 定義轉灰度圖函數
def rgb2gray(img):
# 這裏利用公式來轉換 Y' = 0.299 R + 0.587 G + 0.114 B
return np.dot(img[...,:3],[0.299,0.587,0.114])
# 轉換
image = rgb2gray(image)
# 輸出維度
print(image.shape)
(100, 60, 160)
# 查看圖形
plt.figure()
for i in range(20):
plt.subplot(5,4,i+1) # 繪製前20個驗證碼,以5行4列子圖形式展示
plt.tight_layout() # 自動適配子圖尺寸
plt.imshow(image[i],cmap="Greys")
plt.title("Label:{}".format(text[i])) # 設置標籤爲子圖標題
plt.xticks([]) # 刪除x軸標記
plt.yticks([]) # 刪除y軸標記
plt.show()
f.數據規範化
image = image / 255
image[0]
array([[0.96912157, 0.96912157, 0.96912157, ..., 0.96912157, 0.96912157,
0.96912157],
[0.96912157, 0.96912157, 0.96912157, ..., 0.96912157, 0.96912157,
0.96912157],
[0.96912157, 0.96912157, 0.96912157, ..., 0.96912157, 0.96912157,
0.96912157],
...,
[0.96912157, 0.96912157, 0.96912157, ..., 0.96912157, 0.96912157,
0.96912157],
[0.96912157, 0.96912157, 0.96912157, ..., 0.96912157, 0.96912157,
0.96912157],
[0.96912157, 0.96912157, 0.96912157, ..., 0.96912157, 0.96912157,
0.96912157]])
g.適配Keras圖像數據格式
# 先查看一下數據的維度
print(image.shape,type(image))
(100, 60, 160) <class 'numpy.ndarray'>
# 定義適配函數
def fit_keras_channels(batch,rows=CAPTCHA_HEIGHT,cols=CAPTCHA_WIDTH):
if K.image_data_format() == "channels_first":
batch = batch.reshape(batch.shape[0],1,rows,cols)
input_shape = (1,rows,cols)
else:
batch = batch.reshape(batch.shape[0],rows,cols,1)
input_shape = (rows,cols,1)
return batch,input_shape
# 再重新查看一下數據的維度
image,input_shape = fit_keras_channels(image)
print(image.shape,type(image))
print(input_shape)
(100, 60, 160, 1) <class 'numpy.ndarray'>
(60, 160, 1)
h.對驗證按中每個字符進行one-hot編碼
# 查看一下text中的數據
print(text[:10])
print(len(text[0]))
print(len(text))
['0002', '0004', '0007', '0009', '0011', '0012', '0014', '0015', '0017', '0018']
4
100
# 定義one-hot編碼函數
# CAPTCHA_CHARSET = NUMBER # 驗證碼字符集
# CAPTCHA_LEN = 4 # 驗證碼長度
def text2vec(text,length=CAPTCHA_LEN,charset=CAPTCHA_CHARSET):
text_len = len(text)
# 驗證碼長度校驗
if text_len != length:
raise ValueError("Error:length of captcha should be{},but got {}".format(length,text_len))
# 生成一個形如(CAPTCHA_LEN*CAPTCHA_CHARSET)的一維向量
# 例如,4個純數字的驗證碼生成形如(4*10,)的一維向量
vec = np.zeros(length*len(charset))
for i in range(length):
# One-hot編碼驗證碼中的每個數字
# 每個字符的熱碼 = 索引 +偏移量
vec[charset.index(text[i]) + i*len(charset)] = 1
return vec
text = list(text)
vec = [None]*len(text)
for i in range(len(vec)):
vec[i] = text2vec(text[i])
print(text[0])
print(vec[0])
0002
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]