验证码识别(KNN算法)

算法原理
kNN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。该方法在确定分类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。 kNN方法在类别决策时,只与极少量的相邻样本有关。由于kNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,kNN方法较其他方法更为适合。
算法流程
1.准备数据,对数据进行预处理
2.选用合适的数据结构存储训练数据和测试元组
3.设定参数,如k。
4.维护一个大小为k的的按距离由大到小的优先级队列,用于存储最近邻训练元组。随机从训练元组中选取k个元组作为初始的最近邻元组,分别计算测试元组到这k个元组的距离,将训练元组标号和距离存入优先级队列
5.遍历训练元组集,计算当前训练元组与测试元组的距离,将所得距离L 与优先级队列中的最大距离Lmax
6.进行比较。若L>=Lmax,则舍弃该元组,遍历下一个元组。若L < Lmax,删除优先级队列中最大距离的元组,将当前训练元组存入优先级队列。
7.遍历完毕,计算优先级队列中k 个元组的多数类,并将其作为测试元组的类别。
8.测试元组集测试完毕后计算误差率,继续设定不同的k值重新进行训练,最后取误差率最小的k 值。
代码
1.获得验证码
这里下载500张验证码到source目录下面。

#-*- coding:UTF-8 -*-
import urllib,urllib2,cookielib,string,Image
def getchk(number):
    #创建cookie对象
    cookie = cookielib.LWPCookieJar()
    cookieSupport= urllib2.HTTPCookieProcessor(cookie)
    opener = urllib2.build_opener(cookieSupport, urllib2.HTTPHandler)
    urllib2.install_opener(opener) 
    #首次与教务系统链接获得cookie#
    #伪装browser
    headers = {
        'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Encoding':'gzip,deflate',
        'Accept-Language':'zh-CN,zh;q=0.8',
        'User-Agent':'Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36'
    }
    req0 = urllib2.Request(
            url ='http://mis.teach.ustc.edu.cn',
            headers = headers               #请求头
    )
    # 捕捉http错误
    try :
        result0 = urllib2.urlopen(req0)
    except urllib2.HTTPError,e:
        print e.code
    #提取cookie
    getcookie = ['',]
    for item in cookie:
        getcookie.append(item.name)
        getcookie.append("=")
        getcookie.append(item.value)
        getcookie = "".join(getcookie)

    #修改headers
    headers["Origin"] = "http://mis.teach.ustc.edu.cn"
    headers["Referer"] = "http://mis.teach.ustc.edu.cn/userinit.do"
    headers["Content-Type"] = "application/x-www-form-urlencoded"
    headers["Cookie"] = getcookie
    for i in range(number):
        req = urllib2.Request(
            url ="http://mis.teach.ustc.edu.cn/randomImage.do?date='1469451446894'",
            headers = headers                 #请求头
        )
        response = urllib2.urlopen(req)
        status = response.getcode()
        picData = response.read()
        if status == 200:
            localPic = open("./source/"+str(i)+".jpg", "wb")
            localPic.write(picData)
            localPic.close()
        else:
            print "failed to get Check Code "
if __name__ == '__main__':
    getchk(500)

2.二值化

