python实现Marr-Hildreth算法、Canny边缘检测器算法
1.Marr-Hildreth算法详解
Marr-Hildreth算法是一个用于图像边缘检测的经典算法,其基本思想是通过检测图像的二阶导数(拉普拉斯算子)来找到边缘。该算法首先对图像进行高斯平滑处理,以减少噪声,然后计算拉普拉斯算子,最后通过检测零交叉点来确定边缘。
算法步骤
- 高斯平滑:使用高斯滤波器对图像进行平滑处理,以减少噪声。
- 计算拉普拉斯算子:对平滑后的图像计算拉普拉斯算子。
- 检测零交叉点:在拉普拉斯算子图像中检测零交叉点,这些点即为边缘。
公式
Python 实现
以下是Marr-Hildreth算法的Python实现代码:
import numpy as np
import cv2
import matplotlib.pyplot as plt
def marr_hildreth(image, sigma=1.4):
"""
Marr-Hildreth边缘检测算法实现
参数:
image (numpy.ndarray): 输入的灰度图像
sigma (float): 高斯平滑的标准差
返回:
numpy.ndarray: 边缘检测后的二值化图像
"""
# 1. 高斯平滑
smoothed_image = cv2.GaussianBlur(image, (0, 0), sigma)
# 2. 计算拉普拉斯算子
laplacian = cv2.Laplacian(smoothed_image, cv2.CV_64F)
# 3. 检测零交叉点
zero_crossing = np.zeros_like(laplacian)
for i in range(1, laplacian.shape[0] - 1):
for j in range(1, laplacian.shape[1] - 1):
if laplacian[i, j] == 0:
if (laplacian[i, j-1] < 0 and laplacian[i, j+1] > 0) or (laplacian[i, j-1] > 0 and laplacian[i, j+1] < 0) or \
(laplacian[i-1, j] < 0 and laplacian[i+1, j] > 0) or (laplacian[i-1, j] > 0 and laplacian[i+1, j] < 0):
zero_crossing[i, j] = 255
elif laplacian[i, j] < 0:
if (laplacian[i, j-1] > 0 or laplacian[i, j+1] > 0 or laplacian[i-1, j] > 0 or laplacian[i+1, j] > 0):
zero_crossing[i, j] = 255
return zero_crossing
# 示例用法
if __name__ == "__main__":
# 读取灰度图像
image = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
# 调用Marr-Hildreth边缘检测算法
edges = marr_hildreth(image)
# 显示原始图像和边缘检测后的图像
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(image, cmap='gray')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.title('Marr-Hildreth Edges')
plt.imshow(edges, cmap='gray')
plt.axis('off')
plt.show()
详细解释
读取图像并转换为灰度图像:
image = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
高斯平滑:
smoothed_image = cv2.GaussianBlur(image, (0, 0), sigma)
这里使用OpenCV的
GaussianBlur
函数对图像进行高斯平滑处理。计算拉普拉斯算子:
laplacian = cv2.Laplacian(smoothed_image, cv2.CV_64F)
使用OpenCV的
Laplacian
函数计算平滑后的图像的拉普拉斯算子。检测零交叉点:
zero_crossing = np.zeros_like(laplacian) for i in range(1, laplacian.shape[0] - 1): for j in range(1, laplacian.shape[1] - 1): if laplacian[i, j] == 0: if (laplacian[i, j-1] < 0 and laplacian[i, j+1] > 0) or (laplacian[i, j-1] > 0 and laplacian[i, j+1] < 0) or \ (laplacian[i-1, j] < 0 and laplacian[i+1, j] > 0) or (laplacian[i-1, j] > 0 and laplacian[i+1, j] < 0): zero_crossing[i, j] = 255 elif laplacian[i, j] < 0: if (laplacian[i, j-1] > 0 or laplacian[i, j+1] > 0 or laplacian[i-1, j] > 0 or laplacian[i+1, j] > 0): zero_crossing[i, j] = 255
通过遍历拉普拉斯算子图像中的每个像素,并根据其相邻像素值检测零交叉点,从而确定边缘。
显示和保存边缘检测后的图像:
plt.subplot(1, 2, 1) plt.title('Original Image') plt.imshow(image, cmap='gray') plt.axis('off') plt.subplot(1, 2, 2) plt.title('Marr-Hildreth Edges') plt.imshow(edges, cmap='gray') plt.axis('off') plt.show()
优缺点
优点:
- 噪声抑制:通过高斯平滑处理,可以有效地抑制噪声。
- 边缘检测准确:通过检测零交叉点,能够准确地找到图像中的边缘。
缺点:
- 计算复杂度高:计算高斯平滑和拉普拉斯算子比较耗时。
- 参数敏感:平滑参数(σ)对结果影响较大,需要根据具体图像进行调整。
Marr-Hildreth算法通过高斯平滑和拉普拉斯算子相结合,实现了对图像边缘的有效检测,广泛应用于图像处理和计算机视觉领域。
2.Canny边缘检测器算法详解
Canny边缘检测器是一种经典的图像处理算法,用于检测图像中的边缘。它通过多步骤的处理流程来实现高质量的边缘检测,包括高斯平滑、计算梯度、非极大值抑制、双阈值检测和边缘跟踪。
算法步骤
- 高斯平滑:使用高斯滤波器对图像进行平滑处理,以减少噪声。
- 计算梯度:计算图像的梯度强度和方向。
- 非极大值抑制:对梯度图像进行非极大值抑制,以细化边缘。
- 双阈值检测:通过设定高低阈值,将像素分类为强边缘、弱边缘和非边缘。
- 边缘跟踪:通过连接强边缘像素,形成完整的边缘。
公式
Python 实现
以下是Canny边缘检测器的Python实现代码:
import numpy as np
import cv2
import matplotlib.pyplot as plt
def canny_edge_detection(image, sigma=1.4, low_threshold=20, high_threshold=60):
"""
Canny边缘检测算法实现
参数:
image (numpy.ndarray): 输入的灰度图像
sigma (float): 高斯平滑的标准差
low_threshold (int): 双阈值低阈值
high_threshold (int): 双阈值高阈值
返回:
numpy.ndarray: 边缘检测后的二值化图像
"""
# 1. 高斯平滑
smoothed_image = cv2.GaussianBlur(image, (5, 5), sigma)
# 2. 计算梯度
G_x = cv2.Sobel(smoothed_image, cv2.CV_64F, 1, 0, ksize=3)
G_y = cv2.Sobel(smoothed_image, cv2.CV_64F, 0, 1, ksize=3)
# 3. 计算梯度强度和方向
magnitude = np.sqrt(G_x**2 + G_y**2)
angle = np.arctan2(G_y, G_x) * (180 / np.pi)
angle[angle < 0] += 180 # 调整角度范围从0到180度
# 4. 非极大值抑制
nms_image = np.zeros_like(smoothed_image)
for i in range(1, magnitude.shape[0] - 1):
for j in range(1, magnitude.shape[1] - 1):
if (0 <= angle[i, j] < 22.5 or 157.5 <= angle[i, j] <= 180) or (22.5 <= angle[i, j] < 67.5):
if magnitude[i, j] >= max(magnitude[i, j-1], magnitude[i, j+1]):
nms_image[i, j] = magnitude[i, j]
elif (67.5 <= angle[i, j] < 112.5):
if magnitude[i, j] >= max(magnitude[i-1, j-1], magnitude[i+1, j+1]):
nms_image[i, j] = magnitude[i, j]
elif (112.5 <= angle[i, j] < 157.5):
if magnitude[i, j] >= max(magnitude[i-1, j], magnitude[i+1, j]):
nms_image[i, j] = magnitude[i, j]
# 5. 双阈值检测和边缘跟踪
edges = np.zeros_like(nms_image)
strong_edge = 255
weak_edge = 50
strong_i, strong_j = np.where(nms_image >= high_threshold)
weak_i, weak_j = np.where((nms_image >= low_threshold) & (nms_image < high_threshold))
edges[strong_i, strong_j] = strong_edge
edges[weak_i, weak_j] = weak_edge
# 边缘跟踪
for i in range(1, edges.shape[0] - 1):
for j in range(1, edges.shape[1] - 1):
if edges[i, j] == weak_edge:
if np.any(edges[i-1:i+2, j-1:j+2] == strong_edge):
edges[i, j] = strong_edge
else:
edges[i, j] = 0
return edges
# 示例用法
if __name__ == "__main__":
# 读取灰度图像
image = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
# 调用Canny边缘检测算法
edges = canny_edge_detection(image)
# 显示原始图像和边缘检测后的图像
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.title('Original Image')
plt.imshow(image, cmap='gray')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.title('Canny Edges')
plt.imshow(edges, cmap='gray')
plt.axis('off')
plt.show()
详细解释
读取图像并转换为灰度图像:
image = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)
高斯平滑:
smoothed_image = cv2.GaussianBlur(image, (5, 5), sigma)
这里使用OpenCV的
GaussianBlur
函数对图像进行高斯平滑处理,以减少噪声。计算梯度:
G_x = cv2.Sobel(smoothed_image, cv2.CV_64F, 1, 0, ksize=3) G_y = cv2.Sobel(smoothed_image, cv2.CV_64F, 0, 1, ksize=3)
使用OpenCV的
Sobel
函数计算平滑后的图像在水平和垂直方向的梯度。计算梯度强度和方向:
magnitude = np.sqrt(G_x**2 + G_y**2) angle = np.arctan2(G_y, G_x) * (180 / np.pi) angle[angle < 0] += 180
计算梯度强度和方向,注意调整方向角度范围从0到180度。
非极大值抑制:
for i in range(1, magnitude.shape[0] -
1):
for j in range(1, magnitude.shape[1] - 1):
if (0 <= angle[i, j] < 22.5 or 157.5 <= angle[i, j] <= 180) or (22.5 <= angle[i, j] < 67.5):
if magnitude[i, j] >= max(magnitude[i, j-1], magnitude[i, j+1]):
nms_image[i, j] = magnitude[i, j]
elif (67.5 <= angle[i, j] < 112.5):
if magnitude[i, j] >= max(magnitude[i-1, j-1], magnitude[i+1, j+1]):
nms_image[i, j] = magnitude[i, j]
elif (112.5 <= angle[i, j] < 157.5):
if magnitude[i, j] >= max(magnitude[i-1, j], magnitude[i+1, j]):
nms_image[i, j] = magnitude[i, j]
```
在梯度方向上执行非极大值抑制,以保留局部梯度最大值,细化边缘。
双阈值检测和边缘跟踪:
edges = np.zeros_like(nms_image) strong_edge = 255 weak_edge = 50 strong_i, strong_j = np.where(nms_image >= high_threshold) weak_i, weak_j = np.where((nms_image >= low_threshold) & (nms_image < high_threshold)) edges[strong_i, strong_j] = strong_edge edges[weak_i, weak_j] = weak_edge # 边缘跟踪 for i in range(1, edges.shape[0] - 1): for j in range(1, edges.shape[1] - 1): if edges[i, j] == weak_edge: if np.any(edges[i-1:i+2, j-1:j+2] == strong_edge): edges[i, j] = strong_edge else: edges[i, j] = 0
根据设定的高低阈值,将像素分类为强边缘、弱边缘和非边缘,并通过边缘跟踪将弱边缘连接成完整的边缘。
显示和保存边缘检测后的图像:
plt.subplot(1, 2, 1) plt.title('Original Image') plt.imshow(image, cmap='gray') plt.axis('off') plt.subplot(1, 2, 2) plt.title('Canny Edges') plt.imshow(edges, cmap='gray') plt.axis('off') plt.show()
使用Matplotlib库显示原始图像和Canny边缘检测后的图像。
优缺点
优点:
- 准确性高:能够检测出细节丰富的边缘。
- 抗噪声能力强:通过高斯平滑和非极大值抑制,有效抑制噪声。
- 参数少:只需设置两个阈值参数。
缺点:
- 计算复杂度高:包括高斯滤波和梯度计算等多个步骤,对计算资源要求较高。
- 对图像质量敏感:图像质量对结果影响较大,需要进行适当的预处理。
Canny边缘检测器因其高精度和可控性而被广泛应用于计算机视觉和图像处理领域,特别适用于需要精确边缘检测的应用场景。