跳转至

四位数字验证码识别

这是给 BenderBlog/traintime_pda 做的功能,可以自动通过学校某些网站的验证码,给 app 用户更好的体验。这也是我第一次自己做数据集,训练模型,并且用在正经的地方()。代码开源在 stalomeow/captcha-solver

技术选型

因为是图像相关的任务,所以选择卷积神经网络。接着,我找了下 tensorflowpytorchflutter 插件,对比下来 tflite_flutter 比较好,它是 tensorflow 官方维护的,并且支持的平台也最多。

电费验证码

电费验证码可以在 https://payment.xidian.edu.cn/authImage 获取,分辨率是 200x80。

验证码
验证码

这个验证码比较人性化,相对好做。一开始,我将整个图片转为灰度扔给 CNN 识别,但效果不好,模型也很大。后来,我把验证码的四个数字拆开,CNN 每次只识别一个数字,这样效果很好。这个验证码只要水平 4 等分就能把数字拆开。

模型很小,只有不到 130KB,不会显著增加 app 包体,也方便在各种设备上运行。

keras.models.Sequential([
    keras.layers.Conv2D(16, (3, 3), activation='relu', input_shape=input_shape),
    keras.layers.MaxPooling2D(pool_size=(2, 2)),

    keras.layers.Conv2D(32, (3, 3), activation='relu'),
    keras.layers.MaxPooling2D(pool_size=(2, 2)),

    keras.layers.Conv2D(32, (3, 3), activation='relu'),
    keras.layers.MaxPooling2D(pool_size=(4, 4)),

    keras.layers.Flatten(),
    keras.layers.Dense(64, activation='relu'), # 适当减少神经元
    keras.layers.Dropout(0.3),                 # Dropout 防止过拟合
    keras.layers.Dense(util.get_captcha_num_classes(type), activation='softmax')
])

用几百张图片训练了 1 分钟左右,模型很快就收敛了。

训练结果
训练结果

数据集是自制的,专门针对学校的验证码。工具是用 tkinter 写的,输入验证码后回车,就能自动切分数字并打上标签保存,然后自动获取下一个验证码并 focus 输入框。效率非常高,甚至有点上头。

数据集制作工具
数据集制作工具

模型测试工具基本复用了上面的代码,可以测试模型在实际场景下的表现。下图输入框里的数字是模型预测的。

模型测试工具
模型测试工具

自服务验证码

自服务验证码可以在 https://zfw.xidian.edu.cn/site/captcha 获取,分辨率是 90x34。

验证码,但数字挤在一起
验证码,但数字挤在一起

这个验证码不好识别,数字挤在一起,尤其是数字 1,有时候我自己都会输错。这种情况只能换一张验证码。

正常一点的验证码
正常一点的验证码

思路是将它转化为电费验证码的形式,然后尝试复用旧的模型。

反色 + 二值化
反色 + 二值化

反色再二值化能去掉大部分噪声,但数字很多地方断开了不方便识别。不过,通过这张图能算出数字的包围盒。如果包围盒宽度小于一个阈值,就认为数字都挤在一起,放弃预测。将灰度图中包围盒内的部分上采样为 200x80(电费验证码的分辨率)。上采样类似 bilinear,但不是对四个像素插值,而是取四个像素的最小值,相当于一边放大一边做 形态学腐蚀

处理结果
处理结果

套用电费验证码模型的结果不理想,我又重新训练了一个模型。训练时发现过拟合问题比较严重,所以改用更大的 dropout rate,并加上 L2 正则化。

keras.models.Sequential([
    keras.layers.Conv2D(16, (3, 3), activation='relu', input_shape=input_shape),
    keras.layers.MaxPooling2D(pool_size=(2, 2)),

    keras.layers.Conv2D(32, (3, 3), activation='relu'),
    keras.layers.MaxPooling2D(pool_size=(2, 2)),

    keras.layers.Conv2D(32, (3, 3), activation='relu'),
    keras.layers.MaxPooling2D(pool_size=(4, 4)),

    keras.layers.Flatten(),
    keras.layers.Dense(64, activation='relu', kernel_regularizer=keras.regularizers.l2(0.06)), # L2 正则化防止过拟合
    keras.layers.Dropout(0.55), # Dropout 防止过拟合
    keras.layers.Dense(util.get_captcha_num_classes(type), activation='softmax')
])

单个数字识别率大概是 97%,考虑到验证码各种奇奇怪怪的情况,以及切分数字的准确性,最后总识别率估计有 80% 就不错了。

训练结果
训练结果

文章推荐

推荐一篇类似的文章:基于CNN的四位数字验证码识别_四位验证码识别-CSDN博客

评论