在opencv中找到凸性缺陷? [根据给定的输入图像崩溃..]

Posted

技术标签:

【中文标题】在opencv中找到凸性缺陷? [根据给定的输入图像崩溃..]【英文标题】:finding convexity defects in opencv? [crashes depending on the given input image..] 【发布时间】:2012-09-13 14:52:01 【问题描述】:

我有一个计算图像凸包的程序。我正在尝试使用此信息来计算输入图像中存在的 手指 的数量。从一些冲浪中我发现这样做(数手指)的方法是通过

    寻找轮廓 凸包 凸面缺陷

但是我在使用凸面缺陷函数时遇到了问题。它编译得很好,但在运行时程序会因某些输入图像而崩溃,但不会因其他输入图像而崩溃,我似乎无法弄清楚原因。

这些是输入图像

    this 图片导致崩溃 但this 没有。 this 也会导致崩溃,即使它与上述类似

代码..

#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <opencv/cxcore.h>
#include <stdio.h>

#define CVX_RED     CV_RGB(0xff,0x00,0x00)
#define CVX_GREEN   CV_RGB(0x00,0xff,0x00)
#define CVX_BLUE    CV_RGB(0x00,0x00,0xff)

int main(int argc, char* argv[]) 

  cvNamedWindow( "original", 1 );
  cvNamedWindow( "contours", 1 );
  cvNamedWindow( "hull", 1 );
  IplImage* original_img = NULL;

  original_img = cvLoadImage("img.jpg", CV_LOAD_IMAGE_GRAYSCALE );

  IplImage* img_edge = cvCreateImage( cvGetSize(original_img), 8, 1 );
  IplImage* contour_img = cvCreateImage( cvGetSize(original_img), 8, 3 );
  IplImage* hull_img = cvCreateImage( cvGetSize(original_img), 8, 3 );

  cvThreshold( original_img, img_edge, 128, 255, CV_THRESH_BINARY );

  CvMemStorage* storage = cvCreateMemStorage();
  CvSeq* first_contour = NULL;

  int Nc = cvFindContours(
     img_edge,
     storage,
     &first_contour,
     sizeof(CvContour),
     CV_RETR_LIST // Try all four values and see what happens
  );

  for( CvSeq* c=first_contour; c!=NULL; c=c->h_next ) 
     cvCvtColor( original_img, contour_img, CV_GRAY2BGR );
     cvDrawContours(
        contour_img,
        c,
        CVX_RED,  
        CVX_BLUE,
        0,     
        2,
        8
     );
  

  //----------------------------------------------------------------------Convex Hull

  CvMemStorage* hull_storage = cvCreateMemStorage();
  CvSeq* retHulls = NULL;

  for(CvSeq* i = first_contour; i != NULL; i = i->h_next)
    retHulls = cvConvexHull2(i,hull_storage,CV_CLOCKWISE,0); 
    // with 1 it draws the Hull image but not with 0..?
    // however it needs to be 0 for convexitydefects to work?
  

  printf(" %d elements:\n", retHulls->total );

  // drawing hull

  for( CvSeq* j=retHulls; j!=NULL; j=j->h_next ) 
    cvCvtColor( original_img, hull_img, CV_GRAY2BGR );
    cvDrawContours(
        hull_img,
        j,
        CVX_RED,  
        CVX_BLUE,
        0,        
        2,
        8
     );  

  


  //----------------------------------------------------------------------Convexity Defects??

  CvMemStorage* convexStorage = cvCreateMemStorage();
  CvSeq* defect = NULL;
  defect = cvConvexityDefects(first_contour,retHulls, convexStorage);
  printf(" %d defect:\n", defect->total );


  cvShowImage( "contours", contour_img );
  cvShowImage( "original", original_img );
  cvShowImage( "hull", hull_img );
  cvWaitKey(0);
  cvDestroyWindow( "contours" );
  cvDestroyWindow( "original" );
  cvDestroyWindow( "hull" );
  cvReleaseImage( &original_img );
  cvReleaseImage( &contour_img );
  cvReleaseImage( &hull_img );
  cvReleaseImage( &img_edge );
  return 0;

【问题讨论】:

您是否尝试过使用 C++ 接口来检查问题是否仍然存在或者必须是 C?只是问,因为你也标记了 C++。 @Bob 你好,我认为凸函数没有 c++ 接口。我添加了查看标签。 cv::convexHull 和 cv::convexityDefects exist 在 2.4.2 中。不过我不知道 2.3。 @RuiMarques 请记住,C++ API 使用 C API 在您背后做一些事情。 ;) 升级可能无法解决问题,但值得一试。 an example of using C++ API 埋在一个奇怪的应用程序中(使用 OpenCV 2.4.x) 【参考方案1】:

cvConvexityDefects 期望convexHull 序列(第二个参数)包含contour 序列(第一个参数)的索引:

使用 ConvexHull2 获得的凸包应该包含指向轮廓点的指针或索引,而不是包点本身

    在最简单的情况下,cvFindContours 返回一个简单的轮廓(您的第二张图像),您很幸运,您的代码将提供正确的序列作为第一个参数。

    如果cvFindContours 在轮廓中找到孔(您的第三张图像),或者如果有几个简单的轮廓或带有孔的轮廓(您的第一张图像),您的代码:

      依次找到每个轮廓的凸包,但只记住最后一个(因为循环的每次迭代都会覆盖retHulls变量)

      将与retHulls 中的索引不对应的轮廓的整个层次结构传递给cvConvexityDefects 作为第一个参数。

相反,您应该:

    CV_RETR_EXTERNAL 传递给cvFindContour 以仅获取外部轮廓(您不关心孔的缺陷)

    cvConvexityDefects 移动到最后一个循环中。

类似:

  /* ... */

  if (argc < 2) 
      std::cerr << "Usage: convexity IMAGE\n";
      exit(1);
  

  cvNamedWindow( "original", 1 );
  cvNamedWindow( "contours", 1 );
  cvNamedWindow( "hull", 1 );
  IplImage* original_img = NULL;

  original_img = cvLoadImage(argv[1], CV_LOAD_IMAGE_GRAYSCALE );

  IplImage* img_edge = cvCreateImage( cvGetSize(original_img), 8, 1 );
  IplImage* contour_img = cvCreateImage( cvGetSize(original_img), 8, 3 );
  IplImage* hull_img = cvCreateImage( cvGetSize(original_img), 8, 3 );

  cvThreshold( original_img, img_edge, 128, 255, CV_THRESH_BINARY );

  CvMemStorage* storage = cvCreateMemStorage();
  CvSeq* first_contour = NULL;

  int Nc = cvFindContours(
     img_edge,
     storage,
     &first_contour,
     sizeof(CvContour),
     CV_RETR_EXTERNAL // Try all four values and see what happens
  );

  cvCvtColor( original_img, contour_img, CV_GRAY2BGR );
  for( CvSeq* c=first_contour; c!=NULL; c=c->h_next ) 
     cvDrawContours(
        contour_img,
        c,
        CVX_RED,
        CVX_BLUE,
        0,
        2,
        8
     );
  
  cvShowImage( "contours", contour_img );

  //----------------------------------------------------------------------Convex Hull
  //-------------------------------------------------------------------Convex Defects

  CvMemStorage* hull_storage = cvCreateMemStorage();
  CvSeq* retHulls = NULL;

  cvCvtColor( original_img, hull_img, CV_GRAY2BGR );
  for(CvSeq* i = first_contour; i != NULL; i = i->h_next)
    retHulls = cvConvexHull2(i,hull_storage,CV_CLOCKWISE,0);
    printf(" %d elements:\n", retHulls->total );

    CvSeq* defect = NULL;
    defect = cvConvexityDefects(i,retHulls, NULL); // reuse storage of the contour
    printf(" %d defect:\n", defect->total );

    // drawing hull.... you can't use the one returned above since it only
    // contains indices
    retHulls = cvConvexHull2(i,hull_storage,CV_CLOCKWISE,1);
    cvDrawContours(
        hull_img,
        retHulls,
        CVX_RED,
        CVX_BLUE,
        0,
        2,
        8
     );
  

  cvShowImage( "hull", hull_img );
  /* ... */

【讨论】:

无论是否解决问题,事实是函数不应该因参数无效而崩溃或冻结。这是一个真正的问题,应该在 OpenCV 的问题跟踪器上解决。不过,您的回答似乎不错。当 OP 测试时,我会投票赞成。 我同意。 OpenCV 的文档通常很差,会默默地失败或出现神秘的断言错误。一半的时间我通过反复试验找出细节,其余的 - 通过仔细阅读来源。【参考方案2】:

使用有问题的图像运行您的应用程序会冻结它,但我发现 OpenCV 2.4.2 没有崩溃,问题确实发生在 cvConvexityDefects(),根据gdb

(gdb) bt
#0  0x00000001002b1491 in cvConvexityDefects ()
#1  0x0000000100001a8d in main ()

但不能告诉你为什么。由于参数似乎没问题,你可能想register a new issue here。

【讨论】:

我现在使用的是opencv 2.3,你是说在2.4.2下所有图片都可以工作吗?还是有问题的图像仍然存在同样的问题? 在 2.4.2 上,您的程序挂在 cvConvexityDefects() 而不是崩溃。这只是与您观察到的行为不同的行为,但它仍然表明问题存在。【参考方案3】:

使用 Python 3 和 opencv 4.5.1,我遇到了一个类似的问题,即来自 convehull() 的索引“不单调”。 我发现由于某种原因,从这个函数返回的索引是乱序的。

为了解决这个问题,我只是在将索引传递给 convexityDefects() 之前按降序对 numpy 数组进行了排序。

hull = cv2.convexHull(contours, returnPoints=False)
hull[::-1].sort(axis=0)
defects = cv2.convexityDefects(contours, hull)

【讨论】:

【参考方案4】:

我以前见过这个问题,当时我将 MAT 与 OpenCV 4.4.0 一起使用,我猜崩溃是因为这个错误而发生的。

“凸包索引不是单调的,这可能是在输入轮廓包含函数'convexityDefects'中的自相交的情况下。

当您详细阅读崩溃报告时,解决方案很简单,即索引未按顺序排序,可能是因为轮廓中有一些自相交,(或者它可能是一个小故障,因为2.1 版固定在 3.2 版上,然后又回到 4.4.0 检查:https://github.com/opencv/opencv/issues/4539

因此,为了避免对这个问题产生太多疑虑,我通过在从轮廓中提取缺陷之前利用自身的船体索引来解决它。

如您所见,船体索引是倒置排序的,就好像船体单元大小为 6 一样,这意味着该单元中的索引将是:

[0] = 5
[1] = 4
[2] = 3
[3] = 2
[4] = 1
[5] = 0

但是由于交叉点或其他一些原因,它可能没有按预期排序,例如:

[0] = 6
[1] = 5
[2] = 4
[3] = 2
[4] = 1
[5] = 0

所以我们需要做的就是使用它 检查此代码

vector <vector <Vec4i> > defects(contour.size());  // Defects Vectors
vector <vector <cv::Point> > hullsP(contour.size()); // Hulls contour points
vector <vector <int> > hullsI(contour.size()); // Indices to hulls contour points

for(int i = 0; i < contour.size(); i++)

    convexHull(contour[i], hullsP[i], CV_CLOCKWISE, false);
    convexHull(contour[i], hullsI[i], CV_CLOCKWISE, false);
    
    for(size_t k =0; k < hullsI[i].size(); k++) //Here we resort the indices
    
        if (hullsI[i].size() > 0)
        
            hullsI[i][k] = (int)((hullsI[i].size() - k)-1);
        
    
    
    if(hullsI[i].size() > 3 ) // You need more than 3 indices
    
        convexityDefects(contour[i], hullsI[i], defects[i]);
    

【讨论】:

以上是关于在opencv中找到凸性缺陷? [根据给定的输入图像崩溃..]的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV计算机图像处理 —— 凸性缺陷 + 点多边形测试 + 形状匹配 + 轮廓分层与cv.findContours()

opencv 9 -- 轮廓 特征 二

opencv 9 -- 轮廓 特征 二

OpenCV - 如何消除凸轮扫描仪中的凸面缺陷?

OpenCV 查找图像的紧密匹配

OpenCV竟然可以这样学!成神之路终将不远(二十)