如何检测边缘
利用差分,设置一个阈值,如果差分得到的数值大于此阈值,就可以视为边缘。
计算过程
- 使用相应边缘检测滤波器(如果是卷积核需要进行上下左右翻转);
- 按照卷积方式计算边缘检测结果。
常见的边缘检测算子:
- Robert算子:
[ 1 0 0 − 1 ] \begin{bmatrix} 1 & 0\\ 0 & -1 \end{bmatrix} [100−1]
、
[ 0 1 − 1 0 ] \begin{bmatrix} 0 & 1\\ -1 & 0 \end{bmatrix} [0−110]
类似一维差分,对于图像对应的二维矩阵
E x = ∂ f ( x , y ) ∂ x = f ( x , y ) − f ( x − 1 , y − 1 ) E_x = \frac{\partial f(x,y)}{\partial x} = f(x,y) - f(x-1,y-1) Ex=∂x∂f(x,y)=f(x,y)−f(x−1,y−1)
E y = ∂ f ∂ y = f ( x − 1 , y ) − f ( x , y − 1 ) E_y = \frac{\partial f}{\partial y} = f(x-1,y) - f(x,y-1) Ey=∂y∂f=f(x−1,y)−f(x,y−1) - Sobel算子卷积核:最常用
[ − 1 0 1 − 2 − 1 2 − 1 0 1 ] \begin{bmatrix} -1 & 0 & 1\\ -2 & -1 & 2\\ -1 & 0 & 1 \end{bmatrix} ⎣⎡−1−2−10−10121⎦⎤
、
[ 1 2 1 0 0 0 − 1 − 2 − 1 ] \begin{bmatrix} 1 & 2 & 1\\ 0 & 0 & 0\\ -1 & -2 & -1 \end{bmatrix} ⎣⎡10−120−210−1⎦⎤
∂ f ( x , y ) ∂ x = [ f ( x − 1 , y − 1 ) + 2 f ( x − 1 , y ) + f ( x − 1 , y + 1 ) ] − [ f ( x + 1 , y − 1 ) + 2 f ( x + 1 , y ) + f ( x + 1 , y + 1 ) ] \frac{\partial f(x,y)}{\partial x} = [f(x-1,y-1) + 2f(x-1,y) + f(x-1,y+1)] - [f(x+1,y-1) + 2f(x+1,y) + f(x+1,y+1)] ∂x∂f(x,y)=[f(x−1,y−1)+2f(x−1,y)+f(x−1,y+1)]−[f(x+1,y−1)+2f(x+1,y)+f(x+1,y+1)]
∂ f ( x , y ) ∂ y = [ f ( x − 1 , y + 1 ) + 2 f ( x , y + 1 ) + f ( x + 1 , y − 1 ) ] − [ f ( x − 1 , y − 1 ) + 2 f ( x , y ) + f ( x + 1 , y − 1 ) ] \frac{\partial f(x,y)}{\partial y} = [f(x-1,y+1) + 2f(x,y+1) + f(x+1,y-1)] - [f(x-1,y-1) + 2f(x,y) + f(x+1,y-1)] ∂y∂f(x,y)=[f(x−1,y+1)+2f(x,y+1)+f(x+1,y−1)]−[f(x−1,y−1)+2f(x,y)+f(x+1,y−1)]
边缘强度:
M ( x , y ) = E x 2 ( x , y ) + E y 2 ( x , y ) M(x,y) = \sqrt{{E_x}^2(x,y)+{E_y}^2(x,y)} M(x,y)=Ex2(x,y)+Ey2(x,y)
边缘方向:
θ ( x , y ) = t a n − 1 ( E y ( x , y ) E x ( x , y ) ) \theta(x,y) = tan^{-1}(\frac{E_y(x,y)}{E_x(x,y)}) θ(x,y)=tan−1(Ex(x,y)Ey(x,y)) - Laplace算子:
△ f ( x , y ) = ∇ 2 f ( x , y ) = ∂ 2 f ∂ x 2 + ∂ 2 f ∂ y 2 = f ( x + 1 , y ) + f ( x − 1 , y ) + f ( x , y + 1 ) + f ( x , y − 1 ) − 4 f ( x , y ) \bigtriangleup f(x,y) = \nabla^2f(x,y) = \frac{\partial ^2f}{\partial x^2} + \frac{\partial ^2f}{\partial y^2} = f(x+1,y) + f(x-1,y) + f(x,y+1) + f(x,y-1) - 4f(x,y) △f(x,y)=∇2f(x,y)=∂x2∂2f+∂y2∂2f=f(x+1,y)+f(x−1,y)+f(x,y+1)+f(x,y−1)−4f(x,y)
[ 0 1 0 1 − 4 1 0 1 0 ] \begin{bmatrix} 0 & 1 & 0\\ 1 & -4 & 1\\ 0 & 1 & 0 \end{bmatrix} ⎣⎡0101−41010⎦⎤
、
[ 1 1 1 1 − 8 1 1 1 1 ] \begin{bmatrix} 1 & 1 & 1\\ 1 & -8 & 1\\ 1 & 1 & 1 \end{bmatrix} ⎣⎡1111−81111⎦⎤ - LoG算子
△ [ G σ ( x , y ) ∗ f ( x , y ) ] = [ △ G σ ( x , y ) ] ∗ f ( x , y ) = L o G ∗ f ( x , y ) \bigtriangleup [G_\sigma(x,y) * f(x,y)] = [\bigtriangleup G_\sigma(x,y)] * f(x,y) = LoG * f(x,y) △[Gσ(x,y)∗f(x,y)]=[△Gσ(x,y)]∗f(x,y)=LoG∗f(x,y)
[ − 2 − 4 − 4 − 4 − 2 − 4 0 8 0 − 4 − 4 8 24 8 − 4 − 4 0 8 0 − 4 − 2 − 4 − 4 − 4 − 2 ] \begin{bmatrix} -2 & -4 & -4 & -4& -2\\ -4 & 0 & 8 & 0 & -4\\ -4 & 8 & 24 & 8& -4\\ -4 & 0 & 8 & 0 & -4\\ -2 & -4 & -4 & -4& -2 \end{bmatrix} ⎣⎢⎢⎢⎢⎡−2−4−4−4−2−4080−4−48248−4−4080−4−2−4−4−4−2⎦⎥⎥⎥⎥⎤
总结
- 边缘检测即图像差分
- 常见边缘检测算子包括Robert算子,Sobel算子,LoG算子等,其中Sobel算子最为常用
- 二维图像的边缘具有强度和方向两个性质,比一维差分多了一个性质
Canny算子
已有方法容易受以下问题影响:噪声;断裂;虚检(渐变灰度)
Canny算子核心优点:边缘可自动连通
算法步骤:
- 平滑图像;
- 使用高斯函数完成平滑,以下以
5
∗
5
5*5
5∗5为例:
G ( x , y ) = 1 2 π σ e x p [ − x 2 + y 2 σ 2 ] G(x,y) = \frac{1}{\sqrt{2\pi}\sigma}exp[-\frac{x^2+y^2}{\sigma^2}] G(x,y)=2πσ1exp[−σ2x2+y2]
K = 1 139 [ 2 4 5 4 2 4 9 12 9 4 5 12 15 12 5 4 9 12 9 4 2 4 5 4 2 ] K = \frac{1}{139} \begin{bmatrix} 2 & 4 & 5 & 4& 2\\ 4 & 9 & 12 & 9 & 4\\ 5 & 12 & 15 & 12 & 5\\ 4 & 9 & 12 & 9 & 4\\ 2 & 4 & 5 & 4& 2 \end{bmatrix} K=1391⎣⎢⎢⎢⎢⎡245424912945121512549129424542⎦⎥⎥⎥⎥⎤
- 计算梯度(幅值和方向);
- 使用Sobel边缘检测算子对平滑图像进行x和y方向的边缘检测,假设得到的结果分别为 E x , E y E_x,E_y Ex,Ey
- 进一步计算梯度幅值和方向;
M ( x , y ) = E x 2 ( x , y ) + E y 2 ( x , y ) M(x,y) = \sqrt{{E_x}^2(x,y)+{E_y}^2(x,y)} M(x,y)=Ex2(x,y)+Ey2(x,y)
θ ( x , y ) = t a n − 1 ( E y ( x , y ) E x ( x , y ) ) \theta(x,y) = tan^{-1}(\frac{E_y(x,y)}{E_x(x,y)}) θ(x,y)=tan−1(Ex(x,y)Ey(x,y)) - 方向离散化:离散化为上下左右和斜45°共四个方向
- 梯度幅值进行非极大值抑制;
- 细化梯度幅值图像中的屋脊带,只保留幅值局部变化最大的点:
使用一个 3 ∗ 3 3*3 3∗3邻域作用于幅值阵列的所有点。在每一点上,邻域的中心像素与沿梯度方向的两个梯度幅值的插值结果进行比较,仅保留极大值点。
- 自动边缘连接
- 对上一步得到的图像使用低、高阈值
τ
1
τ
2
\tau_1\tau_2
τ1τ2阈值化,得到三幅图像
Γ 1 [ i , j ] ( M ( i , j ) < τ 1 ) Γ 2 [ i , j ] ( τ 1 ≤ M ( i , j ) ≤ τ 2 ) Γ 3 [ i , j ] ( M ( i , j ) > τ 2 ) %\left\{ \begin{array}{rcl} \Gamma_1[i,j] (M(i,j)<\tau_1)\\ \Gamma_2[i,j] (\tau_1\leq M(i,j)\leq\tau_2)\\ \Gamma_3[i,j] (M(i,j)>\tau_2) %\end{array}\right Γ1[i,j](M(i,j)<τ1)Γ2[i,j](τ1≤M(i,j)≤τ2)Γ3[i,j](M(i,j)>τ2) - Γ 1 \Gamma_1 Γ1对应假边缘,去除
- Γ 3 \Gamma_3 Γ3对应真边缘,全部保留
- Γ 2 \Gamma_2 Γ2连接:临接像素中是否有属于 Γ 3 \Gamma_3 Γ3的像素
- 通过察看弱边缘像素及其8个邻域像素,只要其中一个为强边缘像素,则该弱边缘像素点就可以保留为真实的边缘。
总结
Canny算子的基本优点在于检测准确、对噪声稳健,在实际中广泛应用
程序实例
相关函数
- Sobel算子边缘检测
dst = cv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
- 拉普拉斯滤波
dst = cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])
- Canny算子
dst = cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]])
- 形态学滤波
dst = cv2.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])
#op:形态学操作,包括MORPH_ERODE,MORPH_DILATE, MORPH_OPEN, MORPH_CLOSE, MORPH_GRADIENT, MORPH_TOPHAT, MORPH_BLACKHAT, MORPH_HITMISS
# iterations: 进行操作的次数
dst = cv2.getStructuringElement(shape, ksize[, anchor])
实例
import cv2
import numpy as np
def gauss_noise(image, mean = 0, var = 0.001):
"""
添加高斯噪声
mean: 均值
var: 方差
"""
image = np.array(image/255, dtype = float)
noise = np.random.normal(mean, var ** 0.5, image.shape)
out = image + noise
if out.min() < 0:
low_clip = -1.
else:
low_clip = 0.
out = np.clip(out, low_clip, 1.0)
out = np.uint8(out*255)
# cv.imshow("Gauss_noise", out)
return out
filename = 'C:/python/img/lena.jpg'
img = cv2.imread(filename, 0)
# Sobel算子边缘检测
sobel = cv2.Sobel(img, cv2.CV_16S, 1, 0, ksize = 3)
# Laplacian边缘检测
laplacian = cv2.Laplacian(img, cv2.CV_16S)
# Canny边缘检测,最小阈值50,最大阈值120
canny = cv2.Canny(img, 50, 120)
sobel_show = cv2.convertScaleAbs(sobel) #将18位浮点数转换
lap_show = cv2.convertScaleAbs(laplacian)
cv2.imshow('Sobel', sobel_show)
cv2.imshow('Laplacian', lap_show)
# 显示Canny边缘检测成果
cv2.imshow('Canny', canny)
nimg = gauss_noise(img)
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))
eroded = cv2.erode(img, kernel)
dilated = cv2.dilate(img, kernel)
opened = cv2.morphologyEx(nimg, cv2.MORPH_OPEN, kernel)
closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kernel)
#对原始图像先开后闭可以有效去除噪声
#以下分别为原视图像腐蚀、膨胀,以及加噪声开、闭运算结果
cv2.imshow('Noised image', nimg)
cv2.imshow('Eroded image', eroded)
cv2.imshow('Dilated image', dilated)
cv2.imshow('Opened image', opened)
cv2.imshow('Closed image', closed)
#以下分别计算并显示梯度、顶帽和黑帽变换结果
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
cv2.imshow('Image gradient', gradient)
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
bottomhat = cv2.morphologyEx((img, cv2.MORPH_BLACKHAT, kernel)
cv2.imshow('Tophat', tophat)
cv2.imshow('Blackhat', bottomhat)
#利用顶帽和黑帽变换进行图像增强,并显示结果
enhanced = img + tophat - bottomhat
cv2.imshow('Enhanced image', enhanced)
#计算击中或击不中(HIT)变换结果并显示
kernel = np.array(([0,1,0], [1,-1,1],[0,1,0]), dtype = 'int')
hmt = cv2.morphologyEx(img, cv2.MORPH_HITMISS, kernel)
cv2.imshow('Hit or miss transform', hmt)
cv2.waitKey()
cv2.destroyAllWindows()
总结
- OpenCV提供了Sobel,Canny等函数用于边缘检测
- OpenCV提供了MorphologyEx函数用于形态学滤波
数学形态学扩展应用
图像形态学基本操作——边缘梯度(gradient):
形态学定义为灰度膨胀图像与灰度腐蚀图像的插值:
r
=
(
A
⊕
B
)
−
(
A
⊙
B
)
r = (A\oplus B) - (A\odot B)
r=(A⊕B)−(A⊙B)
图像形态学基本操作——顶帽与黑帽变换
- 顶帽变换(tophat)定义为图像与其开运算的插值:
r = A − ( A ∘ B ) r = A - (A\circ B) r=A−(A∘B)
顶帽操作适用于原始图像背景是暗的,前景是亮的,其可以将明亮的前景突出出来。 - 黑帽变换(blackhat)定义为图像闭运算与其自身的插值:
r = ( A ∙ B ) − A r = (A\bullet B) - A r=(A∙B)−A
黑帽变换可以将明亮背景中暗色的前景突出出来。
顶帽变换和黑帽变换的结合使用能够应用于灰度图像的对比度增强,常用做法:将源图像加上顶帽变换再减去黑帽变换。
图像形态学基本操作——击中或击不中变换(HMT)
输出图像由所有在
B
1
B_1
B1中匹配的像素(击中)和未在
B
2
B_2
B2中匹配的像素(击不中)组成:
r
=
(
A
⊖
B
1
)
∩
(
A
C
⊖
B
2
)
r = (A\ominus B_1) \cap (A^C\ominus B_2)
r=(A⊖B1)∩(AC⊖B2)
结构元素
B
1
B_1
B1和原视图像做腐蚀,
B
2
B_2
B2与原始图像的补做腐蚀运算。
B
1
B_1
B1和
B
2
B_2
B2可合并成一个元素
B
B
B,如下示例:
[
0
1
0
1
0
1
0
1
0
]
[
0
0
0
0
1
0
0
0
0
]
[
0
1
0
1
−
1
1
0
1
0
]
\begin{bmatrix} 0 & 1 & 0\\ 1 & 0 & 1\\ 0 & 1 & 0 \end{bmatrix} \begin{bmatrix} 0 & 0 & 0\\ 0 & 1 & 0\\ 0 & 0 & 0 \end{bmatrix} \begin{bmatrix} 0 & 1 & 0\\ 1 & -1 & 1\\ 0 & 1 & 0 \end{bmatrix}
⎣⎡010101010⎦⎤⎣⎡000010000⎦⎤⎣⎡0101−11010⎦⎤
只有比四邻域像素值都小的位置才会保留下来。