在 OpenCV C/C++ 中只过滤一个轮廓

Posted

技术标签:

【中文标题】在 OpenCV C/C++ 中只过滤一个轮廓【英文标题】:Filter out only one contour in OpenCV C/C++ 【发布时间】:2012-10-12 11:08:56 【问题描述】:

我正在尝试制作一个程序,使用基于 Canny 过滤器和轮廓查找功能的摄像机/网络摄像头检测任何形状的对象。这是我的程序:

int main( int argc, char** argv )

CvCapture *cam;
CvMoments moments;
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* contours = NULL;
CvSeq* contours2 = NULL;
CvPoint2D32f center;
int i;

cam=cvCaptureFromCAM(0);
if(cam==NULL)
    fprintf(stderr,"Cannot find any camera. \n");
    return -1;

while(1)
    IplImage *img=cvQueryFrame(cam);
    if(img==NULL)return -1;
    IplImage *src_gray= cvCreateImage( cvSize(img->width,img->height), 8, 1);
    cvCvtColor( img, src_gray, CV_BGR2GRAY );
    cvSmooth( src_gray,  src_gray, CV_GAUSSIAN, 5, 11);
    cvCanny(src_gray, src_gray, 70, 200, 3);

    cvFindContours( src_gray, storage, &contours, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, cvPoint(0,0));
    if(contours==NULL) contours=contours2;
    contours2=contours;
    cvMoments(contours, &moments, 1);

    double m_00 = cvGetSpatialMoment( &moments, 0, 0 );
    double m_10 = cvGetSpatialMoment( &moments, 1, 0 );
    double m_01 = cvGetSpatialMoment( &moments, 0, 1 );
    float gravityX = (m_10 / m_00)-150;
    float gravityY = (m_01 / m_00)-150;
    if(gravityY>=0&&gravityX>=0)
        printf("center point=(%.f, %.f) \n",gravityX,gravityY); 

    for (; contours != 0; contours = contours->h_next)
        CvScalar color = CV_RGB(250,0,0);
        cvDrawContours(img,contours,color,color,-1,-1, 8, cvPoint(0,0));
    

    cvShowImage( "Input", img );
    cvShowImage( "Contours", src_gray );
    cvClearMemStorage(storage);
    if(cvWaitKey(33)>=0) break;

cvDestroyWindow("Contours");
cvDestroyWindow("Source");
cvReleaseCapture(&cam);

此程序将检测相机捕获的所有轮廓,并打印轮廓的平均坐标。我的问题是如何只过滤掉一个对象/轮廓,以便获得更精确的对象 (x,y) 位置?如果可能,谁能告诉我如何使用 (x,y) 坐标标记对象的中心

提前致谢。干杯

p/s:抱歉,我还不能上传屏幕截图,但如果有任何帮助,这里是the link。

编辑:为了让我的问题更清楚:

例如,如果我只想从上面的屏幕截图中过滤掉正方形,我应该怎么做? 我要过滤掉的对象具有最大的轮廓区域,最重要的是有一个形状(任何形状),而不是直线或曲线 我仍在尝试使用平滑值和精确值,所以如果有人在使用我的程序检测轮廓时遇到问题,请更改值。

【问题讨论】:

【参考方案1】:

我认为它可以相当容易地解决。我建议轮廓检测之前进行一些形态学操作。另外,我建议过滤掉较小的元素,并将最大的元素作为唯一仍在图像中的元素。

我建议:

用于过滤 线条(直线或曲线):您必须决定您自己认为什么是“线条”和“形状”之间的边界。假设您将所有厚度 5 像素或更大 的对象视为对象,而将宽度小于 5 像素的对象视为线条。使用 5x5 正方形或 3 像素大小的菱形形状作为结构元素形态开口可以解决这个问题。

一般用于过滤掉小物体:如果物体是任意形状的,纯粹的形态开放是不行的:你必须做一个代数开式。一种特殊类型的代数开口是面积开口:一种去除图像中(像素)面积小于给定阈值的所有连通分量的操作。如果您对不感兴趣的对象的大小有上限,或者感兴趣的对象的大小有下限 >,该值应用作阈值。使用更大的形态开口可能会得到类似的效果,但不会那么灵活。

用于过滤除最大对象之外的所有对象:听起来应该可以将连接的组件从最小的对象移到最大的对象。尝试标记连接的组件。在二进制(黑白图像)上,此图像转换通过创建灰度图像、将背景标记为 0(黑色)以及每个分量具有不同的、递增的灰度值来工作。最后,每个对象的像素被标记为不同的值。您现在可以简单地查看灰度直方图,并找到像素最多的灰度值。将所有其他灰度级设置为 0(黑色),图像中唯一剩下的对象是最大的对象。

建议是从最简单到最复杂的。不过,我认为 OpenCV 可以帮助解决这些问题。形态学腐蚀、膨胀、打开和关闭在 OpenCV 中实现。我认为您可能需要自己构建一个代数开运算(或结合 OpenCV 基本形态学),但我确信 OpenCV 可以帮助您标记连接的组件并检查生成的灰度图像的直方图。

最后,当只剩下一个对象的像素时,您进行 Canny 轮廓检测。

【讨论】:

感谢您的反馈。为了你的不。 1和2方法,我已经尝试了几个函数来过滤掉不需要的轮廓,但没有效果。为了你的不。 3,你能告诉我一些例子/操作方法吗?顺便说一句,膨胀,打开和关闭(加上 FindContours 和 DrawContours)会为视频处理程序使用太多 RAM,所以我宁愿不使用它 如果是视频(例如对象跟踪),您不要在每一帧上都这样做 - 您在第一帧上进行大量处理,然后使用一些跟踪跟随对象的方法。对于#3,我刚刚搜索了“opencv label connected components”,并在前 3-4 次点击中得到了非常有用的结果。顺便说一句,您应该查看 dsp.stackexchange.com,如果您觉得可以在浏览后获得更多帮助(我认为您可以),可以标记您自己的问题并请求迁移。【参考方案2】:

这是 OpenCV 本身无法(轻松)解决的 blob 处理问题。看看 cvBlobsLib。该库扩展了 OpenCV,带有用于连接组件标签的函数/类。

http://opencv.willowgarage.com/wiki/cvBlobsLib

【讨论】:

不错。感谢您的反馈。我去看看。

以上是关于在 OpenCV C/C++ 中只过滤一个轮廓的主要内容,如果未能解决你的问题,请参考以下文章

带有opencv的手写数字边界框

如何提高轮廓精度?

OpenCV中的新函数connectedComponentsWithStats使用

OPENCV - 直接使用指针访问过滤图像并使用内核矩阵过滤

如何在 OpenCV 中使用高斯过滤单列垫

从特征匹配/单应性中过滤掉误报——OpenCV