验证码识别笔记(一)

因为工作的关系,我在去年下半年做了几个验证码识别的项目。

做完之后,觉得其实验证码识别并不是什么很困难的项目,只要能针对要识别的图片进行内容结构上的分析,将内容分成几个部分,针对不同部分用好的对策进行处理,把图片有意义的内容(例如数字或者字符)分离出来。最后使用一些类似K近邻或者相似度的办法,和手工做好的模板进行匹配,对于国内网站的验证码,大都可以识别出来。

当然,我写这些文章的目的,并不是想教别人用爬虫去爬别人的暗网数据,也不是想教别人写抢票的工具,我只是针对这一类问题,记录一下自己工作中用到的方法和经验。这些方法,对于文本图像复原和损毁书籍的保护同样适用,虽然我现在还没有机会接触到这方面的工作,但我觉得用在这些方面才是有益的。

接下来会用3篇的篇幅依次介绍我的一些思路和方法。

1. 识别对象的特征分析

图1. 验证码样图

上面就是一张程序将要进行识别的验证码图像,经过观察后可以看到里面的文字有两部分组成。

第一部分是大小不规则的多边形。图像的背景是大小相同的正方形格子,而文字里面的大部分都是经过扭曲的不规则多边形。

第二部分是大小为m×n的实心黑色矩形。上图中,大小为1×1的矩形是背景线的交点,而m×n(m>=2或n>=2)的实心黑色矩形则是文字的边缘。

本程序识别的第一步就是拿到这两部分的位置信息,把这两部分组合起来形成文字的轮廓,以和背景区分开来。第一步运行之后的结果为,文字轮廓区域皆为白色,背景区域为黑色

第二步是将取到的轮廓信息和已有的模板进行一一匹配,找到最相似的模板,将模板代表的字符返回,识别结束。

2. 算法步骤

2.1找出图片中所有的m×n(m>=2或n>=2)的实心黑色矩形,分别记录左上点坐标left_top和右下点right_bottom的坐标,保存下来。因为这一步找到的矩形可能存在包含关系,也就是一个矩形可能和另一个矩形有交集,所以要进行矩形区域的合并,合并成两两不相交的矩形区域。

在取到第二部分的区域后,把这些矩形区域都涂成白色,并入第二部分。 

2.2观察图片,边缘的白色会影响最终的结果,所以需要将图片边缘的白色都涂成黑色。

具体做法是,从上、下、左、右四个方向,逐行逐列的从边缘向中心涂上黑色,直至该行该列的下个像素为黑色停止。这一步将边缘也并入图片的背景中。 

2.3对图像进行分析,找出大小不规则的多边形。

具体做法是,对于下面3×3的图像区域,考察位置5上的像素。

图2. 8邻域图 

如果和其相邻的8个位置上大多数像素为白色,则5若为黑色,直接改为白色。

如果和其相邻的8个位置上大多数像素为黑色,则5若为白色,则分两种情况进行处理:

2.3.1 将该位置改为黑色,

2.3.2不将该位置的白色改为黑色。

该考虑的原因是,白色区域有很大可能属于最后的文字区域,不应该轻易改变。

由此得到两种结果,白色改为黑色,白色不改为黑色。而后对这两种结果进行合并。

合并这两种结果的具体做法如下。

仍然考虑位置5上像素内容,不过现在参考的范围为2个3×3的区域。

1. 如果这两个图像块在位置5上的像素内容一致,则不进行改变。

2. 如果这两个图像块在位置5上的像素内容不一致,则考察这2×3×3=18个像素点的内容,计算像素点值为黑色cblack和白色cwhite的像素点数目。

a)如果cblack>cwhite,确定位置5为黑色;

b)如果cblack<cwhite,确定位置5为白色;

c)如果cblack==cwhite,则找离它最近的同像素值的点,(因为此时两个区域上位置5的像素内容肯定一个为黑色,另一个为白色)计算两者距离dis_black和dis_white返回,

    i)如果dis_black<dis_white,确定位置5为黑色;

    ii)如果dis_black>dis_white,确定位置5为白色;

    iii)如果dis_black==dis_white,确定位置5为默认色(白色)。

2.4 涂白2.1找到的实心黑色矩形

2.5 去掉孤立色素点

具体做法是,对于下面3×3的图像区域,考察图2位置5上的像素。 

如果5上为白色,与其相邻8个位置上都是黑色,则5改为黑色;

如果5上为黑色,与其相邻8个位置上都是白色,则5改为白色。

2.6 图片生长,补全图像边缘

仍然考虑3×3的图像区域,对于(1,5,9),(3,5,7),(2,5,8),(4,5,6)的位置区域。如果5位置上的像素值和其他两个位置上的像素值不同,则改为和另两个位置相同的像素值。即

如果另外两个位置像素值为白色,则修改位置5为白色;

如果另外两个位置像素值为黑色,则修改位置5为黑色。

至此,已经得到文字区域的两个部分。下图为处理的样例图片。

图3. 处理后的样例图片

3. 字符识别

3.1 切分由2得到的图片

具体做法如下。 

3.1.1 先找出所有全黑色的垂直线位置{p1, p2, p3, …, pn}

因为字符有一定的宽度,所以设定一个阈值CWIDTH,如果pi-p(i+1)>=CWIDTH,就认为pi和p(i+1)之间有一个字符。最后得到几组(start, end),程序中设计可以找出若干组(start, end),并不是只能找到4组,以支持以后的程序扩展和图片变化。 

3.1.2 对每一个子图像区域,因为此时已经找到每个字图像区域的水平起始位置,接下来对于每一个区域找垂直的起始位置。此处,从上至下,从下至上扫描两次,分别找到垂直起点行,和终点行。 

3.1.3 返回每个字符区域的左上点坐标和右下点坐标 

3.2 计算子图像块的特征值

这一步骤要找具体的理论和算法,根据效果可替换。 

3.3与模板图像的特征进行比较,找到最接近的模板图像,返回其值。

具体代码,可以参考:GitHub

原文地址:https://www.cnblogs.com/flyingpeguin/p/3515428.html