python 识别验证码

keras 视频学习

https://morvanzhou.github.io/tutorials/machine-learning/keras/

学习此篇文章共花费了我1周的业余时间,幸好没丢人 有点结果了

1、下载数据部分

自己写了一个thinkphp 生成验证码,刚开始生成的比较负责,怎么训练都不行后来写得的比较简单,这样就能快速验证自己的模型是否正确

import os
from urllib import request


for idx in range(1000):
    urlString ='http://www.code.com/public/index.php/index'
    response =  request.urlopen(urlString)
//由于thinkphp 验证码是单独的一个机制  本人也不太会php 所以就把验证码的值放在headers里面
    imageName ='../data/'+ str(idx)+'_'+response.headers['code']+'.png'

    with open(imageName,'wb') as img:
        img.write(response.read())

2、处理数据

把数据弧度处理 并二值化 减小计算压力

import os

from PIL import Image

pics_dir='../data'
processed_pics_dir='./data'
# 将图片灰度化以减少计算压力
def preprocess_pics():
    for (dirpath, dirnames, filenames) in os.walk(pics_dir):
        for filename in filenames:
            if filename.endswith('.png'):
                with open(pics_dir + '/' + filename, 'rb') as f:
                    image = Image.open(f)
                    # 直接使用convert方法对图片进行灰度操作
                    image = image.convert('L')
                    image = convert_Image(image)
                    with open(processed_pics_dir + '/' + filename, 'wb') as of:
                        image.save(of)

def convert_Image(img, standard=127.5):
    '''
    【灰度转换】
    '''
    image = img.convert('L')

    '''
    【二值化】
    根据阈值 standard , 将所有像素都置为 0(黑色) 或 255(白色), 便于接下来的分割
    '''
    pixels = image.load()
    for x in range(image.width):
        for y in range(image.height):
            if pixels[x, y] > standard:
                pixels[x, y] = 255
            else:
                pixels[x, y] = 0
    return image


preprocess_pics()  #会覆盖掉手动标注的数据请慎用

3、编写loadData 方法 以方便神经网络调用

import os
import random

import numpy as np
from PIL import Image


IMAGE_HEIGHT=30
IMAGE_WIDTH=110

captcha_chars = '01'
# captcha_chars = '0123456789'
char_idx_mappings = {}
idx_char_mappings = {}

for idx, c in enumerate(list(captcha_chars)):
    char_idx_mappings[c] = idx
    idx_char_mappings[idx] = c

MAX_CAPTCHA = 4 #验证码长度
CHAR_SET_LEN = len(captcha_chars)
# 验证码转化为向量
def text2vec(text):
    text_len = len(text)
    if text_len > MAX_CAPTCHA:
        raise ValueError('验证码最长%d个字符'%MAX_CAPTCHA)
    vector = np.zeros(MAX_CAPTCHA*CHAR_SET_LEN)
    for i, c in enumerate(text):
        idx = i * CHAR_SET_LEN + char_idx_mappings[c]
        vector[idx] = 1
    return vector
# 向量转化为验证码
def vec2text(vec):
    text = []
    vec[vec<0.5] = 0
    char_pos = vec.nonzero()[0]
    for i, c in enumerate(char_pos):
        char_idx = c % CHAR_SET_LEN
        text.append(idx_char_mappings[char_idx])
    return ''.join(text)


# 向量转化为验证码
def vec2text1(vec):
    text = []
    for i in range(MAX_CAPTCHA):
        data = vec[i*CHAR_SET_LEN:(i+1)*CHAR_SET_LEN]
        index = data.tolist().index(max(data))
        text.append(idx_char_mappings[index])
    return ''.join(text)






# processed_pics_dir='../data'
processed_pics_dir='./data'

img_idx_filename_mappings = {}
img_idx_text_mappings = {}
img_idxes = []

index=0
# 首先遍历目录,根据文件名初始化idx->filename, idx->text的映射,同时初始化idx列表
for (dirpath, dirnames, filenames) in os.walk(processed_pics_dir):
    for filename in filenames:
        if filename.endswith('.png'):
            idx = int(filename[0:filename.index('_')])
            text = filename[int(filename.index('_')+1):int(filename.index('.'))]
            img_idx_filename_mappings[idx] = filename
            img_idx_text_mappings[idx] = text
            img_idxes.append(idx)


