TensorFlow 可以用来实现验证码识别的过程,这里识别的验证码是图形验证码,首先用标注好的数据来训练一个模型,然后再用模型来实现这个验证码的识别。

生成验证码

  首先生成验证码,这里使用 Python 的 captcha 库来生成即可,这个库默认是没有安装的,所以需要先安装这个库,另外还需要安装 pillow 库,使用 pip3 即可:

pip3 install captcha pillow

  安装好之后,就可以用如下代码来生成一个简单的图形验证码了:

from captcha.image import ImageCaptcha
from PIL import Image text=''
image=ImageCaptcha()
captcha=image.generate(text)
captcha_image=Image.open(captcha)
captcha_image.show()

  运行之后便会弹出一张图片,结果如下:

  

预处理

  首先定义好了要生成的验证码文本内容,这就相当于已经有了 label ,然后再用它来生成验证码,就可以得到输入数据 x 了。

  在这里首先定义好我们的输入词表,由于大小写字母加数字的词表比较庞大,用含有大小写字母和数字的验证码,一个验证码四个字符,那么一共可能的组合是 (26 + 26 + 10) ^ 4 = 14776336 种组合,这个数量训练起来有点大,所以这里精简一下,只使用纯数字的验证码来训练,这样其组合个数就变为 10 ^ 4 = 10000 种,显然少了很多。

  定义一个词表和其长度变量:

#这里 VOCAB 就是词表的内容,即0到9这10个数字,验证码的字符个数即 CAPTCHA_LENGTH 是4,词表长度是 VOCAB 的长度,即10。
VOCAB=['','','','','','','','','','']
CAPTCHA_LENGTH=4
VOCAB_LENGTH=len(VOCAB)

  接下来定义一个生成验证码数据的方法,这里将返回的数据转为了 Numpy 形式的数组:

from PIL import Image
from captcha.image import ImageCaptcha
import numpy as np def generate_captcha(captcha_text):
"""
get captcha text and np array
:param captcha_text: source text
:return: captcha image and array
"""
image = ImageCaptcha()
captcha = image.generate(captcha_text)
captcha_image = Image.open(captcha)
captcha_array = np.array(captcha_image)
return captcha_array

  这样调用此方法,我们就可以得到一个 Numpy 数组了,这个其实是把验证码转化成了每个像素的 RGB,我们调用一下这个方法试试:

captcha = generate_captcha('')
print(captcha, captcha.shape)
"""
[[[239 244 244]
[239 244 244]
[239 244 244]
...,
...,
[239 244 244]
[239 244 244]
[239 244 244]]]
(60, 160, 3)
"""

  可以看到它的 shape 是 (60, 160, 3),这其实代表验证码图片的高度是 60,宽度是 160,是 60 x 160 像素的验证码,每个像素都有 RGB 值,所以最后一维即为像素的 RGB 值。

  接下来需要定义 label,由于需要使用深度学习模型进行训练,所以这里的 label 数据最好使用 One-Hot 编码,即如果验证码文本是 1234,那么应该词表索引位置置 1,总共的长度是 40,用程序实现一下 One-Hot 编码和文本的互相转换:

def text2vec(text):
"""
text to one-hot vector
:param text: source text
:return: np array
"""
if len(text) > CAPTCHA_LENGTH:
return False
vector = np.zeros(CAPTCHA_LENGTH * VOCAB_LENGTH)
for i, c in enumerate(text):
index = i * VOCAB_LENGTH + VOCAB.index(c)
vector[index] = 1
return vector def vec2text(vector):
"""
vector to captcha text
:param vector: np array
:return: text
"""
if not isinstance(vector, np.ndarray):
vector = np.asarray(vector)
vector = np.reshape(vector, [CAPTCHA_LENGTH, -1])
text = ''
for item in vector:
text += VOCAB[np.argmax(item)]
return text

  上面的 text2vec() 方法就是将真实文本转化为 One-Hot 编码,vec2text() 方法就是将 One-Hot 编码转回真实文本。

  例如这里调用一下这两个方法,我们将 1234 文本转换为 One-Hot 编码,然后在将其转回来:

vector = text2vec('')
text = vec2text(vector)
print(vector, text)
"""
[ 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.
0. 0. 0. 0.]
1234
"""

  接下来构造一批数据,x 数据就是验证码的 Numpy 数组,y 数据就是验证码的文本的 One-Hot 编码,生成内容如下:

import random
from os.path import join, exists
import pickle
import numpy as np
from os import makedirs DATA_LENGTH = 10000
DATA_PATH = 'data' def get_random_text():
text = ''
for i in range(CAPTCHA_LENGTH):
text += random.choice(VOCAB)
return text def generate_data():
print('Generating Data...')
data_x, data_y = [], [] # generate data x and y
for i in range(DATA_LENGTH):
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) # write data to pickle
if not exists(DATA_PATH):
makedirs(DATA_PATH) x = np.asarray(data_x, np.float32)
y = np.asarray(data_y, np.float32)
with open(join(DATA_PATH, 'data.pkl'), 'wb') as f:
pickle.dump(x, f)
pickle.dump(y, f)
#这里定义了一个 get_random_text() 方法,可以随机生成验证码文本,然后接下来再利用这个随机生成的文本来产生对应的 x、y 数据,
#再将数据写入到 pickle 文件里,这样就完成了预处理的操作。

构建模型

  有了数据之后,就可以构建模型了,这里还是利用 train_test_split() 方法将数据分为三部分,训练集、开发集、验证集:

with open('data.pkl', 'rb') as f:
data_x = pickle.load(f)
data_y = pickle.load(f)
return standardize(data_x), data_y train_x, test_x, train_y, test_y = train_test_split(data_x, data_y, test_size=0.4, random_state=40)
dev_x, test_x, dev_y, test_y, = train_test_split(test_x, test_y, test_size=0.5, random_state=40)

  接下来使用三个数据集构建三个 Dataset 对象:

# train and dev dataset
train_dataset = tf.data.Dataset.from_tensor_slices((train_x, train_y)).shuffle(10000)
train_dataset = train_dataset.batch(FLAGS.train_batch_size) dev_dataset = tf.data.Dataset.from_tensor_slices((dev_x, dev_y))
dev_dataset = dev_dataset.batch(FLAGS.dev_batch_size) test_dataset = tf.data.Dataset.from_tensor_slices((test_x, test_y))
test_dataset = test_dataset.batch(FLAGS.test_batch_size)

  然后初始化一个迭代器,并绑定到这个数据集上:

# a reinitializable iterator
iterator = tf.data.Iterator.from_structure(train_dataset.output_types, train_dataset.output_shapes)
train_initializer = iterator.make_initializer(train_dataset)
dev_initializer = iterator.make_initializer(dev_dataset)
test_initializer = iterator.make_initializer(test_dataset)

  接下来就是关键的部分了,在这里使用三层卷积和两层全连接网络进行构造。为了简化写法,直接使用 TensorFlow 的 layers 模块:

# input Layer
with tf.variable_scope('inputs'):
# x.shape = [-1, 60, 160, 3]
x, y_label = iterator.get_next()
keep_prob = tf.placeholder(tf.float32, [])
y = tf.cast(x, tf.float32)
# 3 CNN layers
for _ in range(3):
y = tf.layers.conv2d(y, filters=32, kernel_size=3, padding='same', activation=tf.nn.relu)
y = tf.layers.max_pooling2d(y, pool_size=2, strides=2, padding='same')
# y = tf.layers.dropout(y, rate=keep_prob) # 2 dense layers
y = tf.layers.flatten(y)
y = tf.layers.dense(y, 1024, activation=tf.nn.relu)
y = tf.layers.dropout(y, rate=keep_prob)
y = tf.layers.dense(y, VOCAB_LENGTH)
#这里卷积核大小为 3,padding 使用 SAME 模式,激活函数使用 relu。

  经过全连接网络变换之后,y 的 shape 就变成了 [batch_size, n_classes], label 是 CAPTCHA_LENGTH 个 One-Hot 向量拼合而成的,在这里我们想使用交叉熵来计算,但是交叉熵计算的时候,label 参数向量最后一维各个元素之和必须为 1,不然计算梯度的时候会出现问题。详情参见 TensorFlow 的官方文档:https://www.tensorflow.org/api_docs/python/tf/nn/softmax_cross_entropy_with_logits

NOTE: While the classes are mutually exclusive, their probabilities need not be. All that is required is that each row of labels is a valid probability distribution. If they are not, the computation of the gradient will be incorrect.

  但是现在的 label 参数是 CAPTCHA_LENGTH 个 One-Hot 向量拼合而成,所以这里各个元素之和为 CAPTCHA_LENGTH,所以需要重新 reshape 一下,确保最后一维各个元素之和为 1:

y_reshape = tf.reshape(y, [-1, VOCAB_LENGTH])
y_label_reshape = tf.reshape(y_label, [-1, VOCAB_LENGTH])
#这样就可以确保最后一维是 VOCAB_LENGTH 长度,而它就是一个 One-Hot 向量,所以各元素之和必定为 1。

  然后 Loss 和 Accuracy 就好计算了:

# loss
cross_entropy = tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(logits=y_reshape, labels=y_label_reshape))
# accuracy
max_index_predict = tf.argmax(y_reshape, axis=-1)
max_index_label = tf.argmax(y_label_reshape, axis=-1)
correct_predict = tf.equal(max_index_predict, max_index_label)
accuracy = tf.reduce_mean(tf.cast(correct_predict, tf.float32))

再接下来执行训练即可:

# train
train_op = tf.train.RMSPropOptimizer(FLAGS.learning_rate).minimize(cross_entropy, global_step=global_step)
for epoch in range(FLAGS.epoch_num):
tf.train.global_step(sess, global_step_tensor=global_step)
# train
sess.run(train_initializer)
for step in range(int(train_steps)):
loss, acc, gstep, _ = sess.run([cross_entropy, accuracy, global_step, train_op],
feed_dict={keep_prob: FLAGS.keep_prob})
# print log
if step % FLAGS.steps_per_print == 0:
print('Global Step', gstep, 'Step', step, 'Train Loss', loss, 'Accuracy', acc) if epoch % FLAGS.epochs_per_dev == 0:
# dev
sess.run(dev_initializer)
for step in range(int(dev_steps)):
if step % FLAGS.steps_per_print == 0:
print('Dev Accuracy', sess.run(accuracy, feed_dict={keep_prob: 1}), 'Step', step)
#首先初始化 train_initializer,将 iterator 绑定到 Train Dataset 上,然后执行 train_op,获得 loss、acc、gstep 等结果并输出。

训练

  运行训练过程,结果类似如下:验证集准确率 95% 以上。

...
Dev Accuracy 0.9580078 Step 0
Dev Accuracy 0.9472656 Step 2
Dev Accuracy 0.9501953 Step 4
Dev Accuracy 0.9658203 Step 6
Global Step 3243 Step 0 Train Loss 1.1920928e-06 Accuracy 1.0
Global Step 3245 Step 2 Train Loss 1.5497207e-06 Accuracy 1.0
Global Step 3247 Step 4 Train Loss 1.1920928e-06 Accuracy 1.0
Global Step 3249 Step 6 Train Loss 1.7881392e-06 Accuracy 1.0
...

测试

  训练过程可以每隔几个 Epoch 保存一下模型:

# save model
if epoch % FLAGS.epochs_per_save == 0:
saver.save(sess, FLAGS.checkpoint_dir, global_step=gstep)

  当然也可以取验证集上准确率最高的模型进行保存。

  验证时可以重新 Reload 一下模型,然后进行验证:

# load model
ckpt = tf.train.get_checkpoint_state('ckpt')
if ckpt:
saver.restore(sess, ckpt.model_checkpoint_path)
print('Restore from', ckpt.model_checkpoint_path)
sess.run(test_initializer)
for step in range(int(test_steps)):
if step % FLAGS.steps_per_print == 0:
print('Test Accuracy', sess.run(accuracy, feed_dict={keep_prob: 1}), 'Step', step)
else:
print('No Model Found')

  如果要进行新的 Inference 的话,可以替换下 test_x 即可。

结语

  文章内容参考自静觅 » TensorFlow验证码识别

  代码见:https://github.com/AIDeepLearning/CrackCaptcha

        

