以前寫過一個刷校內網的人氣的工具,Java的(以后再也不行Java程序了),里面用到了驗證碼識別,那段代碼不是我自己寫的:-) 校內的驗證是完全單色沒有任何干撓的驗證碼,識別起來比較容易,不過從那段代碼中可以看到基本的驗證碼識別方式。這幾天在寫一個程序的時候需要識別驗證碼,因為程序是Python寫的自然打算用Python進行驗證碼的識別。
以前沒用Python處理過圖像,不太了解PIL(Python Image Library)的用法,這幾天看了看PIL,發現它太強大了,簡直和ImageMagic,PS可以相比了。(這里有PIL不錯的文檔)
由于上面的驗證碼是24位的jpeg圖像,并且包含了噪點,所以我們要做的就是去噪和去色,我拿PS找了張驗證碼試了試,使用PS濾鏡中的去噪效果還行,但是沒有在PIL找到去噪的函數,后來發現中值過濾后可以去掉大部分的噪點,而且PIL里有現成的函數,接下來我試著直接把圖像轉換為單色,結果發現還是會有不過的噪點留了下來,因為中值過濾時把不少噪點淡化了,但轉換為音色時這些噪點又被強化顯示了,于是在中值過濾后對圖像亮度進行加強處理,然后再轉換為單色,這樣驗證碼圖片就變得比較容易識別了:
上面這些處理使用Python才幾行:
PYTHON:
im = Image.open(image_name)
im = im.filter(ImageFilter.MedianFilter())
enhancer = ImageEnhance.Contrast(im)
im = enhancer.enhance(2)
im = im.convert('1')
im.show()
接下來就是提取這些數字的字模,使用shell腳本下載100幅圖片,抽出三張圖片獲取字模:
PYTHON:
#!/usr/bin/env python
#encoding=utf-8
import Image,ImageEnhance,ImageFilter
import sys
image_name = "./images/81.jpeg"
im = Image.open(image_name)
im = im.filter(ImageFilter.MedianFilter())
enhancer = ImageEnhance.Contrast(im)
im = enhancer.enhance(2)
im = im.convert('1')
#im.show()
#all by pixel
s = 12 #start postion of first number
w = 10 #width of each number
h = 15 #end postion from top
t = 2 #start postion of top
im_new = []
#split four numbers in the picture
for i in range(4):
im1 = im.crop((s+w*i+i*2,t,s+w*(i+1)+i*2,h))
im_new.append(im1)
f = file("data.txt","a")
for k in range(4):
l = []
#im_new[k].show()
for i in range(13):
for j in range(10):
if (im_new[k].getpixel((j,i)) == 255):
l.append(0)
else:
l.append(1)
f.write("l=[")
n = 0
for i in l:
if (n%10==0):
f.write("\n")
f.write(str(i)+",")
n+=1
f.write("]\n")
把字模保存為list,用于接下來的匹配;
提取完字模后剩下來的就是對需要處理的圖片進行與數據庫中的字模進行匹配了,基本的思路就是看相應點的重合率,但是由于噪點的影響在對(6,8)(8,3)(5,9)的匹配時容易出錯,俺自己針對已有的100幅圖片數據采集進行分析,采用了雙向匹配(圖片與字模分別作為基點),做了半天的測試終于可以實現100%的識別率。
PYTHON:
#!/usr/bin/env python
#encoding=utf-8
import Image,ImageEnhance,ImageFilter
import Data
DEBUG = False
def d_print(*msg):
global DEBUG
if DEBUG:
for i in msg:
print i,
print
else:
pass
def Get_Num(l=[]):
min1 = []
min2 = []
for n in Data.N:
count1=count2=count3=count4=0
if (len(l) != len(n)):
print "Wrong pic"
exit()
for i in range(len(l)):
if (l[i] == 1):
count1+=1
if (n[i] == 1):
count2+=1
for i in range(len(l)):
if (n[i] == 1):
count3+=1
if (l[i] == 1):
count4+=1
d_print(count1,count2,count3,count4)
min1.append(count1-count2)
min2.append(count3-count4)
d_print(min1,"\n",min2)
for i in range(10):
if (min1[i] <= 2 or min2[i] <= 2):
if ((abs(min1[i] - min2[i])) <10):
return i
for i in range(10):
if (min1[i] <= 4 or min2[i] <= 4):
if (abs(min1[i] - min2[i]) <= 2):
return i
for i in range(10):
flag = False
if (min1[i] <= 3 or min2[i] <= 3):
for j in range(10):
if (j != i and (min1[j] <5 or min2[j] <5)):
flag = True
else:
pass
if (not flag):
return i
for i in range(10):
if (min1[i] <= 5 or min2[i] <= 5):
if (abs(min1[i] - min2[i]) <= 10):
return i
for i in range(10):
if (min1[i] <= 10 or min2[i] <= 10):
if (abs(min1[i] - min2[i]) <= 3):
return i
#end of function Get_Num
def Pic_Reg(image_name=None):
im = Image.open(image_name)
im = im.filter(ImageFilter.MedianFilter())
enhancer = ImageEnhance.Contrast(im)
im = enhancer.enhance(2)
im = im.convert('1')
im.show()
#all by pixel
s = 12 #start postion of first number
w = 10 #width of each number
h = 15 #end postion from top
t = 2 #start postion of top
im_new = []
#split four numbers in the picture
for i in range(4):
im1 = im.crop((s+w*i+i*2,t,s+w*(i+1)+i*2,h))
im_new.append(im1)
s = ""
for k in range(4):
l = []
#im_new[k].show()
for i in range(13):
for j in range(10):
if (im_new[k].getpixel((j,i)) == 255):
l.append(0)
else:
l.append(1)
s+=str(Get_Num(l))
return s
print Pic_Reg("./images/22.jpeg")
這里再提一下驗證碼識別的基本方法:截圖,二值化、中值濾波去噪、分割、緊縮重排(讓高矮統一)、字庫特征匹配識別。
這里只是針對一般的驗證碼,高級驗證碼的識別這里有篇不錯的文章,太復雜的話涉及的東西就多了,那俺就沒興趣了,人工智能(好恐怖),俺只喜歡簡單的東西。
本文來源于可可熊的窩 http://cocobear.info/blog , 原文地址: http://cocobear.info/blog/2008/08/04/python-pic-recognize/