Captcha Recognition(GIF验证码自动识别)
引言
此次作业通过使用requests库爬取华科统一身份系统登陆页面的GIF验证码,并使用PIL库的Image模块对GIF验证码进行了分解。作业主要工作可分为以下几个主要模块:爬取验证码图片数据模块,灰度处理&二值化模块,降噪模块,图片字符分割模块,素材处理与标记模块,归一化与one-hot编码模块,训练模块,预测和拼接模块,并分别完成这八个模块的设计和实现,最终达到了自动识别GIF验证码的目的。
软件结构和实现方法
操作系统为Windows8系统,python版本是3.7,所用到的模块如下:
- PIL:用来分解GIF
- Scikit-learn:用来训练和调用神经网络模型
- NumPy:处理数据
- Pandas:处理数据
编写的函数及功能如下:
- data_preprocessing.py: 数据爬取和预处理(灰度处理、二值化、降噪)
- img_2_csv.py: img图片转csv数据
- captcha_train.py: 模型训练
- predict.py: 预测和识别验证码
- main.py: 主函数
数据描述
创建0-9十个文件夹,清除爬取的无效字符(空白或干扰)图片,并把处理后的图片手动归类到0-9十个不同的文件夹下,便于训练和测试。
步骤
1.爬取验证码图片数据
首先爬取GIF验证码,使用第三方库requests库爬取华科统一身份验证网页的GIF验证码,使用PIL库的Image模块对GIF分解(一张GIF分解为四张PNG图片)。
2.灰度处理&二值化
灰度处理:在PIL中,从模式”RGB”转为”L”模式是按照下面公式转换L=R* 299/1000 + G * 587/1000+ B*114/1000,此时每个像素点有8位表示,即0-255.为了降噪,需要对图片二值化处理,处理完后每个像素点仅有1位表示,即0或1.
二值化:使用point方法进行二值化,阈值设为200.
3.降噪
本文使用九宫格法进行降噪,降噪步骤为:
1).对某个黑点周边的九宫格里面的黑色点计数
2).如果黑色点少于4个则证明此点为孤立点,然后得到所有的孤立点(本文中黑色点少于4去噪效果最好)
3).对所有孤立点一次批量移除
4.图片字符分割
使用图像编辑软件(Photoshop或者其它)打开验证码图片,放大到像素级别,观察其它一些参数特点:可以得到如下参数:
- 整个图片尺寸是90*58
- 单个字符尺寸是16*20
- 左右字符相距5个像素
- 字符上下距边缘19个像素
5.素材处理与标记
素材处理与标记的结果见数据描述,创建0-9十个文件夹,清除爬取的无效字符(空白或干扰)图片,并把处理后的图片手动归类到0-9十个不同的文件夹下,便于训练和测试。
6.归一化与one-hot编码
对输入的训练集和测试集进行归一化:像素值/255对于输出采用0-9十个字符表示,并将其进行one-hot编码。
7.训练
使用scikit-learn库的MLPClassifier(多层感知器分类模型)进行训练,输入为1620共320个神经元,隐含层为3232的双隐含层,输出为10*1的One-hot编码。具体训练参数如下:
- solver=’sgd’:采用随机梯度下降算法进行训练
- activation=’relu’
- alpha=1e-5
- hidden_layer_sizes=(32,32):隐含层有32*32个神经元,代表第一隐含层32个神经元,第2隐含层32个神经元。
- tol=1e-5
- random_state=1
- max_iter=100
- verbose=10
- learning_rate_init=0.1:学习率初始化为0.1
多层感知器网络(前馈网络)原理如下:
$w_{jk}^{l}$为第$l$层上第$j$个神经元与上一层第$k$个神经元输出之间相对应的权重,$W$表示权重矩阵。
$a_j^l$ 表示第$l$层第$j$个神经元的输出,$A$表示输出矩阵。
$z_j^l$ 表示第$l$层第$j$个神经元的输入,$Z$表示输入矩阵。
$b_j^l$ 表示第$l$层第$j$个神经元的偏置,$B$表示偏置矩阵。
权重矩阵可写作:$w^l=$
$$\begin{bmatrix}
{a_{1,1}^{l}}&{a_{2,1}^{l}}&{\cdots}&{a_{32,1}^{l}}\
{a_{1,2}^{l}}&{a_{2,2}^{l}}&{\cdots}&{a_{32,2}^{l}}\
{\vdots}&{\vdots}&{\ddots}&{\vdots}\
{a_{1,320}^{l}}&{a_{2,320}^{l}}&{\cdots}&{a_{32,320}^{l}}\
\end{bmatrix}$$
以节点$2j$(第2行第$j$个节点)为例,输入信号为:$Z_j^2=\sum_{i=1}^{320}w_{jk}^2a_j^1+b_j^2$
用矩阵来描述即:
$$Z^2=W^2A^1+B^2$$
隐藏层节点经过非线性变换后的输出表示如下:
$$A^2=\text{sigmoid}(Z^2)$$
输入信号在权重矩阵们的帮助下,得到每一层的输出,最终到达输出层。
8.预测和拼接
完整识别流程为:
1) 爬取GIF并分解
2) 灰度处理&二值化
3) 去噪
4) 图片转CSV数据
5) 使用训练好的模型预测,拼接
拼接流程以GIF验证码“7786”为例:
爬取GIF,经过分解,灰度处理,二值化后得到图4所示四张PNG图片,验证码预测结果拼接代码如下图5所示:
- 若3.png的前两位等于2.png的前两位,或1.png的前两位等于2.png的前两位,则:前两位取2.png的前两位;
- 否则,取3.png的前两位。
后两位则直接取0.png的后两位。
# 拼接
# 前两位
if crack_result.get('4')[:2] == crack_result.get('3')[:2] or crack_result.get('2')[:2] == crack_result.get('3')[:2]:
final_result = crack_result.get('3')[:2]
else:
final_result = crack_result.get('4')[:2]
# 后两位
final_result += crack_result.get('1')[-2:]
print(f'final_result: {final_result}')
实验结果
根据预测结果重命名10张GIF,程序运行结果如图5所示:
命名后的GIF如图6所示。
注:由于是GIF动图,因此在截图后无法显示完整验证码信息。
结论
通过对字符图片的训练,测试数据集达到了100%的准确率,此次利用机器学习识别GIF验证码有着很不错的效果。