本文,将会简述如何利用Matlab的强大功能,调用神经网络处理验证码的识别问题。 

预备知识,Matlab基础编程,神经网络基础。 

可以先看下:

Matlab基础视频教程

Matlab经典教程——从入门到精通

神经网络入门

验证码识别原理

Matlab对图像读入处理,去掉噪声点和较浅的点,进行二值化,将图像转变为0/1矩阵,这样就完成了预处理。 

然后要对图像进行切割,取到每个数字的小图片位置,将其缩放至等大小,方便神经网络进一步处理。 

最后将图片转成神经网络能够识别的格式,例如BP网络,则将其转为行向量,深卷积网络,则将其转为矩阵即可。

识别预处理

Matlab对验证码的识别是基于神经网络的,但预处理工作还是占了整体工作的大半,将数据整理好并处理成对应可用的格式,问题就简单了很多。 

Matlab的一大缺陷是不注重数据结构,其结构体无比难用,所以我们这里将尽可能使用矩阵进行处理,而参数较多时,我们也只是简单的将其放入到元胞数组中,不优雅之处,敬请见谅。

首先介绍一下matlab的图像基本处理函数:

img = imread('path') # 返回一个图像的矩阵,其每个元素的值,包含rgb三个通道的数据。
imshow(img) # 显示图像
imgGray = rgb2gray(img) # 转为灰度图像
thresh = graythresh(imgGray); % 自动确定二值化阀值
BW = 1 - im2bw(imgGray,thresh); % 二值化,且取反,黑的部分是0,白的部分是1,
I2 = bwareaopen(BW, 8, 8); % 去除连通分量中小于10的离散点
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们看看目标的图片: 

有很多随机的像素点干扰,我们需要将这些像素点去除,然后进行图像切割。

切割图片实际上很简单,就是对图片中每行每列进行统计,然后将形成的波形进行扫描,每个从0上升又下降到0的区域,就是一个字符。

切割后的图片:

下面我们来写一个完整的函数分割器函数,为了检测正确性,我们这里提供了isshow标记,如果设置为true,将会打印中间的调试信息。


