狭义上讲验证码识别是将验证码图像转化成字符串值,传统方法常常先对验证码图像进行字符分割,再对验证码进行识别,劣势在于字符分割方法往往针对不同风格的验证码需要做修改,某些验证码加入噪声或线条,字符位置不固定及粘连时,字符分割效果不好,也会影响后续字符识别。除了只包含字母和数字的验证码,国内还有一些识别汉字的验证码,还有你以为考你认字符串实际上考你加减乘除的验证码,还有图像匹配和图像分类的验证码,种类和花样很多。所以验证码识别这种东西不可沉迷,毕竟道高一尺魔高一丈,道再高一尺。你破解了别人的验证码,维护人员立马弄出了新花样,换了一批新的验证码,你之前的方法就不好使了,于是你又破解了他的,他又改了新的…看谁能耗的久…不过博主作为菜鸟,做做验证码识别还是很有意义的,如果你也是搞深度学习的新手,不妨接着看下去,能够把数据集的准备和制作、深度网络模型的设计、训练和测试的流程走一遍。

博主是用深度学习做多任务分类的思路来识别验证码。多任务学习是针对数据给出多个监督信息(标签)进行学习,例如识别一张图像中的脸是否是人脸、脸部表情、性别、年龄等,识别图像中车的颜色、车型、姿态等,都属于多任务分类。项目用深度学习做多标签分类,是用深度神经网络对整张验证码图片进行多标签学习,来完成多任务分类,端到端的识别出验证码中的所有字符。这种思路同样可以用于车牌识别中。

用深度学习来做验证码识别,优势在于,只需要找一个合适的网络模型稍加修改,再给网络送入足够的有标签样本进行训练,就能达到很好的识别效果,无论验证码里面是白的黑的正的歪的、何种字体、粘不粘连都可以,也没必要做去噪、二值化、纠正和调各种阈值等各种处理(不然验证码风格稍微一变,你的处理方法就得改),直接端到端识别验证码的效果完全不比先做字符分割再做识别差。但其劣势也很明显,对不同手段生成的不同风格的验证码,都需要收集、爬取或模仿其验证码风格自己写代码生成大量样本,以维持较高的识别率。

为了满足多任务分类要求,对caffe源码进行针对性的修改满足多标签的输入和训练;然后在2012年ImageNet大赛冠军AlexNet网络模型的基础上进行了修改,作为多任务分类的模型;在windows平台下(ubuntu下当然也可以)利用caffe对两个不同的数据集进行了训练和测试;最后利用caffe的matlab接口,修改classfication_demo.m来做批量验证码图片识别,对caffe自带的classification.cpp做修改,也可以满足多任务分类。如果还没有编译caffe(gpu版本)及matlab接口,请先参照我的博客Windows下编译caffe

第一个验证码数据集条件比较理想,在生成时做了添加随机噪声点、字符变形、旋转缩放、随机赋色等处理。训练集64536张,验证集9096张(验证模型好坏),另外有714张用于做测试(测试模型效果),图片大小为88x28,共有数字0~9和大写字母A~Z共36类,每张验证码图像中包含四个字符,数据集下载。经过训练,该验证集图像上四个字符单个字符的识别准确率都达到了99.5%。而用于测试的714张验证码中,有15个识别出错,验证码整体识别结果准确率接近98%,主要是对0和O比较容易搞混。

第二个验证码数据集比较常见,更具有挑战性,生成验证码图像时使用了6种字体并加入随机线条、字体变形、旋转、随机赋色等处理。训练集63926张,验证集11914张,另有4112张用于测试,图片大小为150x40,共有数字0~10和小写字母a~z共36类(实际上该数据集中没有数字0和小写字母o,但并不影响我们用36类做分类),每张验证码图像中包含五个字符,数据集下载,其验证码生成程序来源github链接。经过训练,验证集验证码图像上五个字符单个字符的识别准确率平均达到了88%,最后对4112张图片进行测试,验证码整体识别结果准确率超过63%,虽然效果不是特别理想,但仔细观察下图,会发现随机线条的加入使得很多字符人眼都难以确认。