mydir='./source/';
bw = './bw/';
if mydir(end)~='\'
    mydir=[mydir,'\'];
end
DIRS=dir([mydir,'*.jpg']);  %扩展名
n=length(DIRS);
for i=1:n
    if ~DIRS(i).isdir
        img = imread(strcat(mydir,DIRS(i).name ));
        img = rgb2gray(img);%灰度化
        img = im2bw(img);%0-1二值化
        name = strcat(bw,DIRS(i).name)
        imwrite(img,name);
    end
end

3.分割

mydir='./bw/';
letter = './letter/';
if mydir(end)~='\'
    mydir=[mydir,'\'];
end
DIRS=dir([mydir,'*.jpg']);  %扩展名
n=length(DIRS);
for i=1:n
    if ~DIRS(i).isdir
        img = imread(strcat(mydir,DIRS(i).name ));
        img = im2bw(img);%二值化
        img = 1-img;%颜色反转让字符成为联通域,方便去除噪点
        for ii = 0:3
            region = [ii*20+1,1,19,20];%把一张验证码分成四个20*20大小的字符图片
            subimg = imcrop(img,region);
            imlabel = bwlabel(subimg);
%             imshow(imlabel);
            if max(max(imlabel))>1 % 说明有噪点,要去除
%                 max(max(imlabel))
%                 imshow(subimg);
                stats = regionprops(imlabel,'Area');
                area = cat(1,stats.Area); 
                maxindex = find(area == max(area));
                area(maxindex) = 0;          
                secondindex = find(area == max(area));        
                imindex = ismember(imlabel,secondindex);
                subimg(imindex==1)=0;%去掉第二大连通域,噪点不可能比字符大,所以第二大的就是噪点
            end
            name = strcat(letter,DIRS(i).name(1:length(DIRS(i).name)-4),'_',num2str(ii),'.jpg')
            imwrite(subimg,name);
        end
    end
end

4.旋转

if mydir(end)~='\'
    mydir=[mydir,'\'];
end
DIRS=dir([mydir,'*.jpg']);  %扩展名
n=length(DIRS);
for i=1:n
    if ~DIRS(i).isdir
        img = imread(strcat(mydir,DIRS(i).name ));
        img = im2bw(img);
        minwidth = 20;
        for angle = -60:60
            imgr=imrotate(img,angle,'bilinear','crop');%crop 避免图像大小变化
            imlabel = bwlabel(imgr);
            stats = regionprops(imlabel,'Area');
            area = cat(1,stats.Area);
            maxindex = find(area == max(area));
            imindex = ismember(imlabel,maxindex);%最大连通域为1
            [y,x] = find(imindex==1);
            width = max(x)-min(x)+1;
            if width<minwidth
                minwidth = width;
                imgrr = imgr;
            end
        end
        name = strcat(rotate,DIRS(i).name)
        imwrite(imgrr,name);
    end
end

5.模板选取
现在从rotate文件夹中选取一套模板,涵盖每一个字符,一个字符可以选取多个图片,因为即使有前面的诸多处理也不能保证一个字符的最终呈现形式只有一种,多选几个才能保证覆盖率。把选出来的模板图片存入samples文件夹下,这个过程很耗时耗力。
6.测试
测试代码如下首先对测试验证码进行上述操作,然后和选出来的模板进行比较,采用差分值最小的模板作为测试样本的字符选择,代码如下

% 具有差分最小值的图作为答案
mydir='./test/';
samples = './samples/';
if mydir(end)~='\'
    mydir=[mydir,'\'];
end
if samples(end)~='\'
    samples=[samples,'\'];
end
DIRS=dir([mydir,'*.jpg']);  %扩展?
DIRS1=dir([samples,'*.jpg']);  %扩展名
n=length(DIRS);%验证码总图数
singleerror = 0;%单个错误
uniterror = 0;%一张验证码错误个数
for i=1:n
    if ~DIRS(i).isdir
        realcodes = DIRS(i).name(1:4);
        fprintf('验证码实际字符:%s\n',realcodes);
        img = imread(strcat(mydir,DIRS(i).name ));
        img = rgb2gray(img);
        img = im2bw(img);
        img = 1-img;%颜色反转让字符成为联通域
        subimgs = [];
        for ii = 0:3
            region = [ii*20+1,1,19,20];%奇怪,为什么这样才能均分?
            subimg = imcrop(img,region);
            imlabel = bwlabel(subimg);
            if max(max(imlabel))>1 % 说明有杂点
                stats = regionprops(imlabel,'Area');
                area = cat(1,stats.Area); 
                maxindex = find(area == max(area));
                area(maxindex) = 0;          
                secondindex = find(area == max(area));        
                imindex = ismember(imlabel,secondindex);
                subimg(imindex==1)=0;%去掉第二大连通域
            end
            subimgs = [subimgs;subimg];
        end
        codes = [];
        for ii = 0:3
            region = [ii*20+1,1,19,20];
            subimg = imcrop(img,region);
            minwidth = 20;
            for angle = -60:60
                imgr=imrotate(subimg,angle,'bilinear','crop');%crop 避免图像大小变化
                imlabel = bwlabel(imgr);
                stats = regionprops(imlabel,'Area');
                area = cat(1,stats.Area);
                maxindex = find(area == max(area));
                imindex = ismember(imlabel,maxindex);%最大连通域为1
                [y,x] = find(imindex==1);
                width = max(x)-min(x)+1;
                if width<minwidth
                    minwidth = width;
                    imgrr = imgr;
                end
            end
            mindiffv = 1000000;
            for jj = 1:length(DIRS1)
                imgsample = imread(strcat(samples,DIRS1(jj).name ));
                imgsample = im2bw(imgsample);
                diffv = abs(imgsample-imgrr);
                alldiffv = sum(sum(diffv));
                if alldiffv<mindiffv
                    mindiffv = alldiffv;
                    code = DIRS1(jj).name;
                    code = code(1);
                end
            end
            codes = [codes,code];
        end
        fprintf('验证码测试字符:%s\n',codes);
        num = codes-realcodes;
        num = length(find(num~=0));
        singleerror = singleerror + num;
        if num>0
            uniterror = uniterror +1;
        end
        fprintf('错误个数:%d\n',num);
    end
end
fprintf('\n-----结果统计如下-----\n\n');
fprintf('测试验证码的字符数量:%d\n',n*4);
fprintf('测试验证码的字符错误数量:%d\n',singleerror);
fprintf('单个字符识别正确率:%.2f%%\n',(1-singleerror/(n*4))*100);
fprintf('测试验证码图的数量:%d\n',n);
fprintf('测试验证码图的错误数量:%d\n',uniterror);
fprintf('填对验证码的概率:%.2f%%\n',(1-uniterror/n)*100);
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值