Search code examples
c++opencvdetection

How to draw a single contour in a MemoryStorage


I'm trying to detect the biggest contour into an image using OpenCv Library in C++. My algorithm was working to detect all the contour and perfectly drew them all. But today i've try to only draw the biggest one.

Here is my code using OpenCV in C++ :

#include <cv.h>
#include <highgui.h>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>

using namespace std;

int main()
{

IplImage* img =  cvLoadImage("contour.png");

//show the original image
cvNamedWindow("Raw");
cvShowImage("Raw",img);

//converting the original image into grayscale
IplImage* imgGrayScale = cvCreateImage(cvGetSize(img), 8, 1);
cvCvtColor(img,imgGrayScale,CV_BGR2GRAY);

//thresholding the grayscale image to get better results
cvThreshold(imgGrayScale,imgGrayScale,180,255,CV_THRESH_BINARY);

CvSeq* contours;  //hold the pointer to a contour in the memory block
CvSeq* result;   //hold sequence of points of a contour
CvMemStorage *storage = cvCreateMemStorage(0); //storage area for all contours

//finding all contours in the image
cvFindContours(imgGrayScale, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));

int largest_area=0;
int largest_contour_index=0;

     for(int i = 0; i< contours->total; i++ ) // iterate through each contour.
      {
           double a = cvContourArea (contours, CV_WHOLE_SEQ);  //  Find the area of contour

           if( a > largest_area ){

               largest_area = a;
               largest_contour_index=i; //Store the index of largest contour

            }

      }

int j = 0;
        while (j != largest_contour_index) {

            if (j == largest_contour_index) {


                //obtain a sequence of points of contour, pointed by the variable 'contour'
                result = cvApproxPoly(contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);

                     //if there are 3  vertices  in the contour(It should be a triangle)
                if(result->total==3 )
                {
                //iterating through each point
                CvPoint *pt[3];
                for(int i=0;i<3;i++){
                pt[i] = (CvPoint*)cvGetSeqElem(result, i);
                }

                //drawing lines around the triangle
                cvLine(img, *pt[0], *pt[1], cvScalar(255,0,0),4);
                cvLine(img, *pt[1], *pt[2], cvScalar(255,0,0),4);
                cvLine(img, *pt[2], *pt[0], cvScalar(255,0,0),4);

                }

                //if there are 4 vertices in the contour(It should be a quadrilateral)
                else if(result->total==4 )
                {
                    //iterating through each point
                    CvPoint *pt[4];
                    for(int i=0;i<4;i++){
                    pt[i] = (CvPoint*)cvGetSeqElem(result, i);
                }

                //drawing lines around the quadrilateral
                cvLine(img, *pt[0], *pt[1], cvScalar(0,255,0),4);
                cvLine(img, *pt[1], *pt[2], cvScalar(0,255,0),4);
                cvLine(img, *pt[2], *pt[3], cvScalar(0,255,0),4);
                cvLine(img, *pt[3], *pt[0], cvScalar(0,255,0),4);
                }

                //if there are 7  vertices  in the contour(It should be a heptagon)
                else if(result->total ==7 )
                {
                    //iterating through each point
                    CvPoint *pt[7];
                    for(int i=0;i<7;i++){
                    pt[i] = (CvPoint*)cvGetSeqElem(result, i);
                    }

                    //drawing lines around the heptagon
                    cvLine(img, *pt[0], *pt[1], cvScalar(0,0,255),4);
                    cvLine(img, *pt[1], *pt[2], cvScalar(0,0,255),4);
                    cvLine(img, *pt[2], *pt[3], cvScalar(0,0,255),4);
                    cvLine(img, *pt[3], *pt[4], cvScalar(0,0,255),4);
                    cvLine(img, *pt[4], *pt[5], cvScalar(0,0,255),4);
                    cvLine(img, *pt[5], *pt[6], cvScalar(0,0,255),4);
                    cvLine(img, *pt[6], *pt[0], cvScalar(0,0,255),4);
                }
            }

            contours = contours -> h_next;
            j++;
        }


