Calculating Convexitydefects Using Opencv 2.4 in C++

Calculating convexityDefects using OpenCV 2.4 in c++

From openCV wiki :

Finds the convexity defects of a contour.

So you should include it in your loop.

std::vector<Vec4i> defects; 
vector<cv::vector<int> >hull( contours.size() );

for (int i = 0; i < contours.size(); i++)
{
convexHull( contours[i], hull[i], false );
convexityDefects(contours[i], hull[i], defects[i]);
}

Also, as you mentioned, in wiki is said:

hull – Output convex hull. It is either an integer vector of indices
or vector of points. In the first case, the hull elements are 0-based
indices of the convex hull points in the original array (since the set
of convex hull points is a subset of the original point set). In the
second case, hull elements are the convex hull points themselves.

OpenCv Convexity Defects

std::vector<Vec4i> defects;   

should be

std::vector< std::vector<Vec4i> > defects( contours.size() );

(one for each contour / hull)

C++ OpenCv error in function ConvexityDefects

Okay, the problem was mainly because due to some odd reason i had contours so small that the hull was merely a straight line (meaning consisting only of 2 points).
So the error was referencing to the size of the hull vector, other than in the other posts where it seemed to have something to do with the type of vector.

So, just replacing

if(contours[i].size() > 3 )
with
if(hulls[i].size() > 2 )
works fine.

OpenCV C++ cv::convexityDefects error

The second argument of convexityDefects has to be the type of vector<vector<int>, while yours is vector<Point>.

OpenCV convexity defects drawing

convexityDefects needs a

Convex hull obtained using convexHull() that should contain indices of the contour points that make the hull.

that contains more than 3 indices. So you need this:

vector<vector<Point> >hull(Contours.size());
vector<vector<int> > hullsI(Contours.size()); // Indices to contour points
vector<vector<Vec4i>> defects(Contours.size());
for (int i = 0; i < Contours.size(); i++)
{
convexHull(Contours[i], hull[i], false);
convexHull(Contours[i], hullsI[i], false);
if(hullsI[i].size() > 3 ) // You need more than 3 indices
{
convexityDefects(Contours[i], hullsI[i], defects[i]);
}
}

Then your drawing part is (adapted from here):

/// Draw convexityDefects
for (int i = 0; i < Contours.size(); ++i)
{
for(const Vec4i& v : defects[i])
{
float depth = v[3] / 256;
if (depth > 10) // filter defects by depth, e.g more than 10
{
int startidx = v[0]; Point ptStart(Contours[i][startidx]);
int endidx = v[1]; Point ptEnd(Contours[i][endidx]);
int faridx = v[2]; Point ptFar(Contours[i][faridx]);

line(frame, ptStart, ptEnd, Scalar(0, 255, 0), 1);
line(frame, ptStart, ptFar, Scalar(0, 255, 0), 1);
line(frame, ptEnd, ptFar, Scalar(0, 255, 0), 1);
circle(frame, ptFar, 4, Scalar(0, 255, 0), 2);
}
}
}

Complete code

