OpenCV(cv::Canny())
cv::Canny()
是 OpenCV 中用于边缘检测的函数,基于 John F. Canny 于 1986 年提出的经典 Canny 边缘检测算法。它是一种多阶段边缘检测方法,能够有效检测出图像中的边缘,同时减少噪声和虚假边缘。
1. 函数定义
void cv::Canny(
InputArray image, // 输入图像(单通道,通常是灰度图)
OutputArray edges, // 输出图像,边缘检测结果
double threshold1, // 第一阈值,用于低阈值过滤
double threshold2, // 第二阈值,用于高阈值过滤
int apertureSize = 3, // Sobel 算子内核大小,默认值为 3,必须为奇数
bool L2gradient = false // 是否使用更精确的 L2 范数计算梯度幅值,默认为 false(使用 L1 范数)
);
参数:
-
image
:输入的单通道图像(通常是灰度图)。如果输入的是彩色图像,需要先转换为灰度图(例如使用cv::cvtColor()
)。 -
edges
:输出的单通道图像,表示检测到的边缘。边缘的像素值是 0 或 255(非边缘为 0,边缘为 255)。 -
threshold1
和threshold2
threshold1
是低阈值,用于非最大值抑制。threshold2
是高阈值,用于边缘连接。- 典型设置是
threshold1
较小,threshold2
较大(比如 50 和 150)。边缘像素梯度值高于threshold2
的点被认为是“强边缘”,低于threshold1
的点被忽略,介于两者之间的点只有与强边缘连接时才被保留。
-
apertureSize
:Sobel 算子内核的大小,用于计算图像梯度,必须是奇数且大于等于 3(如 3, 5, 7)。默认值为 3。 -
L2gradient
- 如果为
true
,梯度幅值使用 L2 范数(即 \(\sqrt{(G_x)^2 + (G_y)^2}\))。 - 如果为
false
,梯度幅值使用 L1 范数(即 \(|G_x| + |G_y|\))。 - L2 范数计算更精确,但也更耗时。
- 如果为
2. 工作原理
Canny 边缘检测主要包含以下步骤:
-
高斯平滑:对输入图像进行高斯滤波,减少噪声对边缘检测的影响。
-
计算梯度:使用 Sobel 算子计算图像在水平方向 (\(G_x\)) 和垂直方向 (\(G_y\)) 的梯度,并通过梯度幅值和方向:
\[G = \sqrt{G_x^2 + G_y^2} \quad \text{或} \quad G = |G_x| + |G_y| \]\[\theta = \arctan\left(\frac{G_y}{G_x}\right) \] -
非最大值抑制 (NMS):对梯度幅值图像进行细化,仅保留局部梯度幅值最大的像素点。
-
双阈值检测:使用
threshold1
和threshold2
将像素分类为:- 强边缘:大于
threshold2
。 - 弱边缘:介于
threshold1
和threshold2
之间。 - 非边缘:小于
threshold1
。
- 强边缘:大于
-
边缘跟踪:从强边缘出发,连接与之相邻的弱边缘,形成完整边缘。
3. 示例代码
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 读取图像并转换为灰度图
cv::Mat src = cv::imread("example.jpg", cv::IMREAD_GRAYSCALE);
if (src.empty()) {
std::cerr << "Image not found!" << std::endl;
return -1;
}
// 输出边缘图像
cv::Mat edges;
// 应用 Canny 边缘检测
double lowThreshold = 50;
double highThreshold = 150;
int kernelSize = 3;
cv::Canny(src, edges, lowThreshold, highThreshold, kernelSize, true);
// 显示结果
cv::imshow("Original Image", src);
cv::imshow("Canny Edges", edges);
cv::waitKey(0);
return 0;
}
4. 实际应用
-
特征提取:边缘检测通常是许多计算机视觉任务的第一步,比如目标检测、图像分割和物体识别。
-
轮廓检测:使用
cv::Canny()
检测边缘后,可以结合cv::findContours()
提取图像中的轮廓。 -
图像匹配:边缘检测结果可以作为图像配准、图像拼接等任务的特征。
5. 注意事项
-
噪声敏感性:如果输入图像噪声较多,应先应用高斯滤波或双边滤波去噪,否则可能导致虚假边缘。
-
阈值选择:
threshold1
和threshold2
的选择会显著影响结果,可通过实验调整,也可以结合 Otsu 方法自动计算阈值。 -
单通道输入:
cv::Canny()
只能处理单通道图像,需预处理为灰度图。
6. 输出结果示例
如果输入图像是自然图像(例如风景图),cv::Canny()
输出会是边缘线条清晰的二值图像。通过合理调整阈值,可以控制边缘的密集程度。