2008年08月04日

以前写过一个刷校内网的人气的工具,Java的(以后再也不行Java程序了),里面用到了验证码识别,那段代码不是我自己写的:-) 校内的验证是完全单色没有任何干挠的验证码,识别起来比较容易,不过从那段代码中可以看到基本的验证码识别方式。这几天在写一个程序的时候需要识别验证码,因为程序是写的自然打算用进行验证码的识别。

以前没用处理过图像,不太了解PIL( Image Library)的用法,这几天看了看PIL,发现它太强大了,简直和ImageMagic,PS可以相比了。(这里有PIL不错的文档)
由于上面的验证码是24位的jpeg图像,并且包含了噪点,所以我们要做的就是去噪和去色,我拿PS找了张验证码试了试,使用PS滤镜中的去噪效果还行,但是没有在PIL找到去噪的函数,后来发现中值过滤后可以去掉大部分的噪点,而且PIL里有现成的函数,接下来我试着直接把图像转换为单色,结果发现还是会有不过的噪点留了下来,因为中值过滤时把不少噪点淡化了,但转换为音色时这些噪点又被强化显示了,于是在中值过滤后对图像亮度进行加强处理,然后再转换为单色,这样验证码图片就变得比较容易识别了:

pic
pic1

上面这些处理使用才几行:

:
  1. im = Image.open(image_name)
  2.     im = im.filter(ImageFilter.MedianFilter())
  3.     enhancer = ImageEnhance.Contrast(im)
  4.     im = enhancer.enhance(2)
  5.     im = im.convert('1')
  6.     im.show()

接下来就是提取这些数字的字模,使用shell脚本下载100幅图片,抽出三张图片获取字模:

:
  1. #!/usr/bin/env
  2. #encoding=utf-8
  3.  
  4. import Image,ImageEnhance,ImageFilter
  5. import sys
  6.  
  7. image_name = "./images/81.jpeg"
  8. im = Image.open(image_name)
  9. im = im.filter(ImageFilter.MedianFilter())
  10. enhancer = ImageEnhance.Contrast(im)
  11. im = enhancer.enhance(2)
  12. im = im.convert('1')
  13. #im.show()
  14.                 #all by pixel
  15. s = 12          #start postion of first number
  16. w = 10          #width of each number
  17. h = 15          #end postion from top
  18. t = 2           #start postion of top
  19.  
  20. im_new = []
  21. #split four numbers in the picture
  22. for i in range(4):
  23.     im1 = im.crop((s+w*i+i*2,t,s+w*(i+1)+i*2,h))
  24.     im_new.append(im1)
  25.  
  26. f = file("data.txt","a")
  27. for k in range(4):
  28.     l = []
  29.     #im_new[k].show()
  30.     for i in range(13):
  31.         for j in range(10):
  32.             if (im_new[k].getpixel((j,i)) == 255):
  33.                 l.append(0)
  34.             else:
  35.                 l.append(1)
  36.  
  37.     f.write("l=[")
  38.        
  39.     n = 0
  40.     for i in l:
  41.         if (n%10==0):
  42.             f.write("\n")
  43.         f.write(str(i)+",")
  44.         n+=1
  45.     f.write("]\n")

把字模保存为list,用于接下来的匹配;

提取完字模后剩下来的就是对需要处理的图片进行与数据库中的字模进行匹配了,基本的思路就是看相应点的重合率,但是由于噪点的影响在对(6,8)(8,3)(5,9)的匹配时容易出错,俺自己针对已有的100幅图片数据采集进行分析,采用了双向匹配(图片与字模分别作为基点),做了半天的测试终于可以实现100%的识别率。

