Canny演算法是邊緣檢測的一個經典演算法,比單純用一些微分算子來檢測的效果要好很多,其優勢有以下幾點:
- 邊緣誤檢與漏檢率低,
- 邊緣定位準確,且邊界較細,
- 自帶一定的濾噪功能,或者說,對噪聲的敏感度要比單純算子低,
- 具有多個可調整引數,可影響演算法的時間與時效,
但是Canny相比單純算子來說計算量偏大,下面簡單介紹演算法的程序,
影像去噪:
這一步不是必須的,一般噪聲少的圖,讓Canny自己應付就行,若噪聲較多,一般采用高斯濾波,濾波后,噪聲灰度下降,對邊緣的影響
小于噪點,
獲取梯度強度與方向:
用一階微分算子獲取梯度強度和方向,如sobel算子,強度與方向公式如下:

要用兩個矩陣分別存盤強度和方向,其中方向公式需要根據具體算子情況更改正負號,最終使上下左右、45度方向可以區分開來,例如下圖所示,
這里將上下放在(60,90)和(-90,-60)區間,因為它們在非極大抑制時取的都是上下鄰域;將左右放在(-30,30)區間,因為它們抑制時取的是
左右鄰域;將處于斜向上對角線的梯度角放在(30,60),它們在抑制時取45度的兩個鄰域;將斜向下對角線上的梯度角放在(-60,-30),它們在
抑制時取-45度的兩個鄰域,

非極大抑制:
該步驟目的是洗掉非邊緣像素,主要做法是對每個像素點,與它梯度方向的相鄰兩像素作灰度比較(比如左右兩個像素),如果該像素點
灰度比它兩個相鄰像素都大,則保留其灰度值,否則抑制(賦值0),
雙閾值處理:
設定一個高閾值和低閾值,對灰度大于高閾值的像素點,確認為強邊緣,賦值255,低于低閾值的點賦值0,介于之間的點保留其灰度值,作為弱邊緣,
滯后邊界追蹤:
這一步主要是處理弱邊緣,總的思路是找到每一個弱邊緣的連通域,判斷該連通域與強邊緣有無相鄰,如果存在至少一個像素與強邊緣相鄰,則將該連
通域都作為強邊緣(賦值255),如果沒有一個像素與強邊緣相鄰,則將整個連通域視為噪聲(賦值0),
所以,關鍵在于找到連通域,通常有DFS與BFS兩種演算法可以處理連通域問題,這里采用BFS演算法,思路如下:
1.掃描整個影像,判斷每個像素是否為弱邊緣,如果是,進入步驟2(查找連通域),
2.用connect陣列保存組成連通域的所有點;用weak陣列保存待檢查八領域的弱點;用checked陣列標記已被掃描過的弱點(也就是連通域里的點),標
記1表示已掃描;用變數real_edge記錄是否有檢測到強點,將剛才判定的弱邊緣點壓入connect、weak,checked相應位置標記為1,進入步驟3,
3.創建new_weak用來保存新的待檢測八領域的弱點,依次對weak中每個待檢測點,遍歷其八領域,找到所有未被checked標記的新弱點,壓入new_weak
、connect,checked相應位置標記為1,同時檢查八領域內是否有強邊緣,有則標記real_edge=1,對weak所有待檢測點檢查完后,如果new_weak不為
空,則將new_weak賦給weak,重復步驟3;如果new_weak為空,則表示一個連通域尋找完畢,進入步驟4,
4.如果real_edge=1,則將連通域每個點的灰度賦值255,否則賦值0;彈出connect、weak、checked中所有點,
5.重復步驟1-4,直道影像掃描完畢,
經過以上這些步驟,Canny演算法就已經實作了,我們要做的就是根據影像調整雙閾值和濾波強度,下面是Canny的一個處理實體,其中給出了單純sobel檢測和
Canny檢測的效果,通過下圖可以知道,Canny檢測的邊緣要薄的多,細節處理更好,噪聲也更少,

