zoukankan      html  css  js  c++  java
  • Matlab神经网络验证码识别

    本文,将会简述如何利用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

  • 相关阅读:
    vue学习6
    vue学习5
    vue学习3
    vue学习2
    vue学习1
    idea快速查找和替换快捷键
    mysql三元表达式
    1 Java Lambda表达式forEach无法跳出循环的解决思路
    6 Mybatis Plus and 和 or,分页Page使用
    4 Mybatis Plus使用redis作为二级缓存
  • 原文地址:https://www.cnblogs.com/mrcharles/p/11879819.html
Copyright © 2011-2022 走看看