實戰七:手把手教你用TensorFlow進行驗證碼識別(上)

實戰七:手把手教你用TensorFlow進行驗證碼識別(上)

github下載地址

目錄

  • 準備模型開發環境
  • 生成驗證碼數據集
  • 輸入與輸出數據處理
  • 模型結構設計
  • 模型損失函數設計
  • 模型訓練過程分析
  • 模型部署與效果演示

一、準備模型開發環境

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).其主要依賴pyparsingGraphViz這兩個工具庫

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.]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章