有了数据集之后,下面介绍利用深度学习进行验证码识别的具体步骤,在两种数据集上采用的方法是基本一致的,但第二个数据集似乎更有趣一些,以此为例。

1 数据准备

下载得到的数据集中的前三个文件夹,包括captcha_train、captcha_val和test_images,分别是验证码图像训练集、验证集和用于测试的图片文件夹。

captcha_train.txt和captcha_val.txt分别是captcha_train和captcha_val数据集对应的多标签文本文件,可以由matlab实现,生成captcha_train.txt和captcha_val.txt的create_captcha_train_val_txt.m代码如下。

%create_captcha_train_val_txt.m
%创建验证码的多标签标签txt文件
%验证码类别数为36,包括0~9和a~z(a~z的ascii码范围为97~122,减去87对应10~35,构成0~35共36类)
datadir = 'D:\data\Captcha2\captcha_train'; %train数据集
fp=fopen('D:\data\Captcha2\captcha_train.txt','w'); %新建一个txt文件存放结果
% datadir = 'D:\data\Captcha2\captcha_val'; %test数据集
% fp = fopen('D:\data\Captcha2\captcha_val.txt','w'); %新建一个txt文件存放结果
imagefiles = dir(datadir); %列出目录下所有文件
for i = 3:length(imagefiles) %windows系统下所有文件夹下都有两个隐藏文件排在最前面,跳过filename = imagefiles(i).name; %获取某张图像文件名fprintf(fp,'%s ',filename); %打印文件名for j=1:5 %打印多标签(5个)if abs(filename(j)) >= 97 && abs(filename(j)) <= 122 %如果是a~z,则97~122 -> 10~35label = abs(filename(j)) - 87;fprintf(fp,'%d ',label);       elseif abs(filename(j)) > 122 || abs(filename(j)) < 97 %如果是0~9,保持不变fprintf(fp,'%s ',filename(j));endendfprintf(fp,'\n');
end
fclose(fp); 

然后在caffe的data\目录下新建captcha文件夹,并将这些文件/文件夹都拷贝到captcha目录下。

2 修改caffe源码使其支持多标签输入和训练

caffe源码对hdf5支持多标签,但lmdb格式只支持单标签输入和训练,但lmdb读取效率更高。对caffe源码做以下修改使得caffe对lmdb格式满足多标签输入和训练。这里提供修改后的相应文件下载 多标签修改

打开caffe目录下src\caffe\proto\caffe.proto,找到Datum如下可以发现label是不可重复(单个)int32型变量,这里不要注释掉它,在后面新增可重复(多个)的float型变量labels,然后在caffe目录下找到并删除src\caffe\proto\caffe.pb.cc和include\caffe\proto\caffe.pb.h这两个文件,再重新编译caffe。注:红框表示需要注意或待修改的部分,绿框为新增或修改后的内容。

然后对convert_imageset.cpp(项目convert_imageset中)进行如下修改,修改后可以读取五个标签值。不过修改后也导致后面的ReadImageToDatum函数也要进行修改,转到ReadImageToDatum函数定义处,即在io.hpp(项目libcaffe中include\util\下)中,重载函数ReadImageToDatum并添加新的ReadFileLabelsToDatum函数,然后在io.cpp(项目libcaffe中src\util\下)给出上述两个函数的实现代码,分别参考原来的ReadImageToDatum和ReadFileToDatum进行修改。

然后对data_layer.cpp(libcaffe项目中src\layers下)进行如下两处修改,然后重新编译libcaffe,再右键convert_imageset项目生成,这样就完成了caffe源码的修改,能够支持多标签的输入和训练了。

3 数据格式转换

