"边缘检测"和"图像轮廓"的区别是什么?"

41

我正在处理以下代码:

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;

Mat src, grey;
int thresh = 10;

const char* windowName = "Contours";

void detectContours(int,void*);

int main()
{
    src = imread("C:/Users/Public/Pictures/Sample Pictures/Penguins.jpg");

    //Convert to grey scale
    cvtColor(src,grey,CV_BGR2GRAY);

    //Remove the noise
    cv::GaussianBlur(grey,grey,Size(3,3),0);

    //Create the window
    namedWindow(windowName);

    //Display the original image
    namedWindow("Original");
    imshow("Original",src);

    //Create the trackbar
    cv::createTrackbar("Thresholding",windowName,&thresh,255,detectContours);

    detectContours(0,0);
    waitKey(0);
    return 0;

}

void detectContours(int,void*)
{
    Mat canny_output,drawing;

    vector<vector<Point>> contours;
    vector<Vec4i>heirachy;

    //Detect edges using canny
    cv::Canny(grey,canny_output,thresh,2*thresh);

    namedWindow("Canny");
    imshow("Canny",canny_output);

    //Find contours
    cv::findContours(canny_output,contours,heirachy,CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE,Point(0,0));

    //Setup the output into black
    drawing = Mat::zeros(canny_output.size(),CV_8UC3);



    //Draw contours
    for(int i=0;i<contours.size();i++)
    {
        cv::drawContours(drawing,contours,i,Scalar(255,255,255),1,8,heirachy,0,Point());
    }

    imshow(windowName,drawing);

}

理论上,Contours 意味着检测曲线。 Edge detection 意味着检测边缘。在我的代码中,我使用 Canny 进行了边缘检测,并使用 findContours() 进行曲线检测。以下是结果图像:

Canny 图像

enter image description here

Contours 图像

enter image description here

所以现在,你可以看到没有什么区别!那么,这两者的实际区别是什么?在 OpenCV 教程中,只提供代码。我找到了一个关于“Contours”是什么的解释,但它没有解决这个问题。


1
我建议您在使用cv::Canny()之前先使用cv::GaussianBlur()。这样可以消除大部分杂乱的边缘,同时保留主要的边缘。 - Bull
4个回答

74

边缘是在梯度方向上的图像梯度极值点所计算出来的。你可以将其理解为一维函数中的最小值和最大值。 重要的是,边缘像素是一个局部概念:它们只指出相邻像素之间的显著差异。

轮廓线通常是从边缘中获取的,但它们旨在成为对象轮廓线。因此,它们需要成为封闭曲线。 你可以把它们看作边界(一些图像处理算法和库称之为这样)。 当它们从边缘中获取时,你需要连接边缘以获得闭合轮廓线。


那么,轮廓总是结束在它们开始的地方?这就是区别吗? - Shuvo Sarker
1
是的,轮廓是封闭的,而边缘可能是(多边形)线。 - sansuiso
那么,为什么这两个结果几乎相同呢?或者,这张图片只是不好展示“边缘”和“轮廓”的区别吗? - Scott
那么说所有的轮廓线都是边缘,但并非所有的边缘都是轮廓线,因为边缘不需要闭合,这样说是否正确? - theprogrammer
“_轮廓线_”:“一个轮廓,特别是代表或限制某物形状或形式的轮廓。” - Константин Ван
@Scott 可能是因为他的 findContours 找到的是代表边缘的细线条的轮廓。 - Константин Ван

9
找边缘和轮廓之间的主要区别在于,如果运行找边缘,则输出是新图像。在这个新的(边缘图像)图像中,您将突出显示边缘。有许多算法可以检测边缘请参阅维基百科
例如,Sobel算子会产生平滑的“雾状”结果。在您的特定情况下,问题在于您正在使用Canny边缘检测器。这个检测器比其他检测器更进一步。它实际上运行了更多的边缘细化步骤。因此,Canny检测器的输出是二进制图像,在边缘位置具有1像素宽的线条。
另一方面,轮廓算法处理任意二进制图像。因此,如果您在黑色背景上放置白色填充的正方形。运行轮廓算法后,您将得到一个白色的空心正方形,仅有边界。
轮廓检测的另一个额外好处是,它实际上返回一组点!这很棒,因为您可以进一步使用这些点进行某些处理。
在您的特定情况下,两个图像匹配只是巧合。这并不是规则,在您的情况下,这是由于Canny算法的独特属性。

3
Sobel并不是真正的边缘检测器,它只是提供了梯度信息。Canny能够找到最大的梯度值,也就是梯度峰值,实际上在OpenCV中,Canny()函数的前端实现中使用了Sobel()函数。 - Bull

6

轮廓检测可以做比“仅仅”检测边缘更多的事情。该算法确实可以找到图像的边缘,但同时还将它们放在一个层次结构中。这意味着您可以请求检测到的图像对象的外部边框。如果您只检查边缘,则这是不可能(直接)实现的。

如文档所述,检测轮廓主要用于对象识别,而Canny边缘检测器是一种更“全局”的操作。我不会感到惊讶,如果轮廓算法使用某种形式的Canny边缘检测。


2
由于findContours()函数只能在二值图像上运行,如果它使用Canny边缘检测器,我会感到非常惊讶。 - Bull

2

轮廓的概念被用作处理边缘数据的工具。并非所有边缘都相同。但在许多情况下,例如具有单峰色彩分布(即一种颜色)的对象,边缘本身就是轮廓(轮廓线,形状)。

  1. 检测不仅曲线,还要检测与边缘图连接的任何内容。(连接组件分析)[1]
  2. 适用于具有单峰色彩分布的对象(通过简单的阈值可以轻松找到前景掩码)。你提供的示例图片不适合。

[1]《数字二进制图像拓扑结构分析:边缘跟踪》by Satoshi Suzuki, 1985。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接