以下是matlab代碼實作:
%canny前先高斯濾波 function edge=canny(gaussianimg,lthres,hthres) sobel_operator_x=[-1,0,1; -2,0,2; -1,0,1]; sobel_operator_y=[-1,-2,-1; 0,0,0; 1,2,1]; %梯度強度 diff_x=filter2(sobel_operator_x,gaussianimg); diff_y=filter2(sobel_operator_y,gaussianimg); diff=uint8(sqrt((diff_x.^2+diff_y.^2)/32)); %梯度方向 [sizex,sizey]=size(gaussianimg); angle=zeros(sizex,sizey); edge=uint8(zeros(sizex,sizey)); for i=1:sizex for j=1:sizey angle(i,j)=atan(-diff_y(i,j)/diff_x(i,j))/pi*180; end end %非極大抑制,排除非邊緣像素 for i=2:sizex-1 for j=2:sizey-1 if (angle(i,j)>90||angle(i,j)<-90) break; elseif angle(i,j)>=60 || angle(i,j)<=-60 if (diff(i,j)>diff(i-1,j)&&diff(i,j)>=diff(i+1,j)) edge(i,j)=uint8(diff(i,j)); end elseif (angle(i,j)<=-30) if (diff(i,j)>diff(i-1,j-1)&&diff(i,j)>=diff(i+1,j+1)) edge(i,j)=uint8(diff(i,j)); end elseif angle(i,j)>=30 if (diff(i,j)>diff(i-1,j+1)&&diff(i,j)>=diff(i+1,j-1)) edge(i,j)=uint8(diff(i,j)); end elseif angle(i,j)<30||angle(i,j)>-30 if (diff(i,j)>diff(i,j-1)&&diff(i,j)>=diff(i,j+1)) edge(i,j)=uint8(diff(i,j)); end end end end %雙閾值 for i=1:sizex for j=1:sizey if (edge(i,j)>=hthres) edge(i,j)=255; %一定為邊緣 elseif (edge(i,j)<=lthres) edge(i,j)=0; %一定為非邊緣 end end end %候選邊緣,與已確定邊緣相連才認為是邊緣 for i=2:sizex-1 for j=2:sizey-1 if (edge(i,j)>0&&edge(i,j)<255) real_edge=0;%邊緣真偽標志 checked=zeros(sizex,sizey);%標記已經掃描過的弱點 weak=zeros(100,2);%存盤需要查看八領域的弱點 connect=zeros(100,2);%存盤一條聯通的所有弱點 weak_length=1; connect_length=1; %壓入第一個弱邊緣點 weak(weak_length,:)=[i,j]; connect(connect_length,:)=[i,j]; checked(i,j)=1; while(weak_length>0) new_weak=zeros(100,2); new_weak_length=0; for k=1:weak_length %搜索當前弱點的八領域 x=weak(k,1);y=weak(k,2); if (x>=2&&x<=sizex-1&&y>=2&&y<=sizey-1) for m=x-1:x+1 for n=y-1:y+1 if edge(m,n)>0&&edge(m,n)<255&&checked(m,n)==0%領域有弱點且未被掃描過 new_weak_length=new_weak_length+1; connect_length=connect_length+1; new_weak(weak_length,:)=[m,n];%壓入新弱點集合 connect(connect_length,:)=[m,n]; %壓入連通域 checked(m,n)=1; %標記已掃描 elseif edge(m,n)==255 real_edge=1; %邊緣為真,等待連通域全部被識別 end end end end end weak_length=new_weak_length;%當前深度的弱點集合全部檢查過八領域,開始檢查新一深度的弱點集合 weak=new_weak;%如果新集合沒有弱點,則跳出while end %一個連通域已在connect里形成 for z=1:connect_length edge(connect(z,1),connect(z,2))=uint8((255* real_edge)); end end end end end
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/115724.html
標籤:其他
上一篇:量化投資-資料挖掘技術與實踐
