国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

Python用 KNN 進行驗證碼識別的實現(xiàn)方法

2020-01-04 15:56:35
字體:
供稿:網(wǎng)友

前言

之前做了一個校園交友的APP,其中一個邏輯是通過用戶的教務(wù)系統(tǒng)來確認用戶是一名在校大學(xué)生,基本的想法是通過用戶的賬號和密碼,用爬蟲的方法來確認信息,但是許多教務(wù)系統(tǒng)都有驗證碼,當時是通過本地服務(wù)器去下載驗證碼,然后分發(fā)給客戶端,然后讓用戶自己填寫驗證碼,與賬號密碼一并提交給服務(wù)器,然后服務(wù)器再去模擬登錄教務(wù)系統(tǒng)以確認用戶能否登錄該教務(wù)系統(tǒng)。驗證碼無疑讓我們想使得用戶快速認證的想法破滅了,但是當時也沒辦法,最近看了一些機器學(xué)習(xí)的內(nèi)容,覺得對于大多數(shù)學(xué)校的那些極簡單的驗證碼應(yīng)該是可以用KNN這種方法來破解的,于是整理了一下思緒,擼起袖子做起來!

分析

我們學(xué)校的驗證碼是這樣的:python,驗證碼,KNN,其實就是簡單地把字符進行旋轉(zhuǎn)然后加上一些微弱的噪點形成的。我們要識別,就得逆行之,具體思路就是,首先二值化去掉噪點,然后把單個字符分割出來,最后旋轉(zhuǎn)至標準方向,然后從這些處理好的圖片中選出模板,最后每次新來一張驗證碼就按相同方式處理,然后和這些模板進行比較,選擇判別距離最近的一個模板作為其判斷結(jié)果(亦即KNN的思想,本文取K=1)。接下來按步驟進行說明。

獲得驗證碼

首先得有大量的驗證碼,我們通過爬蟲來實現(xiàn),代碼如下

#-*- coding:UTF-8 -*-import urllib,urllib2,cookielib,string,Imagedef getchk(number): #創(chuàng)建cookie對象 cookie = cookielib.LWPCookieJar() cookieSupport= urllib2.HTTPCookieProcessor(cookie) opener = urllib2.build_opener(cookieSupport, urllib2.HTTPHandler) urllib2.install_opener(opener) #首次與教務(wù)系統(tǒng)鏈接獲得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)

這里下載了500張驗證碼到source目錄下面。如圖:

python,驗證碼,KNN

二值化

matlab豐富的圖像處理函數(shù)能給我們省下很多時間,,我們遍歷source文件夾,對每一張驗證碼圖片進行二值化處理,把處理過的圖片存入bw目錄下。代碼如下

mydir='./source/';bw = './bw/';if mydir(end)~='/' mydir=[mydir,'/'];endDIRS=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); endend

處理結(jié)果如圖:

python,驗證碼,KNN

分割

mydir='./bw/';letter = './letter/';if mydir(end)~='/' mydir=[mydir,'/'];endDIRS=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;%顏色反轉(zhuǎn)讓字符成為聯(lián)通域,方便去除噪點 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 endend

處理結(jié)果如圖:

python,驗證碼,KNN

旋轉(zhuǎn)

接下來進行旋轉(zhuǎn),哪找一個什么標準呢?據(jù)觀察,這些字符旋轉(zhuǎn)不超過60度,那么在正負60度之間,統(tǒng)一旋轉(zhuǎn)至字符寬度最小就行了。代碼如下

if mydir(end)~='/' mydir=[mydir,'/'];endDIRS=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); endend

處理結(jié)果如圖,一共2000個字符的圖片存在rotate文件夾中

python,驗證碼,KNN

模板選取

現(xiàn)在從rotate文件夾中選取一套模板,涵蓋每一個字符,一個字符可以選取多個圖片,因為即使有前面的諸多處理也不能保證一個字符的最終呈現(xiàn)形式只有一種,多選幾個才能保證覆蓋率。把選出來的模板圖片存入samples文件夾下,這個過程很耗時耗力。可以找同學(xué)幫忙~,如圖

python,驗證碼,KNN

測試

測試代碼如下:首先對測試驗證碼進行上述操作,然后和選出來的模板進行比較,采用差分值最小的模板作為測試樣本的字符選擇,代碼如下

% 具有差分最小值的圖作為答案 

