I have a program that calculates the convex hull of an image. I'm trying to use this information in order to count the number of fingers that are present in an input image. From some surfing I found out that the way to do this (count fingers) is by
But I'm having trouble using the convexity defects function. It compiles fine but at runtime the program crashes with certain input images but not with others and I can't seem to figure out why.
These are the input images
code..
#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;
}
Convexity DefectsAny deviation of the object from this hull can be considered as convexity defect. We can visualize it using an image. We draw a line joining start point and end point, then draw a circle at the farthest point. Note.
The convexity defect is defined as the points farthest from the convex points. So, if the finger tips are considered as the convex points, the trough between the fingers can also be considered as convexity defects.
Python - OpenCV & PyQT5 together A convex hull is a convex curve around an object. A convex curve is always bulged out, or at-least flat. A convex hull finds the convexity defects and corrects them.
cvConvexityDefects
expects the convexHull
sequence (second argument) to contain indices into the contour
sequence (first argument):
Convex hull obtained using ConvexHull2 that should contain pointers or indices to the contour points, not the hull points themselves
In the most trivial case, where cvFindContours
returns a single simple contour (your second image) you got lucky and your code would supply the correct sequence as the first parameter.
In case cvFindContours
finds holes in the contour (your third image), or if there are several simple contours or contours with holes (your first image) your code:
finds a convex hull of each contour in turn, but only remembers the last one (since each iteration of the loop overwrites retHulls
variable)
passes the whole hierarchy of the contours, which doesn't correspond to indices in retHulls
, to cvConvexityDefects
as the first argument.
Instead, you should have:
passed CV_RETR_EXTERNAL
to the cvFindContour
to only get the outer contours (you don't care for defects of holes)
moved the cvConvexityDefects
inside the last loop.
Something like:
/* ... */
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 );
/* ... */
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With