CNN识别验证码

1.生成训练数据

(1)使用Spring Boot的kaptcha生成训练数据。

(见https://blog.csdn.net/diOSyu/article/details/98758877

验证码生成配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 生成kaptcha的bean-->
    <bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">
        <property name="config">
            <bean class="com.google.code.kaptcha.util.Config">
                <constructor-arg type="java.util.Properties">
                    <!--设置kaptcha属性 -->
                    <props>
                        <!-- 是否有边框-->
                        <prop key="kaptcha.border ">yes</prop>
                        <prop key="kaptcha.border.color">105,179,90</prop>
                        <!-- 字体颜色 -->
                        <prop key="kaptcha.textproducer.font.color">red</prop>
                        <!-- 图片宽度 -->
                        <prop key="kaptcha.image.width">180</prop>
                        <!-- 图片高度 -->
                        <prop key="kaptcha.image.height">60</prop>
                        <!-- 字体大小 -->
                        <prop key="kaptcha.textproducer.font.size">40</prop>
                        <!-- 字符个数 -->
                        <prop key="kaptcha.textproducer.char.length">4</prop>
                        <!-- 使用哪些字体 -->
                        <prop key="kaptcha.textproducer.font.names">宋体,楷体,微软雅黑</prop>
                        <!-- 使用哪些字符生成验证码 -->
                        <prop key="kaptcha.textproducer.char.string">1234567890</prop>
                        <!-- 干扰线的颜色 -->
                        <prop key="kaptcha.noise.color">black</prop>
                        <!-- 图片样式 -->
                        <prop key="kaptcha.obscurificator.impl">com.google.code.kaptcha.impl.WaterRipple</prop>
                        <!-- 背景实现类 -->
                        <prop key="kaptcha.noise.impl">com.google.code.kaptcha.impl.DefaultNoise</prop>


                        <!--<prop key="kaptcha.session.key">code</prop>-->
                        <!--<prop key="kaptcha.background.clear.from">185,56,213</prop>-->
                        <!--<prop key="kaptcha.background.clear.to">white</prop>-->
                        <prop key="kaptcha.textproducer.char.space">2</prop>
                    </props>
                </constructor-arg>
            </bean>
        </property>
    </bean>
</beans>


生成验证码接口

package com.demo.controller;

import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;

@Controller
public class CaptchaController {
    @Autowired
    private Producer captchaProducer = null;

    @RequestMapping("/kaptcha/{capText}")
    public void getKaptchaImage(@PathVariable("capText") String capText, HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpSession session = request.getSession();
        response.setDateHeader("Expires", 0);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");
        response.setContentType("image/jpeg");
        //生成验证码
        if (capText == null || capText.length() != 4) {
            capText = captchaProducer.createText();
        }
        System.out.println("验证码是:");
        System.out.println(capText);
        session.setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);
        //向客户端写出
        BufferedImage bi = captchaProducer.createImage(capText);
        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(bi, "jpg", out);
        try {
            out.flush();
        } finally {
            out.close();
        }
    }
}

(2)使用Python获取图像,用pickle保存

# -*- coding:utf-8 -*-

from random import choice
from PIL import Image
import requests
import numpy as np
from config import *
import pickle
from preprocess import text2vec


def get_random_text():
    _text = ''
    for _i in range(CAPTCHA_LENGTH):
        _text += choice(VOCAB)
    return _text


def get_one_image(_text):
    _url = 'http://localhost:8080/kaptcha/' + _text
    r = requests.get(_url, stream=True)
    r.raise_for_status()
    return r.raw


def generate_captcha(_text):
    image = Image.open(get_one_image(_text)).convert('RGB')
    data = image.load()
    for y in range(IMAGE_HEIGHT):
        for x in range(IMAGE_WIDTH):
            r, g, b = data[x, y]
            if r > 10 and b < 170 and g < 170 and r - g > 20 and r - b > 20:
                data[x, y] = (0, 0, 0)
            else:
                data[x, y] = (255, 255, 255)
    image_l = image.convert('L')
    image_array = np.array(image_l)
    return image_array


def generate_data():
    print('Generating Data...')
    data_x, data_y = [], []

    # generate data x and y
    for i in range(30000):
        text = get_random_text()
        # get captcha array
        captcha_array = generate_captcha(text)
        # get vector
        vector = text2vec(text)
        data_x.append(captcha_array)
        data_y.append(vector)
        if i % 100 == 0:
            print(i)

    x = np.asarray(data_x, np.float32)
    y = np.asarray(data_y, np.float32)
    with open('./data/data.pkl', 'wb') as f:
        pickle.dump(x, f)
        pickle.dump(y, f)