#include <opencv2\opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
Mat img, frame, img2, img3;
VideoCapture cam(0);
while (true){
cam.read(frame);

cvtColor(frame, img, CV_BGR2HSV);

//thresholding
inRange(img, Scalar(0, 143, 86), Scalar(39, 255, 241), img2);

imshow("hi", img2);

//finding contours
vector<vector<Point>> Contours;
vector<Vec4i> hier;
//morphological transformations
erode(img2, img2, getStructuringElement(MORPH_RECT, Size(3, 3)));
erode(img2, img2, getStructuringElement(MORPH_RECT, Size(3, 3)));

dilate(img2, img2, getStructuringElement(MORPH_RECT, Size(8, 8)));
dilate(img2, img2, getStructuringElement(MORPH_RECT, Size(8, 8)));

//finding the contours required
findContours(img2, Contours, hier, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE, Point(0, 0));

//finding the contour of largest area and storing its index
int lrgctridx = 0;
int maxarea = 0;
for (int i = 0; i < Contours.size(); i++)
{
double a = contourArea(Contours[i]);
if (a> maxarea)
{
maxarea = a;
lrgctridx = i;
}
}
//convex hulls
vector<vector<Point> >hull(Contours.size());
vector<vector<int> > hullsI(Contours.size());
vector<vector<Vec4i>> defects(Contours.size());
for (int i = 0; i < Contours.size(); i++)
{
convexHull(Contours[i], hull[i], false);
convexHull(Contours[i], hullsI[i], false);
if(hullsI[i].size() > 3 )
{
convexityDefects(Contours[i], hullsI[i], defects[i]);
}
}
//REQUIRED contour is detected,then convex hell is found and also convexity defects are found and stored in defects

if (maxarea>100){
drawContours(frame, hull, lrgctridx, Scalar(2555, 0, 255), 3, 8, vector<Vec4i>(), 0, Point());

/// Draw convexityDefects
for(int j=0; j<defects[lrgctridx].size(); ++j)
{
const Vec4i& v = defects[lrgctridx][j];
float depth = v[3] / 256;
if (depth > 10) // filter defects by depth
{
int startidx = v[0]; Point ptStart(Contours[lrgctridx][startidx]);
int endidx = v[1]; Point ptEnd(Contours[lrgctridx][endidx]);
int faridx = v[2]; Point ptFar(Contours[lrgctridx][faridx]);

line(frame, ptStart, ptEnd, Scalar(0, 255, 0), 1);
line(frame, ptStart, ptFar, Scalar(0, 255, 0), 1);
line(frame, ptEnd, ptFar, Scalar(0, 255, 0), 1);
circle(frame, ptFar, 4, Scalar(0, 255, 0), 2);
}
}
}

imshow("output", frame);
char key = waitKey(33);
if (key == 27) break;

}
}

Convexity defects C++ OpenCv

I raised this question because I wasn't able to figure out a solution (it is not only today that I was dealing with the matter hehe), but after all I was able to manage the problem!

I had to change the way I calculated the convex hull, using the index array form. So now we have a vector< int > instead a vector< Point >.

This is the code I used (it works I painted the points over an image):

void HandDetection::findConvexityDefects(vector<Point>& contour, vector<int>& hull, vector<Point>& convexDefects){
if(hull.size() > 0 && contour.size() > 0){
CvSeq* contourPoints;
CvSeq* defects;
CvMemStorage* storage;
CvMemStorage* strDefects;
CvMemStorage* contourStr;
CvConvexityDefect *defectArray = 0;

strDefects = cvCreateMemStorage();
defects = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvSeq),sizeof(CvPoint), strDefects );

//We transform our vector<Point> into a CvSeq* object of CvPoint.
contourStr = cvCreateMemStorage();
contourPoints = cvCreateSeq(CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvSeq), sizeof(CvPoint), contourStr);
for(int i=0; i<(int)contour.size(); i++) {
CvPoint cp = {contour[i].x, contour[i].y};
cvSeqPush(contourPoints, &cp);
}

//Now, we do the same thing with the hull index
int count = (int)hull.size();
//int hullK[count];
int* hullK = (int*)malloc(count*sizeof(int));
for(int i=0; i<count; i++){hullK[i] = hull.at(i);}
CvMat hullMat = cvMat(1, count, CV_32SC1, hullK);

//We calculate convexity defects
storage = cvCreateMemStorage(0);
defects = cvConvexityDefects(contourPoints, &hullMat, storage);
defectArray = (CvConvexityDefect*)malloc(sizeof(CvConvexityDefect)*defects->total);
cvCvtSeqToArray(defects, defectArray, CV_WHOLE_SEQ);
//printf("DefectArray %i %i\n",defectArray->end->x, defectArray->end->y);

//We store defects points in the convexDefects parameter.
for(int i = 0; i<defects->total; i++){
CvPoint ptf;
ptf.x = defectArray[i].depth_point->x;
ptf.y = defectArray[i].depth_point->y;
convexDefects.push_back(ptf);
}

//We release memory
cvReleaseMemStorage(contourStr);
cvReleaseMemStorage(strDefects);
cvReleaseMemStorage(storage);
}
}

This worked for me. If you see something wrong or another way to manage it, please tell me!

finding convexity defects in opencv? [crashes depending on the given input image..]

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

  1. 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.

  2. 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:

    1. finds a convex hull of each contour in turn, but only remembers the last one (since each iteration of the loop overwrites retHulls variable)

    2. 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:

  1. passed CV_RETR_EXTERNAL to the cvFindContour to only get the outer contours (you don't care for defects of holes)

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


Related Topics



Leave a reply



Submit