# 为避免频繁读入文件,将images及labels缓存起来
sample_idx_image_mappings = {}
sample_idx_label_mappings = {}
# 提供给外部取得一批训练数据的接口
def get_batch_data(batch_size):
    images = []
    labels = []
    target_idxes = random.sample(img_idxes, batch_size)
    for target_idx in target_idxes:
        image = None
        if target_idx in sample_idx_image_mappings:
            image = sample_idx_image_mappings[target_idx]
        else:
            with open(processed_pics_dir + '/' + img_idx_filename_mappings[target_idx], 'rb') as f:
                image = Image.open(f)
                image=image.convert('L')
                # 对数据正则化,tensorflow处理时更高效
                image = np.array(image)/255.0

            sample_idx_image_mappings[target_idx] = image
        label = None
        if target_idx in sample_idx_label_mappings:
            label = sample_idx_label_mappings[target_idx]
        else:
            label = text2vec(img_idx_text_mappings[target_idx])
            sample_idx_label_mappings[target_idx] = label
        images.append(image)
        labels.append(label)
    x = np.array(images)
    y = np.array(labels)
    return (x, y)

#测试部分 主要为了debug
(x,y)=get_batch_data(1)
#
print(y[0])
# print(x.reshape(-1, IMAGE_HEIGHT, IMAGE_WIDTH, 1).shape)

4、编写神经网络部分

from pathlib import Path
from keras.models import Sequential
from keras.layers import Dense, InputLayer, Activation
from keras.layers.core import Reshape, Dropout, Flatten
from keras.layers.convolutional import Conv2D
from keras.layers.pooling import MaxPooling2D
from keras.layers import Input, concatenate
from keras.models import Model
from keras.optimizers import Adam

import phpcode_data











model = Sequential()
model.add(InputLayer(input_shape=(phpcode_data.IMAGE_HEIGHT, phpcode_data.IMAGE_WIDTH)))
model.add(Reshape((phpcode_data.IMAGE_HEIGHT, phpcode_data.IMAGE_WIDTH, 1)))

# 三组卷积逻辑,每组包括两个卷积层及一个池化层

model.add(Conv2D(
    filters=32,
    kernel_size=5,
    strides=(1, 1),
    padding='same',
    use_bias=True,
    input_shape=(phpcode_data.IMAGE_HEIGHT, phpcode_data.IMAGE_WIDTH, 1)
))
model.add(Activation('relu'))
model.add(MaxPooling2D(
    pool_size=2,
    strides=2,
    padding='same'
))


model.add(Conv2D(
    filters=64,
    kernel_size=5,
    strides=(1, 1),
    padding='same',
    use_bias=True
))
model.add(Activation('relu'))
model.add(MaxPooling2D(
    pool_size=2,
    strides=2,
    padding='same'
))

model.add(Conv2D(
    filters=128,
    kernel_size=5,
    strides=(1, 1),
    padding='same',
    use_bias=True
))
model.add(Activation('relu'))
model.add(MaxPooling2D(
    pool_size=2,
    strides=2,
    padding='same'
))




model.add(Flatten())


# 全连接层,输出维数是kaptcha_data.MAX_CAPTCHA * kaptcha_data.CHAR_SET_LEN
image_input = Input(shape=(phpcode_data.IMAGE_HEIGHT, phpcode_data.IMAGE_WIDTH))
encoded_image = model(image_input)

encoded_softmax = []
for i in range(phpcode_data.MAX_CAPTCHA):
    out1 = Dense(128, activation="relu")(encoded_image)
    output1 = Dense(phpcode_data.CHAR_SET_LEN, activation="softmax")(out1)
    encoded_softmax.append(output1)
output = concatenate(encoded_softmax)

output1 = Dense(128,activation='relu')(output)
output2 = Dense(phpcode_data.MAX_CAPTCHA * phpcode_data.CHAR_SET_LEN,activation='softmax')(output1)

model = Model(inputs=[image_input], outputs=output2)


# model.add(Dense(1024))
# model.add(Activation('relu'))
#
# model.add(Dense(phpcode_data.MAX_CAPTCHA * phpcode_data.CHAR_SET_LEN))
# model.add(Activation('softmax'))

adam = Adam(lr=1e-4)
# 编译模型,损失函数使用categorical_crossentropy, 优化函数使用adadelta,每一次epoch度量accuracy
model.compile(loss='categorical_crossentropy', optimizer=adam, metrics=['accuracy'])

