利用PIL對簡單的驗證碼進行識別

大笑利用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個= =,感覺應該不算太差。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章