基于Python实现
边缘模型
图像的一阶导数和二阶导数
图中(a)显示了从图中(b)中的一段中提取出来的一幅图像。图中(b)显示了一条水平灰度剖面线。该图形还显示了灰度剖面的一阶导数和二阶导数。当沿着灰度剖面从左到右移动时,我们注意到,在斜坡的开始处和在斜坡上的各个点处,一阶导数为正。而在恒定灰度区域的一阶导数为零。在斜坡的开始处,二阶导数为正;在斜坡的结束处,二阶导数为负;在斜坡上的各点处,二阶导数为零;在恒定灰度区域的各点处,二阶导数为零。对于从亮到暗过渡的边缘,刚刚讨论的导数的符号则正好相反。零灰度轴和二姐导数极值间的连线的交点称为该二阶导数的零交叉点。
典型边缘模型
实际中,数字图像都存在被模糊且带有噪声的边缘,模糊的程度主要取决于聚焦机理(如光学成像中的镜头)中的限制,而噪声水平主要取决于成像系统的电子元件。
Sobel算子
Soble边缘检测算法比较简,实际应用中效率比canny边缘检测效率要高,但是边缘不如Canny检测的准确,但是很多实际应用的场合,sobel边缘却是首选,尤其是对效率要求较高,而对细纹理不太关心的时候。Soble边缘检测通常带有方向性,可以只检测竖直边缘或垂直边缘或都检测。
算子使用两个33的矩阵(图1)算子使用两个33的矩阵(图1)去和原始图片作卷积,分别得到横向G(x)和纵向G(y)的梯度值,如果梯度值大于某一个阈值,则认为该点为边缘点。
sobel算子在x方向上的卷积核:
sobel算子在y方向上的卷积核:
def sobel_x(img):
fil = np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
return abs(signal.convolve(img, fil, mode='same'))
def sobel_y(img):
fil = np.array([[1, 2, 1],
[0, 0, 0],
[-1, -2, -1]])
return abs(signal.convolve(img, fil, mode='same'))
def sobel(img, orthogonal=True):
x = sobel_x(img)
y = sobel_y(img)
out = np.where(x > y, x, y)
return out
效果:
方向梯度法
但上述方法仅仅包含两个方方向的正交梯度,无法体现出八个方向的梯度,因此在此定义8个方向的模板 W i W_i Wi进行计算:
W 1 = [ 1 2 1 0 0 0 − 1 − 2 − 1 ] W_1= \left[ \begin{matrix} 1 & 2 & 1\\ 0 & 0 & 0 \\ -1 & -2 & -1 \\ \end{matrix} \right] W1=⎣⎡10−120−210−1⎦⎤
W 2 = [ − 1 − 2 − 1 0 0 0 1 2 1 ] W_2= \left[ \begin{matrix} -1 & -2 & -1\\ 0 & 0 & 0 \\ 1 & 2 & 1 \\ \end{matrix} \right] W2=⎣⎡−101−202−101⎦⎤
W 3 = [ 1 0 − 1 2 0 − 2 1 0 − 1 ] W_3= \left[ \begin{matrix} 1 & 0 & -1\\ 2 & 0 & -2 \\ 1 & 0 & -1 \\ \end{matrix} \right] W3=⎣⎡121000−1−2−1⎦⎤
W 4 = [ − 1 0 1 − 2 0 2 − 1 0 1 ] W_4= \left[ \begin{matrix} -1 & 0 & 1\\ -2 & 0 & 2 \\ -1 & 0 & 1 \\ \end{matrix} \right] W4=⎣⎡−1−2−1000121⎦⎤
W 5 = [ 0 1 2 − 1 0 1 − 2 − 1 0 ] W_5= \left[ \begin{matrix} 0 & 1 & 2\\ -1 & 0 & 1 \\ -2 & -1 & 0 \\ \end{matrix} \right] W5=⎣⎡0−1−210−1210⎦⎤
W 6 = [ 0 − 1 − 2 1 0 − 1 2 1 0 ] W_6= \left[ \begin{matrix} 0 & -1 & -2\\ 1 & 0 & -1 \\ 2 & 1 & 0 \\ \end{matrix} \right] W6=⎣⎡012−101−2−10⎦⎤
W 7 = [ 2 1 0 1 0 − 1 0 − 1 − 2 ] W_7= \left[ \begin{matrix} 2 & 1 & 0\\ 1 & 0 & -1 \\ 0 & -1 & -2 \\ \end{matrix} \right] W7=⎣⎡21010−10−1−2⎦⎤
W 8 = [ − 2 − 1 0 − 1 0 1 0 1 2 ] W_8= \left[ \begin{matrix} -2 & -1 & 0\\ -1 & 0 & 1 \\ 0 & 1 & 2 \\ \end{matrix} \right] W8=⎣⎡−2−10−101012⎦⎤
边缘梯度的定义式为:
其中,
G
i
(
m
,
n
)
=
f
(
m
,
n
)
∗
W
i
G_i(m,n)=f(m,n)*W_i
Gi(m,n)=f(m,n)∗Wi,下标
i
i
i 代表方向模板的序号,
W
i
W_i
Wi代表第
i
i
i 方向的模板。
∣
G
i
(
m
,
n
)
∣
|G_i(m,n)|
∣Gi(m,n)∣表示第
i
i
i 方向的梯度模值,
N
N
N 代表模板的个数。
方向梯度法检测边缘点的过程如图所示,其中的*表示卷积。
代码:
计算边缘点和方向编码
def sobel_up_x(img):
fil = np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
return abs(signal.convolve(img, fil, mode='same'))
def sobel_down_x(img):
fil = np.array([[1, 0, -1],
[2, 0, -2],
[1, 0, -1]])
return abs(signal.convolve(img, fil, mode='same'))
def sobel_up_y(img):
fil = np.array([[1, 2, 1],
[0, 0, 0],
[-1, -2, -1]])
return abs(signal.convolve(img, fil, mode='same'))
def sobel_down_y(img):
fil = np.array([[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]])
return abs(signal.convolve(img, fil, mode='same'))
def sobel_up_diagonal_x(img):
fil = np.array([[0, 1, 2],
[-1, 0, 1],
[-2, -1, 0]])
return abs(signal.convolve(img, fil, mode='same'))
def sobel_down_diagonal_y(img):
fil = np.array([[-2, -1, 0],
[-1, 0, 1],
[0, 1, 2]])
return abs(signal.convolve(img, fil, mode='same'))
def sobel_down_diagonal_x(img):
fil = np.array([[0, -1, -2],
[1, 0, -1],
[2, 1, 0]])
return abs(signal.convolve(img, fil, mode='same'))
def sobel_up_diagonal_y(img):
fil = np.array([[2, 1, 0],
[1, 0, -1],
[0, -1, -2]])
return abs(signal.convolve(img, fil, mode='same'))
def sobel(img, orthogonal=True):
if orthogonal:
x = sobel_up_x(img)
y = sobel_down_y(img)
out = np.where(x > y, x, y)
return out
else:
up_x = sobel_up_x(img)
down_dy = sobel_down_diagonal_y(img)
down_y = sobel_down_y(img)
down_dx = sobel_down_diagonal_x(img)
down_x = sobel_down_x(img)
up_dy = sobel_up_diagonal_y(img)
up_y = sobel_up_y(img)
up_dx = sobel_up_diagonal_x(img)
# 计算边缘点与方向编码
direct_matrix = -np.ones(img.shape)
direct_matrix[up_x > down_dy] = 0
direct_matrix[down_dy > up_x] = 1
out = np.where(down_dy > up_x, down_dy, up_x)
direct_matrix[down_y > out] = 2
out = np.where(down_y > out, down_y, out)
direct_matrix[down_dx > out] = 3
out = np.where(down_dx > out, down_dx, out)
direct_matrix[down_x > out] = 4
out = np.where(down_x > out, down_x, out)
direct_matrix[up_dy > out] = 5
out = np.where(up_dy > out, up_dy, out)
direct_matrix[up_y > out] = 6
out = np.where(up_y > out, up_y, out)
direct_matrix[up_dx > out] = 7
out = np.where(up_dx > out, up_dx, out)
return out, direct_matrix
在得到方向梯度法的方向编码后,通过灰度值方式显示不能够直观的表现出方向性,因此再次借助了“场”这种表现形式,通过箭头绘制出方向,并赋予边缘点的权重参数以更加直观的表现出方向编码的“场”。
代码:
绘制场
def draw_direct(direct_matrix,weight):
y,x = np.mgrid[direct_matrix.shape[0]:0:-1, 0:direct_matrix.shape[1]:1]
u = np.zeros(direct_matrix.shape)
v = np.zeros(direct_matrix.shape)
max_weight = np.max(weight)
len = 1.0
u[direct_matrix==0] = len
u[direct_matrix == 1] = len/2
v[direct_matrix == 1] = -len/2
v[direct_matrix == 2] = -len
u[direct_matrix == 3] = -len/2
v[direct_matrix == 3] = -len/2
u[direct_matrix == 4] = -len
u[direct_matrix == 5] = -len/2
v[direct_matrix == 5] = len/2
v[direct_matrix == 6] = len
u[direct_matrix == 7] = len/2
v[direct_matrix == 7] = len/2
u = u * weight / max_weight
v = v * weight / max_weight
plt.quiver(x, y, u, v,width=0.001, scale=20,headwidth=6,headlength=6)
效果
图例1
原图像
边缘
边缘向量场
图例2
原图像
边缘
边缘向量场