OpenCV将Canny边缘转换为轮廓

14

我有一个使用OpenCV的应用程序,从办公室内部的网络摄像头(有很多细节)流中获取输入,在其中我需要找到一个人工标记。该标记是白色背景上的黑色正方形。我使用Canny查找边缘和cvFindContours进行轮廓处理,然后使用approxPolyDP等方法进行过滤和查找候选对象,然后使用本地直方图进一步过滤,等等……

这种方法或多或少有效,但并不完全符合我的要求。即使Canny创建了非闭合的线条,FindContours始终返回一个闭合的轮廓。我得到了沿着形成一个循环的线的两侧行走的轮廓。对于Canny图像上的闭合边缘(我的标记),我得到了2个轮廓,一个在内部,另一个在外部。

我有以下两个问题:

  • 每个标记我得到了2个轮廓(不太严重)

  • 最简单的过滤不能使用(拒绝非闭合的轮廓)

所以我的问题是:是否可以针对非闭合的Canny边缘获得非闭合的轮廓? 或者解决上述两个问题的标准方法是什么?

Canny是一个非常好的工具,但我需要一种将2D黑白图像转换为易于处理的内容的方法。例如连接组件,列出元素中的所有像素以便遍历组件。这样我就可以对循环进行过滤,并将其馈送到approxPolyDP中。

更新:我错过了一些重要的细节:标记可以处于任何方向(不是面向相机,没有直角),实际上我正在进行基于标记的2D投影的3D方向估计。

3个回答

17

我在问题中找到了一个干净简单的解决方案。诀窍是在findCountours中启用两级层次结构生成,并查找具有父级的轮廓。这将返回封闭Canny边缘的内部轮廓而不会有其他内容。 非闭合边缘会被自动丢弃,每个标记将只有一个轮廓。

vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(CannyImage, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, Point(0,0) );
for (unsigned int i=0; i<contours.size(); i++)
    if (hierarchy[i][3] >= 0)   //has parent, inner (hole) contour of a closed edge (looks good)
        drawContours(contourImage, contours, i, Scalar(255, 0, 0), 1, 8);

它也可以反过来使用,也就是:寻找具有子轮廓的轮廓(hierarchy[i][2] >= 0),但在我的情况下,父级检查会产生更好的结果。


2
我并不完全同意这个编辑,因为findContours()和drawContours()不一定在同一张图像上操作,除非你想通过过度绘制立即废弃轮廓图像。该编辑还删除了输入findContours()来自Canny()的信息。 - Gyorgy Szekely

2

我曾经遇到过重复轮廓的问题,即使使用膨胀和腐蚀也无法解决:

Mat src=imread("E:\\test.bmp"),gry,bin,nor,dil,erd;
GaussianBlur( src, nor, Size(5,5),0 );  
cvtColor(nor,gry,CV_BGR2GRAY);  
Canny(gry,bin,100,150,5,true);
dilate(bin,dil,Mat());
erode(dil,erd,Mat());
Mat tmp=bin.clone();
vector<vector<Point>> conts;
vector<Vec4i> hier;
findContours(tmp,conts,hier,CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE);

这个图像(test.bmp)包含3个轮廓,但是findContours返回了6个!我使用了阈值处理,问题得到解决:

Mat src=imread("E:\\test.bmp"),gry,bin,nor,dil,erd;
GaussianBlur( src, nor, Size(5,5),0 );  
cvtColor(nor,gry,CV_BGR2GRAY);
threshold(gry,bin,0,255,THRESH_BINARY+THRESH_OTSU);     
vector<vector<Point>> conts;
vector<Vec4i> hier;
findContours(bin,conts,hier,CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE);

现在它返回了4个轮廓,其中第一个是图像边界(索引为0的轮廓),可以很容易地跳过。

1
这是我的做法: 1. 使用Canny算法进行边缘检测。 2. 使用霍夫变换来检测边缘。 3. 检测两条边缘,使它们的角度为90度。

谢谢,对于正面矩形来说这是个好主意,但不幸的是我必须检测它们的任何方向。事实上,我正在做的任务是估计3D空间中标记的方向(已知内部相机参数)。 - Gyorgy Szekely
您可以使用OpenSURF来检测具有匹配模板的标记的任何方向。 - andre_lamothe

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