【入門】Hysteresis Threshold(MATLAB)【数値計算】

【入門】Hysteresis Threshold(MATLAB)【数値計算】 数値計算
【入門】Hysteresis Threshold(MATLAB)【数値計算】

MATLAB、Python、Scilab、Julia比較ページはこちら
https://www.simulationroom999.com/blog/comparison-of-matlab-python-scilab/

はじめに

の、
MATLAB,Python,Scilab,Julia比較 第3章 その53【Hysteresis Threshold④】

非極大値抑制にHysteresis Thresholdを加えた、Canny法を実施して、2値化を行う。
今回はMATLABで実施する。

【再掲】Hysteresis Thresholdを実施するための手順

今回から、Hysteresis Thresholdで2値化をやる。
まずはMATLAB。

最初に手順を再掲しておこう。
トータルの手法としてはCanny法という名称になる。

  • Sobelフィルタ等の微分フィルタで以下を推定
    • x軸、y軸の濃淡変化量
    • 変化強度(ノルム)
  • 「x軸、y軸の濃淡変化量」から勾配方向角を推定
    • arntan関数を利用
  • 勾配方向を垂直(UD)、水平(LR)、斜め右上から右下(RULD)、斜め左上から右下の4パターンに丸め。
  • 勾配方向角に応じて極大値評価をして非極大値だったら「変化強度(ノルム)」 を0値埋め
  • Hysteresis Threshold
    • High以上は白
    • Low未満は黒
    • High-Lowの間の場合は周辺を探査し、エッジが居れば白、いなければ黒
  • 画像出力

MATLABコード

MATLABコードは以下になる。

convolution2d.m

function out = convolution2d(img, kernel)
    [m, n] = size(kernel);  % カーネルサイズ取得
    
    % カーネル中心からみた幅
    dy = int64(fix((m-1)/2));  % カーネル上下幅
    dx = int64(fix((n-1)/2));  % カーネル左右幅
    
    [h, w] = size(img);         % イメージサイズ
    out = zeros(h, w);          % 出力用イメージ
    
    % 畳み込み
    for y = dy+1:(h - dy)
        for x = dx+1:(w-dx)
            out(y, x) = sum( sum( double(img(y-dy:y+dy, x-dx:x+dx)).*kernel ) );
            %imgTmp = double(img(y-dy:y+dy, x-dx:x+dx));
            %out(y, x) = imgTmp(:)'*kernel(:);
        end
    end
end

non_maximum_suppression.m

function out = non_maximum_suppression(G, theta)
    [h, w] = size(G);
    out = G;
    
    % 勾配方向を4方向(LR,UD,RULD,LURD)に近似
    theta( -22.5 <= theta & theta <   22.5) = 0;   % LR    ─
    theta(  22.5 <= theta & theta <   67.5) = 45;  % RULD  /
    theta(  67.5 <= theta & theta <  112.5) = 90;  % UD    │
    theta( 112.5 <= theta & theta <  157.5) = 135; % LURD  \
    theta( 157.5 <= theta & theta <  180.0) = 0;   % LR    ─
    theta(-180.0 <= theta & theta < -157.5) = 0;   % LR    ─
    theta(-157.5 <= theta & theta < -112.5) = 45;  % RULD  /
    theta(-112.5 <= theta & theta <  -67.5) = 90;  % UD    │
    theta( -67.5 <= theta & theta <  -22.5) = 135; % LURD  \
    
    % 現画素の勾配方向に接する2つの画素値を比較し、現画素が極大値でなければ0にする。
    for y = 2:(h - 1)
        for x = 2:(w - 1)
            if theta(y,x) == 0       % LR    ─
                if (G(y,x) < G(y,x+1)) || (G(y,x) < G(y,x-1))
                    out(y,x) = 0;
                end
            elseif theta(y,x) == 45  % RULD  /
                if (G(y,x) < G(y-1,x+1)) || (G(y,x) < G(y+1,x-1))
                    out(y,x) = 0;
                end
            elseif theta(y,x) == 90  % UD    │
                if (G(y,x) < G(y+1,x)) || (G(y,x) < G(y-1,x))
                    out(y,x) = 0;
                end
            else                     % LURD  \
                if (G(y,x) < G(y+1,x+1)) || (G(y,x) < G(y-1,x-1))
                    out(y,x) = 0;
                end
            end
        end
    end
end

hysteresis_threshold.m

function out = hysteresis_threshold(img, low, high, r)
    [h, w] = size(img);
    out = img;
    
    for y = (1+r):(h-r-1)
        for x = (1+r):(w-r-1)
           % 最大閾値より大きければ「エッジ」
           if img(y,x) >= high
               out(y,x) = 255;
           % 最小閾値より小さければ「非エッジ」
           elseif img(y,x) < low
               out(y,x) = 0;
           % 最小閾値と最大閾値の間で、半径rの範囲内に「エッジ」が1つでもあればエッジと判定
           else
               if max(img(y-r:y+r+1, x-r:x+r+1)) >= high
                   out(y,x) = 255;
               else
                   out(y,x) = 0;
               end
           end
        end
    end
end

canny_test.m

function [] = canny_test()
    % 入力画像の読み込み
    img = imread('dog.jpg');
    r = img(:,:,1);
    g = img(:,:,2);
    b = img(:,:,3);
    
    % ガウシアンフィルタ用のkernel
    kernel_gauss = [ 1/16  2/16  1/16; ...
                     2/16  4/16  2/16; ...
                     1/16  2/16  1/16];
    % Sobelフィルタ用のKernel
    kernel_sx = [-1  0  1;...
                 -2  0  2;...
                 -1  0  1];
    kernel_sy = kernel_sx';
    
    % SDTVグレースケール
    gray_sdtv = 0.2990 * r + 0.5870 * g + 0.1140 * b;
    
    % ガウシアンフィルタ
    img_g = convolution2d(gray_sdtv, kernel_gauss);
    
    % Sobelフィルタ
    Gx = convolution2d(img_g, kernel_sx);
    Gy = convolution2d(img_g, kernel_sy);
    
    % 勾配強度と角度
    G = sqrt( Gx.^2 + Gy.^2 );
    theta = atan2(Gy, Gx) * 180 / pi;
    
    % 極大値以外を除去(Non maximum Suppression)
    G_nms = non_maximum_suppression(G, theta);
    
    % Hysteresis Threshold(最小閾値除去、最大閾値以上を残し、且つそこと繋がっている最小閾値以上を残す)
    G_canny = hysteresis_threshold(G_nms, 30, 65, 1); % 0 or 255に2値化
    imwrite(uint8(G_canny),'dog_canny.jpg'); 
end

処理結果

処理結果は以下になる。

犬と自転車(Canny法)MATLAB
犬と自転車(Canny法)MATLAB

考察

結果としては見た通りで、ちゃんと2値化ができている。

ソースコードとしてはかなり増えてしまっているが、
ほとんどは前回までの非極大値抑制と同一。
今回新規に追加したのは、hysteresis_thresholdとcanny_testだけになる。

結果論になるが、ステップアップ式にここまで来た感じになる。

まとめ

  • 非極大値抑制にHysteresis Thresholdを加えた、Canny法による2値化をMATLABで実施。
  • 基本的にはいままでのコードを再利用。
  • 追加分はHysteresis Thresholdの部分。

MATLAB、Python、Scilab、Julia比較ページはこちら

コメント

タイトルとURLをコピーしました