作者 | 李秋键

编辑 | Carol

题图 | 视觉中国

出品| CSDN(ID:CSDNnews)

验证码使我们生活中最为常见的防治爬虫和机器人登录攻击的手段,一般的验证码主要由数字和字母组成,故我们可以设想:我们是否可以根据文本识别训练模型进行识别验证码呢?当然可以,今天我们就将利用KNN实现验证码的识别。

关于KNN基本常识如下:

KNN算法我们主要要考虑三个重要的要素,对于固定的训练集,只要这三点确定了,算法的预测方式也就决定了。这三个最终的要素是k值的选取,距离度量的方式和分类决策规则。

对于k值的选择,没有一个固定的经验,一般根据样本的分布,选择一个较小的值,可以通过交叉验证选择一个合适的k值。

选择较小的k值,就相当于用较小的领域中的训练实例进行预测,训练误差会减小,只有与输入实例较近或相似的训练实例才会对预测结果起作用,与此同时带来的问题是泛化误差会增大,换句话说,K值的减小就意味着整体模型变得复杂,容易发生过拟合;

选择较大的k值,就相当于用较大领域中的训练实例进行预测,其优点是可以减少泛化误差,但缺点是训练误差会增大。这时候,与输入实例较远(不相似的)训练实例也会对预测器作用,使预测发生错误,且K值的增大就意味着整体的模型变得简单。

一个极端是k等于样本数m,则完全没有分类,此时无论输入实例是什么,都只是简单的预测它属于在训练实例中最多的类,模型过于简单。

效果图如下:

实验前的准备

首先我们使用的python版本是3.6.5所用到的库有cv2库用来图像处理;

Numpy库用来矩阵运算;

训练的数据集如下所示:

 

训练模型的搭建

1、获取切割字符轮廓:

我们定义ws和valid_contours数组,用来存放图片宽度和训练数据集中的图片。如果分割错误的话需要重新分割。主要根据字符数量判断是否切割错误,如果切割出有4个字符。说明没啥问题:

代码如下:

#定义函数get_rect_box,目的在于获得切割图片字符位置和宽度
def get_rect_box(contours):print("获取字符轮廓。。。")#定义ws和valid_contours数组,用来存放图片宽度和训练数据集中的图片。如果分割错误的话需要重新分割ws = []valid_contours = []for contour in contours:#画矩形用来框住单个字符,x,y,w,h四个参数分别是该框子的x,y坐标和长宽。因x, y, w, h = cv2.boundingRect(contour)if w < 7:continuevalid_contours.append(contour)ws.append(w)
#w_min是二值化白色区域最小宽度,目的用来分割。w_min = min(ws)
# w_max是最大宽度w_max = max(ws)result = []#如果切割出有4个字符。说明没啥问题if len(valid_contours) == 4:for contour in valid_contours:x, y, w, h = cv2.boundingRect(contour)box = np.int0([[x,y], [x+w,y], [x+w,y+h], [x,y+h]])result.append(box)# 如果切割出有3个字符。参照文章,中间分割elif len(valid_contours) == 3:for contour in valid_contours:x, y, w, h = cv2.boundingRect(contour)if w == w_max:box_left = np.int0([[x,y], [x+w/2,y], [x+w/2,y+h], [x,y+h]])box_right = np.int0([[x+w/2,y], [x+w,y], [x+w,y+h], [x+w/2,y+h]])result.append(box_left)result.append(box_right)else:box = np.int0([[x,y], [x+w,y], [x+w,y+h], [x,y+h]])result.append(box)# 如果切割出有3个字符。参照文章,将包含了3个字符的轮廓在水平方向上三等分elif len(valid_contours) == 2:for contour in valid_contours:x, y, w, h = cv2.boundingRect(contour)if w == w_max and w_max >= w_min * 2:box_left = np.int0([[x,y], [x+w/3,y], [x+w/3,y+h], [x,y+h]])box_mid = np.int0([[x+w/3,y], [x+w*2/3,y], [x+w*2/3,y+h], [x+w/3,y+h]])box_right = np.int0([[x+w*2/3,y], [x+w,y], [x+w,y+h], [x+w*2/3,y+h]])result.append(box_left)result.append(box_mid)result.append(box_right)elif w_max < w_min * 2:box_left = np.int0([[x,y], [x+w/2,y], [x+w/2,y+h], [x,y+h]])box_right = np.int0([[x+w/2,y], [x+w,y], [x+w,y+h], [x+w/2,y+h]])result.append(box_left)result.append(box_right)else:box = np.int0([[x,y], [x+w,y], [x+w,y+h], [x,y+h]])result.append(box)# 如果切割出有3个字符。参照文章,对轮廓在水平方向上做4等分elif len(valid_contours) == 1:contour = valid_contours[0]x, y, w, h = cv2.boundingRect(contour)box0 = np.int0([[x,y], [x+w/4,y], [x+w/4,y+h], [x,y+h]])box1 = np.int0([[x+w/4,y], [x+w*2/4,y], [x+w*2/4,y+h], [x+w/4,y+h]])box2 = np.int0([[x+w*2/4,y], [x+w*3/4,y], [x+w*3/4,y+h], [x+w*2/4,y+h]])box3 = np.int0([[x+w*3/4,y], [x+w,y], [x+w,y+h], [x+w*3/4,y+h]])result.extend([box0, box1, box2, box3])elif len(valid_contours) > 4:for contour in valid_contours:x, y, w, h = cv2.boundingRect(contour)box = np.int0([[x,y], [x+w,y], [x+w,y+h], [x,y+h]])result.append(box)result = sorted(result, key=lambda x: x[0][0])return result

