文档章节

基于Tensorflow的验证码识别

alexqdjay
 alexqdjay
发布于 2017/03/22 18:07
字数 1838
阅读 4617
收藏 59

最近在入门机器学习,本文作为入门阶段的 "Hello World",旨在学习一些Tensorflow的API和机器学习概念。

步骤

  1. 采集验证码图片
  2. 处理图片:灰度、去噪、分割
  3. 分类图片,准备训练数据:将0-9数字图片放入对应文件夹,转化成数据
  4. 编写训练模型
  5. 调用模型,形成识别系统

 

采集验证码图片

随便找一个需要输入图像验证码的网页,最好先简单一点的只有数字的,并且人眼识别也比较容易的。

这步比较简单,关键是去网页上识别验证码接口url,然后按照一些网上教程写个简单的Python程序抓取那些图片。python实现简单爬虫功能

处理图片

电脑装不上PIL,所以只能使用Java去处理,言语只是工具,注重方法。对图片进行灰度、去噪、分割处理。

灰度化

灰度处理是指将彩色图片转化成只有黑白灰色。

每个像素点有Red、Green、Blue三种颜色组成,每个分量用数字0-255表示(十六进制:0x00 - 0xff),当三个分量(RGB)一样时表现出来就是灰度色(白色RGB(255, 255, 255), 黑色RGB(0, 0, 0))。

所以,灰度化是将三分量转化成一样数值的过程

公式如下:

int gray = (int) (0.3 * r + 0.59 * g + 0.11 * b);

再使用二值法将图片色彩转化成非黑即白,可以过滤一些感染的噪音,因为人眼在识别验证码时也会自动过滤掉一些颜色比较淡的点或者色块图形。

如上图,我们只会关注中间颜色比较深的数字而一些背景淡色小字会被过滤掉。

gray = gray < 127 ? 0 : 255;

去噪处理

这里处理比较简陋,就是遍历所有图片中的像素点,如果这个点附近N个点(相邻的一共有8个点)颜色是白色,那么我们判定该是噪音点,将其颜色也设置为白色,这里我设置 N=8。

结合上面二值化处理后效果如下:

分割图片

比较恶的一步,因为数字旋转和字体大小不一所以宽度不定,而且还有重叠。

处理步骤:

  1. 扫描出每个数字左右边界,即找到最左最右的点
  2. 根据这个边界进行横向裁剪
  3. 如果发现最后裁剪出来不是4个数字而是3个或者更少,查看是否存在某个裁剪块的宽度比一般的要大,然后进行平均分割
  4. 对每个裁剪下来的图片再进行纵向裁剪,将上下多余的空白区裁剪掉

效果如下:

分类图片,准备训练数据

创建10个文件夹,命名为0、1、2.... 9,将裁剪后数字图片分类到那些文件夹中。

将图片转化为向量数据,形如:

0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,....

如果规定图片大小为36*36,那么该向量长度为1296。(数字表示该点的颜色,0为白色,1为黑色)

然后再需要一个标签数据

[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]  // 表示0
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0]  // 表示5

一条数据有10位,每个位置分别表示0到9

编写训练模型

刚开始只使用SVM做多分类,感觉有点复杂毕竟需要训练那么多组模型参数,所以放弃。后来单纯使用softmax训练,训练数据大概在1500左右,正确率在55%左右,可能需要更多的训练数据效果会好点,最后参照MNIST使用CNN去训练,同样的训练数据正确率达到80%左右,如果训练数据达到10000以上的话估计会更高(MNIST识别手写准确率在98%左右)。

CNN

TF提供2维卷积函数

tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

x是输入;W是卷积参数如[5,5,1,32],5*5是卷积核大小;第三个参数表示channel,这里因为是灰度所以是1,彩色的话是3;第四个参数表示卷积核的数量,即提取多少特征;strides是移动步长都是1表示遍历每个点;padding参数指定边界处理方式,“SAME”表示输入输出保持一样。