基于TensorFlow的简单验证码识别的更多相关文章

  1. 开发工具类API调用的代码示例合集:六位图片验证码生成、四位图片验证码生成、简单验证码识别等

    以下示例代码适用于 www.apishop.net 网站下的API,使用本文提及的接口调用代码示例前,您需要先申请相应的API服务. 六位图片验证码生成:包括纯数字.小写字母.大写字母.大小写混合.数 ...

  2. 基于SVM的字母验证码识别

    基于SVM的字母验证码识别 摘要 本文研究的问题是包含数字和字母的字符验证码的识别.我们采用的是传统的字符分割识别方法,首先将图像中的字符分割出来,然后再对单字符进行识别.首先通过图像的初步去噪.滤波 ...

  3. 简单验证码识别(matlab)

    简单验证码识别(matlab) 验证码识别, matlab 昨天晚上一个朋友给我发了一些验证码的图片,希望能有一个自动识别的程序. 1474529971027.jpg 我看了看这些样本,发现都是很规则 ...

  4. HNUSTOJ-1696 简单验证码识别(模拟)

    1696: 简单验证码识别 时间限制: 2 Sec  内存限制: 128 MB 提交: 148  解决: 44 [提交][状态][讨论版] 题目描述 验证码是Web系统中一种防止暴力破解的重要手段.其 ...

  5. 基于TensorFlow的车牌号识别系统

    简介 过去几周我一直在涉足深度学习领域,尤其是卷积神经网络模型.最近,谷歌围绕街景多位数字识别技术发布了一篇不错的paper.该文章描述了一个用于提取街景门牌号的单个端到端神经网络系统.然后,作者阐述 ...

  6. windows下简单验证码识别——完美验证码识别系统

    此文已由作者徐迪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 讲到验证码识别,大家第一个可能想到tesseract.诚然,对于OCR而言,tesseract确实很强大,自带 ...

  7. Python3 简单验证码识别思路及实例

    1.介绍 在爬虫中经常会遇到验证码识别的问题,现在的验证码大多分计算验证码.滑块验证码.识图验证码.语音验证码等四种.本文就是识图验证码,识别的是简单的验证码,要想让识别率更高, 识别的更加准确就需要 ...

  8. 基于SVM.NET的验证码识别算法实现

    工作之余,对这个算法做了一些研究,并成功对验证码进行了识别,对普通验证码识别率在90%左右,识别速度相当快,已基于此做过一些自动查询.提交程序(例如投票.发帖等) ,还上过淘宝店,赚过一笔外快,现将相 ...

  9. python简单验证码识别

    在学习python通过接口自动登录网站时,用户名密码.cookies.headers都好解决但是在碰到验证码这个时就有点棘手了:于是通过网上看贴,看官网完成了对简单验证码的识别,如果是复杂的请看大神的 ...

随机推荐

  1. Android TextView文字空格

     表示全角空格, <string name="aaa">你好      啊</string> http://stackoverflow.com/questi ...

  2. [Bayes] Point --> Hist: Estimate "π" by R

    Verify the Monte Carlo sampling variability of "π". p = π/4 与 所得 0.7854 比较接近,故满足 Central L ...

  3. 【GIS】Vue、Leaflet、highlightmarker、bouncemarker

    感谢: https://github.com/brandonxiang/leaflet.marker.highlight https://github.com/maximeh/leaflet.boun ...

  4. Android编译环境——ubuntu12.04上android2.3.4编译错误以及解决

    Android编译环境——ubuntu12.04上android2.3.4编译错误以及解决 分类: android应用开发2013-08-21 09:20 4222人阅读 评论(3) 收藏 举报 li ...

  5. traff.sh

    #!/bin/bash #网络监控 while [ "1" ] do eth=$1 RXpre=$(cat /proc/net/dev | grep $eth | tr : &qu ...

  6. C#自定义按钮、自定义WinForm无边框窗体、自定义MessageBox窗体

    C#自定义按钮.自定义WinForm无边框窗体.自定义MessageBox窗体 C#自定义Button按钮控件 效果展示 C#自定义Winform无边框窗体 效果展示 C#自定义无边框MessageB ...

  7. python-Levenshtein几个计算字串相似度的函数解析

    linux环境下,没有首先安装python_Levenshtein,用法如下: 重点介绍几个该包中的几个计算字串相似度的几个函数实现. 1. Levenshtein.hamming(str1, str ...

  8. Django之ORM版学员管理系统

    班级表 表结构 class Class(models.Model): id = models.AutoField(primary_key=True) # 主键 cname = models.CharF ...

  9. E - Radar Installation

    Assume the coasting is an infinite straight line. Land is in one side of coasting, sea in the other. ...

  10. linux 关闭笔记本自带键盘

    linux 命令行工具 xinput list 找到 AT Translated Set 2 keyboard,其 id为 13 设置值为 0 xinput 如果想恢复,对应的值设为1即可 xinpu ...