def main():
    generate_data()


if __name__ == '__main__':
    main()

2.CNN 

preprocess.py

# -*- coding:utf-8 -*-

from numpy import zeros, reshape, argmax
from config import *


# 将名字转为向量
def text2vec(_text):
    _vector = zeros(OUTPUT_LENGTH)
    for i, c in enumerate(_text):
        idx = i * VOCAB_LENGTH + VOCAB.index(c)
        _vector[idx] = 1
    return _vector


# 将向量转为名字
def vec2text(_vector):
    _vector = reshape(_vector, [CAPTCHA_LENGTH, -1])
    text = [VOCAB[argmax(i)] for i in _vector]
    return ''.join(text)

 config.py

# 字符表
VOCAB = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
# 字符表长度
VOCAB_LENGTH = len(VOCAB)
# 4位验证码
CAPTCHA_LENGTH = 4
# 验证码宽度
IMAGE_WIDTH = 180
# 验证码长度
IMAGE_HEIGHT = 60

# 输入长度
INPUT_LENGTH = IMAGE_WIDTH * IMAGE_HEIGHT
# 输出长度
OUTPUT_LENGTH = VOCAB_LENGTH * CAPTCHA_LENGTH

main.py

# -*-coding:utf-8 -*-

from preprocess import vec2text
from numpy import zeros
from random import randint
from config import *
import tensorflow as tf
import pickle

"""
全局变量
"""
with open('./data/data.pkl', 'rb') as f:
    DATA_X = pickle.load(f)
    DATA_Y = pickle.load(f)
DATA_LENGTH = len(DATA_X)

X = tf.placeholder(tf.float32, [None, INPUT_LENGTH])
Y = tf.placeholder(tf.float32, [None, OUTPUT_LENGTH])
keep_prob = tf.placeholder(tf.float32)

"""
函数
"""


# 得到文件名和图像
def get_vector_and_image():
    i = randint(0, DATA_LENGTH - 1)
    return DATA_Y[i], DATA_X[i]


# 生成一个训练batch
def get_next_batch(batch_size=64):
    _batch_x = zeros([batch_size, INPUT_LENGTH])
    _batch_y = zeros([batch_size, OUTPUT_LENGTH])

    for i in range(batch_size):
        vector, image = get_vector_and_image()
        _batch_x[i, :] = image.flatten()
        _batch_y[i, :] = vector
    return _batch_x, _batch_y