除此之外,还需要池化函数(降采样,将2x2降为1x1提取最显著的特征),这里使用最大池化max_pooling

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

x是输入;ksize表示使用2x2池化,即将2x2的色块降为1x1,最大池化会保留灰度值最高的那个像素点;因为需要整体缩小图片所以这里strides在横向纵向的步长都为2。

构建处理层

这里还加入了dropout防止过拟合。

def weight_variable(shape, name):
    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial, name=name)

def bias_variable(shape, name):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial, name=name)

def conv2d(x, W):
    # stride [1, x_movement, y_movement, 1]
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
    # stride [1, x_movement, y_movement, 1]
    return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')

#define weights & biases
weights = {
    "h1": weight_variable([5, 5, 1, 32], "w_h1"),
    "h2": weight_variable([5, 5, 32, 64], "w_h2"),
    "f1": weight_variable([9*9*64, 1024], "w_f1"),
    "f2": weight_variable([1024, 10], "w_f2")
}

biases = {
    "h1": bias_variable([32], "b_h1"),
    "h2": bias_variable([64], "b_h2"),
    "f1": bias_variable([1024], "b_f1"),
    "f2": bias_variable([10], "b_f2")
}

x_image = tf.reshape(xs, [-1, 36, 36, 1]) # [n_samples, 28,28,1]

## conv1 layer ##
W_conv1 = weights["h1"] # patch 5x5, in size 1, out size 32
b_conv1 = biases["h1"]
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # output size 36x36x32
h_pool1 = max_pool_2x2(h_conv1)                          # output size 18x18x32

## conv2 layer ##
W_conv2 = weights["h2"] # patch 5x5, in size 32, out size 64
b_conv2 = biases["h2"]
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # output size 18x18x64
h_pool2 = max_pool_2x2(h_conv2)                          # output size 9x9x64

## fc1 layer ##
W_fc1 = weights["f1"]
b_fc1 = biases["f1"]
# [n_samples, 9, 9, 64] ->> [n_samples, 9*9*64]
h_pool2_flat = tf.reshape(h_pool2, [-1, 9*9*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

## fc2 layer ##
W_fc2 = weights["f2"]
b_fc2 = biases["f2"]
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

定义Loss和Optimizer

cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction),
                                                  reduction_indices=[1]))       # loss
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

使用交叉熵作为Loss函数,优化器使用Adam,这些TF都有提供。

执行训练

for i in range(150):
        sess.run(train_step, feed_dict={xs: datas, ys: labels, keep_prob: 0.5})

执行150个迭代,这里有个需要优化的地方,datas可以每次选取其中部分训练数据而不是全量的训练,这样速度会快一点。

计算正确率

#y_pre 是每个数据对应各个类别(0-9)的概率如[[0.1,0.2,0.3,0.4...],[0.2,0.1,0.9....]]
y_pre = sess.run(prediction, feed_dict={xs: test_datas, keep_prob: 1})
#tf.argmax返回最大数的索引,再比较预测数据索引和测试数据真实索引
correct_prediction = tf.equal(tf.argmax(y_pre,1), tf.argmax(test_labels,1))
#将上面比较结果True转为1,False转为0,相加计算平均值
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
result = sess.run(accuracy)

使用测试数据test_datas和测试数据对应的标签test_labels进行检验。

调用模型

上面已经执行训练了,当训练完成时可以保存模型,这样在使用模型时可以直接恢复然后调用,不用重新训练。

保存模型

saver = tf.train.Saver()
save_path = saver.save(sess, "./models/save_net.ckpt")

恢复模型

saver = tf.train.Saver()
saver.restore(sess, "./final_models/save_net.ckpt")

总结

代码等整理完成后会发布到github上,仓库地址在后续会贴出来。

如有不准确或者任何好的建议都可以提出来,谢谢!

© 著作权归作者所有

alexqdjay
粉丝 35
博文 26
码字总数 31560
作品 0
浦东
高级程序员
私信 提问
加载中

评论(9)