利用修改后的caffe编译出的convert_imageset将图像数据及标签转换成具有更高读取效率的lmdb格式文件。在caffe目录下新建create_captcha_lmdb.txt文件,在文件中写入如下,其中图像统一缩放到227x227,shuffle表示乱序处理。保存后修改后缀为.bat文件,执行之,开始读取图像和标签并转换成lmdb文件,等待几分钟后,转换生成完毕,可以在data\captcha\下看到生成的captcha_train_lmdb和captcha_val_lmdb文件夹,文件夹比较大,大约接近16G和2G,要预留好足够的内存空间。需要注意的是,我们这里并没有用compute_mean.exe来生成均值文件,是因为我们后面统一采用了ImageNet数据集的均值作为样本均值,ImageNet数据集的均值具有统计特性,而我们的样本又是随机生成的,所以这是合理的做法。

.\Build\x64\Debug\convert_imageset.exe --resize_height=227 --resize_width=227 --backend="lmdb" --shuffle .\data\captcha\captcha_train\ .\data\captcha\captcha_train.txt .\data\captcha\captcha_train_lmdb
echo.
.\Build\x64\Debug\convert_imageset.exe --resize_height=227 --resize_width=227 --backend="lmdb" --shuffle .\data\captcha\captcha_val\ .\data\captcha\captcha_val.txt .\data\captcha\captcha_val_lmdb
echo.
pause

4 设计多任务训练深度网络

在models文件夹下新建captcha文件夹,将models\bvlc_alexnet下的deploy.prototxt、solver.prototxt、train_val.prototxt拷贝到captcha文件夹下,然后分别对这三个文件进行修改。需要注意的是,train_val.prototxt和deploy.prototxt都是网络结构的描述文件,但train_val.prototxt还指出了训练集和验证集样本数据及标签,用于训练和验证;训练好的模型和deploy.prototxt可以用于测试单张或多张图片。

用caffe的python接口提供的draw_net.py可以实现caffe网络结构可视化,也有在线可视化网页 netscope 提供网络结构可视化功能,将train_val.prototxt(或者deploy.prototxt)描述的网络结构画出来。

使用draw_net.py画出的alexnet的网络结构的最后几层如图所示,alexnet是单标签分类网络,后面接了三个全连接层fc6、fc7、fc8,其中全连接层fc6和前面的卷积层相连,fc8同label一起与accuracy层(test模式下)和loss层相连。

而我们现在进行验证码识别的思路是多标签分类,输入一张验证码图像输出多路分类结果。为此我们需要对train_val.prototxt和deploy.prototxt(批量测试时会用到deploy.prototxt)做一定修改。
train_val.prototxt的具体修改方法是对fc6及前面的层保持不变,从fc6后展开多路分类网络,例如第二个数据集验证码中有5个字符,那我们在fc6后连接5个全连接层fc7_1、fc7_2、fc7_3、fc7_4、fc7_5(原先只有1个全连接层fc7),这些全连接层同样经过relu层和dropout层;然后分别与全连接层fc8_1、fc8_2、fc8_3、fc8_4、fc8_5相连,节点个数由1000改成36(0~9、a~z共36类);再由Slice层分割的多标签label_1、label_2、label_3、label_4、label_5、分别和fc8_1、fc8_2、fc8_3、fc8_4、fc8_5一起接上各自对应的accuracy层(test模式下)和loss层。
修改好train_val.prototxt后,就可以据此自行对deploy.prototxt进行修改了,最后在solver.prototxt中修改网络训练参数,这里提供修改后的deploy.prototxt、solver.prototxt、train_val.prototxt,网络模型文件下载

5 训练和测试深度网络