2、数据集图像处理:

在读取数据集后,我们需要对图片数据集进行二值化和降噪处理,以获得更为合适的训练数据。

其中代码如下:

def process_im(im):rows, cols, ch = im.shape#转为灰度图im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)#二值化,就是黑白图。字符变成白色的,背景为黑色ret, im_inv = cv2.threshold(im_gray,127,255,cv2.THRESH_BINARY_INV)#应用高斯模糊对图片进行降噪。高斯模糊的本质是用高斯核和图像做卷积。就是去除一些斑斑点点的。因为二值化难免不够完美,去燥使得二值化结果更好kernel = 1/16*np.array([[1,2,1], [2,4,2], [1,2,1]])im_blur = cv2.filter2D(im_inv,-1,kernel)#再进行一次二值化。ret, im_res = cv2.threshold(im_blur,127,255,cv2.THRESH_BINARY)return im_res

3、切割字符:

在得到字符位置后,我们对图片进行切割和保存

部分代码如下:

#借助第一个函数获得待切割位置和长宽后就可以切割了
def split_code(filepath):#获取图片名filename = filepath.split("/")[-1]#图片名即为标签filename_ts = filename.split(".")[0]im = cv2.imread(filepath)im_res = process_im(im)im2, contours, hierarchy = cv2.findContours(im_res, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#这里就是用的第一个函数,获得待切割位置和长宽boxes = get_rect_box(contours)
#如果没有区分出四个字符,就不切割这个图片if len(boxes) != 4:print(filepath)
# 如果区分出了四个字符,说明切割正确,就可以切割这个图片。将切割后的图片保存在char文件夹下for box in boxes:cv2.drawContours(im, [box], 0, (0,0,255),2)roi = im_res[box[0][1]:box[3][1], box[0][0]:box[1][0]]roistd = cv2.resize(roi, (30, 30))timestamp = int(time.time() * 1e6)filename = "{}.jpg".format(timestamp)filepath = os.path.join("char", filename)cv2.imwrite(filepath, roistd)#cv2.imshow("image", im)#cv2.waitKey(0)#cv2.destroyAllWindows()
# split all captacha codes in training set
#调用上面的split_code进行切割即可。
def split_all():files = os.listdir(TRAIN_DIR)for filename in files:filename_ts = filename.split(".")[0]patt = "label/{}_*".format(filename_ts)saved_chars = glob.glob(patt)if len(saved_chars) == 4:print("{} done".format(filepath))continuefilepath = os.path.join(TRAIN_DIR, filename)split_code(filepath)

4、标注字符:

通过已经标注好的数据集字符读取标签,然后存储标签,以方便和图片达到对应。字符数据集如下:

代码如下:

#用来标注单个字符图片,在label文件夹下,很明显可以看到_后面的就是标签。比如图片里是数字6,_后面就是6def label_data():files = os.listdir("char")for filename in files:filename_ts = filename.split(".")[0]patt = "label/{}_*".format(filename_ts)saved_num = len(glob.glob(patt))if saved_num == 1:print("{} done".format(patt))continuefilepath = os.path.join("char", filename)im = cv2.imread(filepath)cv2.imshow("image", im)key = cv2.waitKey(0)if key == 27:sys.exit()if key == 13:continuechar = chr(key)filename_ts = filename.split(".")[0]outfile = "{}_{}.jpg".format(filename_ts, char)outpath = os.path.join("label", outfile)cv2.imwrite(outpath, im)#和标注字符图反过来,我们需要让电脑知道这个字符叫啥名字,即让电脑知道_后面的就是他字符的名字def analyze_label():print("识别数据标签中。。。")files = os.listdir("label")label_count = {}for filename in files:label = filename.split(".")[0].split("_")[1]label_count.setdefault(label, 0)label_count[label] += 1print(label_count)

