【Matlab】【原創】【數字圖像處理】經典Canny邊緣檢測算子的手動實現

1、使用高斯濾波器平滑輸入圖像,消除高頻噪聲
2、計算輸入圖像的梯度幅值與梯度角度。。。使用sobel算子計算梯度
3、進行非極大值抑制
(1)首先,我們創建角度模板,規定邊緣的四個方向:-45°、0°、45°、90°,並創建一個矩陣,儲存3x3鄰域中,與上述4個方向對應的座標偏移量,即相對於鄰域中心點的右下角、下方、左下角、右方。
(2)其次,由於我們使用的arctan函數計算梯度方向,那麼得到的角度矩陣中的值域就爲-90°到90°,爲了方便下一步尋找在上一步的四個角度中哪一個與梯度方向最近,我們要將梯度方向進行歸一化,即將-90°到90°的值域歸一化到-67.5°到112.5°(其中將小於-67.5°的部分+180度旋轉到對面去)。在這裏插入圖片描述
(3)將每一個像素對應的梯度方向與四個角度做差,求出最小值,這裏我們需要最小值對應的索引,例如索引值1對應-45度,即梯度方向離-45度最近,那麼我們將鄰域中心像素的座標加上-45度對應的偏移量即可,即儲存有座標偏移量矩陣的第一行數據。通過直接調用索引值,我們就能實現在像素鄰域中相應角度的像素選取。
(4)當求出所有距離像素梯度方向最近的角度之後,我們得到了元素爲1、2、3、4的矩陣,即對應角度的索引值。現在,我們計算在每個3x3鄰域中,中心像素與其相應最近角度所在直線方向上的兩個8鄰接像素之間的關係,例如如果某個中心像素的梯度方向距離0°最近,那麼就考察該像素的梯度幅值與它上面和下面的像素的梯度幅值之間的關係。
(5)如果鄰域中心像素的梯度幅值都不小於它的兩個鄰居的值,那麼就保留該梯度幅值;如果情況相反,則丟棄該位置的梯度幅值。
4、進行滯後閾值處理
人爲設置一高一低兩個閾值,認爲大於高閾值的梯度幅值所在位置爲強邊緣的位置,在高閾值和低閾值之間的梯度幅值所在位置爲弱邊緣的位置
5、進行邊緣連接
在存儲有強邊緣的圖像中定位所有強邊緣像素的位置,並將該位置記作是最終的邊緣,然後在每個強邊緣像素的八鄰域中,檢測是否存在弱邊緣像素,如若存在,則將其也記作是最終的邊緣。即:將強邊緣看成是軌道,沿着該軌道檢測沿途的3x3區域中是否存在弱邊緣。
6、進行邊緣細化
由於檢測出的邊緣比較粗,爲了方便使用,利用數學形態學進行細化。

在本例中,使用大小爲13x13,標準差爲2的高斯窗進行高斯濾波。(注:爲了保證在n*n的區域中,二維高斯概率密度函數能包含有99.7%的概率,需要將n設置爲6倍標準差的最小正奇數),高低閾值分別爲0.1與0.04。

f=imread('Fig1006(a).tif');
f=double(f);
f=f/255;
%高斯濾波,平滑圖像,去除高頻噪點
G=fspecial('gaussian',13,2);
f_gaussian=imfilter(f,G,'replicate');
figure(1),imshow(f_gaussian);

%使用Sobel算子計算梯度
S_x=fspecial('sobel');
f_sobel_x=imfilter(f_gaussian,S_x,'replicate');
S_y=S_x';
f_sobel_y=imfilter(f_gaussian,S_y,'replicate');
f_sobel_amp=sqrt(f_sobel_x.^2+f_sobel_y.^2);
f_sobel_ang=atan(f_sobel_y./f_sobel_x);%注意arctan函數的值域爲-pi/2到pi/2,即f_sobel_ang中的值只會在這兩個數之間
figure(2),imshow(f_sobel_amp);
figure(3),imshow(f_sobel_ang);

%非最大值抑制
%將4個基本邊緣方向的角度值存放在向量direction中
direction=[-45 0 45 90];
%將4個基本邊緣方向的角度值對應的鄰域中相對於中心像素位置的平移量保存在2維矩陣direction_offset中
direction_offset=[1 -1;1 0;1 1;0 1];
%將角度單位轉成弧度單位
direction=direction/180*pi;
%將f_sobel_ang中的弧度值轉化進-67.5度對應的弧度到90度對應的弧度範圍間
f_sobel_ang_trans=zeros(size(f_sobel_ang));
for i=1:size(f_sobel_ang,1)
    for j=1:size(f_sobel_ang,2)
        if f_sobel_ang(i,j)<-67.5/180*pi
            f_sobel_ang_trans(i,j)=f_sobel_ang(i,j)+pi;
        else
            f_sobel_ang_trans(i,j)=f_sobel_ang(i,j);
        end
    end
