CV总结之边缘检测
边缘检测
常见算子
边缘检测是图像处理和计算机视觉中的一个基本任务,目的是识别图像中物体的边界。边缘是图像中亮度变化显著的区域,通常标志着物体、表面或形状的边界。边缘检测对于后续的图像分析任务,如特征提取、目标识别和图像分割等,都是非常重要的。
常见边缘检测算法:
- Roberts算子:基于对角线方向的差分,使用两个2x2的卷积核来检测边缘。
- Prewitt算子:使用3x3的卷积核,对图像进行水平和垂直方向的差分,以检测边缘。
- Sobel算子:也是基于3x3的卷积核,但设计得更加精细,可以同时检测水平和垂直方向的边缘。
- Kirsch算子:使用8个不同方向的3x3卷积核来检测边缘,可以检测8个方向上的边缘。
- Robinson算子:类似于Kirsch算子,使用8个3x3的卷积核,但核的值有所不同。
- Canny算子:被认为是最优秀的边缘检测算子之一,它通过高斯滤波、梯度计算、非极大值抑制和双阈值检测等步骤来检测边缘。
- Laplacian算子:基于二阶导数,用于检测图像中的边缘,但对噪声较为敏感。
- Zero-Crossing算子:基于拉普拉斯算子,通过检测拉普拉斯算子的零交叉点来确定边缘位置。
- Shen-Castan滤波器:结合了Canny算子和Laplacian算子的特点,通过非线性映射和阈值处理来检测边缘。
- 结构化森林:一种较新的边缘检测方法,使用深度学习技术来检测边缘。
边缘检测算法的选择:
- 简单性:对于需要快速响应的应用,可能会选择Roberts、Prewitt或Sobel算子。
- 准确性:对于需要高精度边缘检测的应用,Canny算子通常是首选。
- 抗噪声能力:Canny算子和结构化森林等算法具有较强的抗噪声能力。
- 计算资源:对于计算资源有限的情况,可能会选择计算量较小的算法。
每种算法都有其特定的应用场景和优缺点,选择合适的算法需要根据具体的应用需求和条件来决定。
Canny算子
Canny算子是一种经典的边缘检测算法,由John F. Canny在1986年提出。它的目标是在保留图像原有属性的同时,显著减少图像的数据规模,找到最优的边缘检测解。Canny算子以其准确性高、低错误率、单一性和高效性而闻名,被广泛应用于各种计算机视觉系统中。以下是Canny算子的主要步骤和特点:
- 高斯滤波去噪:Canny算法的第一步是使用高斯滤波器平滑图像,以去除噪声。噪声和边缘都属于高频信号,高斯模糊可以减少噪声对边缘检测的影响。高斯核的大小会影响边缘检测的性能,核越大,对噪声的抑制越强,但边缘的定位误差也可能增加。
- 计算梯度强度和方向:Canny算法使用Sobel算子来计算图像的梯度幅度和方向。梯度的方向通常与边缘垂直,梯度的方向被归为四类:垂直、水平和两个对角线(即,0度、45度、90度和135度四个方向)。
- 非极大值抑制:在计算出梯度幅度和方向后,Canny算子通过非极大值抑制技术来过滤掉非边缘像素,使得边缘更加清晰。这个过程保留了每个像素点上梯度强度的极大值,过滤掉其他的值。
- 双阈值检测:经过非极大值抑制后,图像中仍然有很多噪声点。Canny算法使用双阈值技术,设定一个阈值上界和阈值下界,大于上界的认为是强边缘,小于下界的不是边缘,两者之间的是候选项(弱边缘),需进行进一步处理。
- 滞后技术跟踪边缘:最后,Canny算子使用滞后技术来跟踪边缘。如果某一像素位置和强边界相连的弱边界则认为是边缘,其他的弱边界则被删除。
Canny算子的优势在于其能够准确地找到图像中真实的边缘位置,并能够将边缘与噪声区分开,同时保持边缘的连续性和精确性。它适用于实时边缘检测,并且在MATLAB、OpenCV等常用图像处理工具中已有内置的Canny算子API。
使用OpenCV进行Canny边缘检测的Python代码如下:
首先,确保你已经安装了OpenCV库。如果没有安装,可以通过pip安装:
xxxxxxxxxx
pip install opencv-python
然后,你可以使用以下代码进行边缘检测:
xxxxxxxxxx
import cv2
import numpy as np
from matplotlib import pyplot as plt
# 读取图像(注意路径替换,并使用`cv2.IMREAD_GRAYSCALE`参数以灰度模式读取图像)
image = cv2.imread('path_to_your_image.jpg', cv2.IMREAD_GRAYSCALE)
# 检查图像是否成功加载
if image is None:
print("Error: Image not found")
else:
# 应用高斯模糊,减少图像噪声,有助于Canny算子更准确地检测边缘。
blurred_image = cv2.GaussianBlur(image, (5, 5), 0)
# 使用Canny算子检测边缘
# 参数分别为:高阈值和低阈值(阈值决定了哪些边缘被认为是真实的边缘)
edges = cv2.Canny(blurred_image, 50, 150)
# 显示原图和边缘检测结果
plt.figure(figsize=(10, 5))
plt.subplot(121), plt.imshow(image, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(edges, cmap='gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
# 显示图像
plt.show()
Kirsch算子
Kirsch算子是一种边缘检测算法,由R. Kirsch提出。它通过使用8个特定的3x3模板对图像进行卷积操作来检测边缘,这些模板代表了8个不同的方向,能够对图像上的8个特定边缘方向作出最大响应。在运算中,取这8个方向的最大值作为图像的边缘输出。
Kirsch算子的特点:
- 多方向响应:Kirsch算子的8个模板可以检测图像中的多个方向的边缘,这使得它在保持细节和抗噪声方面都有较好的效果。
- 边缘强度与方向:在计算边缘强度的同时可以得到边缘的方向,各方向间的夹角为45º。
- 最大响应值:对于图像上的每一个像素点,Kirsch算子通过卷积求导数,并取8个方向中的最大值作为该点的边缘输出。
Kirsch算子的模板:
Kirsch算子的8个模板如下所示,每个模板对应一个特定的方向,用于检测该方向上的边缘强度:
这些模板通过与图像进行卷积操作来计算每个像素点的梯度幅度,然后选择最大梯度幅度的方向作为该点的边缘方向。Kirsch算子在图像处理领域,尤其是在边缘检测方面,因其有效性和鲁棒性而被广泛应用。
根据搜索结果,以下是使用Python和OpenCV实现Kirsch算子进行边缘检测的代码示例:
xxxxxxxxxx
import cv2
import numpy as np
from matplotlib import pyplot as plt
# 读取图像
image = cv2.imread('path_to_your_image.jpg', cv2.IMREAD_GRAYSCALE)
# 检查图像是否成功加载
if image is None:
print("Error: Image not found")
else:
# 自定义Kirsch算子的8个卷积核
m1 = np.array([[5, 5, 5], [-3, 0, -3], [-3, -3, -3]])
m2 = np.array([[-3, 5, 5], [-3, 0, 5], [-3, -3, -3]])
m3 = np.array([[-3, -3, 5], [-3, 0, 5], [-3, -3, 5]])
m4 = np.array([[-3, -3, -3], [-3, 0, 5], [-3, 5, 5]])
m5 = np.array([[-3, -3, -3], [-3, 0, -3], [5, 5, 5]])
m6 = np.array([[-3, -3, -3], [5, 0, -3], [5, 5, -3]])
m7 = np.array([[5, -3, -3], [5, 0, -3], [5, -3, -3]])
m8 = np.array([[5, 5, -3], [5, 0, -3], [-3, -3, -3]])
filterlist = [m1, m2, m3, m4, m5, m6, m7, m8] # 将各个方向的卷积核放到一起便于统一操作
# 建立三维数组,第0维表示各个方向卷积后的值
filtered_list = np.zeros((8, image.shape[0], image.shape[1]))
# 对图像进行卷积操作
for k in range(8):
out = cv2.filter2D(image, cv2.CV_16S, filterlist[k])
filtered_list[k] = out
# 取八个方向中的最大值作为图像该点滤波之后的新的像素值
final = np.max(filtered_list, axis=0)
# 将像素值大于255的点等于255,小于255的点等于0
final[np.where(final >= 255)] = 255
final[np.where(final < 255)] = 0
# 显示原图和边缘检测结果
plt.figure(figsize=(10, 5))
plt.subplot(121), plt.imshow(image, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(final, cmap='gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
# 显示图像
plt.show()
Kirsch算子和Canny算子区别
Kirsch算子和Canny算子都是图像处理中用于边缘检测的算法,但它们在实现和特点上存在一些区别:
-
原理和方法:
- Kirsch算子:基于一阶导数的方法,使用8个方向的卷积核来检测边缘,每个卷积核对应一个方向,能够检测8个方向的边缘强度。
- Canny算子:非微分边缘检测算子,它是一个多阶段的优化算子,包括高斯滤波、梯度计算、非极大值抑制和双阈值检测等步骤。
-
对噪声的敏感性:
- Kirsch算子:由于考虑了多个方向的像素突变,对噪声的抑制能力不如Canny算子,锐化效果可能不是很理想。
- Canny算子:首先使用高斯滤波器平滑图像以去除噪声,因此对噪声不敏感,不容易受到噪声干扰。
-
边缘定位:
- Kirsch算子:边缘定位不如Canny算子精确,因为它是基于一阶导数的,而Canny算子结合了梯度的方向和幅度。
- Canny算子:边缘定位较准确,常用于噪声较多,灰度渐变的图像。
-
计算复杂度:
- Kirsch算子:由于使用了8个卷积核,计算复杂度相对较高。
- Canny算子:虽然实现较为复杂,但通常被认为是最有效的边缘检测方法之一,尤其是在实际项目如车道线检测中。
-
应用场景:
- Kirsch算子:适用于简单场景下的边缘检测,尤其是当需要快速响应时。
- Canny算子:被广泛用于诸如车道线检测等实际项目中,是最有效的边缘检测方法之一。
总的来说,Canny算子在边缘检测的准确性、鲁棒性以及对噪声的抑制方面表现更好,而Kirsch算子则在计算速度上可能有一定优势,尤其是在对实时性要求较高的应用中。
结构化森林
结构化森林(Structured Forests)是一种基于深度学习的边缘检测方法,它在图像处理领域中被用来快速且有效地识别图像中的边缘。以下是结构化森林的一些关键特点和应用场景:
- 快速边缘检测:结构化森林能够快速预测局部掩模的边缘,这对于实时或近实时的边缘检测应用非常有用。
- 深度学习的应用:结构化森林结合了深度学习技术,通过训练一个结构化的随机森林模型来识别图像中的边缘。这种方法可以处理复杂的图像特征,并在多种场景下表现出良好的性能。
- 提高检测准确性:结构化森林在实验中显示出,它不仅提高了检测的准确性,而且减少了计算时间。这对于需要高效边缘检测的应用场景非常重要。
- 复杂背景下的应用:在复杂相似背景下,结构化森林能够有效地区分相似和随机的裂缝特征,这对于结构健康监测等领域尤为重要。
- 与其他算法的结合:结构化森林可以与其他图像处理技术结合使用,例如与Hough变换结合用于海天线检测。这种方法可以较好地忽略局部干扰边缘,强化边界提取,对复杂海天背景下的海天线检测具备鲁棒性和高准确性。
- OpenCV中的实现:在OpenCV库中,结构化森林被实现为一个快速的边缘检测工具。它可以通过提供预训练的模型文件来使用,这个模型文件可以在GitHub的opencv_extra仓库中找到。
- 代码实现:在实际应用中,结构化森林的边缘检测可以通过几行代码实现。首先,需要将输入图像转换为浮点类型,并除以255进行归一化。然后,使用
createStructuredEdgeDetection
函数创建一个边缘检测对象,并调用detectEdges
方法来检测边缘。最后,可以通过非极大值抑制(NMS)进一步处理边缘图,以得到更清晰的边缘。
结构化森林因其快速、准确和在复杂背景下的有效性,成为了边缘检测领域中一个有前景的方法。
在OpenCV中,结构化森林(Structured Forests)边缘检测可以通过以下步骤进行调用和实现。这里提供一个简单的Python代码示例,展示如何使用OpenCV中的结构化森林进行边缘检测:
x
import cv2
import numpy as np
# 读取图像 (注意路径替换,并使用`cv2.IMREAD_GRAYSCALE`参数以灰度模式读取图像)
image = cv2.imread('path_to_your_image.jpg', cv2.IMREAD_GRAYSCALE)
# 检查图像是否成功加载
if image is None:
print("Error: Image not found")
else:
# 创建结构化森林边缘检测器
structured_forest = cv2.createStructuredEdgeDetection()
# 检测边缘
edges = structured_forest.detectEdges(image)
# 应用非极大值抑制(NMS)来细化边缘
# flags=cv2.Canny_NORM_L2_GRADIENT来应用非极大值抑制,这有助于细化边缘。
edges_nms = structured_forest.detectEdges(image, flags=cv2.Canny_NORM_L2_GRADIENT)
# 显示原始图像和边缘检测结果
cv2.imshow('Original Image', image)
cv2.imshow('Edges', edges)
cv2.imshow('Edges NMS', edges_nms)
# 等待按键后关闭窗口
cv2.waitKey(0)
cv2.destroyAllWindows()