11

I would be grateful to you if you could help me with this issue :)

Relating to this question cvConvexityDefects in OpenCV 2.X / C++?, I have the same problem. The OpenCV C++ wrapper has not the function cvConvexityDefects that appears in the C version, so I tried to write my own version.

Part of the code is (please note that both countour and hull are vector< Point >, calculated separately :

CvSeq* contourPoints;
CvSeq* hullPoints;
CvSeq* defects;
CvMemStorage* storage;
CvMemStorage* strDefects;
CvMemStorage* contourStr;
CvMemStorage* hullStr;
CvConvexityDefect *defectArray = 0;

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

//We start converting vector<Point> resulting from findContours
contourStr = cvCreateMemStorage();
contourPoints = cvCreateSeq(CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvSeq), sizeof(CvPoint), contourStr);
printf("Metiendo valores\n");
for(int i=0; i<(int)contour.size(); i++) {
    CvPoint cp = {contour[i].x,  contour[i].y};
    cvSeqPush(contourPoints, &cp);
}
//Now, the hull points obtained from convexHull c++
hullStr = cvCreateMemStorage(0);
hullPoints = cvCreateSeq(CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvSeq), sizeof(CvPoint), hullStr);
for(int i=0; i<(int)hull.size(); i++) {
    CvPoint cp = {hull[i].x,  hull[i].y};
    cvSeqPush(hullPoints, &cp);
}

//And we compute convexity defects
storage = cvCreateMemStorage(0);
defects = cvConvexityDefects(contourPoints, hullPoints, storage);

The output is Convex hull must represented as a sequence of indices or sequence of pointers in function cvConvexityDefects. Really I don't know how to do conversion in the right way, I've ben searching on the web and tried to adapt/copy/understand some pieces of code, but it is always with the C syntax.

I hope I was clear. Thank you in advance!

Community
  • 1
  • 1
cabreracanal
  • 884
  • 1
  • 13
  • 33
  • As you are writing in C++, it's not appropriate to tag C. – Puppy Jul 24 '11 at 13:32
  • Sorry about that, I put C tag because it is C code what I posted, I want to make some kind of wrapper for cvConvexityDefects to C++ – cabreracanal Jul 24 '11 at 13:36
  • @cabreracanal Hey, I'm having an issue in a similar area, would you be able to have a look here http://stackoverflow.com/questions/12526179/finding-convexity-defects-in-opencv-crashes-depending-on-the-given-input-image – silent Sep 21 '12 at 14:13

2 Answers2

7

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!

cabreracanal
  • 884
  • 1
  • 13
  • 33
  • Hey just wondering, but why did you use a CvMat instead of a sequence for the hull argument in cvConvexityDefects ()? Thank You. – fdh Nov 23 '11 at 02:33
  • Hi, it is because I needed for the cvConvexityDefects some kind of sequence of any type (const void* convexhull) and CvMat came to my mind, but in fact, the examples I found on the web use a CvSeq*, so I think you can use it. – cabreracanal Nov 23 '11 at 08:10
  • Thank You. Just one more question. Instead of making hullK a pointer, would this still work by just using a normal array? For example, instead of what you did, would using "int hullK [hull.size ()]" work? I'm just wondering as I don't have access to a compiler right now so I can't test it out. – fdh Nov 23 '11 at 21:52
  • You are saying this because of the commented line "//int hullK[count];"? I do not have a C compiler right now too, but the way I declared the array is the C way of declaring a dynamic array. In the practice there is no difference at the moment of getting and setting elements in it. The difference is that, in the way I've done it, you have to release manually the memory allocated. Hope this could help you :) – cabreracanal Nov 24 '11 at 16:27
  • 3
    As of openCV 2.4, there is a C++ API version of the method: http://opencv.itseez.com/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=convexity%20defects – Steve Heim Jun 15 '12 at 12:27
  • See this answer for working complete source code: http://stackoverflow.com/a/35190031/763355 – MoDJ Feb 03 '16 at 23:52
3

found some direct approach using the cpp convexityDefects. Typehandling by convexHull-function. It fills by type, int* returns indizes, Point* returns coordinates.

void WorkFrame( Mat img, double minArea )
{
//assumption:
// img already preprocessed, threshold, gray, smooth, morphology whatever..

//get some contours
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( img, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE );

for( int i=0; i<contours.size(); i++ ) 
{
    vector<Point>& c=contours[i];
    double area = contourArea( c );
        if( area<minArea ){ continue; } //filter remaining noise

    //convexHull works typedependent.
    //std::vector<Point> ptHull1; //uncomment and compare to ptHull2
    //convexHull( c, ptHull1 ); //convexHull is smart and fills direct coordinates

    std::vector<int> ihull; 
    convexHull( c, ihull ); //convexHull is smart and fills in contourIndices

    std::vector<Vec4i> defects;
    convexityDefects( c, ihull, defects ); //expects indexed hull (internal assertion mat.channels()==1)

    std::vector< Point > ptHull2;
    std::vector<int>::iterator ii=ihull.begin();
    while( ii!=ihull.end() )
    {
        int idx=(*ii);
        ptHull2.push_back( c[idx] );
        ii++;
    }
    cv::polylines( mat, c, true, Scalar( 0xCC,0xCC,0xCC ), 1 );
    cv::polylines( mat, ptHull2, true, Scalar( 0xFF, 0x20, 0x20 ), 1 );

    std::vector<Vec4i>::iterator d=defects.begin();
    while( d!=defects.end() )
    {
        Vec4i& v=(*d); d++;
        int startidx=v[0]; Point ptStart( c[startidx] );
        int endidx=v[1]; Point ptEnd( c[endidx] );
        int faridx=v[2]; Point ptFar( c[faridx] );

        cv::circle( img, ptStart, 4, Scalar( 0x02,0x60,0xFF ), 2 );
        cv::circle( img, ptEnd,   4, Scalar( 0xFF,0x60,0x02 ), 2 );
        cv::circle( img, ptFar,   4, Scalar( 0x60,0xFF,0x02 ), 2 );
    }
}

}

coolM
  • 31
  • 1