大连馋师
大连馋师
有套路~
距离实用还得加把劲
alexqdjay
alexqdjay 博主

引用来自“不吃肉夹馍”的评论

楼主 粘连验证码有什么处理思路

引用来自“alexqdjay”的评论

没什么思路,如果只是工程化的,那么当识别不了时可以换验证码,一般换五次之内都没什么问题。
自动识别整个验证码的准确率还没去尝试,但是思路基本上就是当识别错误时,换其他高概率的数字尝试,尝试几次后不行就换验证码。
但是如果整体上所有的验证码都是属于很难识别(干扰特别多),其实暂时也没什么办法,有些验证码人类去识别的准确率大概只有30%。
你这边有什么好的思路么?可以交流下

引用来自“不吃肉夹馍”的评论

穷尽了才网上找找 几种算法对 干扰大的识别还是不行

引用来自“黑狗”的评论

看这样行不行
一般有交叉重叠的信息,颜色应该都差不多的,以某一个RGB色值作为参照,把差别大的都作为噪音去掉,剩下的再进行识别
好办法
不吃肉夹馍
不吃肉夹馍

引用来自“黑狗”的评论

引用来自“不吃肉夹馍”的评论

楼主 粘连验证码有什么处理思路

引用来自“alexqdjay”的评论

没什么思路,如果只是工程化的,那么当识别不了时可以换验证码,一般换五次之内都没什么问题。
自动识别整个验证码的准确率还没去尝试,但是思路基本上就是当识别错误时,换其他高概率的数字尝试,尝试几次后不行就换验证码。
但是如果整体上所有的验证码都是属于很难识别(干扰特别多),其实暂时也没什么办法,有些验证码人类去识别的准确率大概只有30%。
你这边有什么好的思路么?可以交流下

引用来自“不吃肉夹馍”的评论

穷尽了才网上找找 几种算法对 干扰大的识别还是不行
看这样行不行
一般有交叉重叠的信息,颜色应该都差不多的,以某一个RGB色值作为参照,把差别大的都作为噪音去掉,剩下的再进行识别

回复@黑狗 : 先处理好识别的了,干扰多的就人工自己输入 谢谢你的建议
黑狗
黑狗

引用来自“不吃肉夹馍”的评论

楼主 粘连验证码有什么处理思路

引用来自“alexqdjay”的评论

没什么思路,如果只是工程化的,那么当识别不了时可以换验证码,一般换五次之内都没什么问题。
自动识别整个验证码的准确率还没去尝试,但是思路基本上就是当识别错误时,换其他高概率的数字尝试,尝试几次后不行就换验证码。
但是如果整体上所有的验证码都是属于很难识别(干扰特别多),其实暂时也没什么办法,有些验证码人类去识别的准确率大概只有30%。
你这边有什么好的思路么?可以交流下

引用来自“不吃肉夹馍”的评论

穷尽了才网上找找 几种算法对 干扰大的识别还是不行
看这样行不行
一般有交叉重叠的信息,颜色应该都差不多的,以某一个RGB色值作为参照,把差别大的都作为噪音去掉,剩下的再进行识别
不吃肉夹馍
不吃肉夹馍

引用来自“不吃肉夹馍”的评论

楼主 粘连验证码有什么处理思路

引用来自“alexqdjay”的评论

没什么思路,如果只是工程化的,那么当识别不了时可以换验证码,一般换五次之内都没什么问题。
自动识别整个验证码的准确率还没去尝试,但是思路基本上就是当识别错误时,换其他高概率的数字尝试,尝试几次后不行就换验证码。
但是如果整体上所有的验证码都是属于很难识别(干扰特别多),其实暂时也没什么办法,有些验证码人类去识别的准确率大概只有30%。
你这边有什么好的思路么?可以交流下
穷尽了才网上找找 几种算法对 干扰大的识别还是不行
alexqdjay
alexqdjay 博主

引用来自“不吃肉夹馍”的评论

