美文网首页
python 识别验证码

python 识别验证码

作者: 魔童转世 | 来源:发表于2018-09-04 18:32 被阅读0次

    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

    相关文章

      网友评论

          本文标题:python 识别验证码

          本文链接:https://www.haomeiwen.com/subject/ndjnwftx.html