1. 程式人生 > >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的離散點

我們看看目標的圖片:
這裡寫圖片描述

有很多隨機的畫素點干擾,我們需要將這些畫素點去除,然後進行影象切割。

切割圖片實際上很簡單,就是對圖片中每行每列進行統計,然後將形成的波形進行掃描,每個從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

我們這個函式實現了對圖片的預處理工作,成功的將大部分圖片分割成了小圖片,放到返回的元胞陣列中,但這還有一個重要的問題,就是切割後的圖片並不等大小。

並且,我們為了讓這些圖片能夠方便的進行訓練,希望將他們歸好類別,方便標記。將影象等大小十分簡單,只需要將影象的最大的長和寬找到,然後對矩陣進行擴充套件,多餘的位置補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

分別用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

然後訓練並比較正確與否:

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

經過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

但遺憾的是,識別正確率並不是100%,而是70%,由於有3組資料在預處理時失敗了,並沒有被正確的二值化,造成了無法識別,但可以看出,神經網路的識別正確率還是相當高的。

深度卷積網路的影象識別

我們這裡使用了一個流行的深度學習工具包DeepLearnToolbox,這個工具包可以在github上被找到。
將其下載下來,然後新增兩個path路徑,將其引用:

path(path, 'DeepLearnToolbox-master/CNN/')
path(path, 'DeepLearnToolbox-master/util/')

然後我們構建一個卷積網路的結構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);

而卷積網路的配置如下:

    % 學習率  
    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;  %如果已經訓練過,載入儲存的網路就可以了

這樣我們來觀察一下網路的結構,這是一個複雜的網路,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);  

這裡寫圖片描述

附: 原始碼倉庫