5

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

  1. 寻找轮廓
  2. 凸包
  3. 凸缺陷

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

这些是输入图像

  1. 图像导致崩溃
  2. 事实并非如此。
  3. 即使它与上述类似,这也会导致崩溃

代码..

#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

4 回答 4

3

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

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

不过不能告诉你为什么。由于参数似乎没问题,您可能想在此处注册一个新问题

于 2012-09-24T03:29:40.037 回答
3

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

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

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

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

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

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

相反,您应该拥有:

  1. 传递CV_RETR_EXTERNALcvFindContour只得到外部轮廓(你不关心孔的缺陷)

  2. 移动了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 );
  /* ... */
于 2012-09-27T08:49:41.280 回答
2

使用 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)
于 2021-01-25T13:05:00.843 回答
1

我以前见过这个问题,那是我将 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]);
    }
}
于 2020-09-09T00:18:09.963 回答