在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()