学的东西很多,无奈一看就懂、一放就忘,所谓好记性不如烂笔头,今天开始写博客吧!废话不多说,开始正文。
一、概述
现在各大网站都采用验证码来防止机器人自动注册、登录等,避免这些无效流量对自己服务器产生压力,这些验证码肯定起到了一定作用,但道高一尺魔高一丈,上有政策下有对策,验证码大多还是能被识别出来的,只是根据验证码的复杂程度,识别难度也高低不同。
高级点的可能要涉及到复杂的图像识别技术,先占个坑,后面有精力再去研究。
查阅资料时候,发现知乎上有关于验证码识别方面的段子,也分享给大家乐呵乐呵:点击打开链接。
二、参考
主要参考了这位大神的文章:点击打开链接,阅读时可以先看人家的,我主要是对图像切割和根据自己要识别的具体验证码对代码做了部分改动。
三、目标验证码
我要识别的验证码是下面这样类型的,算得上是验证码中最简单的类型了。
分析:
1、单个字符每次出现时,大小样式颜色都保持一致,所以只要切割验证码能得到完整的与基准图片保持一致的单字符图片,就能保证准确识别。为保证这一点,基准图片与后面待识别的字符,要用同样的方法来切割;
2、识别就采用上面大神的文章中那种最笨的方法,一个像素点一个像素点来比较像素。
四、切割
分析:
1、首先,这个验证码背景不是全白,有渐变效果,可以按qq截图快捷键后拖动鼠标在图片上观察;
小技巧:按下截图快捷键显示(#FFFFFF)和松开快捷键显示(255,255,255)。
2、有效内容的颜色偏深,这里用rgb三原色相加的和是否大于565(这个值可以设定不同的数,然后通过对同一张或多张验证码多次切割,观察是否都正确得到目标图片,当每次都得到正确图片时,该数字就有效)来判断某一点是否属于有效内容区域的点;
3、图片切割区域判断:
以二维坐标系角度来看图片,以图片左下角为原点(0,0),先确定一个字符的切割的区域。
确定有效区域的左边界x:
//确定某一x值,然后遍历y值,当存在某一点像素值为有效像素值时,该x值即为左边界值
for (int x1 = x; x1 < width; ++x1) {
for (int y1 = y; y1 < height; ++y1) {
if (isBlack(img.getRGB(x1, y1)) == 1) {
x = x1;
System.out.println("切割起点x==" + x);
break a;
}
}
}
同理确定有效区域的下边界y:
//确定某一y值,然后遍历x值,当存在某一点像素值为有效像素值时,该y值即为下边界值
for (int y1 = y; y1 < height; ++y1) {
for (int x1 = x; x1 < width; ++x1) {
if (isBlack(img.getRGB(x1, y1)) == 1) {
y = y1;
System.out.println("切割起点y==" + y);
break b;
}
}
}
确定有效区域的右边界:
//从有效区域左边界往右,x固定时,当该x上所有y值对应的点的像素值都不为有效点时,该x为有效区域的右边界
for (int x1 = x; x1 < width; ++x1) {
for (int y1 = y; y1 < height; ++y1) {
if (isWhite(img.getRGB(x1, y1)) == 1) {
if (y1 == height - 1) {
childImgWidth = x1 - x;
System.out.println("切割宽度childImgWidth==" + childImgWidth);
break c;
}
continue;
} else {
break;
}
}
}
确定有效区域的上边界:
//从有效区域下边界往上,y固定时,当该y上所有x值对应的点的像素值都不为有效点时,该y为有效区域的上边界
for (int y1 = y; y1 < height; ++y1) {
for (int x1 = x; x1 < width; ++x1) {
if (isWhite(img.getRGB(x1, y1)) == 1) {
if (x1 == width - 1) {
childImgHeight = y1 - y;
System.out.println("切割高度childImgHeight==" + childImgHeight);
break d;
}
continue;
} else {
break;
}
}
}
4、循环:
后面每张图的切割都与之前完全一样的方法,有几个字符就循环几次。
5、延伸:
针对所有的这种出现时,大小、样式固定的图片,都可以采用这种方式来切割。但字符位置不固定的情况,切割出来的图片可能包含部分无效区域,类似于上面切割的“+”和“-”,这时候需要做二次切割,将无效区域给去除掉,刚好只留有效区域(基准图片和待识别图片都要这样做,因为这样才能获取准确的参照和待比对图片)。
五、基准图片生成
这里别的人都管它叫训练,其实就是从目标网站,获取大量的待识别验证码,然后切割出基准图片,每张基准图片对应一个真实的数值或者字符(这里是用基准图片的名字来标识的),识别的时候,如果图片数据一样,则待识别图片对应的内容就是基准图片对应的字符。
(这里的代码我就不上了,后面完整代码会传到CSDN上)
六、图像预处理和识别
这部分与参考文章内容一致,就不在赘述。
注意:因为所有图片的长宽不一致,所以识别的时候,在比较前,先判断该图片与基准图片宽高是否一致,不一致则说明不是该基准图片,直接比对下一张。
七、Java图片读写
用标准的Java IO流即可实现,跟普通File读写一致,图片带后缀名完整名称写全即可;也可以用Apache的拓展包commons-httpclient.jar
八、随记
看到参考文章下面的一条评论,觉得有用,记录下来,后面有空再研究:
图像处理有很多理论都没有用到,像腐蚀...;字体变形,这个真可以开一门课程了。我只会简单的,谢谢楼主。图像如果粘连怎么处理,数字交织在一起怎么处理,多种图形方式在一起怎么处理。人眼有一种特性,能够识别连接的图形。莫列波纹 优酷视频。所以重点是提取链接符号,建立图形库,比如能够识别一段曲线(垃圾信息),一个笔画(部分正确信息),多个笔画构成的字符(正确信息)。其实更绝的是,都是离散度点,用不同的颜色构成数字。人体对背景的感知是什么样子?剥离背景的方式。可定存在一个数学表达式,表示内容。这个公式是什么才是关键。目前只是 文字=图片-背景;背景=?;文字的下一步处理是什么?把公式提取出来,就可以写论文了。