使用 OpenCV 函数cv::Canny实现 Canny 边缘检测器

理论

Canny 边缘检测器 [ 48]由 John F. Canny 于 1986 年开发。Canny 算法也被许多人称为最佳检测器,它旨在满足三个主要标准:

  • 低错误率:意味着仅对存在的边缘进行良好的检测。
  • 良好的定位:检测到的边缘像素和真实边缘像素之间的距离必须最小化。
  • 最小响应:每个边缘只有一个检测器响应。

步骤

  1. 滤除任何噪音。高斯滤波器就是用于此目的。以下是高斯核的示例s我是埃=5可能用到的如下所示:

  2. 找到图像的强度梯度。为此,我们遵循与 Sobel 类似的程序:
    1. 应用一对卷积掩模(十和是说明:

    2. 使用以下方法查找梯度强度和方向:

      该方向四舍五入为四个可能的角度之一(即 0、45、90 或 135)
  3. 应用非最大抑制。这会删除不被视为边缘一部分的像素。因此,只剩下细线(候选边缘)。
  4. 滞后:最后一步。Canny 确实使用两个阈值(上限和下限):

    1. 如果像素梯度高于上限则该像素被接受为边缘
    2. 如果像素梯度值低于下限则被拒绝。
    3. 如果像素梯度介于两个阈值之间,那么只有它与高于上阈值的像素相连时才会被接受。

    Canny 建议上限下限之比在 2:1 至 3:1 之间。

  5. 欲了解更多详细信息,您可以随时查阅您最喜欢的计算机视觉书籍。

代码

C++

  • 教程代码如下所示。您也可以从此处下载
    
    #include "opencv2/imgproc.hpp"
    #include "opencv2/highgui.hpp"
    #include <iostream>
    
    using namespace cv;
    
    Mat src, src_gray;
    Mat dst, detected_edges;
    
    int lowThreshold = 0;
    const int max_lowThreshold = 100;
    const int ratio = 3;
    const int kernel_size = 3;
    const char* window_name = "Edge Map";
    
    static void CannyThreshold(int, void*)
    {
        blur( src_gray, detected_edges, Size(3,3) );
    
        Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
    
        dst = Scalar::all(0);
    
        src.copyTo( dst, detected_edges);
    
        imshow( window_name, dst );
    }
    
    int main( int argc, char** argv )
    {
      CommandLineParser parser( argc, argv, "{@input | fruits.jpg | input image}" );
      src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR ); // Load an image
    
      if( src.empty() )
      {
        std::cout << "Could not open or find the image!\n" << std::endl;
        std::cout << "Usage: " << argv[0] << " <Input image>" << std::endl;
        return -1;
      }
    
      dst.create( src.size(), src.type() );
    
      cvtColor( src, src_gray, COLOR_BGR2GRAY );
    
      namedWindow( window_name, WINDOW_AUTOSIZE );
    
      createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
    
      CannyThreshold(0, 0);
    
      waitKey(0);
    
      return 0;
    }

  • 这个程序是做什么的?
    • 要求用户输入一个数值来设置我们的Canny 边缘检测器的下限(通过跟踪条)。
    • 应用Canny 检测器并生成蒙版(黑色背景上表示边缘的亮线)。
    • 将获得的蒙版应用到原始图像上并将其显示在窗口中。

解释(C++代码)

  1. 创建一些需要的变量:

    Mat src, src_gray;
    Mat dst, detected_edges;
    
    int lowThreshold = 0;
    const int max_lowThreshold = 100;
    const int ratio = 3;
    const int kernel_size = 3;
    const char* window_name = "Edge Map";

    请注意以下几点:

    1. 我们设立 3:1 的下限与上限比率(变量比率)。
    2. 我们将内核大小设置为3(Sobel 运算由 Canny 函数内部执行)。
    3. 我们为下限设定了一个最大值100。
  2. 加载源图像:
    CommandLineParser解析器(argc,argv,“{@input | fruit.jpg | 输入图像}”);
    src = imread ( samples::findFile( parser.get< String >( "@input" ) ), IMREAD_COLOR ); // 加载图像
    if ( 源文件为空() )
    {
    std::cout << "无法打开或找到图像!\n" << std::endl;
    std::cout << "用法:" << argv[0] << " <输入图像>" << std::endl;
    返回-1;
    }
  3. 创建与src(将成为dst)相同类型和大小的矩阵:
    dst.创建(src.size ),src.类型());
  4. 将图像转换为灰度(使用函数cv::cvtColor):
    cvtColor(src,src_gray,COLOR_BGR2GRAY);
  5. 创建一个窗口来显示结果:
    namedWindow(窗口名称,WINDOW_AUTOSIZE );
  6. 创建一个 Trackbar 供用户输入我们的 Canny 检测器的下限:
    createTrackbar ( "最小阈值:" , window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
    请注意以下几点:
    1. Trackbar 控制的变量是lowThreshold,其限制是max_lowThreshold(我们之前将其设置为 100)
    2. 每次Trackbar注册一个动作时,都会调用回调函数CannyThreshold 。
  7. 让我们一步一步检查一下CannyThreshold函数:
    1. 首先,我们用核大小为 3 的过滤器来模糊图像:
      模糊(src_gray,detected_edges,大小(3,3));
    2. 其次,我们应用 OpenCV 函数cv::Canny
      Canny(检测到的边缘,检测到的边缘,低阈值,低阈值*比率,内核大小);
      其中的参数为:
      • detect_edges:源图像,灰度
      • detect_edges:检测器的输出(可以与输入相同)
      • lowThreshold:用户移动 Trackbar 时输入的值
      • highThreshold:在程序中设置为下限的三倍(按照 Canny 的建议)
      • kernel_size:我们将其定义为 3(内部使用的 Sobel 核的大小)
  8. 我们用零填充dst图像(意味着图像完全是黑色)。
    dst = 标量::all(0);
  9. 最后,我们将使用函数cv::Mat::copyTo仅映射被标识为边缘(在黑色背景上)的图像区域。cv::Mat::copyTo将src图像复制到dst。但是,它只会复制具有非零值位置的像素。由于 Canny 检测器的输出是黑色背景上的边缘轮廓,因此生成的dst在除检测到的边缘之外的所有区域中都将是黑色。
    src.copyTo ( dst,detected_edges);
  10. 我们展示结果:
    imshow (窗口名称,目标);

结果

  • 编译上述代码后,我们可以运行它,并将图像的路径作为参数。例如,使用以下图像作为输入:

  • 移动滑块,尝试不同的阈值,我们得到以下结果:

  • 注意图像是如何叠加到边缘区域的黑色背景的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值