5、KNN模型训练:

KNN算法我们直接使用OpenCV自带的KNN函数即可。通过读取数据集和标签,加载模型训练即可。代码如下:

#训练模型,用的是k相邻算法def get_code(im):#将读取图片和标签print("读取数据集和标签中。。。。")[samples, label_ids, id_label_map] = load_data()#k相邻算法print("初始化中...")model = cv2.ml.KNearest_create()#开始训练print("训练模型中,请等待!")model.train(samples, cv2.ml.ROW_SAMPLE, label_ids)#处理图片。即二值化和降噪im_res = process_im(im)#提取轮廓im2, contours, hierarchy = cv2.findContours(im_res, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)#获取各切割区域位置和长宽boxes = get_rect_box(contours)#判断有没有识别出4个字符,如果没有识别出来,就不往下运行,直接结束了if len(boxes) != 4:print("cannot get code")result = []#如果正确分割出了4个字符,下面调用训练好的模型进行识别。for box in boxes:#获取字符长宽roi = im_res[box[0][1]:box[3][1], box[0][0]:box[1][0]]#重新设长宽。roistd = cv2.resize(roi, (30, 30))#将图片转成像素矩阵sample = roistd.reshape((1, 900)).astype(np.float32)#调用训练好的模型识别ret, results, neighbours, distances = model.findNearest(sample, k = 3)#获取对应标签idlabel_id = int(results[0,0])#根据id得到识别出的结果label = id_label_map[label_id]#存放识别结果result.append(label)return result

模型调用

if __name__ == "__main__":file=os.listdir("test")filepath="test/"+file[4]im = cv2.imread(filepath)preds = get_code(im)preds="识别结果为:"+preds[0]+preds[1]+preds[2]+preds[3]print(preds)canny0 = imimg_PIL = Image.fromarray(cv2.cvtColor(canny0, cv2.COLOR_BGR2RGB))myfont = ImageFont.truetype(r'simfang.ttf', 18)draw = ImageDraw.Draw(img_PIL)draw.text((20, 5), str(preds), font=myfont, fill=(255, 23, 140))img_OpenCV = cv2.cvtColor(np.asarray(img_PIL), cv2.COLOR_RGB2BGR)cv2.imshow("frame", img_OpenCV)key = cv2.waitKey(0)print(filepath)

到这里,我们整体的程序就搭建完成,下面为我们程序的运行结果:

源码地址:

链接:https://pan.baidu.com/s/1Ir5QNjUZaeTW26T8Gb3txQ

提取码:9eqa

作者简介:

李秋键,CSDN博客专家,CSDN达人课作者。硕士在读于中国矿业大学,开发有taptap竞赛获奖等等。

【END】

6月3日20:00,CSDN 创始人&董事长、极客帮创投创始合伙人蒋涛携手全球顶级开源基金会主席、董事,聚焦中国开源现状,直面开发者在开源技术、商业上的难题,你绝不可错过的开源巅峰对谈!立即免费围观

更多精彩推荐☞AI 看脸算命,3 万张自拍揭露:颜值即命?
☞5 月编程语言排行榜:C 重回第一,今年编程语言名人堂冠军还会是它吗?| 原力计划
☞芯片供应被掐断,华为能否安全渡劫?
☞来了来了!趋势预测算法大PK
☞附代码 | OpenCV实现银行卡号识别,字符识别算法你知多少?
☞15 岁黑进系统,发挑衅邮件意外获 Offer,不惑之年捐出全部财产,Twitter CEO 太牛了
你点的每个“在看”,我都认真当成了喜欢

