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 范数)
);

参数:

  1. image:输入的单通道图像(通常是灰度图)。如果输入的是彩色图像,需要先转换为灰度图(例如使用 cv::cvtColor())。

  2. edges:输出的单通道图像,表示检测到的边缘。边缘的像素值是 0 或 255(非边缘为 0,边缘为 255)。

  3. threshold1threshold2

    • threshold1 是低阈值,用于非最大值抑制。
    • threshold2 是高阈值,用于边缘连接。
    • 典型设置是 threshold1 较小,threshold2 较大(比如 50 和 150)。边缘像素梯度值高于 threshold2 的点被认为是“强边缘”,低于 threshold1 的点被忽略,介于两者之间的点只有与强边缘连接时才被保留。
  4. apertureSize :Sobel 算子内核的大小,用于计算图像梯度,必须是奇数且大于等于 3(如 3, 5, 7)。默认值为 3。

  5. L2gradient

    • 如果为 true,梯度幅值使用 L2 范数(即 \(\sqrt{(G_x)^2 + (G_y)^2}\))。
    • 如果为 false,梯度幅值使用 L1 范数(即 \(|G_x| + |G_y|\))。
    • L2 范数计算更精确,但也更耗时。


2. 工作原理

Canny 边缘检测主要包含以下步骤:

  1. 高斯平滑:对输入图像进行高斯滤波,减少噪声对边缘检测的影响。

  2. 计算梯度:使用 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) \]

  3. 非最大值抑制 (NMS):对梯度幅值图像进行细化,仅保留局部梯度幅值最大的像素点。

  4. 双阈值检测:使用 threshold1threshold2 将像素分类为:

    • 强边缘:大于 threshold2
    • 弱边缘:介于 threshold1threshold2 之间。
    • 非边缘:小于 threshold1
  5. 边缘跟踪:从强边缘出发,连接与之相邻的弱边缘,形成完整边缘。



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. 实际应用

  1. 特征提取:边缘检测通常是许多计算机视觉任务的第一步,比如目标检测、图像分割和物体识别。

  2. 轮廓检测:使用 cv::Canny() 检测边缘后,可以结合 cv::findContours() 提取图像中的轮廓。

  3. 图像匹配:边缘检测结果可以作为图像配准、图像拼接等任务的特征。



5. 注意事项

  1. 噪声敏感性:如果输入图像噪声较多,应先应用高斯滤波或双边滤波去噪,否则可能导致虚假边缘。

  2. 阈值选择threshold1threshold2 的选择会显著影响结果,可通过实验调整,也可以结合 Otsu 方法自动计算阈值。

  3. 单通道输入cv::Canny() 只能处理单通道图像,需预处理为灰度图。



6. 输出结果示例

如果输入图像是自然图像(例如风景图),cv::Canny() 输出会是边缘线条清晰的二值图像。通过合理调整阈值,可以控制边缘的密集程度。



posted @ 2024-11-21 07:18  做梦当财神  阅读(137)  评论(0编辑  收藏  举报