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

步了学长的老路,准备搞一下旧版正方的图片验证码(。
当然这篇重点是堆一下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的话就不用写这个了根本什么都没有学到

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容