边缘检测是一种图像处理技术,用于识别图像中目标或区域的边界(边缘)。边缘是图像中最重要的特征之一。我们通过图像的边缘来了解图像的基本结构。因此,计算机视觉处理管道在应用中广泛地使用边缘检测。
1.如何检测边缘?
边缘的特征是像素强度的突然变化。为了检测边缘,我们需要在邻近的像素中寻找这些变化。来吧,让我们探讨一下OpenCV中可用的两种重要边缘检测算法的使用:Sobel边缘检测和Canny边缘检测。我们将讨论这些理论,并演示它们在OpenCV中的使用。
2.简单的代码示例
首先,看一看演示边缘检测的代码。我们将详细讨论每一行代码,以便您能够完全理解它。
(1)Python
import cv2
# 读取原始图像
img = cv2.imread('test.jpg')
# 显示原始图像
cv2.imshow('Original', img)
cv2.waitKey(0)
# 转换为灰度图
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 模糊图像以更好地检测边缘
img_blur = cv2.GaussianBlur(img_gray, (3,3), 0)
# Sobel边缘检测
sobelx = cv2.Sobel(src=img_blur, ddepth=cv2.CV_64F, dx=1, dy=0, ksize=5) # X轴上的Sobel边缘检测
sobely = cv2.Sobel(src=img_blur, ddepth=cv2.CV_64F, dx=0, dy=1, ksize=5) # Y轴上的Sobel边缘检测
sobelxy = cv2.Sobel(src=img_blur, ddepth=cv2.CV_64F, dx=1, dy=1, ksize=5) # 结合X和Y的Sobel边缘检测
# 显示Sobel边缘检测图像
cv2.imshow('Sobel X', sobelx)
cv2.waitKey(0)
cv2.imshow('Sobel Y', sobely)
cv2.waitKey(0)
cv2.imshow('Sobel X Y using Sobel() function', sobelxy)
# Canny边缘检测
edges = cv2.Canny(image=img_blur, threshold1=100, threshold2=200)
# 显示Canny边缘检测图像
cv2.imshow('Canny Edge Detection', edges)
cv2.waitKey(0)
(2)C++
#include <opencv2/opencv.hpp>
#include <iostream>
// 命名空间
using namespace std;
using namespace cv;
int main()
{
// 读取原始图像
Mat img = imread("test.jpg");
// 显示原始图像
imshow("original Image", img);
waitKey(0);
// 转换为灰度图
Mat img_gray;
cvtColor(img, img_gray, COLOR_BGR2GRAY);
// 模糊图像以更好地检测边缘
Mat img_blur;
GaussianBlur(img_gray, img_blur, Size(3,3), 0);
// Sobel边缘检测
Mat sobelx, sobely, sobelxy;
Sobel(img_blur, sobelx, CV_64F, 1, 0, 5);
Sobel(img_blur, sobely, CV_64F, 0, 1, 5);
Sobel(img_blur, sobelxy, CV_64F, 1, 1, 5);
// 显示Sobel边缘检测图像
imshow("Sobel X", sobelx);
waitKey(0);
imshow("Sobel Y", sobely);
waitKey(0);
imshow("Sobel XY using Sobel() function", sobelxy);
waitKey(0);
// Canny 边缘检测
Mat edges;
Canny(img_blur, edges, 100, 200, 3, false);
// 显示Canny边缘检测图像
imshow("Canny edge detection", edges);
waitKey(0);
destroyAllWindows();
return 0;
}
查看Sobel图像在x方向上主要识别垂直边缘(即那些在x方向上梯度最大的)。而在y方向上则表示水平边(即在y方向上梯度最大的)。仔细看看这两张照片上老虎的条纹。注意,在Sobel图像中,条纹的垂直边缘在x方向上更加明显。对Sobel图像进行两个方向的梯度,将原始图像提取为边缘结构表示,使其结构完整性保持不变。
我们使用的下限为100,上限为200。正如你所看到的,该算法已经识别出了图像中的主要边缘,在这个过程中消除了那些对整体结构不太重要的边缘。然而,结果可以很容易地调整,所以去尝试不同的图像,改变模糊的数量,并尝试不同的阈值来获得自己的感觉。
3.Sobel边缘检测
Sobel边缘检测是应用最广泛的边缘检测算法之一。Sobel算子检测像素强度突然变化的边缘,如下图所示。
当我们画出强度函数的一阶导数时,强度的增加就更加明显了。
从上图可以看出,在梯度高于某一特定阈值的区域可以检测到边缘。此外,导数的突然变化也会显示像素强度的变化。记住这个,我们可以用3×3核来近似导数。我们使用一个核来检测X方向上像素强度的突然变化,另一个在Y方向上。
这些是用于Sobel边缘检测的核:
当这些核与原始图像进行卷积时,就得到了Sobel边缘图像.
- 如果我们只使用垂直核,卷积产生一个Sobel图像,边缘在x方向增强
- 使用水平核生成一个Sobel图像,在y方向上增强边缘。
设
G
x
G_x
Gx和
G
y
G_y
Gy分别表示x和y方向上的强度梯度。如果A和B表示上面定义的X和Y核:
G
x
=
A
∗
I
G
y
=
B
∗
I
G_x = A * I\\G_y = B * I
Gx=A∗IGy=B∗I其中
∗
*
∗为卷积算子,
I
I
I为输入图像。
梯度幅值 G G G的最终近似可计算为: G = G x 2 + G y 2 G=\sqrt{G^2_x+G^2_y} G=Gx2+Gy2
梯度的方向可以近似为: Θ = a r c t a n ( G y G x ) \Theta=arctan(\frac{G_y}{G_x}) Θ=arctan(GxGy)
在下面的代码示例中,我们使用Sobel()
函数进行计算:Sobel(src, ddepth, dx, dy)
参数ddepth
指定输出图像的精度,而dx
和dy
指定每个方向的导数的顺序。例如:
- 如果
dx=1 dy=0
,我们计算x方向上的一阶Sobel像。 - 如果
dx=1 dy=1
,我们计算两个方向上的一阶Sobel像 - 如果
dx=0 dy=1
,我们计算y方向上的一阶Sobel像。
4.Canny边缘检测
Canny边缘检测是当今最流行的边缘检测方法之一,因为它具有很强的鲁棒性和灵活性。这一过程分为四个阶段,包括:
- 降噪
- 计算图像的强度梯度
- 伪边抑制
- 迟滞阈值法
1)降噪
在Canny边缘检测中,高斯模糊滤波器被用来去除或最小化不必要的细节,这些细节可能导致不理想的边缘。看看下面两张图片中的老虎,右边的图片应用了高斯模糊。正如你所看到的,它看起来有点模糊,但仍然保留了大量的细节,从这些细节可以计算出边缘。
2)计算图像的强度梯度
一旦图像被平滑(模糊),它被一个包含水平和垂直的Sobel核过滤。然后使用这些滤波操作的结果来计算每个像素的强度梯度大小(
G
G
G)和方向(
Θ
\Theta
Θ),如下所示。
G
=
G
x
2
+
G
y
2
Θ
=
a
r
c
t
a
n
(
G
y
G
x
)
G=\sqrt{G^2_x+G^2_y}\\\Theta=arctan(\frac{G_y}{G_x})
G=Gx2+Gy2Θ=arctan(GxGy)
然后将梯度方向四舍五入到最近的45度角。下图(右)显示了这个组合处理步骤的结果。
3)伪边抑制
在降低噪声和计算强度梯度之后,这一步的算法使用了一种称为非最大边缘抑制的技术来过滤掉不需要的像素(实际上可能不构成边缘)。为了做到这一点,每个像素与它的邻近像素比较,在正和负梯度方向。如果当前像素的梯度大小大于其相邻像素,则保持不变。否则,当前像素的大小设置为零。下面的图片演示了一个示例。如你所见,老虎皮毛的许多边缘都被明显弱化了。
4)迟滞阈值法
在Canny边缘检测的最后一步中,将梯度值与两个较小的阈值进行比较。
- 如果梯度幅值高于较大的阈值,则这些像素与强边缘相关联,并包含在最终的边缘图中。
- 如果梯度幅值低于较小的阈值,则像素被抑制,并从最终的边缘图中排除。
- 所有其他像素,其梯度大小落在这两个阈值之间,被标记为“弱”边缘(即它们成为最终边缘图中的候选者)。
- 如果“弱”像素与那些与强边缘相关的像素相连接,那么它们也会被包含在最终的边缘图中。
面是使用OpenCV应用Canny边缘检测的语法:Canny(image, threshold1, threshold2)
Canny()函数实现了上面描述的方法。我们只提供Canny边缘检测算法使用的两个阈值,OpenCV处理所有实现细节。在调用Canny()函数之前,不要忘记模糊图像。这是一个强烈推荐的预处理步骤。
在性能方面,Canny边缘检测得到了最好的结果,因为它不仅使用了Sobel边缘检测,而且还使用了非最大抑制和迟滞阈值。这为在算法的最后阶段如何识别和连接边提供了更多的灵活性。
总结
我们讨论了什么使边缘检测成为如此重要的图像处理技术,并重点了解了它的两个最重要的算法(Sobel边缘检测和Canny边缘检测)。在演示它们在OpenCV中的使用时,我们强调了为什么模糊是如此重要的预处理步骤。
您还看到了Canny边缘检测实际上是如何使用Sobel算子来计算数值导数的。并且具有鲁棒性和灵活性,甚至使用非最大抑制和迟滞阈值来获得最大优势。最后,您了解了为什么Canny边缘检测是执行边缘检测的首选和最广泛使用的方法。