楼主 粘连验证码有什么处理思路
没什么思路,如果只是工程化的,那么当识别不了时可以换验证码,一般换五次之内都没什么问题。
自动识别整个验证码的准确率还没去尝试,但是思路基本上就是当识别错误时,换其他高概率的数字尝试,尝试几次后不行就换验证码。
但是如果整体上所有的验证码都是属于很难识别(干扰特别多),其实暂时也没什么办法,有些验证码人类去识别的准确率大概只有30%。
你这边有什么好的思路么?可以交流下
不吃肉夹馍
不吃肉夹馍
楼主 粘连验证码有什么处理思路
alexqdjay
alexqdjay 博主

引用来自“红薯”的评论

代码放到码云上,我们予以推荐把
哈哈,可以。😄
红薯
红薯
代码放到码云上,我们予以推荐把
一次基于Tensorflow+CNN的验证码识别之旅

对于本次基于卷积神经网络识别验证码有着非常大的兴趣,所以尝试性地去做了测试,过程当中踩了不少坑,也参考了许多前辈的博客和教程,最终识别率可达到98.25% 一、下图是训练的过程: 二、实...

DYBOY
2018/11/26
0
0
Python视频教程,带你玩转Python深度学习,TensorFlow框架使用教程分享!

课程内容: 该阶段是深度学习的入门课程,主要介绍经典的深度学习框架TensorFlow的使用,IO操作,以及神经网络基础、卷积神经网络的相关知识,并用卷积神经网络原理搭建、设计自己的网络,实...

小小倾听
01/02
156
0
ResNet网络识别验证码实战练习——高准确率的机器学习模型

原文链接:https://finthon.com/resnet-captcha/ 简介 在使用Python+Tensorflow的CNN技术快速识别验证码一文中,我们使用了3层简单的CNN卷积神经网络完成了验证码的识别,准确率不是特别高。...

MrLonelyZC88
01/07
0
0
tensorflow 实现端到端的OCR:二代身份证号识别

最近在研究OCR识别相关的东西,最终目标是能识别身份证上的所有中文汉字+数字,不过本文先设定一个小目标,先识别定长为18的身份证号,当然本文的思路也是可以复用来识别定长的验证码识别的。...

某杰
2017/08/08
0
0
在实时音视频中,基于 TensorFlow 实现图像识别(内附 demo)

近两年来,Python在众多编程语言中的热度一直稳居前五,热门程度可见一斑。 Python 拥有很活跃的社区和丰富的第三方库,Web 框架、爬虫框架、数据分析框架、机器学习框架等,开发者无需重复造...

声网Agora
08/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

mysql-connector-java升级到8.0后保存时间到数据库出现了时差

在一个新项目中用到了新版的mysql jdbc 驱动 <dependency>     <groupId>mysql</groupId>     <artifactId>mysql-connector-java</artifactId>     <version>8.0.18</version> ......

ValSong
59分钟前
5
0
Spring Boot 如何部署到 Linux 中的服务

打包完成后的 Spring Boot 程序如何部署到 Linux 上的服务? 你可以参考官方的有关部署 Spring Boot 为 Linux 服务的文档。 文档链接如下: https://docs.ossez.com/spring-boot-docs/docs/r...

honeymoose
今天
6
0
Spring Boot 2 实战:使用 Spring Boot Admin 监控你的应用

1. 前言 生产上对 Web 应用 的监控是十分必要的。我们可以近乎实时来对应用的健康、性能等其他指标进行监控来及时应对一些突发情况。避免一些故障的发生。对于 Spring Boot 应用来说我们可以...

码农小胖哥
今天
6
0
ZetCode 教程翻译计划正式启动 | ApacheCN

原文:ZetCode 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远。 ApacheCN 学习资源 贡献指南 本项目需要校对,欢迎大家提交 Pull Request。 ...

ApacheCN_飞龙
今天
4
0
CSS定位

CSS定位 relative相对定位 absolute绝对定位 fixed和sticky及zIndex relative相对定位 position特性:css position属性用于指定一个元素在文档中的定位方式。top、right、bottom、left属性则...

studywin
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部