:
  1. #!/usr/bin/env
  2. #encoding=utf-8
  3.  
  4. import Image,ImageEnhance,ImageFilter
  5. import Data
  6.  
  7. DEBUG = False
  8.  
  9. def d_print(*msg):
  10.     global DEBUG
  11.     if DEBUG:
  12.         for i in msg:
  13.             print i,
  14.         print
  15.     else:
  16.         pass
  17.  
  18.  
  19. def Get_Num(l=[]):
  20.     min1 = []
  21.     min2 = []
  22.     for n in Data.N:
  23.         count1=count2=count3=count4=0
  24.         if (len(l) != len(n)):
  25.             print "Wrong pic"
  26.             exit()
  27.         for i in range(len(l)):
  28.             if (l[i] == 1):
  29.                 count1+=1
  30.                 if (n[i] == 1):
  31.                     count2+=1
  32.         for i in range(len(l)):
  33.             if (n[i] == 1):
  34.                 count3+=1
  35.                 if (l[i] == 1):
  36.                     count4+=1
  37.         d_print(count1,count2,count3,count4)
  38.  
  39.         min1.append(count1-count2)
  40.         min2.append(count3-count4)
  41.     d_print(min1,"\n",min2)
  42.     for i in range(10):
  43.         if (min1[i] <= 2 or min2[i] <= 2):
  44.             if ((abs(min1[i] - min2[i])) <10):
  45.                 return i
  46.     for i in range(10):            
  47.         if (min1[i] <= 4 or min2[i] <= 4):
  48.             if (abs(min1[i] - min2[i]) <= 2):
  49.                 return i
  50.  
  51.     for i in range(10):
  52.         flag = False
  53.         if (min1[i] <= 3 or min2[i] <= 3):
  54.             for j in range(10):
  55.                 if (j != i and (min1[j] <5 or min2[j] &lt;5)):
  56.                     flag = True
  57.                 else:
  58.                     pass
  59.             if (not flag):
  60.                 return i
  61.     for i in range(10):            
  62.         if (min1[i] <= 5 or min2[i] <= 5):
  63.             if (abs(min1[i] - min2[i]) <= 10):
  64.                 return i
  65.     for i in range(10):
  66.         if (min1[i] <= 10 or min2[i] <= 10):
  67.             if (abs(min1[i] - min2[i]) <= 3):
  68.                 return i
  69.  
  70. #end of function Get_Num
  71.  
  72. def Pic_Reg(image_name=None):
  73.     im = Image.open(image_name)
  74.     im = im.filter(ImageFilter.MedianFilter())
  75.     enhancer = ImageEnhance.Contrast(im)
  76.     im = enhancer.enhance(2)
  77.     im = im.convert('1')
  78.     im.show()
  79.                     #all by pixel
  80.     s = 12          #start postion of first number
  81.     w = 10          #width of each number
  82.     h = 15          #end postion from top
  83.     t = 2           #start postion of top
  84.     im_new = []
  85.     #split four numbers in the picture
  86.     for i in range(4):
  87.         im1 = im.crop((s+w*i+i*2,t,s+w*(i+1)+i*2,h))
  88.         im_new.append(im1)
  89.  
  90.     s = ""
  91.     for k in range(4):
  92.         l = []
  93.         #im_new[k].show()
  94.         for i in range(13):
  95.             for j in range(10):
  96.                 if (im_new[k].getpixel((j,i)) == 255):
  97.                     l.append(0)
  98.                 else:
  99.                     l.append(1)
  100.        
  101.         s+=str(Get_Num(l))
  102.     return s
  103. print Pic_Reg("./images/22.jpeg")

这里再提一下验证码识别的基本方法:截图,二值化、中值滤波去噪、分割、紧缩重排(让高矮统一)、字库特征匹配识别。
这里只是针对一般的验证码,高级验证码的识别这里有篇不错的文章,太复杂的话涉及的东西就多了,那俺就没兴趣了,人工智能(好恐怖),俺只喜欢简单的东西。

标签 :

5 楼了已经

发表评论

在下面加入你的评论,或者 trackback 从你的博客站点。 订阅本文的评论。

:

:

:

« 过自己的生活
» Fedora 8配置Heartbeat