验证码识别之数据处理
当我们想要自己通过深度学习网络训练识别网上的各种验证码时,首先需要下载一定量的图片,而且还需要给这些验证码图片标注好正确的答案。所以,一开始我们要借助一些打码平台来帮助我们完成这部分工作,当然如果不嫌麻烦的话自己一张张图标注也是可以的……
当我们完成标注工作后,还需要对这些验证码图片进行处理,使之适合输入到我们的网络中。
对图像处理时我们需要用到OpenCV-python
这个库,先使用pip install OpenCV-python
安装一下。
在windows系统中,若出现导入cv2
模块后,使用pycharm
调用cv2
的方法全部标黄,并且不提示方法名的情况,可以进入cv2
的__init__.py
中,注释全部代码,然后在最后加上以下代码:
1 | import sys |
之后重启Pycharm
应该可以解决。
图像模糊(滤波)
模糊操作时图像处理中最简单和常用的操作之一,主要是为了给图像预处理时降低噪声。
模糊的过程与卷积神经网络的原理类似,我们都知道图像其实是由一个个的像素点构成,我们可以选取特定大小的一个模板,然后扫描整幅图像,每一次扫描对整个模板内的所有像素点的值进行运算(不同的模糊操作采用的运算不一样),将得到的值赋值给中间的像素点。
主要有以下几种常见的模糊操作:
均值模糊
均值滤波是典型的线性滤波算法,它是指在图像上对目标像素给一个模板,该模板包括了其周围的临近像素(如果模板大小为3*3,则是目标像素为中心的周围8个像素,构成一个滤波模板),再用模板中的全体像素的平均值来代替原来像素值。
1 | cv2.blur(src, ksize, dst=None, anchor=None, borderType=None) |
中值模糊
中值滤波与均值滤波的区别仅在于,对模板内的所有像素值进行排序,取中值来代替中心点的值。
1 | cv2.medianBlur(src, ksize, dst=None) |
高斯模糊
而高斯滤波就是计算加权平均值。
1 | cv2.GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None) |
针对本次处理的验证码图片:
我们可以使用中值滤波,图中的噪声叫做椒盐噪声,是幅值近似相等但是随机的分布在不同位置,图中既有污染的点,也有干净的点。
模糊处理不一定是必须的,具体的实践过程中,需要针对图片的具体情况而定,若需去噪,选择采用哪种滤波方式也要视情况而定,不好选择的情况下可以逐个尝试。
图像灰度化
1 | import cv2 |
灰度化处理也不是必须的,但是针对大部分验证码图片,颜色对其识别几乎没有影响,可以考虑将其灰度化后再训练,这样可以减少数据处理量(由三通道转变为单通道)。若灰度化后背景会影响到验证码字符就不要灰度化。
cv2
常用操作
这里插入一些cv2
的基本操作,方便我们对图片的处理,以及在处理过程中观察效果。
读取图片
1
2
3import cv2
img = cv2.imread('1.jpg',cv2.IMREAD_GRAYSCALE)参数:
filepath
:要读入图片的完整路径;flags
:读入图片的标志,flags的可选值:cv2.IMREAD_COLOR
:默认参数,读入一副彩色图片,忽略alpha通道;cv2.IMREAD_GRAYSCALE
:读入灰度图片;cv2.IMREAD_UNCHANGED
:顾名思义,读入完整图片,包括alpha通道。这里的图片路径一般不能包含中文字符,否则有可能读取失败
显示图片
1
2
3
4
5
6
7
8
9
10
11import cv2
# 这里定义一个窗口,后面可以拖动窗口,否则无法拖动
cv2.namedWindow('window_name', cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO)
cv2.imshow('window_name',img)
# 等待键盘输入,单位为毫秒;参数为0表示无限等待;不调用waitKey的话,窗口会一闪而逝,看不到显示的图片
cv2.waitKey(0)
# 删除指定窗口
cv2.destroyWindow('window_name')
# 销毁所有窗口
cv2.destroyAllWindows()保存图片
1
2cv2.imwrite('1.png', img, [int(cv2.IMWRITE_JPEG_QUALITY),95])
cv2.imwrite('1.png',img,[int(cv2.IMWRITE_PNG_COMPRESSION),9])第一个参数是要保存的文件名,第二个参数是要保存的图像的名字。
可选的第三个参数,它针对特定的格式:
对于
JPEG
,其表示的是图像的质量,用0 - 100的整数表示,默认95;对于
png
,第三个参数表示的是压缩级别,默认为3。注意:
cv2.IMWRITE_JPEG_QUALITY
类型为 long ,必须转换成 int;cv2.IMWRITE_PNG_COMPRESSION
, 从0到9 压缩级别越高图像越小。图片复制
1
imgcopy=img.copy()
颜色空间转换
1
2
3
4
5# RGB彩色图片转为灰度图片
img2 = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
# 灰度图片转为RGB彩色图片
img3 = cv2.cvtColor(img,cv2.COLOR_GRAY2RGB)模糊处理(上面已经有了)
二值化
二值化就是让图像的像素点矩阵中的每个像素点的灰度值为0(黑色)或者255(白色),也就是让整个图像呈现只有黑和白的效果。在灰度化的图像中灰度值的范围为0~255,在二值化后的图像中的灰度值范围是0或者255。
全局阈值
1
cv2.threshold(src, thresh, maxval, type, dst=None)
参数
src
,输入数组;thresh
,阈值;maxval
,最大值;type
,阈值类型。参数type有以下几种类型:
THRESH_BINARY
像素值大于阈值时,取Maxval
,否则取0THRESH_BINARY_INV
像素值大于阈值时,设置为0,否则取Maxval
THRESH_TRUNC
像素值大于阈值时,设置为阈值,否则不变THRESH_TOZERO
像素值大于阈值时,不变,否则取0THRESH_TOZERO_INV
像素值大于阈值时,设置为0,否则不改变返回值
threshold
函数有两个返回值,其中第二个返回值是二值化后的灰度图。当我们指定了阈值参数thresh
,第一个返回值ret
就是我们指定的thresh。换句话说,我们可以不指定阈值参数thresh。通常情况,我们一般不知道设定怎样的阈值thresh才能得到比较好的二值化效果,只能去试。如对于一幅双峰图像(理解为图像直方图中存在两个峰),我们指定的阈值应尽量在两个峰之间的峰谷。这时,就可以用第四个参数
THRESH_OTSU
,它对一幅双峰图像自动根据其直方图计算出合适的阈值(对于非双峰图,这种方法得到的结果可能不理想)。1
cv2.threshold(img, 0, 255, cv2.THRESH_OTSU)
此外还有另一种自适应阈值的参数:
1
cv2.threshold(img, 0, 255, cv2.THRESH_TRIANGLE)
以上自适应阈值都是将像素值大于阈值的取
Maxval
,否则取0。局部阈值
根据图片一小块区域的值来计算对应区域的阈值,从而得到也许更为合适的图片。
1
dst = cv2.adaptiveThreshold(src, maxval, thresh_type, type, Block Size, C)
返回值:
dst
: 二值化后的灰度图参数:
rc
: 输入图,只能输入单通道图像,通常来说为灰度图maxval
: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值thresh_type
: 阈值的计算方法,包含以下2种类型:(1)、
cv2.ADAPTIVE_THRESH_MEAN_C
(局部邻域块的平均值,该算法是先求出块中的均值,再减去常数C);(2)、
cv2.ADAPTIVE_THRESH_GAUSSIAN_C
(局部邻域块的高斯加权和。该算法是在区域中(x, y)周围的像素根据高斯函数按照他们离中心点的距离进行加权计算,再减去常数C。)type
:二值化操作的类型,与固定阈值函数相同,包含以下5种类型:cv2.THRESH_BINARY; cv2.THRESH_BINARY_INV; cv2.THRESH_TRUNC; cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV
Block Size
: 图片中分块的大小,一般选择为3、5、7……等C
:阈值计算方法中的常数项
Block Size和C经常分别选择11和2;实际应用中通过测试取较合适的值
以下是两种二值化结果展示:
全局阈值cv2.THRESH_OTSU
:
局部阈值cv2.ADAPTIVE_THRESH_MEAN_C
:
看上去区别不大。。。
保存数据
当我们对图片做完相应的处理后需要将图片数据保存为3D
张量(图像数据一般为3D
张量),保存时需要用到Python的numpy
库。
1 | import numpy as np |
我们对所有训练图片全部执行上述操作后,train_imgs
就变成了一个4D
张量,形状为(samples
, height
, width
, channels
),之后我们再讲数据类型设置为float32
再保存即可。
1 | train_imgs = np.array(train_imgs, dtype='float32') |
不过还需要指出的是,回忆我们在cifar10
简单图像识别的学习中,处理训练数据时会将像素值(0~255 范围内)缩放到 [0, 1] 区间,其中《python深度学习》书中说“神经网络喜欢处理较小的输入值”。
一般来说,将取值相对较大的数据(比如多位整数,比网络权重的初始值大很多)或异质数据(heterogeneous data
,比如数据的一个特征在0~1 范围内,另一个特征在100~200 范围内) 输入到神经网络中是不安全的。这么做可能导致较大的梯度更新,进而导致网络无法收敛。为了让网络的学习变得更容易,输入数据应该具有以下特征:
1、 取值较小:大部分值都应该在 0~1 范围内;
2、 同质性(homogenous
):所有特征的取值都应该在大致相同的范围内。
所以我们这里二值化时可以采用一种比较巧妙的方式,直接将像素值控制在[0, 1]区间:
1 | cv2.threshold(img, 0, 1, cv2.THRESH_OTSU) |
标签处理
不仅要处理每张验证码图片,每张验证码图片对应的验证码字符也是要处理的,处理方式如下:
1 | from string import ascii_lowercase, digits |
与处理图像一样,我们对所有训练图片对应的验证码字符全部执行上述操作后,会生成一个3D
张量,形状为(samples
, labels_len
, num_classes
),之后我们再讲数据类型设置为float32
再保存即可。
注意:这里是按照36个字符进行分类,所有的小写字母和数字,针对的情况是一般需要输入验证码的网站,英文字符不区分大小写,所以我们就全部当做小写字母来进行训练,如果该网站的验证码字符区分大小写,就要分成26x2+10=62
个分类进行训练。
数据分类
一般我们利用神经网络训练验证码时,需要将数据集分为三类,即训练集、验证集和测试集,如果图片存储在windows系统下,并且文件名是诸如验证码字符+一串随机字符的形式,那么这些文件是会被按照文件名顺序存放的,这个时候分类一定不能直接按顺序分类,这样会导致每个数据集的数据特征具有局部性,这样会导致训练效果下降。
可以采用以下代码,随机将数据分类:
1 | import os |
加载数据
numpy
保存的数据形式为x_train.npy
,我们在进行训练之前同样使用numpy
加载数据:
1 | import numpy as np |