% 图片分割器 function y = cutting(img, isshow)
if nargin < 2; isshow = false; end
if isshow;
imshow(img); % 显示彩色图像
end
imgGray = rgb2gray(img); % 转为灰度图像 thresh = graythresh(imgGray); % 自动确定二值化阀值 (这个不太好,有时会整体删除一个字)
BW = 1 - im2bw(imgGray,thresh); % 二值化
I2 = bwareaopen(BW, 8, 8); % 去除连通分量中小于10的离散点 varray = sum(I2);
imgsize = size(I2); if isshow
figure; % 打开一个新的窗口显示灰度图像
imshow(imgGray); % 显示转化后的灰度图像 harray = sum(I2');
x1 = 1 : imgsize(1, 1);
x2 = 1 : imgsize(1, 2);
figure; % 打开一个新的窗口显示分割图
plot(x1, harray, 'r+-', x2, varray, 'y*-'); figure; % 打开一个新的窗口显示灰度图像
imshow(I2); % 显示转化后的灰度图像
end va = mean(varray); % 计算平均值
harray = sum(I2');
vb = mean(harray); %% matlab 设计的实在太烂!真是我有史以来见过的最烂的语言
%% 函数只有搅成一坨的情况下才能正确运行
%% 他们根部不知道如何用闭包,以及合理的封装对象 isanum = false;
sumy = 0;
for i = 1 : imgsize(1, 1)
if harray(i) > vb;
if isanum == false;
isanum = true;
cvb = i;
end
else
if isanum;
isanum = false;
cve = i;
sumy = sumy + 1;
if isshow;
hold on;
plot([0 imgsize(1,2)], [cvb cvb],'r--');
plot([0 imgsize(1,2)], [cve cve], 'r--');
end
end
end
end y = {}
sumy = 0;
for i = 1 : imgsize(1, 2);
if varray(i) > va;
if isanum == false;
isanum = true;
ctb = i;
end
else
if isanum;
isanum = false;
cte = i;
sumy = sumy + 1;
if isshow;
hold on;
plot([ctb ctb], [0 imgsize(1,1)],'r--');
plot([cte cte], [0 imgsize(1,1)],'r--');
end
t = I2(cvb:cve, ctb:cte);
y{sumy} = t;
end
end
end
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

我们这个函数实现了对图片的预处理工作,成功的将大部分图片分割成了小图片,放到返回的元胞数组中,但这还有一个重要的问题,就是切割后的图片并不等大小。

并且,我们为了让这些图片能够方便的进行训练,希望将他们归好类别,方便标记。将图像等大小十分简单,只需要将图像的最大的长和宽找到,然后对矩阵进行扩展,多余的位置补0即可。

%% 将数字分类放置
for i = 1 : length(imgs_name)
img_name = imgs_name{i};
imgs = cutting(imread(['train/',img_name,'.jpg']), false);
if (length(imgs) == length(img_name))
imgs_num_size = length(img_name);
for j = 1 : imgs_num_size
tmp_num = str2num(img_name(j)) + 1;
imgs_sample_num(tmp_num) = imgs_sample_num(tmp_num) + 1;
imgs_sample{tmp_num, imgs_sample_num(tmp_num)} = imgs{j};
tmp_size = size(imgs{j});
end
end
end max_size = [16 16]; %% 归一化所有样本,使其等大小
for i = 1 : 10
for j = 1 : imgs_sample_num(i)
temp = zeros(max_size);
imgs_size = size(imgs_sample{i, j});
temp(1:imgs_size(1,1), 1:imgs_size(1,2)) = imgs_sample{i, j};
imgs_sample{i, j} = temp;
% figure;
% imshow(temp);
end
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

分别用BP网络和深卷积网络来进行图像识别

BP网络结构

由于已经做好了充足的预处理工作,那么接下来的识别就十分简单了,BP网络和深卷积网络都有对应的库支持操作,所以我们只需要编写配置代码就可以了。

BP网络就是简单的三层结构,由于层数太大可能带来误差残差太小等问题,造成训练困难,我们这里使用足够多的隐层节点保障BP网络的精度就可以了。

输入就是整个图像转为1维向量,输出则是可能属于的类别的概率,是一个10维的向量,要确定分类结果,就将其中最大的数字找到即可。

BP网络的训练及识别

那么,我们就可以开始组织训练数据了。


% 创建数据集
%% buildtrainset: 用来创建神经网络适合的训练集
function [inputs outputs] = buildtrainset(imgs, number)
i = 1;
for k = 1 : 10
for j = 1 : number(k)
input = imgs{k, j};
input_size = numel(input);
inputs(i, :) = reshape(input', input_size, 1);
outputs(i, :) = zeros(10, 1);
outputs(i, k) = 1;
i = i + 1;
end
end
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

然后训练并比较正确与否:

function y = runbp(imgs_sample, imgs_sample_num, max_size)
% bp 网络训练
[a, b] = buildtrainset(imgs_sample, imgs_sample_num);
net = bpann(a', b'); % bp 测试
image_dir=dir('image/*.jpg');
for i = 1: length(image_dir)
str_name = image_dir(i).name;
imgs_test{i} = str_name(1:4);
end rightnum = 0;
sumnum = 0; for i = 1 : length(imgs_test)
img_name = imgs_test{i};
imgs = cutting(imread(['image/',img_name,'.jpg']), false);
if (length(imgs) == length(img_name))
for j = 1 : length(img_name)
tmp_num = str2num(img_name(j)) + 1; %% 等大小化
temp = zeros(max_size);
imgs_size = size(imgs{j});
temp(1:imgs_size(1,1), 1:imgs_size(1,2)) = imgs{j};
imgs{j} = temp; input_size = numel(temp);
testInput(j, :) = reshape(temp', input_size, 1);
end
size(testInput)
Y = sim( net , testInput' ); mans = [1:4];
for j = 1 : length(img_name)
ymax = 0;
yans = NaN;
for k = 1 : 10
if (ymax < Y(k, j))
ymax = Y(k, j);
yans = k;
end
end
mans(j) = yans-1; sumnum = sumnum + 1;
if (mans(j) == str2num(img_name(j)))
rightnum = rightnum + 1;
end
end img_name
mans end
end
rightdata = [rightnum, sumnum-rightnum]
pie(rightdata, {'right', 'wrong'}); end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

经过1500次左右的迭代,收敛精度基本达到了要求:

识别结果:

1830
mans =
1 8 3 0 2940
mans =
2 9 4 0 3742
mans =
3 7 4 2 5980
mans =
5 9 8 0 6739
mans =
6 7 3 9 8240
mans =
8 2 4 0 8324
mans =
8 3 2 4
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

但遗憾的是,识别正确率并不是100%,而是70%,由于有3组数据在预处理时失败了,并没有被正确的二值化,造成了无法识别,但可以看出,神经网络的识别正确率还是相当高的。

深度卷积网络的图像识别

我们这里使用了一个流行的深度学习工具包DeepLearnToolbox,这个工具包可以在github上被找到。 

将其下载下来,然后添加两个path路径,将其引用:

path(path, 'DeepLearnToolbox-master/CNN/')
path(path, 'DeepLearnToolbox-master/util/')
  • 1
  • 2

然后我们构建一个卷积网络的结构struct,并利用类似BP的方式,将数据集构造好:

    % 网络训练集构造
[a, b] = buildtrainset_cnn(imgs_sample, imgs_sample_num); % 16×16的原图片 cnn.layers = {
struct('type', 'i') %input layer
struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer
struct('type', 's', 'scale', 2) %sub sampling layer
struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer
struct('type', 's', 'scale', 2) %sub sampling layer
};
cnn = cnnsetup(cnn, a, b);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

而卷积网络的配置如下:

    % 学习率
opts.alpha = 2;
% 每次挑出一个batchsize的batch来训练,也就是每用batchsize个样本就调整一次权值,而不是
% 把所有样本都输入了,计算所有样本的误差了才调整一次权值
opts.batchsize = size(a, 3);
% 训练次数,用同样的样本集。我训练的时候:
% 1的时候 11.41% error
% 5的时候 4.2% error
% 10的时候 2.73% error
opts.numepochs = 2000; % cnn = cnntrain(cnn, a, b, opts); % 如果是还未训练
load cnn_save cnn; %如果已经训练过,载入保存的网络就可以了
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这样我们来观察一下网络的结构,这是一个复杂的网络,input是一个16×16的图片,而每次卷积的核都是5×5的,还会有两个降维层。

然后我们会进行测试,和BP网络几乎一样

% 测试
image_dir=dir('image/*.jpg');
for i = 1: length(image_dir)
str_name = image_dir(i).name;
imgs_test{i} = str_name(1:4);
end rightnum = 0;
sumnum = 0; for i = 1 : length(imgs_test)
img_name = imgs_test{i};
imgs = cutting(imread(['image/',img_name,'.jpg']), false);
if (length(imgs) == length(img_name))
for j = 1 : length(img_name)
tmp_num = str2num(img_name(j)) + 1; %% 等大小化
temp = zeros(max_size);
imgs_size = size(imgs{j});
temp(1:imgs_size(1,1), 1:imgs_size(1,2)) = imgs{j};
imgs{j} = temp; input_size = size(temp);
testInput(:, :, j) = reshape(temp', input_size(1,1), input_size(1,2));
end % 然后就用测试样本来测试 cnn = cnnff(cnn, testInput);
cnn.o
[~, mans] = max(cnn.o); img_name
mans = mans-1
% [~, a] = max(y);
% bad = find(mans ~= a);
end
end %plot mean squared error
plot(cnn.rL);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

附: 源码仓库

https://github.com/sunxfancy/ANN2

Matlab神经网络验证码识别的更多相关文章

  1. MATLAB简易验证码识别程序介绍

    本推文主要识别的验证码是这种: 第一步: 二值化 所谓二值化就是把不需要的信息通通去除,比如背景,干扰线,干扰像素等等,只剩下需要识别的文字,让图片变成2进制点阵. 第二步: 文字分割 为了能识别出字 ...

  2. 简单验证码识别(matlab)

    简单验证码识别(matlab) 验证码识别, matlab 昨天晚上一个朋友给我发了一些验证码的图片,希望能有一个自动识别的程序. 1474529971027.jpg 我看了看这些样本,发现都是很规则 ...

  3. 写给程序员的机器学习入门 (八) - 卷积神经网络 (CNN) - 图片分类和验证码识别

    这一篇将会介绍卷积神经网络 (CNN),CNN 模型非常适合用来进行图片相关的学习,例如图片分类和验证码识别,也可以配合其他模型实现 OCR. 使用 Python 处理图片 在具体介绍 CNN 之前, ...

  4. 验证码进阶(TensorFlow--基于卷积神经网络的验证码识别)

    本人的第一个深度学习实战项目,参考了网络上诸多牛人的代码,在此谢过,因时间久已,不记出处,就不一一列出,罪过罪过. 我的数据集是我用脚本在网页上扒的,标签是用之前写的验证码识别方法打的.大概用了400 ...

  5. 神经网络实现Discuz验证码识别

    最近自己尝试了网上的验证码识别代码项目,该小项目见以下链接: https://cuijiahua.com/blog/2018/01/dl_5.html 数据也就用了作者上传的60000张Discuz验 ...

  6. 字符型图片验证码识别完整过程及Python实现

    字符型图片验证码识别完整过程及Python实现 1   摘要 验证码是目前互联网上非常常见也是非常重要的一个事物,充当着很多系统的 防火墙 功能,但是随时OCR技术的发展,验证码暴露出来的安全问题也越 ...

  7. 基于tensorflow的‘端到端’的字符型验证码识别源码整理(github源码分享)

    基于tensorflow的‘端到端’的字符型验证码识别 1   Abstract 验证码(CAPTCHA)的诞生本身是为了自动区分 自然人 和 机器人 的一套公开方法, 但是近几年的人工智能技术的发展 ...

  8. [验证码识别技术]字符验证码杀手--CNN

    字符验证码杀手--CNN 1 abstract 目前随着深度学习,越来越蓬勃的发展,在图像识别和语音识别中也表现出了强大的生产力.对于普通的深度学习爱好者来说,一上来就去跑那边公开的大型数据库,比如I ...

  9. 基于python语言的tensorflow的‘端到端’的字符型验证码识别源码整理(github源码分享)

    基于python语言的tensorflow的‘端到端’的字符型验证码识别 1   Abstract 验证码(CAPTCHA)的诞生本身是为了自动区分 自然人 和 机器人 的一套公开方法, 但是近几年的 ...

随机推荐

  1. Spring声明式事务的实现方式选择(JDK动态代理与cglib)

    1.简介 Spring声明式事务的具体实现方式是动态决定的,与具体配置.以及事务代理对象是否实现接口等有关. 2.使用JDK动态代理的情况 在满足下面两个条件时,Spring会选择JDK动态代理作为声 ...

  2. 2018-2019-2-20175323 java实验五 网络编程与安全

    20175323 java实验五 网络编程与安全 任务一 ①编写MyBC.java实现中缀表达式转后缀表达式的功能 ②编写MyDC.java实现从上面功能中获取的表达式中实现后缀表达式求值的功能 基本 ...

  3. nginx按日分割日志

    #!/bin/bash #按日切割nginx日志并压缩,加入crontab每天0:00切割 #作者:fafu_li #时间: source /etc/profile #加载系统环境变量 source ...

  4. DRF的视图组件

    目录 DRF的视图组件 两大视图类 六大视图工具类 九大工具视图类 两大视图集基类 DRF的视图组件 DRF的视图组件大概分以下几类 两大视图类 APIView.GenericAPIView from ...

  5. DRF的序列化组件

    目录 DRF的序列化组件 Serializer组件 序列化 反序列化 ModelSerializer组件 序列化和反序列化 自定义Response方法 基表相关 DRF中ORM的多表关联操作 外键设计 ...

  6. 使Excel中单元格内英文为Arial Narrow 中文为宋体显示打印

    因为在对数据表进行排版格式整理的时候,发现Arial Narrow字体是不支持中文的,所以中文默认为宋体,但是显示出来却不是宋体,需要双击单元格中文才显示为宋体,这样打印出来才为宋体 但是如果有很多单 ...

  7. 17-MySQL-Ubuntu-数据表的查询-分页(六)

    分页(limit) 注: (1)limit位于SQL语句的最后面; (2)limit 2; 2表示查询前两条数据; (3)limit 0,2;  0表示查询第1页的起始数据的下标,2表示每页有两条数据 ...

  8. SQL Server实现跨库查询(跨库select insert)

    方法一: select  * from servername.dbo.tablename 方法二: select * from OPENDATASOURCE(         'SQLOLEDB',  ...

  9. C 二维数组与指针

    http://c.biancheng.net/view/2022.html 1. 区分指针数组和数组指针 指针数组:存放指针的数组,如 int *pstr[5] = NULL; 数组中每个元素存放的是 ...

  10. 多个for循环使用

    for循环 例子 语法 vue.js的for循环 <div id="myfor"><li v-for="student in studentList&q ...