在做好上述准备的基础上,在caffe目录下新建train_captcha.txt,在里面写入如下语句,保存后修改后缀名为.bat文件,双击执行开始训练。博主在NVIDIA GTX1060 6G单gpu上对第二个数据集训练迭代15000次,耗时约两个半小时,之后accuracy和loss趋于稳定,但loss还是有点大。虽然模型在第一个数据集上字符准确率达到99.5%以上,但在第二个数据集上的效果并不是特别理想,如图第1、2、3、4、5个字符在验证集上的准确率约为95.76%、90.61%、88.09%、91.50%、96.21%

.\Build\x64\Debug\caffe.exe train -solver .\models\captcha\solver.prototxt -gpu 0
echo.
pause

最后利用caffe的matlab接口调用caffe来对验证码图片进行批量测试,在caffe下的matlab\demo下新建test_captcha.m对验证码测试图片进行批量测试,参考classification.demo进行修改得到test_captcha.m,生成的测试结果输出到test_captcha_result.txt中,其matlab代码如下,测试要用到deploy.prototxt和训练好的.caffemodel网络模型,这里提供训练好的迭代了15000次的captcha_iter_15000.caffemodel 模型文件下载。测试结果显示,4112张验证码有1220个识别出错,识别准确率为70.33%,在GTX1060 6G单gpu上平均每张验证码识别耗时10ms。

%利用训练好的caffe网络模型对验证码图片进行批量测试
%参照caffe-master\matlab\demo\classification_demo.m
%%%对第二种验证码类型进行批量测试
function test_captcha()
clear;
addpath('..');%添加上级目录搜索路径
addpath('.');%添加当前目录搜索路径
caffe.set_mode_gpu(); %设置gpu模式
caffe.set_device(0); %gpu的id为0
%caffe.set_mode_cpu();
net_model = 'C:\Users\Administrator\Desktop\caffe-master-captcha\models\captcha\deploy.prototxt'; %网络模型deploy.prototxt
net_weights = 'C:\Users\Administrator\Desktop\caffe-master-captcha\models\captcha\captcha_iter_24000.caffemodel'; %训练好的模型文件
phase = 'test'; %不做训练,而是测试
net = caffe.Net(net_model, net_weights, phase); %获取网络tic;
error = 0;
%批量读取图像进行测试
datadir = 'C:\Users\Administrator\Desktop\caffe-master-captcha\data\Captcha\test_images';
imagefiles = dir(datadir);
fp = fopen('captcha_test_result.txt','w');
for i = 3:length(imagefiles)im = imread(fullfile(datadir,imagefiles(i).name));input_data = {prepare_image(im)}; %图像数据预处理scores = net.forward(input_data); %做前向传播%获取前向传播多任务的最后层向量,然后求出最好成绩和对应的类别scores_1 = net.blobs('prob_1').get_data();scores_2 = net.blobs('prob_2').get_data();scores_3 = net.blobs('prob_3').get_data();scores_4 = net.blobs('prob_4').get_data();scores_5 = net.blobs('prob_5').get_data();best_score = zeros(5,1);best = zeros(5,1);[best_score(1),best(1)] = max(scores_1);[best_score(2),best(2)] = max(scores_2);[best_score(3),best(3)] = max(scores_3);[best_score(4),best(4)] = max(scores_4);[best_score(5),best(5)] = max(scores_5);%输出识别结果filename = imagefiles(i).name;fprintf(fp,'%s',filename);%图片名称flag = 1; %是否识别正确for j = 1:5best(j) = best(j) - 1; %matlab从1开始,改为从0开始if best(j) >= 10 %a~zbest(j) = best(j) + 87; %10~35 -> 97~122fprintf(fp,' %s',char(best(j)));if char(best(j)) ~= filename(j)flag = 0;endelsefprintf(fp,' %d',best(j));if best(j) ~= (abs(filename(j)) - 48) %0~9对应的ascii码为48~57flag = 0;endend endif flag == 0error = error + 1;%disp(error);fprintf('error: %d\n',error);fprintf(fp,'*********'); %识别错误,输出中做明显标记endfprintf(fp,'\r\n');
end
total_time = toc;
%打印到结果文本中
fprintf(fp,'total_time: %.3f s\n',total_time);
fprintf(fp,'aver_time: %.3f s\n',total_time/(length(imagefiles)-2));
fprintf(fp,'error/total: %d/%d\n',error,length(imagefiles)-2);
fprintf(fp,'accurary: %.4f\n',1.0 - (error*1.0)/(length(imagefiles)-2));
fclose(fp);
%打印到屏幕上
fprintf('total_time: %.3f s\n',total_time);
fprintf('aver_time: %.3f s\n',total_time/(length(imagefiles)-2));
fprintf('error/total: %d/%d\n',error,length(imagefiles)-2);
fprintf('accurary: %.4f\n',1.0 - (error*1.0)/(length(imagefiles)-2));
%disp(['error/total: ',num2str(error),'/',num2str(length(imagefiles)-2)]);
endfunction im_data = prepare_image(im)
%d = load('../+caffe/imagenet/ilsvrc_2012_mean.mat');
%mean_data = d.mean_data;%resize to 227 x 227
im = imresize(im,[227 227],'bilinear');
%caffe的blob顺序是[w h c num]
%matlab:[h w c] rgb -> caffe:[w h c] bgr
im_data = im(:,:,[3,2,1]); %rgb -> bgr
im_data = permute(im_data,[2,1,3]); %[h w c] -> [w h c]
[w,h,~] = size(im_data);
%ImageNet数据集的均值具有统计规律,这里可以直接拿来使用
mean_data(:,:,1) = ones(w,h) .* 123; %r
mean_data(:,:,2) = ones(w,h) .* 117; %g
mean_data(:,:,3) = ones(w,h) .* 104; %bim_data = single(im_data);
im_data = im_data - single(mean_data);
end