我佛了!用 KNN 实现验证码识别,又 Get 到一招相关推荐

  1. 我佛了!用KNN实现验证码识别,又 Get 到一招!

    作者| 李秋键 责编| Carol 出品| AI科技大本营(ID:rgznai100) 头图 | CSDN付费下载自视觉中国 验证码使我们生活中最为常见的防治爬虫和机器人登录攻击的手段,一般的验证码主 ...

  2. 基于Python的KNN数字验证码识别

    一.主要内容 本项目基于Python爬虫爬取验证码图片,对图片进行去噪.分割,通过KNN算法训练模型,实现验证其准确率. 二.系统流程 首先从指定的网页中爬取验证码图片数据,然后对数据进行一个去噪和分 ...

  3. 基于Python的验证码识别技术

    基于Python的验证码识别技术 作者:强哥 概述 前言 准备工作 识别原理 图像处理 切割图像 人工标注 训练数据 检测结果 搞笑一刻 福利一刻 推荐阅读 前言 很多网站登录都需要输入验证码,如果要 ...

  4. 验证码识别的原理python_Python验证码识别处理实例

    一.准备工作与代码实例 1.PIL.pytesser.tesseract (1)安装PIL:下载地址:http://www.pythonware.com/products/pil/(CSDN下载) 下 ...

  5. cnn验证码识别代码_中文项目:快速识别验证码,CNN也能为爬虫保驾护航

    原标题:中文项目:快速识别验证码,CNN也能为爬虫保驾护航 机器之心专栏 作者:Nick Li 随着卷积网络的推广,现在有各种各样的快捷应用,例如识别验证码和数学公式等.本文介绍了一个便捷的验证码识别 ...

  6. 验证码识别论文总结---外文篇

    文章目录 <A Survey on Breaking Technique of Text-Based CAPTCHA> <Selective Learning Confusion C ...

  7. 国税局发票查验中英文验证码识别最新版,识别率99.9%

    采用深度学习进行发票查验验证码模型的训练,在我电脑上模型训练的环境如下: 显卡:RTX 2080TI tensorflow-gpu:2.5.3 1 训练集和测试集的准备 发票查验的验证码分为4种类型, ...

  8. 基于Python的简单验证码识别

    原文链接:有图有真相 摘要:验证码在网络安全方面发挥着关键作用,验证码的主要目的是区分人类和计算机,用来防止自动化脚本对网站的一些恶意行为.目前绝大多数网站都利用验证码来阻止恶意脚本程序的入侵.验证码 ...

  9. python验证码识别算法_Python网站验证码识别

    0x00 识别涉及技术 验证码识别涉及很多方面的内容.入手难度大,但是入手后,可拓展性又非常广泛,可玩性极强,成就感也很足. 验证码图像处理 验证码图像识别技术主要是操作图片内的像素点,通过对图片的像 ...

最新文章

  1. 什么是SAAS 即软件即服务模式
  2. 报名 | 搜狐×清华:第三届内容识别算法大赛,比武招新两不误!
  3. if else流程判断
  4. 4G EPS 中建立 UE 与 eNB 之间的 RRC 连接
  5. 一周一论文(翻译 总结)—— [NSDI 17] TUX2: Distributed Graph Computation for Machine Learning 面向机器学习的分布式图处理系统
  6. Android接入支付宝支付实现
  7. 机器学习流行算法一览
  8. hadoop配置文件的加载机制
  9. spring学习(46):spring的单例bean
  10. python多线程单核_002_Python多线程相当于单核多线程的论证
  11. 下个软件包可能泄露信用卡信息,Python包存储库PyPI又爆恶意代码,下载达3万次,你中招了吗?...
  12. python shell运行当前程序、可以按下_Python下调用Linux的Shell命令的方法
  13. 野火stm32呼吸灯程序_说一说STM32启动过程
  14. android 屏幕管理软件,Android Screencast下载
  15. C语言储蓄系统编程,C语言:银行储蓄系统的开发(初级)-Go语言中文社区
  16. 多媒体计算机技术英文,掌握多媒体计算机技术中各专业语句的英文表达方式.doc...
  17. java期末大作业:记事本
  18. DirectX11学习笔记01
  19. 银联在线java接口开发_银联在线Java接口开发
  20. java序号带圈_疯狂创客圈 -- Java 高并发社群

热门文章

  1. 关于作业提交要求声明 2017-09-22
  2. location对象相关
  3. 临时上传的文件-20170707
  4. Android 中的冷启动和热启动
  5. Android 使用Webview时,有时抛 java.lang.Throwable: EventHub.removeMessages(int what = 107) warning...
  6. 结构体,文件操作,指针,简单练习
  7. Oracle数据空间的管理
  8. win8 无法打开任务管理器
  9. 生成对抗网络GAN损失函数loss的简单理解
  10. 将Matting结果转化为二分割结果