1. 邊緣檢測的概念
邊緣檢測是圖像處理與計算機視覺中極為重要的一種分析圖像的方法,至少在我做圖像分析與識別時,邊緣是我最喜歡的圖像特征。邊緣檢測的目的就是找到圖像中亮度變化劇烈的像素點構成的集合,表現出來往往是輪廓。如果圖像中邊緣能夠精確的測量和定位,那么,就意味着實際的物體能夠被定位和測量,包括物體的面積、物體的直徑、物體的形狀等就能被測量。在對現實世界的圖像采集中,有下面4種情況會表現在圖像中時形成一個邊緣。
- 深度的不連續(物體處在不同的物平面上);
- 表面方向的不連續(如正方體的不同的兩個面);
- 物體材料不同(這樣會導致光的反射系數不同);
- 場景中光照不同(如被樹萌投向的地面);
上面的圖像是圖像中水平方向7個像素點的灰度值顯示效果,我們很容易地判斷在第4和第5個像素之間有一個邊緣,因為它倆之間發生了強烈的灰度跳變。在實際的邊緣檢測中,邊緣遠沒有上圖這樣簡單明顯,我們需要取對應的閾值來區分出它們。
2. 邊緣檢測的基本方法
2.1 一階微分邊緣算子
一階微分邊緣算子也稱為梯度邊緣算子,它是利用圖像在邊緣處的階躍性,即圖像梯度在邊緣取得極大值的特性進行邊緣檢測。梯度是
一個矢量,它具有方向$\theta$和模$|\Delta I|$:
$$\Delta I = \begin{pmatrix} \frac{\partial I}{\partial x} \\ \frac{\partial I}{\partial y} \end{pmatrix}$$
$$|\Delta I| = \sqrt{(\frac{\partial I}{\partial x} )^2+(\frac{\partial I}{\partial y} )^2} = \sqrt{I_x^2+I_y^2} $$
$$ \theta = arctan(I_y / I_x)$$
梯度的方向提供了邊緣的趨勢信息,因為梯度方向始終是垂直於邊緣方向,梯度的模值大小提供了邊緣的強度信息。
在實際使用中,通常利用有限差分進行梯度近似。對於上面的公式,我們有如下的近似:
$$\frac{\partial I}{\partial x} = \lim_{h\to 0}\frac{I(x+\Delta x,y)-I(x,y)}{\Delta x} \approx I(x+1,y)-I(x,y),(\Delta x = 1)$$
$$\frac{\partial I}{\partial y} = \lim_{h\to 0}\frac{I(x,y+\Delta xy)-I(x,y)}{\Delta y} \approx I(x,y+1)-I(x,y),(\Delta y = 1)$$
2.2 Roberts邊緣檢測算子
1963年,Roberts提出了這種尋找邊緣的算子。Roberts邊緣算子是一個2x2的模板,采用的是對角方向相鄰的兩個像素之差。從圖像處理的實際效果來看,邊緣定位較准,對噪聲敏感。在Roberts檢測算子中:
$$\frac{\partial I}{\partial x} = I(i,j)-I(i+1,j+1)$$
$$\frac{\partial I}{\partial y} = I(i+1,j)-I(i,j+1)$$
可以導出Roberts在點$(i+1/2,j+1/2)$處的水平與豎直邊緣檢測卷積核為:
$$m_x = \begin{bmatrix}1&0 \\ 0&-1\end{bmatrix}, m_y=\begin{bmatrix}0&-1 \\ 1&0\end{bmatrix}$$
2.3 Prewitt邊緣檢測算子
Prewitt利用周圍鄰域8個點的灰度值來估計中心的梯度,它的梯度計算公式如下:
$$\frac{\partial I}{\partial x} = I(i-1,j+1)+I(i,j+1)+I(i+1,j+1) – I(i-1, j-1)-I(i,j-1)-I(i+1,j-1)$$
$$\frac{\partial I}{\partial y} =-I(i+1,j-1)+I(i+1,j)+I(i+1,j+1) - I(i-1,j-1)+I(i-1,j)+I(i-1,j+1) $$
所以,Prewitt的卷積核為:
$$m_x = \begin{bmatrix}-1&0&+1 \\ -1&0&+1 \\ –1&0&+1\end{bmatrix}, m_y=\begin{bmatrix}-1&-1&-1 \\ 0&0&0 \\ +1&+1&+1\end{bmatrix}$$
2.4 Sobel邊緣檢測算子
比起Prewitt算子,Sobel也是用周圍8個像素來估計中心像素的梯度,但是Sobel算子認為靠近中心像素的點應該給予更高的權重,所以Sobel算子把與中心像素4鄰接的像素的權重設置為2或-2。
Sobel邊緣檢測算子的卷積核為:
$$m_x = \begin{bmatrix}-1&0&+1 \\ -2&0&+2\\ –1&0&+1\end{bmatrix}, m_y=\begin{bmatrix}-1&-2&-1 \\ 0&0&0 \\ +1&+2&+1\end{bmatrix}$$
Sobel進行邊緣檢測的實現可以參考我原來寫的一篇博文:圖像特征檢測:sobel邊緣檢測,重要的是梯度圖像計算后的閾值的確定與邊緣的非極大值抑制算法,Roberts與Prewitt原理與sobel一致。
Sobel邊緣檢測的C++實現:https://github.com/RonnyYoung/ImageFeatures/blob/master/source/sobel.cpp
3. 二階微分算子
學過微積分我們都知道,邊緣即是圖像的一階導數局部最大值的地方,那么也意味着該點的二階導數為零。二階微分邊緣檢測算子就是利用圖像在邊緣處的階躍性導致圖像二階微分在邊緣處出現零值這一特性進行邊緣檢測的。
對於圖像的二階微分可以用拉普拉斯算子來表示:
$$\nabla^2I = \frac{\partial^2I}{\partial x^2}+\frac{\partial^2I}{\partial y^2}$$
我們在像素點$(i,j)$的$3\times 3$的鄰域內,可以有如下的近似:
$$\frac{\partial^2I}{\partial x^2} = I(i,j+1)-2I(i,j)+I(i,j-1)$$
$$\frac{\partial^2I}{\partial y^2}=I(i+1,j)-2I(i,j)+I(i-1,j)$$
$$\nabla^2I = –4I(i,j)+I(i,j+1)+I(i,j-1)+I(i+1,j)+I(i-1,j)$$
對應的二階微分卷積核為:
$$\boldsymbol{m} = \begin{bmatrix}0&1&0 \\ 1&4&1\\ 0&1&0\end{bmatrix}$$
所以二階微分檢測邊緣的方法就分兩步:1)用上面的Laplace核與圖像進行卷積;2)對卷積后的圖像,取得那些卷積結果為0的點。
雖然上述使用二階微分檢測邊緣的方法簡單,但它的缺點是對噪聲十分敏感,同時也沒有能夠提供邊緣的方向信息。為了實現對噪聲的抑制,Marr等提出了LOG的方法。
為了減少噪聲對邊緣的影響,首先圖像要進行低通濾波,LOG采用了高斯函數作為低通濾波器。高斯函數為:
$$G(x,y)=\frac{1}{2\pi\sigma^2}e^{-\frac{x^2+y^2}{2\sigma^2}}$$
上面的公式中$\sigma$決定了對圖像的平滑程度。高斯函數生成的濾波模板尺寸一般設定為$6\sigma +1$(加1是會了使濾波器的尺寸為奇數)。使用高斯函數對圖像進行濾波並對圖像濾波結果進行二階微分運算的過程,可以轉換為先對高斯函數進行二階微分,再利用高斯函數的二階微分結果對圖像進行卷積運算:
$$\nabla^2[G(x,y)*f(x,y)] = \nabla^2[G(x,y)]*f(x,y)$$
$$\nabla^2G(x,y) = -\frac{1}{2\pi\sigma^4}[1-\frac{x^2+y^2}{\sigma^2}]\cdot exp(-\frac{x^2+y^2}{2\sigma^2})$$
關於LOG算子的計算在上一篇文章:圖像特征提取:斑點檢測中有實現的代碼。
4. Canny邊緣檢測
canny邊緣檢測實際上是一種一階微分算子檢測算法,但為什么這里拿出來說呢,因為它幾乎是邊緣檢測算子中最為常用的一種,也是個人認為現在最優秀的邊緣檢測算子。Canny提出了邊緣檢測算子優劣評判的三條標准:
- 高的檢測率。邊緣檢測算子應該只對邊緣進行響應,檢測算子不漏檢任何邊緣,也不應該將非邊緣標記為邊緣。
- 精確定位。檢測到的邊緣與實際邊緣之間的距離要盡可能的小。
- 明確的響應。對每一條邊緣只有一次響應,只得到一個點。
Canny邊緣檢測之所以優秀是因為它在一階微分算子的基礎上,增加了非最大值抑制和雙閾值兩項改進。利用非極大值抑制不僅可以有效地抑制多響應邊緣,而且還可以提高邊緣的定位精度;利用雙閾值可以有效減少邊緣的漏檢率。
Canny邊緣檢測主要分四步進行:
- 去噪聲;
- 計算梯度與方向角;
- 非最大值抑制;
- 滯后閾值化;
其中前兩步很簡單,先用一個高斯濾波器對圖像進行濾波,然后用Sobel水平和豎直檢測子與圖像卷積,來計算梯度和方向角。
非極大值抑制
圖像梯度幅值矩陣中的元素值越大,說明圖像中該點的梯度值越大,但這不不能說明該點就是邊緣(這僅僅是屬於圖像增強的過程)。在Canny算法中,非極大值抑制是進行邊緣檢測的重要步驟,通俗意義上是指尋找像素點局部最大值,將非極大值點所對應的灰度值置為0,這樣可以剔除掉一大部分非邊緣的點。
根據上圖可知,要進行非極大值抑制,就首先要確定像素點C的灰度值在其8值鄰域內是否為最大。圖中藍色的線條方向為C點的梯度方向,這樣就可以確定其局部的最大值肯定分布在這條線上,也即出了C點外,梯度方向的交點dTmp1和dTmp2這兩個點的值也可能會是局部最大值。因此,判斷C點灰度與這兩個點灰度大小即可判斷C點是否為其鄰域內的局部最大灰度點。如果經過判斷,C點灰度值小於這兩個點中的任一個,那就說明C點不是局部極大值,那么則可以排除C點為邊緣。這就是非極大值抑制的工作原理。
在理解的過程中需要注意以下兩點:
- 中非最大抑制是回答這樣一個問題:“當前的梯度值在梯度方向上是一個局部最大值嗎?” 所以,要把當前位置的梯度值與梯度方向上兩側的梯度值進行比較;
- 梯度方向垂直於邊緣方向。
但實際上,我們只能得到C點鄰域的8個點的值,而dTmp1和dTmp2並不在其中,要得到這兩個值就需要對該兩個點兩端的已知灰度進行線性插值,也即根據圖中的g1和g2對dTmp1進行插值,根據g3和g4對dTmp2進行插值,這要用到其梯度方向。
滯后閾值化
由於噪聲的影響,經常會在本應該連續的邊緣出現斷裂的問題。滯后閾值化設定兩個閾值:一個為高閾值$T_h$,一個為低閾值$T_l$。如果任何像素邊緣算子的影響超過高閾值,將這些像素標記為邊緣;響應超過低閾值(高低閾值之間)的像素,如果與已經標記為邊緣的像素4-鄰接或8-鄰接,則將這些像素也標記為邊緣。所以不整個過程描述如下:
- 如果該像素的梯度值小於$T_l$,則該像素為非邊緣像素;
- 如果該像素的梯度值大於$T_h$,則該像素為邊緣像素;
- 如果該像素的梯度值介於$T_l$與$T_h$之間,需要進一步檢測該像素的$3\times 3$鄰域內的8個點,如果這8個點內有一個或以上的點梯度超過了$T_h$,則該像素為邊緣像素,否則不是邊緣像素。
Canny邊緣檢測的實現
https://github.com/RonnyYoung/ImageFeatures/blob/master/source/canny.cpp
5. 參考資料
1. 《圖像局部不變特征與描述》王永明,王貴錦。