邊界偵測 (Edge Detection)
Posted on Fri 06 July 2018 in Digital Image Processing
通常在一張影像當中,我們會感興趣的地方會是亮度變化比較大的地方,因為這些地方可以被當作用來描述該影像的特徵,其中一種由亮度變化形成的特徵就是影像的邊界 (edge) 位置。
影像梯度 (Image gradient)
由於我們對影像有亮度變化的地方有興趣,因此需要一種數學描述這種變化,即為 image gradient。
Image gradient 是一 2D 向量 (vector),對應水平和垂直方向,方向定義為亮度最有可能增加的方向,簡單來說就是暗 -> 亮,長度對應變化率。在影像中一點\((x,y)\)的梯度可以表示為
$$\triangledown f = \begin{bmatrix}
\frac{\partial f}{\partial x} \\
\frac{\partial f}{\partial y}
\end{bmatrix} = \begin{bmatrix}
g_x \\
g_y \end{bmatrix},$$
其方向為
$$\theta = \tan^{-1} \begin{bmatrix}
\frac{g_y}{g_x}
\end{bmatrix},$$
其長度為
$$g = \sqrt{g_{x}^2+g_{y}^2}.$$
Sobel operator
Sobel operator 或 Sobel filter 是一種常被用來計算影像梯度運算子,其方法為利用\(3 \times 3\)的 kernel 對影像做 convolution ( 摺積 )。
假設\(\mathbf{A}\)為來源影像,\(\mathbf{G_x}\)與\(\mathbf{G_y}\)分別對應水平與垂直方向的梯度圖,代表來源影像中每一個點的梯度,則可以表示為
$$\mathbf{G_x}=\begin{bmatrix}
1 & 0 & -1\\
2 &0 & -2\\
1 & 0 & -1
\end{bmatrix} \ast \mathbf{A} \text{ and }
\mathbf{G_y}=\begin{bmatrix}
1 & 2 & 1\\
0 & 0 & 0\\
-1 & -2 & -1
\end{bmatrix} \ast \mathbf{A}.$$
所以梯度的強度圖可以寫成
$$\mathbf{G} = \sqrt{\mathbf{G_x}^2 + \mathbf{G_y}^2}.$$
同理梯度的方向圖可以寫成
$$\Theta = \tan^{-1} \begin{bmatrix}
\frac{\mathbf{G_y}}{\mathbf{G_x}}
\end{bmatrix}.$$
Canny edge detector
Canny edge detector 是一種用來偵測影像中邊界的演算法,基本版的流程分為五個步驟:
- 使用 Gaussian filter 平滑化影像,目的是為了降噪
- 計算影像梯度的強度
- 利用 non-maximum suppression 找出強邊界,比較每一個像素,只留下強度比相鄰像素的強度都強的像素
- 應用 double threshold 分出強邊界與弱邊界,設定 high threshold 與 low threshold,如果強度大於 high threshold 分為強邊界,若強度小於 high threshold 且大於 low threshold 分為弱邊界,其他則會被排除
- 排除沒有跟強邊界相連的弱邊界
範例
scikit-image 有實作 Sobel filter 與 Canny edge detector,Sobel filter 輸出為梯度強度影像,Canny edge detector 輸出為二值影像,如下所示:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from skimage.data import camera
from skimage.filters import sobel
from skimage import feature
image = camera()
edge_sobel = sobel(image)
edge_canny = feature.canny(image, sigma=2.0)
fig, ax = plt.subplots(ncols=2, sharex=True, sharey=True, figsize=(8, 4))
ax[0].imshow(edge_sobel, cmap=plt.cm.gray)
ax[0].set_title('Sobel Edge Detection', fontsize=12)
ax[0].axis('off')
ax[1].imshow(edge_canny, cmap=plt.cm.gray)
ax[1].set_title('Canny Edge Detection', fontsize=12)
ax[1].axis('off')
plt.show()