输入待识别的处理后的图片,完全可用于验证码图片的识别,然后对这3000张图片进行处理和切割,这个算法也是需要开发人员仔细研究所要识别的字符图片的特点来制定的 有97%新玩家认为验证码识别 系统(验证码识别是什么意思)值得一读!
{image}
4条解答一.验证码识别的基本介绍
验证码的主要目的是强制人机交互来抵御机器自动化攻击的。大部分的验证码设计者并不得要带返领,不了解图像处理,机器视觉 ,模式识别,人工智能的基本概念 。
利用验证码,可以发财 ,当然要犯罪:比如招商银行密码只有6位,验证码形同虚设,计算机很快就能破解一个有钱的账户 ,很多帐户是可以网上交易的。也有设计的比较好的,比如Yahoo,Google,Microsoft等。而国内Tencent的中文验证码虽然难,但算不上好 。
处理知识
人工智能 ,模式识别,机器视觉,图像处理的基本知识 比如我们要从一副图片中 ,识别出验证码;比如我们要从一副图片中 ,检测并识别出一张人脸。
1.图像采集:验证码呢,就直接通过HTTP抓HTML,然后分析出图片的url ,然后下载保存就可以了。如果是人脸检测识别,一般要通过视屏采集设备,采集回来 ,通过A/D转操作,存为数字图片或者视频 。
2.预处理:检测是正确的图像格式,转换到合适的格式 ,压缩,剪切出ROI,去除噪音 ,灰度化,转换色彩空间这些。
3.检测:车牌检测识别系统要先找到车牌的大概位置,人脸检测系统要找出图片中所有的人脸(包括疑似人脸);验证码识别呢 ,主要是找出文字所在的主要区域。
4.前处理:人脸检测和识别 ,会对人脸在识别前作一些校正,比如面内面外的旋转,扭曲等 。我这里的验证码识别 ,“一般”要做文字的切割。
5.训练:通过各种模式识别,机器学习算法,来挑选和训练合适数量的训练集。不是训练的样本越多越好 。过学习 ,泛化能力差的问题可能在这里出现 。这一步不是必须的,有些识别算法是不需要训练的。
6.识别:输入待识别的处理后的图片,转换成分类器需要的输入格式 ,然后通过输出的类和置信度,来判断大概可能是哪个字母。识别本质上就是分类 。
图像处理:一般指针对数字图像的某种数学处理。比如投影,钝化 ,锐化,细化,边缘检测 ,二值化 ,压缩,各种数据变换等等。
1.二值化:一般图片都是彩色的,按照逼真程度 ,可能很多级别 。为了降低计算复杂度,方便后续的处理,如果在不损失关键信息的情况下 ,能将图片处理成黑白两种颜色,那就最好不过了。
2.细化:找出图像的骨架,图蠢顷饥像线条可能是很宽的 ,通过细化将宽度将为1,某些地方可能大于1。不同的细化算法,可能有不同的差异 ,比如是否更靠近线条中间,比如是否保持联通行等 。
3.边缘检测:主要是理解边缘的概念。边缘实际上是图像中图像像素属性变化剧烈的地方。可能通过一个固定的门限值来判断,也可能是自适应的 。门限可能是图像全局的 ,也可能是局部的。不能说那个就一定好 ,不过大部分时候,自适应的局部的门限可能要好点。被分析的,可能是颜色 ,也可能是灰度图像的灰度 。
机器视觉:利用计算机来模式乎燃实现人的视觉 。比如物体检测,定位,识别。按照对图像理解的层次的差别 ,分高阶和低阶的理解。
模式识别:对事物或者现象的某种表示方式(数值,文字,我们这里主要想说的是数值) ,通过一些处理和分析,来描述,归类 ,理解,解释这些事物,现象及其某种抽象 。
人工智能:这种概念比较宽 ,上面这些都属于人工智能这个大的方向。简单点不要过分学院派的理解就是 ,把人类的很“智能 ”的东西给模拟出来协助生物的人来处理问题,特别是在计算机里面。
经验:
目前这方面的技术难点主要在于验证吗图片的分割方面,对于识别的匹配 ,OCR技术已经很成熟了,完全可用于验证码图片的识别,但是复杂的验证码图片大多粘连 ,分割处理比较麻烦
二.验证码识别软件哪个好
需要一个能够自动识别验证码的软件能够自动识别验证码的,准确率高点 。一直用的优优云,准确率很高 ,主要是稳定。网页验证码用 《网页自动操作通用工具》
三.“验证码”为何故意弄得花里胡哨看不清
就是为了防止机器人因为现在的外挂太牛X了,如果不弄得那么花里胡哨,外挂或攻击软件就能自己辨别验证码 ,这样也就失去了验证码的意义,验证码就是为了让人亲自进行辨认。看来是用来测试使用者的视力及判断、思维能力,无能力者就不要使用了 。
四.如何利用Python做简单的验证码识别
1 摘要
验证码是目前互联网上非常常见也是非常重要的一个事物 ,充当着很多系统的 防火墙 功能 ,但是随时OCR技术的发展,验证码暴露出来的安全问题也越来越严峻。本文介绍了一套字符验证码识别的完整流程,对于验证码安全和OCR识别技术都有一定的借鉴意义。
然后经过了一年的时间 ,笔者又研究和get到了一种更强大的基于CNN卷积神经网络的直接端到端的验证识别技术(文章不是我的,然后我把源码整理了下,介绍和源码在这里面):
基于python语言的tensorflow的‘端到端’的字符型验证码识别源码整理(github源码分享)
2 关键词
关键词:安全,字符图片,验证码识别,OCR,Python,SVM,PIL
3 免责声明
本文研究所用带宽悉素材来自于某旧Web框架的网站 完全对外公开 的公共图片资源 。
本文只做了该网站对外公开的公共图片资源进行了爬取 , 并未越权 做任何多余操作。
本文在书写相关报告的时候已经 隐去 漏洞网站的身份信息。
本文作者 已经通知 网站相关人员此系统漏洞,并积极向新系统转移 。
本报告的主要目的也仅是用于 OCR交流学习 和引起大家对 验证安全的警觉 。
4 引言
关于验证码的非技术部分的介绍,可以参考以前写的一篇科普类的文章:
互联网安全防火墙(1)--网络验证码的科普
里面对验证码的种类 ,使用场景,作用,主要的识别技术等等进行了讲解 ,然而并没有涉及到任何技术内容。本章内容则作为它的 技术补充 来给出相应的识别的解决方案,让读者对验证码的功能及安全性问题有更深刻的认识。
5 基本工具
要达到本文的目的,只需要简单的编程知识即可 ,因为现在的机器学习领域的蓬勃发展 ,已经有很多封装好的开源解决方案来进行机器学习 。普通程序员已经不需要了解复杂的数学原理,即可以实现对这些工具的应用了。
主要开发环境:
-
python3.5
-
python SDK版本
-
PIL
-
图片处理库
-
libsvm
-
开源的svm机器学习库
关于环境的安装,不是本文的重点 ,故略去。
6 基本流程
一般情况下,对于字符型验证码的识别流程如下:
-
准备原始图片素材
-
图片预处理
-
图片字符切割
-
图片尺寸归一化
-
图片字符标记
-
字符图片特征提取
-
生成特征和标记对应的训练数据集
-
训练特征标记数据生成识别模型
-
使用识别模型预测新的未知图片集
-
达到根据“图片”就能返回识别正确的字符集的目标
7 素材准备
7.1 素材选择
由于本文是以初级的学习研究目的为主,要求 “有代表性 ,但又不会太难 ” ,所以就直接在网上找个比较有代表性的简单的字符型验证码(感觉像在找漏洞一样) 。
最后在一个比较旧的网站(估计是几十年前的网站框架)找到了这个验证码图片。
原始图:
放大清晰图:
此图片能满足要求,仔细观察其具有如下特点。
有利识别的特点 :
-
由纯阿拉伯数字组成
-
字数为4位
-
字符排列有规律
-
字体是用的统一字体
以上就是本文所说的此验证码简单的重要原因 ,后续代码实现中会用到
不利识别的特点 :
- 图片背景有干扰噪点
这虽然是不利特点,但是这个干扰门槛太低,只需要简单的方法就可以除去
7.2 素材获取
由于在做训练的时候 ,需要大量的素材,所以不可能用手工的方式一张张在浏览器中保存,故建议写个自动化下载的程序 。
主要步骤如下:
-
通过浏览器的抓包功能获取随机图片验证码生成接口
-
批量请求接口以获取图片
-
将图片蠢乎保存到本地磁盘目录中
这些都是一些IT基本技能 ,本文就不再详细展开了。
关于网络请求和文件保存的代码 ,如下:
-
def downloads_pic(**kwargs):
-
pic_name = ('pic_name', None)
-
url = 'httand_code_captcha/'
-
巧唤 res = (url, stream=True)
-
with open(pic_path + pic_name+'.bmp', 'wb') as f: for chunk in _content(chunk_size=1024): if chunk: # filter out keep-alive new chunks (chunk)
-
()
-
()
循环执行N次,即可保存N张验证素材了。
下面是收集的几十张素材库保存到本地文件的效果图:
8 图片预处理
虽然目前的机器学习算法已经相当先进了,但是为了减少后面训练时的复杂度 ,同时增加识别率,很有必要对图片进行预处理,使其对机器识别更友好 。
针对以上原始素材的处理步骤如下:
-
读取原始图片素材
-
将彩色图片二值化为黑白图片
-
去除背景噪点
8.1 二值化图片
主要步骤如下:
-
将RGB彩图转为灰度图
-
将灰度图按照设定阈值转化为二值图
-
image = (img_path)
-
imgry = ('L') # 转化为灰度图table = get_bin_table()
-
out = (table, '1')
上面引用到的二值函数的定义如下:
def get_bin_table(threshold=140): """ 获取灰度转二值的映射table :param threshold: :return: """ table = [] for i in range(256): if i < threshold: (0) else: (1) return table
由PIL转化后变成二值图片:0表示黑色,1表示白色。二值化后带噪点的 6937 的像素点输出后如下图:
如果你是近视眼 ,然后离屏幕远一点,可以隐约看到 6937 的骨架了。
8.2 去除噪点
在转化为二值图片后,就需要清除噪点 。本文选择的素材比较简单 ,大部分噪点也是最简单的那种 孤立点,所以可以通过检测这些孤立点就能移除大量的噪点 。
关于如何去除更复杂的噪点甚至干扰线和色块,有比较成熟的算法: 洪水填充法 Flood Fill ,后面有兴趣的时间可以继续研究一下。
本文为了问题简单化,干脆就用一种简单的自己想的 简单办法 来解决掉这个问题:
-
对某个 黑点 周边的九宫格里面的黑色点计数
-
如果黑色点少于2个则证明此点为孤立点,然后得到所有的孤立点
-
对所有孤立点一次批量移除。
下面将详细介绍关于具体的算法原理 。
将所有的像素点如下图分成三大类
-
顶点A
-
非顶点的边界点B
-
内部点C
种类点示意图如下:
其中:
-
A类点计算周边相邻的3个点(如上图红框所示)
-
B类点计算周边相邻的5个点(如上图红框所示)
-
C类点计算周边相邻的8个点(如上图红框所示)
当然 ,由于基准点在计算区域的方向不同 ,A类点和B类点还会有细分:
-
A类点继续细分为:左上,左下,右上 ,右下
-
B类点继续细分为:上,下,左 ,右
-
C类点不用细分
然后这些细分点将成为后续坐标获取的准则。
主要算法的python实现如下:
-
def sum_9_region(img, x, y): """
-
9邻域框,以当前点为中心的田字框,黑点个数
-
:param x:
-
:param y:
-
:return: """
-
todo 判断图片的长宽度下限
-
cur_pixel = ((x, y)) # 当前像素点的值
-
width =
-
height = if cur_pixel == 1: # 如果当前点为白色区域,则不统计邻域值
-
return 0 if y == 0: # 第一行
-
if x == 0: # 左上顶点,4邻域
-
中心点旁边3个点
-
sum = cur_pixel \ + ((x, y + 1)) \ + ((x + 1, y)) \ + ((x + 1, y + 1)) return 4 - sum elif x == width - 1: # 右上顶点
-
sum = cur_pixel \ + ((x, y + 1)) \ + ((x - 1, y)) \ + ((x - 1, y + 1)) return 4 - sum else: # 最上非顶点,6邻域
-
sum = ((x - 1, y)) \ + ((x - 1, y + 1)) \ + cur_pixel \ + ((x, y + 1)) \ + ((x + 1, y)) \ + ((x + 1, y + 1)) return 6 - sum elif y == height - 1: # 最下面一行
-
if x == 0: # 左下顶点
-
中心点旁边3个点
-
sum = cur_pixel \ + ((x + 1, y)) \ + ((x + 1, y - 1)) \ + ((x, y - 1)) return 4 - sum elif x == width - 1: # 右下顶点
-
sum = cur_pixel \ + ((x, y - 1)) \ + ((x - 1, y)) \ + ((x - 1, y - 1)) return 4 - sum else: # 最下非顶点,6邻域
-
sum = cur_pixel \ + ((x - 1, y)) \ + ((x + 1, y)) \ + ((x, y - 1)) \ + ((x - 1, y - 1)) \ + ((x + 1, y - 1)) return 6 - sum else: # y不在边界
-
if x == 0: # 左边非顶点
-
sum = ((x, y - 1)) \ + cur_pixel \ + ((x, y + 1)) \ + ((x + 1, y - 1)) \ + ((x + 1, y)) \ + ((x + 1, y + 1)) return 6 - sum elif x == width - 1: # 右边非顶点
-
print('%s,%s' % (x, y))
-
sum = ((x, y - 1)) \ + cur_pixel \ + ((x, y + 1)) \ + ((x - 1, y - 1)) \ + ((x - 1, y)) \ + ((x - 1, y + 1)) return 6 - sum else: # 具备9领域条件的
-
sum = ((x - 1, y - 1)) \ + ((x - 1, y)) \ + ((x - 1, y + 1)) \ + ((x, y - 1)) \ + cur_pixel \ + ((x, y + 1)) \ + ((x + 1, y - 1)) \ + ((x + 1, y)) \ + ((x + 1, y + 1)) return 9 - sum
Tips:这个地方是相当考验人的细心和耐心程度了,这个地方的工作量还是蛮大的,花了半个晚上的时间才完成的。
计算好每个像素点的周边像素黑点(注意:PIL转化的图片黑点的值为0)个数后 ,只需要筛选出个数为 1或者2 的点的坐标即为 孤立点 。这个判断方法可能不太准确,但是基本上能够满足本文的需求了。
经过预处理后的图片如下所示:
对比文章开头的原始图片,那些 孤立点 都被移除掉 ,相对比较 干净 的验证码图片已经生成。
9 图片字符切割
由于字符型 验证码图片 本质就可以看着是由一系列的 单个字符图片 拼接而成,为了简化研究对象,我们也可以将这些图片分解到 原子级 ,即: 只包含单个字符的图片 。
于是 ,我们的研究对象由 “N种字串的组合对象” 变成 “10种阿拉伯数字” 的处理,极大的简化和减少了处理对象。
9.1 分割算法
现实生活中的字符验证码的产生千奇百怪,有各种扭曲和变形。关于字符分割的算法 ,也没有很通用的方式 。这个算法也是需要开发人员仔细研究所要识别的字符图片的特点来制定的。
当然,本文所选的研究对象尽量简化了这个步骤的难度,下文将慢慢进行介绍。
使用图像编辑软件(PhoneShop或者其它)打开验证码图片 ,放大到像素级别,观察其它一些参数特点:
可以得到如下参数:
-
整个图片尺寸是 40*10
-
单个字符尺寸是 6*10
-
左右字符和左右边缘相距2个像素
-
字符上下紧挨边缘(即相距0个像素)
这样就可以很容易就定位到每个字符在整个图片中占据的像素区域,然后就可以进行分割了 ,具体代码如下:
-
def get_crop_imgs(img): """
-
按照图片的特点,进行切割,这个要根据具体的验证码来进行工作. # 见原理图
-
:param img:
-
:return: """
-
child_img_list = [] for i in range(4):
-
x = 2 + i * (6 + 4) # 见原理图
-
y = 0
-
child_img = ((x, y, x + 6, y + 10))
-
child_img_(child_img) return child_img_list
然后就能得到被切割的 原子级 的图片元素了:
9.2 内容小结
基于本部分的内容的讨论,相信大家已经了解到了,如果验证码的干扰(扭曲 ,噪点,干扰色块,干扰线……)做得不够强的话 ,可以得到如下两个结论:
-
4位字符和位字符的验证码区别不大
-
纯字母
-
不区分大小写 。分类数为26
-
区分大小写 。分类数为52
-
纯数字。分类数为10
-
数字和区分大小写的字母组合。分类数为62
-
纯数字 和 数字及字母组合 的验证码区别不大
在没有形成 指数级或者几何级 的难度增加 ,而只是 线性有限级 增加计算量时,意义不太大 。
10 尺寸归一
本文所选择的研究对象本身尺寸就是统一状态:6*10的规格,所以此部分不需要额外处理。但是一些进行了扭曲和缩放的验证码 ,则此部分也会是一个图像处理的难点。
11 模型训练步骤
在前面的环节,已经完成了对单个图片的处理和分割了 。后面就开始进行 识别模型 的训练了。
整个训练过程如下:
-
大量完成预处理并切割到原子级的图片素材准备
-
对素材图片进行人为分类,即:打标签
-
定义单张图片的识别特征
-
使用SVM训练模型对打了标签的特征文件进行训练 ,得到模型文件
12 素材准备
本文在训练阶段重新下载了同一模式的4数字的验证图片总计:3000张。然后对这3000张图片进行处理和切割,得到张原子级图片 。
在这张图片中删除一些会影响训练和识别的强干扰的干扰素材,切割后的效果图如下:
13 素材标记
由于本文使用的这种识别方法中 ,机器在最开始是不具备任何 数字的观念的。所以需要人为的对素材进行标识,告诉 机器什么样的图片的内容是 1……。
这个过程叫做 “标记 ” 。
具体打标签的方法是:
-
为0~9每个数字建立一个目录,目录名称为相应数字(相当于标签)
-
人为判定 图片内容 ,并将图片拖到指定数字目录中
-
每个目录中存放100张左右的素材
-
一般情况下,标记的素材越多,那么训练出的模型的分辨能力和预测能力越强。例如本文中 ,标记素材为十多张的时候 ,对新的测试图片识别率基本为零,但是到达100张时,则可以达到近乎100%的识别率
14 特征选择
对于切割后的单个字符图片 ,像素级放大图如下:
从宏观上看,不同的数字图片的本质就是将黑色按照一定规则填充在相应的像素点上,所以这些特征都是最后围绕像素点进行。
字符图片 宽6个像素 ,高10个像素 ,理论上可以最简单粗暴地可以定义出60个特征:60个像素点上面的像素值 。但是显然这样高维度必然会造成过大的计算量,可以适当的降维 。
通过查阅相应的文献 [2] ,给出另外一种简单粗暴的特征定义:
-
每行上黑色像素的个数,可以得到10个特征
-
每列上黑色像素的个数,可以得到6个特征
最后得到16维的一组特征 ,实现代码如下:
-
def get_feature(img): """
-
获取指定图片的特征值,
-
1. 按照每排的像素点,高度为10,则有10个维度,然后为6列,总共16个维度
-
:param img_path:
-
:return:一个维度为10(高度)的列表 """
-
width, height =
-
pixel_cnt_list = []
-
height = 10 for y in range(height):
-
pix_cnt_x = 0 for x in range(width): if ((x, y)) == 0: # 黑色点
-
pix_cnt_x += 1
-
pixel_cnt_(pix_cnt_x) for x in range(width):
-
pix_cnt_y = 0 for y in range(height): if ((x, y)) == 0: # 黑色点
-
pix_cnt_y += 1
-
pixel_cnt_(pix_cnt_y) return pixel_cnt_list
然后就将图片素材特征化,按照 libSVM 指定的格式生成一组带特征值和标记值的向量文