验证码识别(KNN算法)

xiaoxiao2021-02-28  44

算法原理 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);
转载请注明原文地址: https://www.6miu.com/read-54401.html

最新回复(0)