# 定义CNN
def inference(w_alpha=0.01, b_alpha=0.1):
    _num_channels = 1
    _conv1_deep = 32
    _conv2_deep = 64
    _conv3_deep = 64

    _fc_size = 1024

    x = tf.reshape(X, shape=[-1, IMAGE_HEIGHT, IMAGE_WIDTH, 1])
    # 3 conv layer

    _conv1_size = 3

    w_c1 = tf.Variable(w_alpha * tf.random_normal([_conv1_size, _conv1_size, _num_channels, _conv1_deep]))
    b_c1 = tf.Variable(b_alpha * tf.random_normal([_conv1_deep]))

    conv1 = tf.nn.conv2d(x, w_c1, strides=[1, 1, 1, 1], padding='SAME')
    conv1 = tf.nn.relu(tf.nn.bias_add(conv1, b_c1))

    conv1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
    conv1 = tf.nn.dropout(conv1, keep_prob)

    _conv2_size = 3

    w_c2 = tf.Variable(w_alpha * tf.random_normal([_conv2_size, _conv2_size, _conv1_deep, _conv2_deep]))
    b_c2 = tf.Variable(b_alpha * tf.random_normal([_conv2_deep]))

    conv2 = tf.nn.conv2d(conv1, w_c2, strides=[1, 1, 1, 1], padding='SAME')
    conv2 = tf.nn.relu(tf.nn.bias_add(conv2, b_c2))

    conv2 = tf.nn.max_pool(conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    conv2 = tf.nn.dropout(conv2, keep_prob)

    _conv3_size = 3

    w_c3 = tf.Variable(w_alpha * tf.random_normal([_conv3_size, _conv3_size, _conv2_deep, _conv3_deep]))
    b_c3 = tf.Variable(b_alpha * tf.random_normal([_conv3_deep]))

    conv3 = tf.nn.conv2d(conv2, w_c3, strides=[1, 1, 1, 1], padding='SAME')
    conv3 = tf.nn.relu(tf.nn.bias_add(conv3, b_c3))
    conv3 = tf.nn.max_pool(conv3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    conv3 = tf.nn.dropout(conv3, keep_prob)

    #
    pool_shape = conv3.get_shape().as_list()
    nodes = pool_shape[1] * pool_shape[2] * pool_shape[3]

    # Fully connected layer
    w_d = tf.Variable(w_alpha * tf.random_normal([nodes, _fc_size]))
    b_d = tf.Variable(b_alpha * tf.random_normal([_fc_size]))

    dense = tf.reshape(conv3, [-1, nodes])
    dense = tf.nn.relu(tf.add(tf.matmul(dense, w_d), b_d))
    dense = tf.nn.dropout(dense, keep_prob)

    w_out = tf.Variable(w_alpha * tf.random_normal([_fc_size, OUTPUT_LENGTH]))
    b_out = tf.Variable(b_alpha * tf.random_normal([OUTPUT_LENGTH]))
    out = tf.add(tf.matmul(dense, w_out), b_out)
    return out


# 训练
def train():
    # 局部变量
    _learning_rate = 0.001
    _acc = 0.99

    # 定义前向传播
    o = inference()
    loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=o, labels=Y))
    train_op = tf.train.AdamOptimizer(learning_rate=_learning_rate).minimize(loss)

    # 计算正确率
    max_index_logits = tf.argmax(tf.reshape(o, [-1, CAPTCHA_LENGTH, VOCAB_LENGTH]), 2)
    max_index_labels = tf.argmax(tf.reshape(Y, [-1, CAPTCHA_LENGTH, VOCAB_LENGTH]), 2)
    correct_predict = tf.equal(max_index_logits, max_index_labels)
    accuracy = tf.reduce_mean(tf.cast(correct_predict, tf.float32))

    # 训练
    saver = tf.train.Saver()
    with tf.Session() as sess:
        # 初始化变量
        sess.run(tf.global_variables_initializer())
        step = 1
        while True:
            batch_x, batch_y = get_next_batch(128)
            # _, loss_ = sess.run([train_op, loss], feed_dict={X: batch_x, Y: batch_y, keep_prob: 0.5})
            sess.run(train_op, feed_dict={X: batch_x, Y: batch_y, keep_prob: 0.5})

            # 每100 step计算一次准确率
            if step % 100 == 0:
                batch_x_test, batch_y_test = get_next_batch(256)
                acc = sess.run(accuracy, feed_dict={X: batch_x_test, Y: batch_y_test, keep_prob: 1.})
                print('Step =', step, ', Accuracy =', acc)
                # 如果准确率大于50%,保存模型,完成训练
                if acc > _acc:
                    saver.save(sess, "./captcha.model", global_step=step)
                    break

            step += 1


def test():
    _test_num = 100
    test_x = DATA_X[:_test_num]
    test_y = DATA_Y[:_test_num]

    output = inference()

    saver = tf.train.Saver()

    wrong_num = 0
    with tf.Session() as sess:
        saver.restore(sess, tf.train.latest_checkpoint('./'))
        for i in range(_test_num):
            text = vec2text(test_y[i])
            image = test_x[i].flatten()
            out = sess.run(output, feed_dict={X: [image], keep_prob: 1})
            predict_text = vec2text(out[0])
            print("正确: {}  预测: {} {}".format(text, predict_text, text == predict_text))
            if text != predict_text:
                wrong_num += 1

    print(wrong_num, '/', _test_num)


def main():
    # 训练模型
    train()
    # 测试模型
    # test()


if __name__ == '__main__':
    main()

main.py先运行train函数,输出如下:

Step = 100 , Accuracy = 0.09863281
Step = 200 , Accuracy = 0.8125
Step = 300 , Accuracy = 0.99121094

Process finished with exit code 0

main.py生成模型后, 注视了train函数,运行test函数, 输出如下:

正确: 3118  预测: 1110 False
正确: 9753  预测: 9753 True
正确: 8838  预测: 8838 True
正确: 9718  预测: 9718 True
正确: 4615  预测: 4615 True
正确: 7017  预测: 7017 True
正确: 7693  预测: 7693 True
正确: 7362  预测: 7362 True
正确: 6405  预测: 6405 True
正确: 5032  预测: 5032 True
1 / 10

Process finished with exit code 0

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值