【模式识别与图像处理】Sobel,Canny,Susan,Harris,正方形梯度算子的边缘检测和角点检测
一、实验原理
Sobel:
对传进来的图像像素做卷积,卷积的实质是在求梯度值,或者说给了一个加权平均,其中权值就是所谓的卷积核;然后对生成的新像素灰度值做值运算,以此来确定边缘信息。
卷积核及公式:

求出水平和竖直方向的的梯度Gx和Gy后
近似梯度就可以用下面的方法算出来:

Canny:
使用4个mask检测水平、垂直以及对角线方向的边缘。原始图像与每个mask所作的卷积都存储起来。对于每个点我们都标识在这个点上的最大值以及生成的边缘的方向。这样我们就从原始图像生成了图像中每个点亮度梯度图以及亮度梯度的方向。Canny使用了两个值--高阈值与低值。首先从一个较大的闽值开始,标识出比较确信的真实边缘,然后从这些边缘开始在图像中跟踪整个的边缘。使用一个较小的值,以便跟踪曲线的模糊部分。
Susan:
使用一个圆形模板,通过检测模板中的像素与中心位置像素的偏离程度,来判断中心位置像素是否为边缘或角点。如果周边像素与中心位置像素偏差较小,则认为周边像素与中心位置像素相似,中心位置为非边缘;如果周边像素与中心位置偏差较大,则认为中心位置为边缘或角点。

Harris:
Harris角点检测的基本思想是通过计算图像灰度变化的矩阵来判断一个点是否是角点。主要步骤如下:
-
计算图像梯度:对图像进行水平和垂直方向上的Sobel滤波,得到梯度图像 𝐼𝑥Ix 和 𝐼𝑦Iy。
-
构建矩阵M:
\[M=\begin{bmatrix}Ix^2&IxIy \\IxIy&Iy^2\end{bmatrix} \]其中,\(𝐼𝑥^2\)、\(𝐼𝑦^2\) 和 \(𝐼𝑥𝐼𝑦\) 是在一个窗口内的求和。
-
计算响应值R:
\[𝑅=det(𝑀)−𝑘⋅(trace(𝑀))^2 \]其中,det(𝑀) 是矩阵 𝑀M 的行列式,trace(M) 是矩阵 𝑀 的迹,𝑘 是经验参数,通常取值在 0.04 到 0.06 之间。
-
非极大值抑制:对响应值 𝑅R 进行非极大值抑制,保留局部最大值作为角点。
正方形梯度算子:
与Sobel类似,掩膜上不同

并在R+G,G+B,R+B三个通道上计算,然后输出其最大值。
二、实验结果
街道图像的边缘分割:









建筑图像的边缘分割:









机场图像的边缘分割:









