简单验证码的识别
验证码图片均取自于国内某知名信息安全网站,通过图像处理、模板对比识别等步骤,实现了该类简单验证码图片的识别功能。同时对程序实现了可视化界面,并集成了(验证码)图片下载、(灰度值)门限手动调节等扩展功能。代码存在github,传送门请戳我。
一、程序内容及原理
本程序以Python实现,主要借助了PIL(Python Image Library,实现读取图片、读取灰度值等图像处理相关功能)及tkinter(实现界面设计相关功能)两个库。图像处理及识别的主程序的总体流程如下:导入(读取)验证码图片,图像处理,图像识别,识别结果输出。
图一 程序总体流程
(一)、图像处理
图像处理的主要流程如下。
图二 图像处理流程
1.读取图片
利用PIL库中的PIL.Image.open函数实现对验证码图片的读取,并以PIL.Image的格式返回(我们简称为im)。下文的图像处理及图像分割都是基于im格式完成的。
在主程序(imgRe.py)中,目标图片的读取需要手动输入图片名称(可能还需输入图片所在路径);在可视化程序(imgRe_GUI.py)中,只需通过点击“选取图片”即可方便快捷地选取目标图片。
图1.1 imgRe.py中的图片读取方式
图1.2 可视化程序中的图片读取方式
2.图片降噪(二值化)
图片降噪的目的是把不同灰度值的背景、干扰线等多余的信息去掉,只留下单一的背景以及所需的验证码信息,以便进行接下来的图片切割、识别等步骤。在本程序中,通过二值化处理使验证码图片成为二进制点阵(及相应的位图),以此实现降噪的功能,实现该功能的函数命名为“denoise”,处于类Div中。
本项目中,可以发现该类验证码有如下特点:灰度值分布规律较明显,验证码信息灰度值为0或255,背景信息的灰度值处于2至253之间;验证码较“方正”,无需进行太多的图像标准化处理。基于这些特点,本程序使用截取灰度值门限、灰度值映射的方法对图片进行二值化处理,从而达到降噪的目的。例如,下图2.1为某验证码图片的示意图,图2.2为该图的灰度直方图示意图,(已进行归一化处理,横坐标的灰度值从左至右为0至255)。
图2.1 某验证码图片示意图
图2.2 验证码图片灰度直方图示意图
从图2.2可见,其灰度值为255的像素点较多(对应原图中的白色验证码信息“S2JW”),而灰度值为10左右的像素点占比最多(对应原图的黑色环形图像),其他灰度值像素点错落分布(对应原图中的灰色背景),因此将灰度值门限设置为253-255以截取右方的验证码信息,把截取到的门限内的灰度值映射为0(对应黑色),其他灰度值(0-253)全部映射为1(对应白色),以此实现图片二值化及降噪处理,可以得到验证码信息与单一背景的位图,其示意图如下。
图2.3 验证码图片降噪后示意图
3.图片切割
图像分割就是指把图像分成各具特性的区域并提取出感兴趣的目标的技术和过程。图像分割是由图像处理进到图像分析识别的关键步骤,这是因为图像的分割、目标的分离、特征的提取将原始图像转化为更抽象更紧凑的形式,使得更高层的分析和理解成为可能。本程序中,图片切割的目的是将降噪后的验证码图片根据其验证码信息切割为对应的图片“碎片”,每个“碎片”对应唯一一个验证码信息,以便于模板进行比对、识别,实现该功能的函数命名为imDiv(及_div),处于类Div中。
在切割的算法中,程序首先选定某一纵列进行纵向扫描,若发现该列中有像素点像素值为零,即认为发现了验证码信息,将该列标记为“start”,随后继续扫描直至发现某一列中整列未出现验证码信息(即像素值为零的点),认为该验证码信息结束,将该列标记为“end”;以上完成一个验证码信息的横向扫描,接着进行横向扫描,道理同上,最后纪录下该验证码信息的横向和纵向的起始坐标点。
图3.1 切割后的“碎片”示意图
将“碎片”的二进制点阵进行直接打印输出结果如下。
图3.2 “碎片”二进制点阵示意图
(二)、图像识别
图像识别采取的是模板对比的识别方法。将切割后的验证码“碎片”分别于各张模板一一比对,分别得出相似度的值,最后取相似度最高的模板图名称作为该“碎片”的识别结果。其“碎片”与某张模板进行比对时的流程如下。
图一 比对流程
部分模板图片如下。
图二 部分模板图片
1.获取信息坐标
对于碎片图或模板图,首先需要获得其验证码信息的像素点所在的坐标。如图3.2中的符合的坐标点集合为(1,0),(2,0),(3,0)等等(以左上角作为原点)。实现该功能的函数命名为getPoints。
图1.1 坐标点集合示意图
2.计算相似度
计算两图的坐标点集合的相似度,使用的是欧几里得距离的方法,并且我对该方法进行了改动:加入了均方差的检测法和归一化的处理方法,使得即使错位的方法也可以进行识别。实现该功能的函数命名为fitting,处于类Recognize中。
通过比对“碎片”图的坐标点集合与模板图片的坐标点集合,在二维平面的情况下,我们可以利用公式:
求出两图中每一对对应点间的欧几里得距离。
若两图相同,则每对对应点间的欧几里得距离应该都为零,也就是相似度非常高。
当两图发生了一些错位,比如下列的a和b两个示意的坐标点集合,它们间对应点的欧几里得距离都为根号二而不为零,说明他们的相对位置是相同的,可是由于错位或原点不同等原因导致绝对位置不同。
图2.1 坐标点集合
可计算出这两个集合对应点的欧几里得距离(为了方便计算和显示这里取距离值的平方)均为2。接着对这个距离值的数列取均方差。计算数列的均方差并进行归一化的函数命名为var,同属于类Recognize中。
图2.2 a、b数列的对应点欧几里得距离
那么这个数列的均方差为零,表示a、b两个坐标点集合对应点间的欧几里得距离都相等,我们即可认为这两个坐标点集合有着非常高的相似度(相似度的值最高为1,最低为0)。
图2.3 验证a、b两个集合的相似度
二、程序执行结果
这里主要展示可视化(即imgRe_GUI.py)程序的执行结果,即软件的运行情况。初始化时程序界面如下。
图一 程序初始化界面
1.“选取图片”之后的程序运行截图
点击“选取图片”之后,程序自动展示如下三个示意图:“原图”、“降噪后的二值化图”以及“灰度直方图”。其运行结果如下。
图二 程序读图后界面
2.“自动识别”按钮之后的程序运行截图
点击“自动识别”之后,程序右下栏上部分显示的是图像切割处理后的“碎片”图片,下方便验证码图片的识别结果。该识别结果为文本形式,可直接复制粘贴。
图三 程序识图后界面
3.其他功能的程序运行截图
其他功能主要包括“下载图片”和“调节门限”。点击“下载图片”并拉动拖条选择下载数量后,程序后台将自动下载对应数量的验证码图片。图片自动存于程序相同目录下的“简单验证码”文件夹中,若无该文件夹,程序将自动创建该文件夹。
“调节门限”功能一般较为少用,因为对于该类简单验证码程序可自动判读选择灰度值门限。但是当选取的图片较为复杂或用户对识别结果不满时,可自行选择门限,程序将自动将选取的门限中的灰度值的像素映射为0,其他的为1。一旦确定最小值,程序会弹窗让用户选择门限最大值,最大值的区间从已选定的最小值开始至255。
三、总结
这项技术源自于程序自动登录、注册时对于破解验证码这个“拦路虎”的欲望,同时该技术也可适用于车牌识别、身份证识别等场景,具有一定的研究价值。
遗憾的是,目前的算法还是很基础很简单,因此只能应对这种干扰较少、验证码“中规中矩”的验证码图片。对于出现了干扰线、验证码文字扭曲的图片暂时未能较好处理和识别。希望在未来有时间能继续交流学习,不断改进算法。另一个则是,进行可视化界面设计时是使用tkinter这个库进行设计的,并不能实现太复杂的设计。以后有机会可争取使用QT实现更完善的界面。
最后想说的是,整个项目的整体思路参考了网上的文章或相关介绍,所以我希望通过消化之后的这些很简单但又基本的思路和代码能继续为他人、为正在做类似研发的有需要的人提供微薄帮助。希望高手多多指教。