我有一个程序来计算图像的凸包。我正在尝试使用此信息来计算输入图像中存在的手指的数量。从一些冲浪我发现这样做的方式(计数手指)是
但是我在使用凸性缺陷函数时遇到了麻烦。编译很好,但在运行时程序崩溃与某些输入图像,但没有其他人,我似乎无法弄清楚为什么。
这些是输入图像
代码..
#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;
}
答案 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获得的凸包应该包含轮廓点的指针或索引,而不是船体点本身
在最简单的情况下,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 );
/* ... */
答案 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]);
}
}