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.


For those who don't read the comments or didn't see it the solution is to use:

vector<cv::vector<int> >hull;

to create the hull instead of:

vector<cv::vector<Point> >hull;

as convexityDefects only works on hulls stored as a series of indices rather than a series of Points.

Sadly this gives another problem as drawContours only draws contours stored as a series of Points and not as a series of indices! So if you want to draw your hulls at a later stage you may want to create 2 stores when you are finding the hulls, one for drawing and one for finding defects from. Something along the lines of the following works:

  // create storage space
  vector<vector<int> > hullsI(contours.size());
  vector<vector<Point> > hullsP(contours.size());
  vector<vector<Vec4i> > defects(contours.size());

  for(int i = 0; i <contours.size(); ++i){
     //find the hulls
     convexHull(contours[i], hullsI[i], true);
     convexHull(contours[i], hullsP[i], true);
     //find the defects  
     convexityDefects(contours[i], hullsI[i], defects[i]);
     }

It may be more efficient to just use a different method to draw the hulls rather than calculating them twice but this was the most elegant way I saw of doing it.

Also I'm still getting the hang of C/C++ myself (Java guy) but I believe if you add using namespace cv to the top of the code you can save having to have cv:: throughout your code.

Hope I haven't stepped on toes Innuendo, not my intention at all if I have.


i had problems like mentioned above but at last i write the correct one :)

vector<vector<Point>> hull( contours.size() );
vector<vector<int> > hullsI(contours.size());
vector<vector<Vec4i>> convdefect(contours.size());

for( int i = 0; i < contours.size(); i++ )
{ 
    convexHull( Mat(contours[i]), hull[i], false);
    convexHull( Mat(contours[i]), hullsI[i], false);        
    if(hullsI[i].size() > 3 )
        convexityDefects(contours[i],hullsI[i],convdefect[i]);
}
/// Draw contours + hull results
Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
    size_t count = contours[i].size();
    if( count <300 )
        continue;

    //Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255));
    drawContours( drawing, contours, i, Scalar(255,0,0), 1, 8, vector<Vec4i>(), 0, Point());
    drawContours( drawing, hull, i, Scalar(0,0,255), 1, 8, vector<Vec4i>(), 0, Point());
}

/// Draw convexityDefects
for( int i = 0; i< contours.size(); i++ )
{
    size_t count = contours[i].size();
    if( count <300 )
        continue;

    vector<Vec4i>::iterator d=convdefect[i].begin();
    while( d!=convdefect[i].end() ) {
        Vec4i& v=(*d);
        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] );
        float depth = v[3] / 256;

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