end
f_sobel_ang_appro=zeros(size(f_sobel_ang));%保存弧度近似的結果
%尋找與f_sobel_ang_trans中的每個弧度最近的向量direction中的弧度對應的索引值,並將近似弧度的索引值保存在f_sobel_ang_appro中
for i=1:size(f_sobel_ang_trans,1)
    for j=1:size(f_sobel_ang_trans,2)
        [~,indices]=min(abs(f_sobel_ang_trans(i,j)-direction));
        f_sobel_ang_appro(i,j)=indices;
    end
end
f_sup=zeros(size(f,1)+2,size(f,2)+2);%保存最大值抑制的結果
%在f_sobel_amp的任一3x3鄰域中判斷鄰域中心值是否全部大於沿近似角度方向和反方向上的兩個鄰域值。
%爲避免滑窗時溢出,先進行圖像邊界填充
f_sobel_amp=padarray(f_sobel_amp,[1,1],'replicate');
%爲方便角度信息使用,對f_sobel_ang_appro進行圖像邊界填充
f_sobel_ang_appro=padarray(f_sobel_ang_appro,[1,1],'replicate');
for i=2:size(f_sobel_amp,1)-1
    for j=2:size(f_sobel_amp,2)-1
        a=[i,j]+direction_offset(f_sobel_ang_appro(i,j),:);%鄰域像素的座標計算
        if f_sobel_amp(i,j) >= f_sobel_amp(a(1),a(2)) && ... %a(1)爲行數,a(2)爲列數
           f_sobel_amp(i,j) >= f_sobel_amp(a(1),a(2)) 
            f_sup(i,j)=f_sobel_amp(i,j);
        else
            f_sup(i,j)=0;
        end
    end
end
%將最大值抑制結果的尺寸調整成輸入圖像的尺寸
f_sup=f_sup(2:size(f_sup,1)-1,2:size(f_sup,2)-1);
figure(4),imshow(f_sup);

%滯後閾值處理
%Canny建議低閾值:高閾值=1:3
low=0.04;
high=0.1;
f_sup_low=(f_sup>=low);%儲存強邊緣+弱邊緣
f_sup_high=(f_sup>=high);%儲存強邊緣
f_sup_thresh=f_sup_low-f_sup_high;%儲存弱邊緣,即low<=f_sup_thresh<=high
figure(5),imshow(f_sup_thresh);
figure(6),imshow(f_sup_high);

%邊緣連接操作
%由於需要對圖像進行3x3鄰域的滑窗操作,所以需要擴展圖像大小
f_sup_thresh=padarray(f_sup_low,[1,1],'replicate');
f_sup_high=padarray(f_sup_high,[1,1],'replicate');
f_sup_conn=zeros(size(f_sup_thresh));
neigh=[-1 -1;-1 0;-1 1;0 -1;0 1;1 -1;1 0;1 1];%8鄰域座標偏移模板
for i=2:size(f_sup_conn,1)-1
    for j=2:size(f_sup_conn,2)-1
        %判斷在第i行,第j列是否存在強邊緣
         if f_sup_high(i,j)==1
             %若存在,則將該點看作是邊緣點,顯示在f_sup_conn中
             f_sup_conn(i,j)=1;
             %在點(i,j)的8鄰域中進行操作,檢查是否存在弱邊緣點
             for k=1:8
                 %若8鄰域中的某一點是弱邊緣點,則將該點看做是邊緣點,顯示在f_sup_conn中
                 if f_sup_thresh(i+neigh(k,1),j+neigh(k,2))==1
                     f_sup_conn(i+neigh(k,1),j+neigh(k,2))=1;
                 end
             end
         end
    end
end
f_sup_conn=f_sup_conn(2:size(f_sup_conn,1)-1,2:size(f_sup_conn,2)-1);
%使用圖像形態學進行邊緣細化
f_sup_conn=bwmorph(f_sup_conn,'skel',Inf);
figure(7),imshow(f_sup_conn);

%對比
f_comparison=edge(f,'canny',[low,high],2);
figure(8),imshow(f_comparison);

實驗結果:
1、輸入圖像:
在這裏插入圖片描述

2、先高斯濾波,後使用sobel算子得到的梯度幅值圖像
在這裏插入圖片描述
3、梯度方向圖像
在這裏插入圖片描述
上圖中,亮度相同的區域代表梯度方向相同

4、非極大值抑制
在這裏插入圖片描述
與梯度幅值圖進行比較,可以看出,非極大值抑制後,抑制了由高斯濾波而產生的邊緣模糊,就像是沒有開啓抗鋸齒的遊戲效果。

5、弱邊緣圖像
在這裏插入圖片描述
6、強邊緣圖像
在這裏插入圖片描述
可以看到邊緣比較粗,需要細化。

7、邊緣連接並細化後的圖像
在這裏插入圖片描述
8、與matlab自帶的canny算子進行比較
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章