//sow the image in which identified shapes are marked
cvNamedWindow("Tracked");
cvShowImage("Tracked",img);

cvWaitKey(0); //wait for a key press

//cleaning up
cvDestroyAllWindows();
cvReleaseMemStorage(&storage);
cvReleaseImage(&img);
cvReleaseImage(&imgGrayScale);

return 0;

}

At the execution of the program on this image : enter image description here There is no drawing shape on.

Thanks in advance.


Solution

  • In the first loop, I think, that you don't really iterate through all the contours. You should iterate as you do in the second loop, using h_next. Also, as you search for the biggest contour, you could store its pointer to access it later

    CvSeq* biggestContour;
    double a = 0;
    for(int i = 0; i< contours->total; i++ ) // iterate through each contour.
    {
        a = cvContourArea (contours, CV_WHOLE_SEQ);  //  Find the area of contour
    
        if( a > largest_area ){
            larget_area = a;
            biggestContour = contours; // store the biggest one
    
        }
    
        contours = contours -> h_next;
    
    }
    
    // do your work using the above biggestContour
    
    //obtain a sequence of points of contour, pointed by the variable 'contour'
    result = cvApproxPoly(biggestContour, sizeof(CvContour), storage, CV_POLY_APPROX_DP, cvContourPerimeter(biggestContour)*0.02, 0);
    
    [...] 
    

    EDIT: I will post some code, that you might find useful. You can't use it as it is, but you'll see how to iterate through your contours. I used it to find all contours in an image, and then filter some of them based on various criteria

    // we are going to use these
    CvMemStorage* strgContours;
    CvSeq* seqContours;
    CvSeq* seqCurrent;
    
    // create the MemStorage
    strgContours = cvCreateMemStorage(0);
    
    // you can clone your input image and work on the new one, if you wish
    IplImage* imgTemp = (IplImage*)cvClone(input);
    
    // scan the image for contours
    CvContourScanner scanner;
    scanner = cvStartFindContours( imgTemp, strgContours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
    
    double maxContourArea = 0;
    while( (seqCurrent = cvFindNextContour( scanner )) != 0 ) 
    {
        // Now you can work with seqCurrent, that holds current contour
    
        [...] 
    
        //the area is computed using the Green formula, 
        //thus the returned area and the number 
        //of non-zero pixels can be different.
        double area = cvContourArea( seqCurrent );   //same as "cvContourArea( c , CV_WHOLE_SEQ );"   
    
        [...]                   
    
        // you can even discard contours that do not match your criteria
        if( area < 100 ) {
            cvSubstituteContour( scanner, 0 );
            continue;
        }
    
        [...]
    
        // This is how you draw your contour on an IplImage
        // create your image
        IplImage* imgTest = cvCreateImage( size , IPL_DEPTH_8U , 3);            
        // make it black
        cvZero( imgTest );
        // draw the contour
        cvDrawContours( imgTest, seqCurrent, cvScalarAll(255), cvScalarAll(0), -1, CV_FILLED, 8, cvPoint(0,0));                     
        // do what you want to do with your image       
        [...]       
        // and then release it
        cvReleaseImage( &imgTest);                                  
    }
    
    // seqContours will hold all contours found and were not discarded
    seqContours = cvEndFindContours( &scanner );
    
    // paint the found regions back into the image
    IplImage* imgContours = cvCreateImage( size , IPL_DEPTH_8U , 3);            
    cvZero( imgContours );
    for( seqCurrent=seqContours; seqCurrent != 0; seqCurrent = seqCurrent->h_next ) 
    {               
        cvDrawContours( imgContours, seqCurrent, cvScalarAll(255), cvScalarAll(0), -1, CV_FILLED, 8, cvPoint(0,0));
    }
    
    // do things...
    
    // Free memory
    cvReleaseImage( &imgContours);  
    cvReleaseImage( &imgTemp);  
    cvReleaseMemStorage(&strgContours);