目录
2.计算图像梯度(Gradient Calculation)
3. 非极大值抑制(Non-Maximum Suppression)
5. 边缘连接(Edge Tracking by Hysteresis)
二. 高斯拉普拉斯算法(Laplacian of Gaussian)
这一节来说边缘检测算法先上一小段代码。
// 边缘检测
void MainWindow::on_pushButton_edge_clicked()
{
// 调用Canny边缘检测函数
cv::Canny(grayImg, edgeImage, 180, 120);
// 转换回RGB类型图像
cv::cvtColor(edgeImage, edgeImage, cv::COLOR_GRAY2BGR);
QImage displayImg = QImage(edgeImage.data, edgeImage.cols, edgeImage.rows, edgeImage.step, QImage::Format_RGB888);
QImage pixmap = setImageSize(displayImg, ui->dstImage);
}
上面的代码中写出了canny算法的用法,那么接下来来详细介绍canny边缘检测算法。
一. Canny 边缘检测算法
Canny 边缘检测算法是图像处理中的一种经典算法,它通过多步骤处理来精确地检测图像中的边缘。其主要包括五个步骤:高斯滤波、计算梯度、非极大值抑制、双阈值处理、边缘连接。下面结合你的代码对该算法的原理、公式及各个参数进行详细解释。
1.高斯滤波(Gaussian Filter)
在 OpenCV 的 cv::Canny
函数中,高斯滤波的操作是自动执行的,并且它使用默认的参数来进行图像平滑处理。在调用 cv::Canny
时,你无需手动设置高斯滤波的参数,函数内部会自动应用一个 5x5 的高斯核进行平滑处理,以减少噪声的干扰。
默认情况下,OpenCV 会根据高斯核的大小自动计算标准差 σ。通常情况下,σ 与高斯核大小成正比。具体地,σ 的默认值为 0
,即 OpenCV 会自动根据核大小计算合适的标准差。
虽然 cv::Canny
函数内部自动处理高斯滤波,但如果想自定义高斯滤波的参数,可以在调用 Canny
之前手动应用高斯滤波。可以使用 OpenCV 提供的 cv::GaussianBlur
函数来实现。这样就可以控制高斯核大小和标准差。
cv::GaussianBlur(grayImg, grayImg, cv::Size(5,5), 1.5);
// 调用Canny边缘检测函数,这两个参数分别代表了高阈值和低阈值:
cv::Canny(grayImg, edgeImage, 180, 120);
以下是自动执行高斯滤波
以下是手动执行高斯滤波
可以看到手动执行高斯滤波和自动执行高斯滤波之间还是存在较大的区别。
2.计算图像梯度(Gradient Calculation)
在消除噪声后,Canny 算法会计算图像的梯度,计算图像梯度是关键的一步,它用于确定图像中每个像素点的边缘强度(梯度幅值)和方向(梯度方向)。以确定每个像素点的边缘强度和方向。通常使用Sobel 算子来计算图像梯度。
-
水平方向梯度
:
这里的每个符号含义是:
:表示图像的亮度值,也就是每个像素的灰度值。
:表示图像在水平方向 x 上的亮度变化率,或者可以理解为水平导数。
在实际计算时,Sobel 算子通过一个 3x3 的滤波器(卷积核)来近似地计算图像在 x 方向上的导数。Sobel 算子在水平方向的滤波器是:
在图像中,亮度的变化率可以用来发现边缘。边缘的位置就是亮度变化剧烈的地方。这样设置滤波器的目的是为了更强烈的表示两侧的亮度变化,得到较大的梯度。
-
垂直方向梯度
:
其中:
:表示图像在垂直方向 y 上的亮度变化率,也就是垂直方向的导数。
垂直方向的 Sobel 算子滤波器是:
将这个滤波器与图像进行卷积,得到每个像素在垂直方向上的梯度值 Gy。
通过 和
计算每个像素点的梯度幅值 G 和方向 θ:
-
梯度幅值:
一旦计算了水平方向和垂直方向的梯度 Gx 和 Gy,我们就可以计算每个像素的梯度幅值,即边缘的强度。这通过勾股定理(矢量的模)来计算:
其中:
- G:表示每个像素点的梯度幅值,或边缘的强度。它是水平方向梯度和垂直方向梯度的平方和的平方根。
- Gx 和 Gy:分别是水平方向和垂直方向的梯度值(亮度变化)。
这个梯度幅值 G 越大,说明该像素点处的亮度变化越剧烈,也就是说这里更有可能是一个边缘。
-
梯度方向:
除了梯度的幅值,还可以计算梯度的方向,即边缘的方向。梯度方向 θ 是通过水平和垂直梯度的比值来计算的,公式为:
其中:
- θ:表示每个像素点的梯度方向,即边缘的方向。结果是图像中边缘的倾斜角度,通常以弧度或角度表示。
- Gx 和 Gy:分别为该像素点的水平方向和垂直方向梯度值。
梯度方向告诉我们边缘的“走向”,即边缘是朝着哪个方向变化。
3. 非极大值抑制(Non-Maximum Suppression)
非极大值抑制的目的是细化边缘,去除梯度幅值图像中非边缘的点。具体做法是:
- 对每个像素点,检查其沿着梯度方向的前后像素。如果当前像素的梯度幅值不是最大的,则将其置为 0。
这一过程可以理解为在边缘上做“尖锐化处理”,让边缘更加清晰。
4. 双阈值处理(Double Thresholding)
双阈值处理通过设定高低两个阈值,来区分图像中的强边缘、弱边缘和非边缘点。你的代码中提到了两个重要参数:
cv::Canny(grayImg, edgeImage, 180, 120);
这两个参数分别代表了高阈值和低阈值:
- 高阈值(180):用于判断“强边缘”。梯度幅值大于高阈值的像素点会被标记为边缘。
- 低阈值(120):用于判断“弱边缘”。梯度幅值介于低阈值和高阈值之间的像素点会被标记为可能的边缘,但需要连接到强边缘才能最终被认为是边缘。
双阈值的机制确保了强边缘的保留,而弱边缘仅在与强边缘连接时才保留,这减少了边缘检测结果中的噪声。
5. 边缘连接(Edge Tracking by Hysteresis)
在经过双阈值处理之后,Canny 算法会通过边缘连接来进一步优化边缘图像。该步骤的核心是:
- 强边缘(高于高阈值的像素点)被保留为边缘。
- 弱边缘(介于高阈值和低阈值之间的像素点)仅当它们与强边缘相连时,才被保留。
这个步骤确保了弱边缘不会孤立存在,
提高了边缘检测的准确性。
6. Canny 边缘检测算法优劣
Canny 边缘检测算法
Canny 边缘检测是一种多阶段的边缘检测算法,主要步骤包括:
- 高斯平滑:首先对图像进行高斯滤波,以减少噪声。
- 梯度计算:使用 Sobel 算子计算图像梯度,获取梯度的幅值和方向。
- 非极大值抑制:对梯度图进行处理,抑制非边缘的像素,留下局部梯度极大值的边缘像素。
- 双阈值检测:使用高阈值和低阈值分离强边缘和弱边缘,进而处理连接的弱边缘。
优点:
- 精度高:Canny 边缘检测的多步骤(如非极大值抑制和双阈值处理)确保了边缘的精细检测。
- 抗噪能力强:通过初步的高斯平滑,Canny 对图像中的噪声有较好的抑制能力。
- 边缘连接良好:通过双阈值处理和边缘追踪,能有效连接弱边缘,保持边缘的连续性。
缺点:
- 计算复杂:由于步骤较多,Canny 边缘检测的计算复杂度较高,不适合实时应用。
- 参数依赖性强:Canny 算法的效果依赖于高斯平滑参数和双阈值的选择,需要精细调整以应对不同的场景。
二. 高斯拉普拉斯算法(Laplacian of Gaussian)
LoG 算法是一种二阶导数边缘检测算法,它结合了高斯平滑和拉普拉斯算子。具体来说,LoG 先对图像进行高斯滤波(平滑处理)以减少噪声,然后通过拉普拉斯算子计算边缘强度。边缘出现在拉普拉斯算子检测到的零交叉处。
高斯拉普拉斯算法(LoG)边缘检测的步骤可以概括为以下几个部分:
- 高斯平滑:用高斯滤波器对图像进行卷积,平滑噪声。
- 拉普拉斯算子:用拉普拉斯算子计算二阶导数,以找到边缘。
- 零交叉检测:边缘出现在拉普拉斯算子的输出从正值变为负值的地方(即零交叉点)。
1. 高斯滤波
2. 拉普拉斯算子
高斯平滑后的图像使用拉普拉斯算子进行处理,拉普拉斯算子是二阶导数的扩展形式,它用于检测边缘。拉普拉斯算子的离散形式如下:
其中, 和
分别表示函数 f(x,y) 对 x 和 y 的二阶偏导数。
- 一阶导数
或
可以看作沿 x 或 y 方向的“斜率”,它反映了图像在这两个方向上的亮度变化情况。对于图像来说,一阶导数反映了灰度值的梯度或变化。
- 二阶导数
或
则捕捉到图像亮度变化的加速度,也就是检测到一阶导数何时从增加变为减少(或者反之)。具体到图像处理,它可以捕捉到灰度值从变化到稳定的转折点,也就是灰度值急剧变化的地方,这通常就是边缘的所在。
为何要将 x 和 y 方向的二阶导数相加?
自然图像中的亮度变化并不仅限于某一个方向,而是在不同的方向都有可能发生变化。为了全面捕捉图像中所有可能的边缘或亮度变化,必须同时考虑水平 (x 方向) 和垂直 (y 方向) 的亮度变化。
拉普拉斯算子是各向同性的(isotropic),也就是说,它对所有方向的变化都是均等处理的。在图像中,边缘可能以任何方向存在,x 和 y 方向的二阶导数之和能够确保边缘无论位于何处,都能被检测到。这使得拉普拉斯算子在边缘检测中具有较好的方向无关性。
这个公式反映了图像灰度值的局部变化率,灰度值的变化越大,对应位置的导数值也越大。因此,通过对图像应用拉普拉斯算子,能够检测到灰度值变化最剧烈的地方,也就是图像的边缘。
对于图像的离散拉普拉斯算子,常用的滤波器核为:
或者更大一些的核:
一句话总结:
拉普拉斯算子就是通过计算在x,y两方向上的二阶导数之和,二阶导越接近0越有可能是边缘,也就是亮度变化越剧烈的就越有可能是边缘。
3. 零交叉检测
拉普拉斯算子的输出结果通常会包含大量的正负值。为了确定真正的边缘,需要检测零交叉点,即拉普拉斯响应从正变为负的地方。
步骤:
- 检测零交叉点,边缘出现在拉普拉斯响应的正负交界处。
OpenCV 中没有直接的零交叉检测函数,但是可以通过检查拉普拉斯结果矩阵的符号变化来手动实现。
拉普拉斯算子的结果:
拉普拉斯算子的卷积结果是一个矩阵,矩阵中的每个元素对应图像中某一像素点的二阶导数结果。
边缘通常位于二阶导数为零或接近零的位置,特别是拉普拉斯算子的响应从正变为负(或者从负变为正)的交界处。
这些位置被称为“零交叉点”(Zero-Crossing Points),表示图像亮度从增加到减少,或者从减少到增加的区域,即边缘所在的位置。
零交叉的含义:
在图像的一个局部区域,如果拉普拉斯响应的值由正变负,或者由负变正,意味着该区域存在强烈的亮度变化,即可能是边缘。
反之,如果拉普拉斯响应的值在一个区域内始终为正或为负,那么该区域内没有显著的亮度变化,通常不含边缘。
4. 代码参数讲解
下面是高斯拉普拉斯代码的实现部分
// 先对图像进行高斯平滑,sigma 控制平滑程度
cv::GaussianBlur(srcImage, blurredImage, cv::Size(5, 5), 1.0);
// 对平滑后的图像应用拉普拉斯算子
cv::Laplacian(blurredImage, edgeImage, CV_16S, 3);
cv::convertScaleAbs(edgeImage, edgeImage); // 转换为 8-bit 图像
第一步的高斯滤波就不赘述了,已经说了很多遍,还没明白的去看上一节讲高斯滤波部分。然后是
cv::Laplacian(blurredImage, edgeImage, CV_16S, 3);
blurredImage, edgeImage:一个原图像,一个目的图像,可以理解为输入输出。
CV_16S
: 输出图像的深度(数据类型)
CV_16S
表示 16 位有符号整数(short 型),用于存储拉普拉斯算子的结果。拉普拉斯算子计算的是二阶导数,二阶导数的结果可以是负值,因此需要使用有符号的类型。- 如果使用默认的 8 位无符号图像(
CV_8U
),负值会被截断为零,这样会丢失重要的边缘信息。为了解决这个问题,通常使用 16 位有符号整数类型CV_16S
来存储负值结果。
3
: 卷积核大小(kernel size)
- 这里的
3
表示拉普拉斯算子使用的核大小为 3x3。 - 拉普拉斯算子是通过卷积操作计算的,卷积核的大小决定了拉普拉斯算子处理的局部区域大小。卷积核越大,边缘检测的效果越模糊,越小则对细节更加敏感。
- 常见的核大小有 3、5、7 等,选择不同的核大小会影响边缘检测结果。
- 较小的核(如 3x3)能检测到细小的边缘,较小的核能够对细节更敏感,且对小的边缘变化响应更好。但会受噪声影响更大;
- 较大的核(如 5x5)则对图像进行更平滑的边缘检测,会使得局部的细节变化被“平滑”掉,边缘检测的灵敏度降低。随着核的增大,局部像素的细微变化不再被捕捉到,整个图像的响应变得更加模糊,甚至丢失细节。
下图为高斯拉普拉斯边缘检测结果
5. 高斯拉普拉斯算法优劣
LoG (Laplacian of Gaussian) 边缘检测结合了高斯滤波和拉普拉斯算子,具体步骤如下:
- 高斯滤波:对图像进行高斯平滑,减少噪声干扰。
- 拉普拉斯算子:计算图像的二阶导数,通过找到零交叉点来检测边缘。
优点:
- 噪声鲁棒性好:LoG 算法中的高斯滤波部分可以有效去除噪声,使得边缘检测在噪声较多的环境中表现良好。
- 边缘检测精度高:通过拉普拉斯算子的二阶导数,LoG 能更敏感地检测到图像中突变的边缘。
缺点:
- 边缘精度不如 Canny:LoG 没有 Canny 的非极大值抑制和双阈值处理,导致边缘的精度较低,容易出现虚边缘或断裂边缘。
- 对噪声敏感:虽然高斯滤波可以去噪,但因为是基于二阶导数的算法,LoG 对高频噪声较为敏感,可能会误检某些边缘。
- 边缘不连续:由于缺乏边缘连接机制,LoG 检测的边缘可能会出现断裂。
三. Canny算法和高斯拉普拉斯算法对比
特性 | Canny 边缘检测 | 高斯-拉普拉斯 (LoG) 边缘检测 |
---|---|---|
抗噪能力 | 高,通过初始的高斯平滑处理噪声 | 中等,通过高斯滤波处理噪声 |
边缘精度 | 高,通过非极大值抑制和双阈值处理保证精度 | 较低,容易出现虚边缘或多重边缘 |
边缘连续性 | 强,双阈值处理和边缘追踪可连接弱边缘 | 较差,边缘可能不连续 |
计算复杂度 | 高,多个步骤,包括高斯滤波、梯度计算等 | 较低,仅需高斯滤波和拉普拉斯算子 |
对噪声的敏感性 | 较低,高斯滤波和双阈值处理提高了鲁棒性 | 较高,尤其对高频噪声较为敏感 |
参数依赖性 | 高,需要调整高斯核大小、阈值等参数 | 中等,需要设置高斯核大小,但无其他阈值 |
边缘定位 | 精确,通过梯度方向处理 | 较不精确,依赖零交叉点 |
适用场景 | 适合对边缘精度要求较高、噪声较少的场景 | 适合噪声较多,但对精细边缘要求不高的场景 |