[Python] SEU-jwc验证码识别器设计思路

来源:互联网 发布:java工程师就业班 编辑:程序博客网 时间:2024/06/17 11:47

SEU-jwc验证码识别


代码

处理前后的对比图
原始验证码图片
处理后的图片


教务处选课系统和学生在线系统的验证码是这样的

SEU教务系统的验证码

可以推测出验证码生成的过程:
1. 随机生成的四位数
2. 竖直方向上扭曲形变
3. 加上两条横线

我首先想到的是用现有的OCR模块,pytesser,直接识别。pytesser模块的使用方法如下:

img = Image.open('pic.jpg')print pytesser.image_to_string(img)

直接识别的效果非常糟糕,输出结果驴唇不对马嘴。所以我打算先把这两条恼人的线给去掉。

干扰线

描述干扰线

要去掉一条直线,首先要能够描述它。这里我用最左边的点(起点)和斜率来表示。而为了求出斜率,又要找到这条线上的另外一个点,我选择的是直线最右边的点。

观察图像,可以发现直线的最左边是基本顶到的,而右边的直线距离边界还有一段距离,所以需要对图像进行裁剪,使其左右都是顶到的。这样一来,寻找最左跟最右的点就可以在图像矩阵的第一列和最后一列中完成了。

裁剪后的验证码

寻找左顶点,只需要分别从上往下和从下往上找到第一个出现的黑色像素点即可,右顶点的寻找也是同理。但为了让坐标更接近线的中点,我让其又分别上下移动了几个像素。找到两对顶点后,下一步便是要对其进行匹配

匹配的结果只有两种,也就是一个起点可能的斜率只有两个,只要比较一下两个斜率的优劣即可。这里的比较我采用了一种简单的判据:这个(起点,斜率)组合表示的直线在中点的像素是不是黑色的。如果是,那么便认为这个组合能够表示干扰线。但也有以下需要考虑的情况:
1. 存在数字的部分在判断的点上,影响了结论;
2. 两条线某一边的端点十分接近,导致无论怎么组合中点都是黑色的。

关于第一种情况,需要之前裁剪时选择好尺寸。因为验证码的每个数字所在的水平位置都是相同的,所以一旦选择好了尺寸,那么图像的中垂线上是不会出现数字的部分。而第二种情况则确实会造成困扰,干扰线重叠部分的消除也是还有待改进的部分。

去除干扰线

假设我们已经能很好地描述干扰线了(求出的每一个纵坐标都在线的中间),下一步我们就可以着手把这条线干掉了。要去除干扰线,最困难的地方在于如何处理线和数字相交的地方。

一种非常朴素的处理方法是根据线的宽度来判断:当前点向上第三个像素和向下第三个像素都为白(线的竖直宽度小于5),则认为该线没有与数字相交,是可去除的,并把该点及其上下两个像素内的点都涂成白色。

这种粗糙的判断方法并没有取得较好的结果,甚至连孤立的干扰线也无法完全消除。其原因可能是干扰线是倾斜的,其竖直宽度并不一定是恒定的,对判断产生了影响。

另外,这种判据无法区别干扰线与数字的交点和两条干扰线的交点,结果往往会留下一块较大的黑斑,影响下一步的OCR。不过无论如何,与原图直接进行OCR相比,处理后的图片已经有一定几率识别出正确的验证码了。

后来翻看opencv文档时,受到了图像滤波函数启发,想出了另一种方法,其大致思路如下:同样遍历一条直线,若一个点向上数三个点和向下数三个点都是白色,那么认为这个点很有可能是多余的,将该点与其上第二个点和其下第二个点涂成浅色;如果不满足上述条件,则该点有一定可能是需要保留的,于是只将其与其下一个点涂成浅色。

填充后的验证码

直观来看,这种处理方法相当于把一根粗的干扰线拆成两根细线,并在线中间填充一定的灰度。

然后,对图像做滤波。我采用的kernel是kernel = np.ones((6, 6), np.float32) / 36,也就是6*6的全一矩阵除以36。这一步操作后图像会变得模糊,边缘会更加不明显,即高频成分会被滤除。滤波后的效果如图。

模糊处理后的验证码

可以看到干扰线和数字的明暗有较明显的区别。因此,再对图像取阈值进行二值化,就可以基本把干扰线去除了。

处理的最终结果

最后,毫无疑问,就是要对处理过的图像再进行OCR。经过我自己的测试,选择好适合的参数,正确率能基本保持在32%左右。

讨论

这一部分会根据测试结果,对一些特殊情况进行讨论分析。

  • 两条干扰线在左或右顶点处相交
    这种情况会导致所描述的直线不在干扰线的中间位置,导致难以准确地对该点进行相应处理。
    干扰线在端点处相交
  • 干扰线的端点与上下边界相交
    同上,这会使得线的中点难以定位,而且最终很有可能导致干扰线无法消除。
    干扰线端点与下边界相交
  • 干扰线与数字的底端/顶端重合
    这种情况下,根据设定的阈值不同,可能会把数字的部分也消去,或在数字旁边留下干扰线的部分。
    干扰线与数字底端重合

以上情况直观来看会产生干扰线消除不完整、数字部分边缘缺失等效果,但对于OCR识别准确率的影响尚且没能有量化的描述。

另外,由于OCR的字符集不只有数字,再加上变形与干扰线的作用,会导致数字有可能被误识别为字母或符号。因此,基于对这些误识别的观察,我手动添加了一些映射规则(如’Q’到’0’,’%’到’8’等),略微提升了识别的正确率。(32%到36%)

最后,最恼人的当属将一个数字识别为另一个数字。较匪夷所思的是,’3’与’8’、’5’与’6’常被混淆,如果能将验证码反形变成正常的数字,应该能让准确率大大提升。

一个1000次的测试结果:
正确识别:363
识别出错误的四位数:105
未能识别出四位数:532


一些其他相关的问题

想尝试用打包软件打成exe,照我的情况来看,cx_Freeze好过pyInstaller和py2exe。打包后的exe在没有python环境的电脑中可能会有奇怪的问题。我遇到的是缺少numpy相关的DLL,在pip更新numpy后得以解决。另外,在没有安装tesseract的电脑中会报错windows error [error 2],安装tesseract后即可解决。

0 0
原创粉丝点击