“边缘检测”和“图像轮廓”之间的区别

Posted

技术标签:

【中文标题】“边缘检测”和“图像轮廓”之间的区别【英文标题】:Difference between "Edge Detection" and "Image Contours" 【发布时间】:2013-06-10 19:44:16 【问题描述】:

我正在编写以下代码:

#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 图片

轮廓图像

所以现在,如您所见,没有区别!那么,这两者之间的实际区别是什么?在 OpenCV 教程中,只给出了代码。我找到了关于什么是“轮廓”的解释,但它没有解决这个问题。

【问题讨论】:

我建议你在 cv::Canny() 之前使用 cv::GaussianBlur()。这可能会在保留主要边缘的同时消除大部分杂乱。 【参考方案1】:

边缘被计算为图像梯度在梯度方向上的极值点。 如果有帮助,您可以将它们视为一维函数中的最小值和最大值。 关键是,边缘像素是一个局部概念:它们只是指出相邻像素之间的显着差异。

轮廓通常是从边缘获得的,但它们的目的是作为对象轮廓。 因此,它们需要是闭合曲线。 您可以将它们视为边界(一些图像处理算法和图书馆这样称呼它们)。 从边缘获取时,需要将边缘连接起来才能获得闭合轮廓。

【讨论】:

非常感谢您的回复。我真的很感激:) 那么,轮廓总是在它们开始的地方结束?这有区别吗? 是的,轮廓是闭合的,而边缘可能是(多边形)线。 那么,为什么这两个结果几乎相同?或者,这张图片是否只是不好显示edgescontours 之间的区别? 那么说所有的轮廓都是边但不是所有的边都是轮廓是正确的吗,因为边不需要闭合?【参考方案2】:

查找边缘和计数之间的主要区别在于,如果您运行查找边缘,则输出是新图像。在这个新的(边缘图像)图像中,您将突出显示边缘。检测边缘的算法有很多look at wiki see also。

例如,Sobel 算子给出了平滑的“模糊”结果。在您的特定情况下,问题是您正在使用 Canny 边缘检测器。这个比其他检测器更进一步。它实际上运行了进一步的边缘细化步骤。因此,Canny 检测器的输出是二进制图像,用 1 px 宽的线条代替边缘。

另一方面,Contours 算法处理任意二进制图像。因此,如果您在黑色背景上放置白色填充正方形。运行Contours 算法后,你会得到白色的空方块,只有边框。

轮廓检测的另一个额外好处是,它实际上返回了一组点!太好了,因为您可以进一步使用这些点进行一些处理。

在您的特定情况下,两个图像匹配只是巧合。它不是规则,在你的情况下,这是因为 Canny 算法的独特属性。

【讨论】:

Sobel 并不是真正的边缘检测器,它只是提供梯度。然而,Canny 找到了最大梯度,即梯度中的峰值。 Canny() 的 OpenCV 实现实际上在其前端使用了 Sobel()。【参考方案3】:

轮廓实际上可以做的不仅仅是“只是”检测边缘。该算法确实找到了图像的边缘,但也将它们置于层次结构中。这意味着您可以请求在图像中检测到的对象的外部边界。如果你只检查边缘,这样的事情是不可能(直接)发生的。

从文档中可以看出,检测轮廓主要用于对象识别,而精巧的边缘检测器是一种更“全局”的操作。如果轮廓算法使用某种精巧的边缘检测,我不会感到惊讶。

【讨论】:

因为 findContours() 适用于二进制图像,如果它使用 Canny 边缘检测器,我会感到非常惊讶。【参考方案4】:

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

    不仅检测曲线,还检测边缘图上的任何连接。 (连通分量分析)[1] 适用于具有单峰颜色分布的对象(使用简单阈值很容易找到前景蒙版)。您的示例图片不合适。

[1]数字化二进制的拓扑结构分析 由 Satoshi Suzuki 拍摄的 Border Follow 图片,1985 年。

【讨论】:

以上是关于“边缘检测”和“图像轮廓”之间的区别的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV边缘检测

图像轮廓(1)

图像轮廓之查找并绘制轮廓

OpenCV轮廓检测

youcans 的 OpenCV 例程200篇195.绘制图像轮廓(cv.drawContours)

自适应 Canny 边缘检测