利用PIL寫了一個能識別驗證碼的程序
驗證碼的抓取地址:http://system.ruanko.com/validateImage.jsp
原理:
將圖片進行處理去除干擾後分割成單個驗證碼,再和模板文件逐個進行比對,找出與其相似的模板文件
主要過程:
下載圖片:由downpic實現,默認下載20張圖片(這個函數只是測試用的)
↓
圖片去幹擾,:由process實現,會將圖片變成黑白並去除噪點
→
↓
圖片分割:由delims實現,該函數會將驗證碼上面的數字分開,並保存在temp文件夾內
↓
製作模板:這個過程需要自己在temp文件夾中找合適的模板文件,改名成圖片上數字後放到template文件夾內
↓
識別圖片:由recognized或recognized2實現,一個是找不同,一個是找相同的
代碼:
import os;
import urllib;
import urllib.request;
from PIL import Image as im;
from PIL import ImageEnhance;
def process(filename): #處理圖片
img=im.open(filename,"r");
enhancer=ImageEnhance.Color(img);
enhancer=enhancer.enhance(0); #變成黑白
enhancer=ImageEnhance.Brightness(enhancer); #這下面的參數是經過測試後圖片效果最好的。。。
enhancer=enhancer.enhance(2); #提高亮度
enhancer=ImageEnhance.Contrast(enhancer);
enhancer=enhancer.enhance(8); #提高對比度
enhancer=ImageEnhance.Sharpness(enhancer);
enhancer=enhancer.enhance(20); #銳化
return enhancer;
def delims(image,numbers=4,index=0,rect=()): #分割
"""
image爲圖片,
numbers爲圖片上驗證碼的個數,
index沒什麼用,生成臨時圖片要用的,
rect爲要切割的矩形元組,有4個值,爲左上右下
"""
if len(rect): #圖片會被處理成一定的大小再進行切割
image=image.crop((rect));
width,height=image.size;
for i in range(numbers):
img=image.crop((int(width/numbers)*i,0,int(width/numbers)*(i+1),height));
img.save("./temp/%d_%d.jpg" % (index,i));
def createtempfile(numbers=4,rect=()): #生成臨時文件,需要自己去裏面找合適的圖片作爲模板
list=os.listdir("./number");
for index,i in enumerate(list):
delims(process("./number/{0}".format(i)),numbers,index,rect);
def createtemplate(): #生成模板列表
list=[];
for root,dirs,files in os.walk("./template"):
for file in files:
list.append(os.path.join(root,file));
return list;
def recognize(filename,numbers,template,rect): #圖片識別,找不同
"""
filename爲要識別的驗證碼,
numbers爲驗證碼上面數字的個數,
template爲模板列表,
rect,爲要切割的矩形元組,有4個值,爲左上右下
"""
if len(rect):
image=process(filename).crop((rect));
if not len(template):
print("模板列表不能爲空,請先篩選作爲模板的文件並放到template文件夾內!")
return ;
width,height=image.size;
name="";
for i in range(numbers):
img=image.crop((int(width/numbers)*i,0,int(width/numbers)*(i+1),height));
subwidth,subheight=img.size;
rank=[];
for item in template:
temp=im.open(item,"r");
diff=0;
for w in range(subwidth):
for h in range(subheight):
if(img.getpixel((w,h))!=temp.getpixel((w,h))):
diff+=1;
rank.append((diff,os.path.basename(item).split(".")[0]));
rank.sort();
name+=str(rank[0][1]);
image.save("./recognized/"+name+".jpg")
# return name;
def recognize2(filename,numbers,template,rect): #圖片識別,找相同
if len(rect):
image=process(filename).crop((rect));
if not len(template):
print("模板列表不能爲空!")
return ;
width,height=image.size;
name="";
for i in range(numbers):
img=image.crop((int(width/numbers)*i,0,int(width/numbers)*(i+1),height));
subwidth,subheight=img.size;
rank=[];
for item in template:
temp=im.open(item,"r");
same=0;
for w in range(subwidth):
for h in range(subheight):
if(img.getpixel((w,h))==temp.getpixel((w,h))):
same+=1;
rank.append((same,os.path.basename(item).split(".")[0]));
rank.sort(reverse=True);
name+=str(rank[0][1]);
image.save("./recognized/"+name+".jpg")
# return name;
def downpic(numbers=10): #下載圖片,numbers爲要下載的數目,僅作測試用
url="http://system.ruanko.com/validateImage.jsp";
for i in range(numbers):
open("./number/%d.jpg" % i,"wb").write(urllib.request.urlopen(url).read());
def createdir():
cwd=os.getcwd()+"\\";
try: #生成需要的目錄
os.mkdir(cwd+"number");
except: #文件夾存在則忽略
pass;
try:
os.mkdir(cwd+"temp");
except:
pass;
try:
os.mkdir(cwd+"template");
except:
pass;
try:
os.mkdir(cwd+"recognized");
except:
pass;
def main():
createdir();
downpic(20); #下載的圖片在number文件夾內
createtempfile(rect=(4,3,56,16));#對圖片進行處理,生成的臨時圖片在temp文件夾內,用於找合適的模板圖片
"""整理完模板後就可以進行驗證碼的識別了"""
list=createtemplate(); #生成模板列表,對比的時候需要用到
piclist=os.listdir("./number"); #列舉需要識別的圖片
for item in piclist: #識別圖片
recognize("./number/{0}".format(item),4,list,(4,3,56,16));
main();
識別後的圖片:
隨機的20張圖片的測試效果,錯了7個= =,感覺應該不算太差。。。