三、实验分析
Sobel:
优点:Sobel 算法使用简单的卷积操作,容易实现和理解,计算速度较快,适合实时应用,能够提供边缘方向的信息。
缺点: 对噪声敏感,检测效果较粗糙,边缘定位的精度相对较低,容易导致边缘模糊。
Canny:
优点:通过高斯滤波器平滑图像,减少噪声影响,使用非极大值抑制和双阈值处理,能够准确定位边缘,能够检测出较为完整和连续的边缘。
缺点: 需要设置高斯滤波器的标准差和双阈值,参数选择对结果影响较大,在处理弱边缘和灰度变化较小的区域时,效果不理想。
Susan:
优点: 多对噪声和局部变化具有较好的鲁棒性,能够准确检测出图像中的边缘和角点,不需要预设滤波器,能够根据图像特征自适应调整。
缺点: 由于需要计算每个像素的局部结构,计算量较大,速度较慢,算法实现相对复杂,理解和编码难度较大。
Harris:
优点:Harris角点检测对图像的旋转和光照变化具有较好的鲁棒性。在现代计算机上,Harris角点检测的计算速度较快,适用于实时应用。对于局部显著变化的点,Harris角点检测能准确定位。
缺点:Harris角点检测对图像的缩放变化不敏感,这意味着在多尺度图像中可能会错过一些角点。在噪声较多的图像中,响应值 𝑅R 可能会受到较大影响,导致误检或漏检。参数 𝑘k 的选择依赖经验,不同图像可能需要不同的参数,调参过程较为繁琐。
正方形梯度算子:
没有为0的项,更为紧凑,充分利用图像数据,对称性好,抗噪声干扰能力强。改变宽度参数wd就可以得到不同宽度的纹理,不过由上图图像可见,wd=1的情况下,可能会导致纹理过细而使边界不明显,wd=3的情况下可能会导致过粗而混乱边界,而wd=2的效果则比较具有泛化效益。因此在多数情况下,默认wd=2更具普遍意义。
实验结果可以发现,wd=2的正方形梯度算子在各种情况下都有较好表现效果,具有更好的泛用性。
四、实验代码
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
import cv2 as cv
# Sobel
def SOBEL(img):
sobelx = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=3)
sobelx = cv.convertScaleAbs(sobelx)
# cv.imshow('sobelx', sobelx)
sobely = cv.Sobel(img, cv.CV_64F, 0, 1, ksize=3)
sobely = cv.convertScaleAbs(sobely)
# cv.imshow('sobely', sobely)
sobel = cv.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
# cv.imshow('sobel', sobel)
plt.figure()
plt.title("Sobel")
plt.imshow(sobel, cmap=cm.gray)
plt.axis('off')
print('sobel end')
# Canny
def CANNY(img):
min, max = 50, 150
canny = cv.Canny(img, min, max)
plt.figure()
plt.title("Canny")
plt.imshow(canny, cmap=cm.gray)
plt.axis('off')
print('canny end')
# Susan
def SUSAN(img_src, t=10):
# 圆形掩膜
susanmask = np.ones((7, 7))
susanmask[0, 0] = 0
susanmask[0, 1] = 0
susanmask[0, 5] = 0
susanmask[0, 6] = 0
susanmask[1, 0] = 0
susanmask[1, 6] = 0
susanmask[5, 0] = 0
susanmask[5, 6] = 0
susanmask[6, 0] = 0
susanmask[6, 1] = 0
susanmask[6, 5] = 0
susanmask[6, 6] = 0
img = img_src.copy()
row_s, col_s = 3, 3
row_e, col_e = img_src.shape[0] - 3, img.shape[1] - 3
n_max = 0
n_arr = 37 * np.ones(img.shape) # 初始认为没有角点
for r in range(row_s, row_e): # 遍历所有行
for c in range(col_s, col_e): # 遍历所有列
susan_zone = img[r - 3:r + 3 + 1, c - 3:c + 3 + 1] # 获取矩形区域
susan_zone = susan_zone[susanmask != 0] # 使用mask截取圆形区域
r0 = img[r, c]
similarity = np.exp(-((1.0 * susan_zone - r0) / t) ** 6)
n = np.sum(similarity)
if n > n_max:
n_max = n
n_arr[r, c] = n
g = n_max / 2
R = np.zeros(img.shape)
index = n_arr < g # 小于g,认为是可能的角点,越小,可能性越大
R[index] = g - n_arr[index] # 取反,所以R越大,是角点的可能性越大
plt.figure()
plt.title("Susan")
plt.imshow((6.37 * n_arr).astype(np.uint8), cmap=cm.gray)
plt.axis('off')
img_show = cv.cvtColor(img_src, cv.COLOR_GRAY2BGR)
img_show[R != 0] = (255, 0, 0)
plt.figure()
plt.title("original corners")
plt.imshow(img_show, cmap=cm.gray)
plt.axis('off')
print('susan end')
return R
def Harris(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
dst = cv.cornerHarris(gray, blockSize=3, ksize=5, k=0.05)
image_dst = image[:, :, :]
image_dst[dst > 0.01 * dst.max()] = [0, 0, 255]
plt.figure()
plt.title("Harris")
plt.imshow(image_dst, cmap=cm.gray)
plt.axis('off')
class SquareOp:
def __init__(self, _img, _k=2):
self.img = np.array(_img).astype(np.float64)
self.k = _k;
self.maskx = np.ones((2 * _k, 2 * _k), dtype=np.float64)
self.masky = np.ones((2 * _k, 2 * _k), dtype=np.float64)
self.maskx[:_k, :] = -1.0
self.masky[:, _k:] = -1.0
def padding(self):
n, m, l = self.img.shape
w = self.k
tmp = np.zeros((n + 2 * w - 1, m + 2 * w - 1, l), dtype=np.float64)
tmp[w - 1:w + n - 1, w - 1:w + m - 1] = self.img
self.img = tmp
def calc(self, ca, cb, mask):
val = self.img[:, :, ca] + self.img[:, :, cb]
val = cv.filter2D(val, cv.CV_64F, mask)
val = val[2 * self.k - 1:, 2 * self.k - 1:]
val = cv.convertScaleAbs(val)
return val
def sqre_x(self):
x = self.calc(0, 1, self.maskx)
y = self.calc(1, 2, self.maskx)
z = self.calc(0, 2, self.maskx)
G = np.maximum(x, y, z)
return G
def sqre_y(self):
x = self.calc(0, 1, self.masky)
y = self.calc(1, 2, self.masky)
z = self.calc(0, 2, self.masky)
G = np.maximum(x, y, z)
return G
def run(self):
self.padding()
Gx = self.sqre_x()
Gy = self.sqre_y()
G = cv.addWeighted(Gx, 0.5, Gy, 0.5, 0)
plt.figure()
plt.title("SquareOp, wd=%d" % self.k)
plt.imshow(G, cmap=cm.gray)
plt.axis('off')
return G
if __name__ == '__main__':
img = cv.imread("jiedao0.jpg")
plt.figure()
plt.title("RGBImage")
plt.imshow(img, cmap=cm.gray)
plt.axis('off')
grayimg = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
plt.figure()
plt.title("GrayImage")
plt.imshow(grayimg, cmap=cm.gray)
plt.axis('off')
SOBEL(grayimg)
CANNY(grayimg)
SUSAN(grayimg)
Harris(img)
sqr = SquareOp(img, 1)
sqr.run()
sqr2 = SquareOp(img, 2)
sqr2.run()
sqr3 = SquareOp(img, 3)
sqr3.run()
plt.show()
print('end')

浙公网安备 33010602011771号