美文网首页
仓库:利用kNN识别简单图片验证码

仓库:利用kNN识别简单图片验证码

作者: ylylhl | 来源:发表于2019-05-21 21:16 被阅读0次

步了学长的老路,准备搞一下旧版正方的图片验证码(。
当然这篇重点是堆一下KNN,真要想做识别的话搞这个基础算法没前途的而且也没准确率,卷积神经网络使我快乐(……
附学长文章链接:实战“正方教务管理系统”验证码识别

整体思路是手动标记内容->二值化->分割图片并按照图像内容分类->测试数据这样子。

用了真实数据集甚至图像还没去噪,领会精神就好(。

前期准备

随便挑一个旧版正方教务系统,拿到生成验证码图像的网页。这里随便找了个没做频繁访问处理的学校的页面:

121.22.25.47/CheckCode.aspx

抓包写headers的过程不再赘述。构造爬虫脚本:

#coding = utf-8
'''
●getPIC v1.0

作者:Yemu
创建日期:19.5.21
最近修改时间:19.5.21
程序目的:爬取教务系统验证码图片
程序备注:
        1.用于爬取KNN算法中所需的原始数据集
        2.因为可复用性为零所以直接莽了,没封装函数啥的,问题不大
关于作者栏的署名:
        复用性极低的辣鸡程序=Yemu
        带有探索和尝试性质=Kadoya
        不知道写哪个id好的时候=ylylhl
'''

import requests
import os

path='./origin/'
try:
    os.makedirs(path)
except:
    print("dir has been exist.")
    
url="http://121.22.25.47/CheckCode.aspx"
cookie="ASP.NET_SessionId=sgoze555qu3hqef42kchnm55"
for n in range(0,100):
    try:
        headers={
            "Host": "121.22.25.47",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Cache-Control": "max-age=0",
            "Proxy-Connection": "keep-alive",
            "Cookie": cookie,
            "Referer": "http://121.22.25.47/",
            "Upgrade-Insecure-Requests": "1",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"
            }
        response = requests.get(url,headers=headers)
        filename=path+str(n)+'.gif'
        with open(filename,'wb') as f:
            f.write(response.content)
    except Exception as e:
        print('error:',e)
        continue
print('done')

如下图,然后就到了令人去世的手动改名时间(。

二值化&分割图片脚本如下,分割区域直接搬了学长的数据(靠)
可以用opencv或者别的什么降噪,也可以做旋转处理,然懒(
或者直接指定rgb手动二值化,同样可以达到降噪的效果。

#coding = utf-8
'''
●splitPIC v1.0

作者:Yemu
创建日期:19.5.21
最近修改时间:19.5.21
程序目的:二值化+分割图片为单个字符+获取特征值并写入文件(=分类好的数据集
程序备注:
        1.get_feature函数是抄来的(安详
          参考链接:https://www.cnblogs.com/beer/p/5672678.html
        2.懒得做去噪处理了,做了的话KNN正确率会更高一点
'''

import os
from PIL import Image

def get_feature(img):   #获取指定图片特征值,即数每行每列黑色像素点
    width, height = img.size
    pixel_cnt_list = []
    for y in range(height):
        pix_cnt_x = 0
        for x in range(width):
            if img.getpixel((x, y)) == 0:  #黑色点
                pix_cnt_x += 1
        pixel_cnt_list.append(pix_cnt_x)
    for x in range(width):
        pix_cnt_y = 0
        for y in range(height):
            if img.getpixel((x, y)) == 0:  #黑色点
                pix_cnt_y += 1
        pixel_cnt_list.append(pix_cnt_y)
    return pixel_cnt_list

if __name__ == '__main__':
    path='./origin/'
    filelist=[path+i for i in os.listdir(path)]
    d={}
    for i in filelist:
        img=Image.open(i)
        img=img.convert('L').convert('1')
        boxs = [(5, 1, 17, 21), (17, 1, 29, 21), (29, 1, 41, 21), (41, 1, 53, 21)]
        for x in range(len(boxs)):
            roi = img.crop(boxs[x])
            d.setdefault(i[len(path):][x], [])
            d[i[len(path):][x]].append(roi)

    f=open('data.txt','a')
    for i in d.keys():
        #new_path='./data2/'+i+'/'
        #os.makedirs(new_path)
        for j in range(len(d[i])):
            #filename=new_path+str(j)+'.png'
            #d[i][j].save(filename)
            s=i+' '+' '.join(str(i) for i in get_feature(d[i][j]))+'\n'
            #格式:对应字符+图像特征值
            f.write(s)

这时生成的数据集如下图,前期准备已经完成。

kNN算法

简单介绍

kNN算法的核心思想是如果一个样本在特征空间中的k个最相邻的样本中的大多数属于某一个类别,则该样本也属于这个类别。不得不说,百科上这张图挺好的,借用一下:

kNN实现起来也很简单 比k-means还要简单,只需计算测试向量距每个点的欧氏距离即可。相对的缺点也很明显,(因为每个点都要算一遍所以)数据集大的时候算起来真的很慢(。
虽然很相似,但对于k-means和kNN的区别说明,推荐阅读这篇文章:Kmeans算法与KNN算法的区别

python实现

#coding = utf-8
'''
●kNN v1.0

作者:Kadoya
创建日期:19.5.21
最近修改时间:19.5.21
程序目的:k-最近邻
主要算法说明:
        1.分别计算测试数据距离每个已分类数据的欧几里得距离
        2.选出k个距离最小的对象,这些对象中出现次数最多的类即为该测试数据的所属类
程序备注:
        1.我爱摸鱼摸鱼爱我(?
        2.魔改了一下,建议的kNN输入数据包括如下内容:
              训练集D、测试对象z(属性值构成的向量)、类别标签集合L、数量k
              也就是knn(D,z,L,k):return c,c属于L。
          不过我这里的kNN是knn(D,z,k),L已经包含在D里面了,准确地说是D[i][0]。
更新历史:
        5.21 v1.0
            ①基本功能完成,小bug不计其数,智障操作不计其数
Attribute Information:
   1. 类别标签
   2-33. 属性值
'''
from splitPIC import get_feature
from PIL import Image
from collections import Counter

#import os

def stardust(data):#基本处理  参数:原始数据
    lost_star=[]
    for i in data:
        lost_star.append(i.strip('\n').split(' '))
    lost_star=[[i[0]]+list(map(float,i[1:])) for i in lost_star[:]]#str转为float
    return lost_star

def knn(test_data,train_data,k):#参数:测试数据,已分类好的数据集,k值
    d={}
    for s in range(len(train_data)):#遍历数据集
        t=sum([pow(test_data[i]-train_data[s][i+1],2) for i in range(len(train_data[s])-1)])#计算欧几里得距离
        d[s]=t  #key:value=图像所在行数:测试图距该图距离
    s=sorted(d.items(),key = lambda x:x[1])[:k]#从小到大排序
    last=[train_data[i[0]][0] for i in s]#挑选前k个最近的点
    p=Counter(last).most_common(1)#出现最多次的类即为测试数据的归属类
    return p[0][0]
        
if __name__ == '__main__':   
    path = r'data.txt'
    with open(path) as f:
        data = f.readlines()
    dataSet=stardust(data)

    img=Image.open('testnew.gif')
    result=''
    img=img.convert('L').convert('1')
    boxs = [(5, 1, 17, 21), (17, 1, 29, 21), (29, 1, 41, 21), (41, 1, 53, 21)]
    for x in range(len(boxs)):
        roi = img.crop(boxs[x])
        result+=knn(get_feature(roi),dataSet,10)
    print(result)

##    #测试正确率
##    path='./origin/'
##    filelist=[path+i for i in os.listdir(path)]
##    error=0
##    for i in filelist:
##        img=Image.open(i)
##        result=''
##        img=img.convert('L').convert('1')
##        boxs = [(5, 1, 17, 21), (17, 1, 29, 21), (29, 1, 41, 21), (41, 1, 53, 21)]
##        for x in range(len(boxs)):
##            roi = img.crop(boxs[x])
##            result+=knn(get_feature(roi),dataSet,10)
##        print('========')
##        print(result)
##        if result!=i[len(path):-4]:
##            error+=1
##            print('wrong, correct answer is',i[len(path):-4])
##        print('========')
##    print(error/len(filelist))

正确率在50左右,不出意料的低(。手动粗糙地指定rgb二值化了一下升到了60%,将训练集增加到200个后正确率提升至80%,但是kNN的话80+应该是极限了,也没有继续下去的必要。
姑且就这样了,没什么特殊需要注意的点……窃以为书上的十个算法都比较适合新手入门练习。虽说重复造轮子没有意义,造古老的轮子更没有意义,但个人时间充裕的话这种行为对造轮子的个体还是有所裨益的。写过kmeans的话就不用写这个了根本什么都没有学到

相关文章

网友评论

      本文标题:仓库:利用kNN识别简单图片验证码

      本文链接:https://www.haomeiwen.com/subject/tkqjzqtx.html