摘要:接下来按步骤进行说明。总结这种方法的可扩展性很弱,而且只适用于简单的验证码,那种根本就别提了。
前言
之前做了一个校园交友的APP,其中一个逻辑是通过用户的教务系统来确认用户是一名在校大学生,基本的想法是通过用户的账号和密码,用爬虫的方法来确认信息,但是许多教务系统都有验证码,当时是通过本地服务器去下载验证码,然后分发给客户端,然后让用户自己填写验证码,与账号密码一并提交给服务器,然后服务器再去模拟登录教务系统以确认用户能否登录该教务系统。验证码无疑让我们想使得用户快速认证的想法破灭了,但是当时也没办法,最近看了一些机器学习的内容,觉得对于大多数学校的那些极简单的验证码应该是可以用KNN这种方法来破解的,于是整理了一下思绪,撸起袖子做起来!
分析我们学校的验证码是这样的:,其实就是简单地把字符进行旋转然后加上一些微弱的噪点形成的。我们要识别,就得逆行之,具体思路就是,首先二值化去掉噪点,然后把单个字符分割出来,最后旋转至标准方向,然后从这些处理好的图片中选出模板,最后每次新来一张验证码就按相同方式处理,然后和这些模板进行比较,选择判别距离最近的一个模板作为其判断结果(亦即KNN的思想,本文取K=1)。接下来按步骤进行说明。
获得验证码首先得有大量的验证码,我们通过爬虫来实现,代码如下
#-*- 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)
这里下载了500张验证码到source目录下面。如图:
二值化matlab丰富的图像处理函数能给我们省下很多时间,,我们遍历source文件夹,对每一张验证码图片进行二值化处理,把处理过的图片存入bw目录下。代码如下
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
处理结果如图:
分割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
处理结果如图:
旋转接下来进行旋转,哪找一个什么标准呢?据观察,这些字符旋转不超过60度,那么在正负60度之间,统一旋转至字符宽度最小就行了。代码如下
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处理结果如图,一共2000个字符的图片存在rotate文件夹中
模板选取现在从rotate文件夹中选取一套模板,涵盖每一个字符,一个字符可以选取多个图片,因为即使有前面的诸多处理也不能保证一个字符的最终呈现形式只有一种,多选几个才能保证覆盖率。把选出来的模板图片存入samples文件夹下,这个过程很耗时耗力。可以找同学帮忙~,如图
测试测试代码如下
首先对测试验证码进行上述操作,然后和选出来的模板进行比较,采用差分值最小的模板作为测试样本的字符选择,代码如下% 具有差分最小值的图作为答案 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 ",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 width0 uniterror = uniterror +1; end fprintf("错误个数:%d ",num); end end fprintf(" -----结果统计如下----- "); fprintf("测试验证码的字符数量:%d ",n*4); fprintf("测试验证码的字符错误数量:%d ",singleerror); fprintf("单个字符识别正确率:%.2f%% ",(1-singleerror/(n*4))*100); fprintf("测试验证码图的数量:%d ",n); fprintf("测试验证码图的错误数量:%d ",uniterror); fprintf("填对验证码的概率:%.2f%% ",(1-uniterror/n)*100); 结果:
验证码实际字符:2B4E 验证码测试字符:2B4F 错误个数:1 验证码实际字符:4572 验证码测试字符:4572 错误个数:0 验证码实际字符:52CY 验证码测试字符:52LY 错误个数:1 验证码实际字符:83QG 验证码测试字符:85QG 错误个数:1 验证码实际字符:9992 验证码测试字符:9992 错误个数:0 验证码实际字符:A7Y7 验证码测试字符:A7Y7 错误个数:0 验证码实际字符:D993 验证码测试字符:D995 错误个数:1 验证码实际字符:F549 验证码测试字符:F5A9 错误个数:1 验证码实际字符:FMC6 验证码测试字符:FMLF 错误个数:2 验证码实际字符:R4N4 验证码测试字符:R4N4 错误个数:0 -----结果统计如下----- 测试验证码的字符数量:40 测试验证码的字符错误数量:7 单个字符识别正确率:82.50% 测试验证码图的数量:10 测试验证码图的错误数量:6 填对验证码的概率:40.00%可见单个字符准确率是比较高的的了,但是综合准确率还是不行,观察结果至,错误的字符就是那些易混淆字符,比如E和F,C和L,5和3,4和A等,所以我们能做的事就是增加模板中的样本数量,以期尽量减少混淆。
增加了几十个样本过后再次试验,结果:验证码实际字符:2B4E 验证码测试字符:2B4F 错误个数:1 验证码实际字符:4572 验证码测试字符:4572 错误个数:0 验证码实际字符:52CY 验证码测试字符:52LY 错误个数:1 验证码实际字符:83QG 验证码测试字符:83QG 错误个数:0 验证码实际字符:9992 验证码测试字符:9992 错误个数:0 验证码实际字符:A7Y7 验证码测试字符:A7Y7 错误个数:0 验证码实际字符:D993 验证码测试字符:D993 错误个数:0 验证码实际字符:F549 验证码测试字符:F5A9 错误个数:1 验证码实际字符:FMC6 验证码测试字符:FMLF 错误个数:2 验证码实际字符:R4N4 验证码测试字符:R4N4 错误个数:0 -----结果统计如下----- 测试验证码的字符数量:40 测试验证码的字符错误数量:5 单个字符识别正确率:87.50% 测试验证码图的数量:10 测试验证码图的错误数量:4 填对验证码的概率:60.00%可见无论是单个字符识别正确率还是整个验证码正确的概率都有了提升。能够预见:随着模板数量的增多,正确率会不断地提高。
总结这种方法的可扩展性很弱,而且只适用于简单的验证码,12306那种根本就别提了。
更新7.28
总之就是学习的道路还很长,我会慢慢的改善这种方法的。再增加了几十个模板后结果如下:
验证码实际字符:2B4E 验证码测试字符:2B4E 错误个数:0 验证码实际字符:4572 验证码测试字符:4572 错误个数:0 验证码实际字符:52CY 验证码测试字符:52LY 错误个数:1 验证码实际字符:83QG 验证码测试字符:83QG 错误个数:0 验证码实际字符:9992 验证码测试字符:9992 错误个数:0 验证码实际字符:A7Y7 验证码测试字符:A7Y7 错误个数:0 验证码实际字符:D993 验证码测试字符:D993 错误个数:0 验证码实际字符:F549 验证码测试字符:F549 错误个数:0 验证码实际字符:FMC6 验证码测试字符:FMLF 错误个数:2 验证码实际字符:R4N4 验证码测试字符:R4N4 错误个数:0 -----结果统计如下----- 测试验证码的字符数量:40 测试验证码的字符错误数量:3 单个字符识别正确率:92.50% 测试验证码图的数量:10 测试验证码图的错误数量:2 填对验证码的概率:80.00%增加测试集至20个,结果如下:
验证码实际字符:2B4E 验证码测试字符:2B4E 错误个数:0 验证码实际字符:4572 验证码测试字符:4572 错误个数:0 验证码实际字符:4Q33 验证码测试字符:4Q33 错误个数:0 验证码实际字符:52CY 验证码测试字符:52LY 错误个数:1 验证码实际字符:6S3C 验证码测试字符:6S3C 错误个数:0 验证码实际字符:7456 验证码测试字符:74F5 错误个数:2 验证码实际字符:775D 验证码测试字符:775D 错误个数:0 验证码实际字符:792Z 验证码测试字符:7PZZ 错误个数:2 验证码实际字符:83QG 验证码测试字符:83QG 错误个数:0 验证码实际字符:8JXB 验证码测试字符:BJXB 错误个数:1 验证码实际字符:9992 验证码测试字符:9992 错误个数:0 验证码实际字符:9LW7 验证码测试字符:9LW7 错误个数:0 验证码实际字符:A7Y7 验证码测试字符:A7Y7 错误个数:0 验证码实际字符:D993 验证码测试字符:D993 错误个数:0 验证码实际字符:F549 验证码测试字符:F549 错误个数:0 验证码实际字符:FMC6 验证码测试字符:FMLF 错误个数:2 验证码实际字符:KXCV 验证码测试字符:KXCV 错误个数:0 验证码实际字符:L9AC 验证码测试字符:E9AC 错误个数:1 验证码实际字符:R4N4 验证码测试字符:R4N4 错误个数:0 验证码实际字符:Z89V 验证码测试字符:Z89V 错误个数:0 -----结果统计如下----- 测试验证码的字符数量:80 测试验证码的字符错误数量:9 单个字符识别正确率:88.75% 测试验证码图的数量:20 测试验证码图的错误数量:6 填对验证码的概率:70.00%结果还算是比较稳定的,再次增加模板数量:
验证码实际字符:2B4E 验证码测试字符:2B4E 错误个数:0 验证码实际字符:4572 验证码测试字符:4572 错误个数:0 验证码实际字符:4Q33 验证码测试字符:4Q33 错误个数:0 验证码实际字符:52CY 验证码测试字符:52LY 错误个数:1 验证码实际字符:6S3C 验证码测试字符:6S3C 错误个数:0 验证码实际字符:7456 验证码测试字符:74F5 错误个数:2 验证码实际字符:775D 验证码测试字符:775D 错误个数:0 验证码实际字符:792Z 验证码测试字符:792Z 错误个数:0 验证码实际字符:83QG 验证码测试字符:83QG 错误个数:0 验证码实际字符:8JXB 验证码测试字符:8JXB 错误个数:0 验证码实际字符:9992 验证码测试字符:9992 错误个数:0 验证码实际字符:9LW7 验证码测试字符:9LW7 错误个数:0 验证码实际字符:A7Y7 验证码测试字符:A7Y7 错误个数:0 验证码实际字符:D993 验证码测试字符:D993 错误个数:0 验证码实际字符:F549 验证码测试字符:F549 错误个数:0 验证码实际字符:FMC6 验证码测试字符:FMLF 错误个数:2 验证码实际字符:KXCV 验证码测试字符:KXCV 错误个数:0 验证码实际字符:L9AC 验证码测试字符:E9AC 错误个数:1 验证码实际字符:R4N4 验证码测试字符:R4N4 错误个数:0 验证码实际字符:Z89V 验证码测试字符:Z89V 错误个数:0 -----结果统计如下----- 测试验证码的字符数量:80 测试验证码的字符错误数量:6 单个字符识别正确率:92.50% 测试验证码图的数量:20 测试验证码图的错误数量:4 填对验证码的概率:80.00%正确率有提升,但是也不是很明显。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/38066.html
摘要:在本次课程中,着重讲解的是传统的机器学习技术及各种算法。回归对连续型数据进行预测趋势预测等除了分类之外,数据挖掘技术和机器学习技术还有一个非常经典的场景回归。 摘要: 什么是数据挖掘?什么是机器学习?又如何进行Python数据预处理?本文将带领大家一同了解数据挖掘和机器学习技术,通过淘宝商品案例进行数据预处理实战,通过鸢尾花案例介绍各种分类算法。 课程主讲简介:韦玮,企业家,资深IT领...
阅读 2242·2021-10-09 09:41
阅读 3341·2021-09-13 10:34
阅读 1896·2019-08-30 12:59
阅读 535·2019-08-29 17:27
阅读 1034·2019-08-29 16:07
阅读 2937·2019-08-29 13:15
阅读 1286·2019-08-29 13:14
阅读 1519·2019-08-26 12:18