也可以对caffe的example\cpp_classification\下的classification.cpp进行修改来满足多任务分类,用于验证码识别,这里提供修改好的classification.cpp 测试代码下载 ,不过这里还只做了单张图片测试,果然c++相比matlab还是要麻烦点>_<

因为第一个验证码数据集条件比较理想,模型表现很完美,但第二个数据集的表现就没那么优秀了,出现过拟合后尝试增加训练的batchsize、适当调整学习率、增加dropout来降低loss,还可以考虑用更深的网络进一步提高识别的准确率。不过博主认为单纯的刷准确率意义不大,最理想的情况是在网络模型只做很小修改的情况下,对采取不同风格和方法生成的验证码都能比较好的识别效果。

至此,完成了验证码识别的整个流程,你有收获吗?

深度学习caffe实战(一)验证码识别相关推荐

  1. 基于深度学习的图标型验证码识别系统(包含完整代码、界面)

    深度学习应用于图像处理领域应该说有很长一段时间了,相关的研究成果也有很多的积累了,从项目和实践入手是我觉得的最好最快速有效的学习手段,当下很多主流的验证码识别系统大都是基于神经网络设计开发而来的,在处 ...

  2. 基于深度学习的图标型验证码识别系统

    深度学习应用于图像处理领域应该说有很长一段时间了,相关的研究成果也有很多的积累了,从项目和实践入手是我觉得的最好最快速有效的学习手段,当下很多主流的验证码识别系统大都是基于神经网络设计开发而来的,在处 ...

  3. [雪峰磁针石博客]计算机视觉opcencv工具深度学习快速实战1人脸识别

    使用OpenCV提供的预先训练的深度学习面部检测器模型,可快速,准确的进行人脸识别. 2017年8月OpenCV 3.3正式发布,带来了高改进的"深度神经网络"(dnn deep ...

  4. 用深度学习破解12306图片验证码,识别率超96%- web效果版

    12306最新版图片验证码已被破解已是公开的秘密.比如市面上那么专业多抢票软件,早就破解了这个图片验证码.     那他们到底是怎么实现的呢???     据本人猜测,应该都是基于近2年流行的深度学习 ...

  5. 第二节:Keras深度学习框架实战之手写体识别

    一:概述 手写体识别是keras所自带的七个数据集中的一个,用于识别0-9的10阿拉伯数字的手写体,该数据集大约有6000张28*28的手写体图片组成.我们本节课将通过后写体识的这样一个例子,先给大家 ...

  6. 深度学习Caffe实战笔记(6)Windows caffe平台用Siamese网络跑自己的数据

    终于到了介绍如何使用Siamese网络跑自己的数据了,在网上.论坛上.群里关于用Siamese网络的资料很多,但是实战的资料很少,难道是因为太容易了吗?反正博主查阅了各种地方,几乎没有找到Siames ...

  7. 深度学习Caffe实战笔记(19)Windows平台 Faster-RCNN 制作自己的数据集

    万里长征第一步,就是要制作自己的数据集,过程还是比较繁琐的,特别是标注的过程,这篇博客先介绍如果制作voc2007自己的数据集用于faster-rcnn训练,下一篇博客介绍如何用faster-rcnn ...

  8. Pytorch深度学习基础 实战天气图片识别(基于ResNet50预训练模型,超详细)

    文章目录 一.概述 二.代码编写 1. 数据处理 2. 准备配置文件 3. 自定义DataSet和DataLoader 4. 构建模型 5. 训练模型 6. 编写预测模块 三.效果展示 四.源码地址 ...

  9. 深度学习项目实战(一):猫狗识别

    深度学习项目实战(一):猫狗识别 文章目录 深度学习项目实战(一):猫狗识别 项目背景: 数据读取: 网络架构 卷积神经网络训练 项目背景: 猫狗识别是卷积神经网络的入门实战案例,目的在于计算机可以识 ...

最新文章

  1. java -- 线程的生命周期
  2. Go 开发关键技术指南 | Go 面向失败编程 (内含超全知识大图)
  3. 5月 PC 浏览器市场份额:Chrome 即将突破 60%
  4. Qt弹出的窗口始终位于界面最前面的方法
  5. 3d打印主要的切片参数类型_3D打印混凝土工艺参数对成型精度的影响
  6. php 其他页面获取session_PHP五十个提升执行效率的小技巧,和常见问题
  7. (转)解读NTFS(一)
  8. aspen求理论塔板数_aspen 塔设计
  9. 用scanline取BMP上某点的颜色,代码如下,为什么可以编译,运行时却出错呢?...
  10. 呼叫中心客服交流三大法宝
  11. knight tour java,Knight Tour Problem
  12. 贝叶斯分类与贝叶斯网络
  13. 2013=726 整合,优化,利用自身资源。 让自己的时间更有意义,最大化利用
  14. 趣味Python — 不到20行代码制作一个 “手绘风” 视频
  15. 数码相册的实现(手势切换图片,用链表加快效率,jpeg解码)
  16. 【VS2015】Win7 X64上面安装VS2015
  17. 怎么用微信打开wifi连接到服务器,微信一键连wifi在哪里 微信一键连wifi怎么使用...
  18. 关于增量模型和迭代模型的区别
  19. PX4多传感器优先级判断
  20. websphere 实用_将WebSphere Cast Iron Studio PGP活动与外部PGP实用程序一起使用

热门文章

  1. RFID相关原理知识及RFID应用领域详细介绍
  2. Linux网课笔记-Linux指令练习题
  3. 分享十个法国平面设计的创意思维
  4. python joblib.dump_python 2/3 joblib.dump() 和 joblib.load()
  5. 大数据企业应用合作解决方案案例
  6. 公共基础知识教材pdf计算机,计算机公共基础知识1.pdf
  7. JavaScript实现灯泡开关
  8. 关于iic协议和对AT24C02进行读写数据的理解和代码解读
  9. 无root手机版抓android包工具,超好用
  10. [C++]输入三个整数x,y,z,请把这三个数由小到大输出