文本验证码识别项目的总结
毕业论文是要做这个方面的,最近把之前做的一些工作总结一下,这里只说具体思路,展示部分代码,为后面想要做文本验证码识别之类的朋友,提供思路和要检索的词条内容。如有错误,还请各位大佬们批评指正。
索引词条
如果想要快一点开始的,有关这个方向的一些关键词可以直接去搜索了解
- 灰度化
- 二值化
- 全局阈值
- OSTU阈值
- 竖直投影切割
- 连通域算法/CFS算法(这俩是一个算法)
- 滴水算法
- 聚类算法
- 维诺图骨架形态分析
验证码识别的步骤
- 数据获取:自己爬虫爬取并生成验证码数据集;自己去找网上找现成数据集;自己生成验证码
- 预处理:灰度化、二值化、去除噪声、切除周围白色边框
- 将验证码进行切割,切割方法主要有竖直投影算法、连通域算法(CFS算法),滴水算法、SOM算法
- 生成数据集和验证集,并将其放入神经网络中识别
数据获取
- 可以使用selenium来模拟浏览器把验证码的位置进行截图保存(这里有一点栽坑的是,因为是模拟浏览器,有的网站名一样,但是要点击某些东西,才能触发验证码的出现。之前就没有点击,以为可能有会验证码,老是没有成功,是去请教了一位网友才知道的,耽搁了我很久时间)。后来发现只要一个验证码真的很简单,直接发送请求就可以了(但是之前花了我巨多时间,(lll¬ω¬))
- 代码中的获取验证码的url,其实只要点击验证码右击检查,就能看见src后面等于的那一堆就是了
下述代码是怎么获取验证码的
def code_get(pic_num,scr,path):
i = 0
while i < pic_num:
#这里的headers需要根据自己的浏览器进行修改的,我用的是谷歌浏览器。
picture = rq.get(url=scr+str(i), headers= {'user-agent':'Chrome/10'} )
with open(path+'code'+str(i)+'.jpg','wb') as f:
f.write(picture.content)
print("图片"+str(i)+"爬取完成")
i+=1
time.sleep(random.random() * 3)
# 这个是千图网的验证码的获取的php代码
url = 'https://www.58pic.com/index.php?m=userinfo&a=getImgCaptch&v= '
n = eval(input("请输入要爬取的图片数量:"))
预处理
*灰度化
原理:L=R0.299 +G0.587 +B*0.114
使用:使用python库的PIL的convert函数即可
from PIL import Image
src = ''
img = Image.open(src)
grey = img.convert('L')
grey.save('1.bmp')
*二值化
白话解说:就是设置一个阈值,如果大于该阈值的就设置为白色,小于该阈值的就设置为黑色,将图片变为非黑即白的样子。
- 全局阈值法:就是设置一个阈值,所有图像都使用这一个阈值
- ostu阈值:一位日本学者研究出来的,大致意思对于每张图片通过计算算出合适的阈值。(已经有实现的库了),想要使用的自行检索 skimage库中的filters.threshold_otsu
- 注意!! 此处有坑,记得图片最后保存为bmp格式,像我之前保存为jpg格式,导致之后实验过程中间出现介于黑色和白色之间的像素值。(原因:jpeg是有损的,bmp是无损的)
*去除噪声
这里我使用的方法,思路是通过检查每一个黑色像素的点,若该点四周的↑,↓,←,→,↖,↗,↙,↘八个点的像素值,如果一共有大于等于5个像素值为白色,则认为该点为噪声点,将其像素值变为白色。
切割
这就是八仙过海,各现神通了,神仙打架的地方。
*竖直投影切割
通过计算每一列所含的像素值之和,称其为竖直投影,然后选取其中值为0的地方进行切割,适用于简单的没有粘结的验证码字符
*连通域切割/CFS切割
通过判断某个像素是不是黑色且被访问过,如果没有被访问,就压入栈中,然后访问该点四周的点,并选取没有被访问的压入栈中。适合与字符之间没有粘结,但是有部分扭曲,导致竖直投影不太方便。
*滴水算法切割
模拟水滴掉落的,来进行切割。这里主要适用于字符之间有粘结的情况。感觉这个的难点在于选取对的起始点,至于每一个点的下一个点在哪里,公式都很清晰。选取起始点,我看的论文中,说选取投影过程中的极小值点比较好,我的实验结果也是这样。
生成数据集和测试集,以及识别验证码
我是通过生成tfrecord数据集格式,但是之后可能不支持了,所以不要学我。
我这里也栽跟头了,之前一直不知道怎么生成数据集和测试集,并且很傻的一件事情在于,我用的LeNet网络只能识别9个类别,后来发现只要修改最后的节点数目为我所需要的就可以了。
我是直接使用LeNet网络,比较简单的,所以就没什么特别说的。