前些时间,由于工作关系,需要做验证码识别相关的工作,由于之前没有接触过这方面,所以一切重头开始,经过一个月的查阅资料、做实验尝试,总算作出了一个可以用的东西。下面尝试总结一下这一个多月学习的东西,可以加深下自己的印象,也可以向大家分享一下心得。
验证码的主要用途是用来区分对象是机器还是人。因为如果对象是机器的话,就可以在短时间内提交大量的表单,从而造成破坏,甚至可以造成一些无法估量的损失。若在提交之前有验证码的话,就可以在一定程度上防止这种事情发生。而了解验证码识别的原理,可以有效预防生成的验证码被识别,从而加大自身的安全性,故“未知攻,焉有防”。
下面就开始对如何识别验证码作出介绍,同时本文中只会给出思路和部分代码,避免引起一些不必要的麻烦。
这里我们指的验证码是字符和数字的组合,其他验证码并不在此做介绍。我们引用的例子是这张验证码:
目前验证码识别的原理一般是将验证码中的字符按照顺序分割开来,之后对图片中的每个字符进行识别,之后将字符识别结果组合在一起,即为识别的结果。这个过程大致有以下的步骤:
1.去噪,二值化
2.分割字符
3.标准化字符
4.识别
各个步骤的具体描述如下所示:
1.去噪,二值化
标题的意思是将去除验证码图片中干扰部分,之后只留下背景和内容,并将内容全部处理为一种颜色(原因在4中讲解)。
二值化处理:
去噪处理:
此处的原理是验证码图片中内容和无用部分一般会有一个颜色上明显的区分,如果内容和其他区别不明显,会给人带来很差的用户体验。我们可以通过取色器观察内容和其他的颜色值,从而找出一个阀值,在此阀值上的颜色都为内容,阀值下都是无用的部分。之后遍历图片,保留阀值上的颜色,将其置为一种颜色,将阀值下的颜色置为背景色。这样图片中就只有两种颜色,就完成了二值化的过程。
在不少时候,验证码中会有躁点之类的噪音存在,所以在此也需要将这些噪音去除。在此介绍去除噪点的方法:判断此像素的相邻像素,如果相邻超过N(这需要看具体情况)个像素与此像素颜色不一样,就可以认为此像素为噪点,将其置为背景色即可。下面是检查相邻像素的代码,在检查之前已经将图片进行了二值化。
下面来进行第二步,字符分割
2.字符分割
字符分割一直是验证码识别的重点和难点,只有将字符分割成功,才有可能进行到下面的步骤。但是目前没有通用的分割方法,一般是根据具体的情况来具体应用。本文介绍一种简单情况下的处理方法:扫描法。使用这种方法的前提是验证码中各个字符没有连接。效果如下:
分割后:
具体的方法是这样的:
已知图片的高为y,宽为x。我们将图片的左下角视为坐标(0,0),左上角坐标视为(0,y),右下角坐标视为(x,0)。
之后沿着图片的x轴,在x轴上的某点,对0到y-1高度的像素进行遍历,来获取每个字符的开始和结尾。如果某一列中像素都是背景色,同时下一列中有内容,则此列为字符的开始。获取字符结尾的原理与此相似。
3.图片的标准化
在此步骤中一般做以下(不限于)几件事情:
1.扶正倾斜的字符。
验证码中的字符一般会有各个角度的倾斜,从而相同的字符在经过不同角度的倾斜后,也会呈现出不同的姿态。扶正倾斜的字符,可以减少字符倾斜对识别造成的影响。
扶正可以采用旋转卡壳算法。可以将图片旋转,由-30度开始,到30度结束,选宽度最小的一张作为扶正的图片。
扶正的效果:
2.如果字符粗细不均的话,可以细化下字符。
有些验证码中的字符会粗细不均,将其细化后,可以减少采样,从而减少粗细不均对识别造成的影响。
3.缩放字符,将字符缩放为一个统一的大小。
有些验证码中的相同的字符会有大小不同的形式,所以需要将其处理为一种形式,理由同上。
这里简单介绍下缩放图片的一个java开源库--Scalr.使用非常方便,缩放代码如下所示:
4.识别
这里就到了最后的一步,目前比较有效的方法是采用采用神经网络进行识别,训练量大的话识别率会很客观。关于神经网络在此不做赘述,网上关于神经网络的资料员有很多,这里介绍一个java的神经网络库:Encog。这里是图片识别的示例,修改下就可以使用了。目前来看,识别效果还可以。
以上就是验证码识别的一种方法,关于加强验证码安全的可以阅读参考资料中的文章,里面总结的很全面,我就不在这里献丑了。
水平有限,多多指教。
谢谢
欢迎关注我的微博,ID:小牛_yetuweiba。
参考资料:
http://drops.wooyun.org/tips/141。
文中的思路主要来自此篇文章,这是一篇几乎毫无水分的文章。在此感谢作者。
最后,博客园的markdown的语法真折磨人。