mydir='./test/';samples = './samples/';if mydir(end)~='/' mydir=[mydir,'/'];endif samples(end)~='/' samples=[samples,'/'];endDIRS=dir([mydir,'*.jpg']); %擴展?DIRS1=dir([samples,'*.jpg']); %擴展名n=length(DIRS);%驗證碼總圖數(shù)singleerror = 0;%單個錯誤uniterror = 0;%一張驗證碼錯誤個數(shù)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;%顏色反轉(zhuǎn)讓字符成為聯(lián)通域 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('錯誤個數(shù):%d/n',num); endendfprintf('/n-----結(jié)果統(tǒng)計如下-----/n/n');fprintf('測試驗證碼的字符數(shù)量:%d/n',n*4);fprintf('測試驗證碼的字符錯誤數(shù)量:%d/n',singleerror);fprintf('單個字符識別正確率:%.2f%%/n',(1-singleerror/(n*4))*100);fprintf('測試驗證碼圖的數(shù)量:%d/n',n);fprintf('測試驗證碼圖的錯誤數(shù)量:%d/n',uniterror);fprintf('填對驗證碼的概率:%.2f%%/n',(1-uniterror/n)*100);

結(jié)果:

驗證碼實際字符:2B4E
驗證碼測試字符:2B4F
錯誤個數(shù):1
驗證碼實際字符:4572
驗證碼測試字符:4572
錯誤個數(shù):0
驗證碼實際字符:52CY
驗證碼測試字符:52LY
錯誤個數(shù):1
驗證碼實際字符:83QG
驗證碼測試字符:85QG
錯誤個數(shù):1
驗證碼實際字符:9992
驗證碼測試字符:9992
錯誤個數(shù):0
驗證碼實際字符:A7Y7
驗證碼測試字符:A7Y7
錯誤個數(shù):0
驗證碼實際字符:D993
驗證碼測試字符:D995
錯誤個數(shù):1
驗證碼實際字符:F549
驗證碼測試字符:F5A9
錯誤個數(shù):1
驗證碼實際字符:FMC6
驗證碼測試字符:FMLF
錯誤個數(shù):2
驗證碼實際字符:R4N4
驗證碼測試字符:R4N4
錯誤個數(shù):0 

-----結(jié)果統(tǒng)計如下----- 

測試驗證碼的字符數(shù)量:40
測試驗證碼的字符錯誤數(shù)量:7
單個字符識別正確率:82.50%
測試驗證碼圖的數(shù)量:10
測試驗證碼圖的錯誤數(shù)量:6
填對驗證碼的概率:40.00%

可見單個字符準確率是比較高的的了,但是綜合準確率還是不行,觀察結(jié)果至,錯誤的字符就是那些易混淆字符,比如E和F,C和L,5和3,4和A等,所以我們能做的事就是增加模板中的樣本數(shù)量,以期盡量減少混淆。

增加了幾十個樣本過后再次試驗,結(jié)果:

驗證碼實際字符:2B4E
驗證碼測試字符:2B4F
錯誤個數(shù):1
驗證碼實際字符:4572
驗證碼測試字符:4572
錯誤個數(shù):0
驗證碼實際字符:52CY
驗證碼測試字符:52LY
錯誤個數(shù):1
驗證碼實際字符:83QG
驗證碼測試字符:83QG
錯誤個數(shù):0
驗證碼實際字符:9992
驗證碼測試字符:9992
錯誤個數(shù):0
驗證碼實際字符:A7Y7
驗證碼測試字符:A7Y7
錯誤個數(shù):0
驗證碼實際字符:D993
驗證碼測試字符:D993
錯誤個數(shù):0
驗證碼實際字符:F549
驗證碼測試字符:F5A9
錯誤個數(shù):1
驗證碼實際字符:FMC6
驗證碼測試字符:FMLF
錯誤個數(shù):2
驗證碼實際字符:R4N4
驗證碼測試字符:R4N4
錯誤個數(shù):0 

-----結(jié)果統(tǒng)計如下----- 

測試驗證碼的字符數(shù)量:40
測試驗證碼的字符錯誤數(shù)量:5
單個字符識別正確率:87.50%
測試驗證碼圖的數(shù)量:10
測試驗證碼圖的錯誤數(shù)量:4
填對驗證碼的概率:60.00%

可見無論是單個字符識別正確率還是整個驗證碼正確的概率都有了提升。能夠預(yù)見:隨著模板數(shù)量的增多,正確率會不斷地提高。

總結(jié)

這種方法的可擴展性很弱,而且只適用于簡單的驗證碼,12306那種根本就別提了。


注:相關(guān)教程知識閱讀請移步到python教程頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 盱眙县| 镇巴县| 玉田县| 泊头市| 于都县| 永胜县| 绍兴市| 岳池县| 图木舒克市| 临沭县| 佛山市| 福海县| 工布江达县| 日土县| 敦煌市| 浮梁县| 罗田县| 沅陵县| 拜城县| 门源| 共和县| 吴旗县| 平和县| 东光县| 化州市| 调兵山市| 通化县| 武陟县| 大足县| 砚山县| 泾川县| 施秉县| 云霄县| 中西区| 凤凰县| 秦皇岛市| 云阳县| 南宁市| 阳高县| 新竹市| 云林县|