# 模型可视化
from keras.utils import plot_model
plot_model(model, to_file='captcha_recognition_model.png')


if Path('kaptcha_recognition.h5').is_file():
    model.load_weights('kaptcha_recognition.h5')
batch_size = 100

for epoch in range(10000):
    print("epoch {}...".format(epoch))
    (x_train, y_train) = phpcode_data.get_batch_data(batch_size)
    train_result = model.train_on_batch(x=x_train, y=y_train)
    print(' loss: %.6f, accuracy: %.6f' % (train_result[0], train_result[1]))
    if epoch % 5 == 0:
        # 保存模型的权值`
        model.save_weights('kaptcha_recognition.h5')
        # 当准确率大于0.5时,说明学习到的模型已经可以投入实际使用,停止计算

5、验证部分

from pathlib import Path

import numpy as np
from PIL import Image
from keras import Sequential, Input, Model
from keras.engine import InputLayer
from keras.layers import Flatten, Dense, Activation, Conv2D, MaxPooling2D, concatenate, Reshape
from keras.optimizers import Adam

import phpcode_data



model = Sequential()
model.add(InputLayer(input_shape=(phpcode_data.IMAGE_HEIGHT, phpcode_data.IMAGE_WIDTH)))
model.add(Reshape((phpcode_data.IMAGE_HEIGHT, phpcode_data.IMAGE_WIDTH, 1)))

# 三组卷积逻辑,每组包括两个卷积层及一个池化层

model.add(Conv2D(
    filters=32,
    kernel_size=5,
    strides=(1, 1),
    padding='same',
    use_bias=True,
    input_shape=(phpcode_data.IMAGE_HEIGHT, phpcode_data.IMAGE_WIDTH, 1)
))
model.add(Activation('relu'))
model.add(MaxPooling2D(
    pool_size=2,
    strides=2,
    padding='same'
))


model.add(Conv2D(
    filters=64,
    kernel_size=5,
    strides=(1, 1),
    padding='same',
    use_bias=True
))
model.add(Activation('relu'))
model.add(MaxPooling2D(
    pool_size=2,
    strides=2,
    padding='same'
))

model.add(Conv2D(
    filters=128,
    kernel_size=5,
    strides=(1, 1),
    padding='same',
    use_bias=True
))
model.add(Activation('relu'))
model.add(MaxPooling2D(
    pool_size=2,
    strides=2,
    padding='same'
))




model.add(Flatten())


# 全连接层,输出维数是kaptcha_data.MAX_CAPTCHA * kaptcha_data.CHAR_SET_LEN
image_input = Input(shape=(phpcode_data.IMAGE_HEIGHT, phpcode_data.IMAGE_WIDTH))
encoded_image = model(image_input)

encoded_softmax = []
for i in range(phpcode_data.MAX_CAPTCHA):
    out1 = Dense(128, activation="relu")(encoded_image)
    output1 = Dense(phpcode_data.CHAR_SET_LEN, activation="softmax")(out1)
    encoded_softmax.append(output1)
output = concatenate(encoded_softmax)

output1 = Dense(128,activation='relu')(output)
output2 = Dense(phpcode_data.MAX_CAPTCHA * phpcode_data.CHAR_SET_LEN,activation='softmax')(output1)

model = Model(inputs=[image_input], outputs=output2)


adam = Adam(lr=1e-4)
# 编译模型,损失函数使用categorical_crossentropy, 优化函数使用adadelta,每一次epoch度量accuracy
model.compile(loss='categorical_crossentropy', optimizer=adam, metrics=['accuracy'])


if Path('kaptcha_recognition.h5').is_file():
    model.load_weights('kaptcha_recognition.h5')


def get_single_image(filename):
    images = []
    with open(filename, 'rb') as f:
        image = Image.open(f)
        image = image.convert('L')
        images.append(np.array(image)/255)
    return np.array(images)
# 计算某一张图片的验证码
predicts = model.predict(get_single_image('data/82_1010.png'), batch_size=1)

print(predicts)
print('predict: %s' % phpcode_data.vec2text1(predicts[0]))

代码放在github上了 如果有人看到了 我希望能加我微信 咱们一起学习https://github.com/weihaigang/codeimage

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容