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

标签 c++ c opencv video-processing

我正在尝试制作一个程序,使用基于 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 .

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

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

最佳答案

我认为这很容易解决。我建议 轮廓检测之前进行一些形态学操作。另外,我建议过滤“掉”较小的元素,并让最大的元素成为图像中唯一的元素。

我建议:

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

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

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

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

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

关于c++ - 在 OpenCV C/C++ 中只过滤掉一个轮廓,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12857764/

相关文章:

c++ - 将 IPv4 地址从 ifaddrs 结构分配给 sockaddr_in 结构

c++ - 在缓冲区中推送多个变量值

c++ - c++中STL的priority_queue

c++ - 模拟练习的时间复杂度

c - 为什么我不能用 dev c 编译简单的项目

c - 使用按位运算符计算 1 的数量

java - 翻译图像而不是子图像复制

python | cv2.imshow() 加载数组作为 BGR?

c++ - 通过 C++ 中的包装器将参数列表传递给构造函数

c++ - OpenCV用图片实例训练SVM错误