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

时间:2012-09-21 07:39:33

标签: c++ c image-processing opencv convex-hull

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

  1. 寻找轮廓
  2. Convex Hull
  3. 凸性缺陷
  4. 但是我在使用凸性缺陷函数时遇到了麻烦。编译很好,但在运行时程序崩溃与某些输入图像,但没有其他人,我似乎无法弄清楚为什么。

    这些是输入图像

    1. this图片导致崩溃
    2. this没有。
    3. this即使与上述
    4. 类似,也会导致崩溃

      代码..

      #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;
      }
      

4 个答案:

答案 0 :(得分:3)

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

(gdb) bt
#0  0x00000001002b1491 in cvConvexityDefects ()
#1  0x0000000100001a8d in main ()
但是,不能告诉你原因。由于参数似乎没问题,您可能需要register a new issue here

答案 1 :(得分:3)

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

  

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

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

  2. 如果cvFindContours在轮廓中找到了洞(您的第三张图片),或者您的代码中有多个简单的轮廓或带孔的轮廓(您的第一张图片):

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

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

  3. 相反,你应该:

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

    2. cvConvexityDefects移到最后一个循环中。

    3. 类似的东西:

        /* ... */
      
        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 );
        /* ... */
      

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:0)

我以前曾经见过这个问题,那是当我将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]);
    }
}