阅读:2698回复:0
初探验证码识别
◆0 背景介绍
全自动区分计算机和人类的图灵测试(英语:Completely Automated Public Turing test to tell Computers and Humans Apart,简称CAPTCHA),俗称验证码。CAPTCHA这个词最早是在2002年由卡内基梅隆大学的路易斯·冯·安、Manuel Blum、Nicholas J.Hopper以及IBM的John Langford所提出。CAPTCHA是一种区分用户是计算机或人类的公共全自动程序,在CAPTCHA测试中,作为服务器的计算机会自动生成一个问题让用户来解答。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。因为计算机无法解答CAPTCHA的问题,所以回答出问题的用户就可以被认为是人类。 但是由于这个测试是由计算机来考人类,而不是像标准图灵测试中那样由人类来考计算机,所以更确切的讲CAPTCHA是一种反向图灵测试。[ 1 ] ◆1 常见验证码分类 文本验证码 文本验证码常以问答形式出现,如:给出问题要求用户答案,给出古诗上举要求用户写出下句等等。 因为所有的验证码问题和答案都要事先在数据库中存好,所以这类验证码数量有限。攻击者可以先将问题库中的所有问题先爬取下来并准备相应的答案库,破解时只需利用正则表达式将验证问题提取出来,然后从答案库中找到相应答案即可。 静态图验证码 静态图验证码是目前应用最广的一类验证码,这类验证码要求用户输入验证码图片上所显示的文字或数字,通过扭曲、变形、干扰等方法避免被光学字符识别(OCR, Optical Character Recognition)之类的电脑程序自动辨识出图片上的文字和数字。 但是由于许多验证码的设计者对验证码的意义理解的不到位,并且缺乏相关安全知识和经验,所以目前在用的很多验证码都是可以被轻松攻破的。 动态图验证码 动态图验证码看似更为复杂,但是实际上动态验证码提供了更大的信息冗余,冗余越大,提供的信息就越多,因此也越容易被识别。例如,在某一帧原本粘连严重的两字字符很能在另一帧中就比较好的分离开了。 语音验证码 许多开发者考虑到部分视觉障碍者,提供了语音验证码的功能,通过播放语音,让用户输入听到的内容来完成验证。图片验证码的识别主要是基于图像处理技术,而语音验证码的识别主要是基于音频处理,但是他们在识别的基本原理上是相同的。 短信验证码 随着手机的普及,现在很多网站、应用开始使用短信验证码。服务器将验证码发送到用户预留的手机号中,然后要求用户输入收到的验证码内容。 短信验证码的设计目的与上述三种验证码稍有不同,它不仅区分用户是人类还是计算机计算机,它还主要用于验证是否是用户本人操作。但是由于部分开发人员的安全意识不足,这类验证码也可能被轻易地攻破。 ◆2 验证码识别原理与过程 验证码识别主要分成三部分:预处理,字符分割,字符识别。下面以静态图验证码(后面将简称为:图像验证码)为例来具体介绍识别原理。 预处理 预处理主要是将验证码图片进行色度空间转换、去除干扰、降噪、旋转等操作,为字符分割的时候提供一个质量较好的图片。 色度空间转换 在预处理是常用到色度空间转换,其中最主要的一种色度空间的转换就是二值化。二值化目的是将前景(主要为有效信息,即验证码信息)与背景(多为干扰信息)分离,尽最大程度讲有效信息提取出来,降低色彩空间维度,降低复杂度。 常用的方法:阈值法 统计一张图片(彩色图需转成256色灰度图)的灰度直方图后可以看到该图片在各灰度级上的像素分布数量。以下图的验证码为例,我们可以看到最左边(即纯黑色)与右侧其他灰度级像素的分布有明显一段隔开的区域,而图中纯黑色区域正好是有效信息(即验证码)。因此我们可以在该段隔开的区域里设一个阈值,像素值大于阈值的置为白色,小于像素值的置为黑色。 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 下图为通过上述办法二值化后的结果,背景已完全被去除,而有效信息被完整的保留了下来。 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 但是有时当前景与背景像素的灰度值交织在一起时,我们则很难通过阈值法提取出有效信息。以下面这张验证码为例,我们可以从其灰度直方图中看到所有像素点几乎都聚集在了一起。 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 我们将阈值设在峰值左侧尝试二值化,可以从结果看出,这时有效信息非但没有被提取出来,反而带入了更强的干扰。对于此类验证码我们则需要在二值化之前先去除干扰。 代码如下: #!python def Binarized(Picture): Pixels = Picture.load() (Width, Height) = Picture.size Threshold = 80 # 阈值 for i in xrange(Width): for j in xrange(Height): if Pixels[i, j] > Threshold: # 大于阈值的置为背景色,否则置为前景色(文字的颜色) Pixels[i, j] = BACKCOLOR else: Pixels[i, j] = TEXTCOLOR return Picture 去除干扰 上述实验已证明对于一些干扰较大的验证码我们需要先对其进行去干扰处理。去干扰的具体方法需要根据给定的验证码做有针对性的设计。 以某银行验证码为例,仔细观察可以发现验证码部分笔画宽度相对较宽,而干扰线宽度仅为1像素。针对此特性我设计了一种分离有效信息和干扰信息的算法。 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 具体算法过程如下: 将验证码转成256色灰度图像后,用一个33的窗口以此覆盖图像中的每一个像素点,然后将窗口中9个点的像素值进行排序后取中值Vmid,比较Vmid与33窗口中中心像素的值。如果二者差值大于预设的阈值,则判断该点颜色接近于白色还是黑色,若接近白色则将该点置为白色(255),若接近于黑色则置为黑色(0)。重复三次左右即可得到一个基本稳定的结果。 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 通过对比可以看出处理后的验证码区域灰度已被加深成黑色,与干扰线和背景的颜色已经明显区分开。从处理后的灰度直方图可以看出,像素点已主要集中在黑色(0)和白色(255)两个灰度级,这时在用阈值法二值化即可得到一个比较令人满意的结果了。 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 图片:2015010814091614383%E9%AA%8C%E8%AF%81%E7%A0%81%E8%AF%86%E5%88%AB1586.png 代码如下: #!python def Enhance(Picture): '''分离有效信息和干扰信息''' Pixels = Picture.load() Result = Picture.copy() ResultPixels = Result.load() (Width, Height) = Picture.size xx = [1, 0, -1, 0, 1, -1, 1, -1] yy = [0, 1, 0, -1, -1, 1, 1, -1] Threshold = 50 Window = [] for i in xrange(Width): for j in xrange(Height): Window = [i, j] for k in xrange(8): # 取3*3窗口中像素值存在Window中 if 0 |
|