zoukankan      html  css  js  c++  java
  • 无聊入门一下传说中的验证码识别技术,学习笔记

    无聊入门一下传说中的验证码识别技术,学习笔记 « Xiaoxia[PG]

    无聊入门一下传说中的验证码识别技术,学习笔记

    背景

    笔记本硬盘坏了,数据没了,盘里面的数据价值肯定超过联想数据恢复的RMB1500高价,不过当时身五分银,还是算了。。。
    新硬盘需要重装很多东西,今晚在群里看到给宿舍评分的给力投票,网址上的验证码做得很水,想涉足一下传说中的验证码识别技术!

    原理

    对于简单的验证码,使用的原理也相当的简单。
    现在有两个长度一致的二进制的数字,1110111011101和1010111011100,比较他们的相似度可以使用XOR运算!

    1110111011101 XOR 1010111011100 = 0100000000001

    结果中,1的数目越小,相似度越高。现在比较两张单色的图片,也可以使用这样的方法。这两张图必须是规格相同的,

    把两张单色的图片进行Xor,结果残留下来的白点越小,表示相似度越高!!!

    1. for y in range(h):  
    2.     for x in range(w):  
    3.         im2.putpixel((x,y), im1.getpixel((x,y)) ^ im2.getpixel((x,y)))  
    4. im2.show()  

    上面两个1的进行XOR运算,得到了不同像素点的个数。如上图所示,一共有3个。

    同时,我们把右图的1跟其他字符或数字的模板进行比较,看看相差多大。

    XOR统计结果 比较字模
    3 1
    12 I
    15 T
    25 Y
    30 2
    34 Z
    37 0
    38 L
    38 X
    39 E
    40 3
    40 7
    40 C
    40 J
    41 S
    42 F
    43 4
    43 G
    43 V
    44 9
    44 A
    44 K
    45 5
    47 6
    47 8
    47 O
    47 P
    48 Q
    51 M
    52 N
    54 D
    54 R
    55 B
    55 U
    56 W
    60 H

    相差最大的是H,比较相似的是I,的确1和I是有点类似的,但是差距还是比较明显的。所以识别率会很高!

    准备

    1. 一台运行着Ubuntu10.10的笔记本
    2. 接入互联网

    获取样本

    从网站上得到获取的验证码地址为

    http://su.100steps.net/2007/vote/verify.php

    图片浏览器设置一下不要平滑图片,放大图片之后,就可以看到小小的验证码图片是由一大堆像素点拼凑而成的。
    获取样本无非是为了找规律,样本越多对我们分析越好。

    因为这个验证码的规律很明显,我们暂且就获取50张吧,当然不是自己一个一个去下载,写个简单的脚本来处理!

    1. import urllib, random  
    2. for i in range(50):  
    3.     url = 'http://su.100steps.net/2007/vote/verify.php'  
    4.     print "download", i  
    5.     file("./code/%04d.png" % random.randrange(10000), "wb").write(  
    6.         urllib.urlopen(url).read())i  

    用上面的代码保存到downloadcode.py,在当前目录建立一个code目录。然后执行
    python downloadcode.py
    下载了50个验证码!

    观测样本

    为了看清楚点,去除噪点,转换成黑白单色图!

    通过观察一大堆验证码,得出如下结论:
    1. 验证码不复杂,只使用了一种字体类型
    2. 验证码不复杂,只使用了一种字体大小
    3. 验证码不复杂,只使用了一种字体颜色
    4. 验证码不复杂,每张由5个字母或数字构成,5个字母或数字的位置都是固定的
    5. 验证码不复杂,没有图片的歪曲变形,没有干扰线条或图案

    因此,这堆验证码非常适合识别验证码技术的初学者小试牛刀!

    提取字模

    既然已经知道每个字母或数字在图片中的具体位置,就可以从这些样本之中,把它们提取出来!!!
    每个字模的大小都是8x10,宽8高10,单位像素。

    好吧,让脚本帮我们提取这些字模:

    1. import Image, os  
    2. j = 1  
    3. for f in os.listdir("."):  
    4.     if f.endswith(".png"):  
    5.         img = Image.open(f).convert("1")  
    6.         for i in range(5):  
    7.             x = 10 + i*18  
    8.             y = 6  
    9.             img.crop((x, y, x+8, y+10)).save("font/%d.bmp" % j)  
    10.             print "j=",j  
    11.             j += 1  

    执行脚本,结果生成了250个字模。

    从这些字模之中,每个数字或字母,我们只需要一个看上去比较标准的就够了。飞之同学说,如果知道它是什么字体,就可以直接用脚本生成一堆标准的模板就行了,省去自己去匹配的麻烦呢。

    识别程序

    代码的流程比较简单,先加载标准的模板,然后读取一张验证码的图片,裁剪出这张图片的每个数字或字母的局部图,然后跟我们的标准模板进行比较,统计像素点不相同的个数。然后把每个字模的统计结果进行排序,不相同点个数最小的,相似度就应当是最高的了!!!

    下面的代码把识别出来的验证码另存到result目录下,并以结果命名!

    1. import os, Image  
    2.   
    3. # load font modules  (char, image)  
    4. fontMods = []  
    5. for i in range(10):  
    6.     fontMods.append((str(i), Image.open("./good/%02d.bmp" % i)))  
    7. for i in range(26):  
    8.     c = chr(ord('A') + i)  
    9.     fontMods.append((c, Image.open("./good/%s.bmp" % c)))  
    10.   
    11. def recognize(f):  
    12.     im = Image.open(f)  
    13.     im2 = im.convert('1')  
    14.     # check 5 fonts  
    15.     result = "./result/"  
    16.     for i in range(5):  
    17.         x = 10 + i*18  
    18.         y = 6  
    19.         target = im.crop((x, y, x+8, y+10))  
    20.         points = []  
    21.         for mod in fontMods:  
    22.             diffs = 0  
    23.             for yi in range(10):  
    24.                 for xi in range(8):  
    25.                     if mod[1].getpixel((xi, yi)) != target.getpixel((xi, yi)):  
    26.                         diffs += 1  
    27.             points.append((diffs, mod[0]))  
    28.         points.sort()  
    29.         result += points[0][1]  
    30.     result += ".png"  
    31.     print "save to", result  
    32.     im.save(result);  
    33.   
    34. for imgfile in os.listdir("."):  
    35.     if imgfile.endswith(".png"):  
    36.         recognize(imgfile)  

    效果图片:

    50个验证码中,有两三个出现了把E识别成B的现象,其他问题都不大,识别成功率超过90%。

    小结

    机器识别验证码,能够超过90%的正确率已经是相当不错的了。有的验证码肉眼识别也达不到90%,所以有的网站提供的验证码,我要更换好几次!!!
    如果是用来刷票,超过50%的识别率都已足够啦!

    这次试验只是用来进行入门,复杂一些的验证码就不能只是这么做了,还需要考虑很多问题,对图片预处理复杂很多吧,识别的时候也可以考虑加入学习功能(例如神经网络)来逐步提高识别的成功率。因此,仅仅是菜鸟入门,大牛可以无视之。

  • 相关阅读:
    Eclipse快捷键大全(转载)
    IE9浏览Flash页面时显示错位并不停地闪烁
    flash全屏事件和键盘按下事件部分不能触发问题
    AS3摘要(转载)
    【as3手册小记】ActionScript 中处理全屏模式的注意事项
    巧用FlashPaper 让Word文档变Flash
    AS3视频照相截图(转载)
    Json串到json对象的转换
    映射文件详解(转)
    Jquery .ajax方法分析(一)
  • 原文地址:https://www.cnblogs.com/lexus/p/2379115.html
Copyright © 2011-2022 走看看