An introduction to contours

Introduction

In this tutorial, you’ll get to know how to use contours. You can think of contours as a boundary. Consider the following thresholded image:

A thresholded image

Using contours, the computer can create a list of points for each “patch” or “blob” of white in the above image. Then you can do whatever you want with these points… figure out the center of the patch, or calculate its approximate size… etc.

The project

In this tutorial, we’ll try and detect all quadrilaterals (polygons with 4 sides) in a thresholded image. We use a thresholded image because it has a clear differentiation: it’s either black or white. And this really helps in generating accurate contours. You can go through the thresholding tutorial to learn about that.

Start off by creating a new C++ Win32 console application. Choose any name you want and click OK. Accept the defaults and click Finish.

Goto the Project > Properties > Configuration Properties > Linker > Input, and put the following piece of text in the Additional Dependencies: “cv.lib cvaux.lib cxcore.lib highgui.lib”, without the quotes of course. Now we’re ready to use OpenCV.

With that done, we’ll dive straight into the code… starting with the headers. Add these lines:

#include <cv.h>
#include <highgui.h>

And then add these lines to the main function:

int main()
{
   IplImage* img = cvLoadImage("C:\\thresholded.jpg");
   IplImage* contourDrawn = 0;
   cvNamedWindow("original");
   cvShowImage("original", img);

We load a thresholded image from disk into img. Note the 0… that indicates that the image we’re loading is already grayscale. Then we create a pointer to an image, called countourDrawn. And then we create a new window titled “original” and display the original image in it.

Then add these line:

    contourDrawn = DetectAndDrawQuads(img);
    cvNamedWindow("contours");
    cvShowImage("contours", contourDrawn);
 
    cvWaitKey(0);
    return 0;
}

We’ll work on the DetectAndDrawQuads function in a few moments. It just takes an image… and finds all possible quads in it, and returns a new image (which as all the contours drawn in it). This image is stored in contourDrawn. And then it is displayed in a window called “contours”. And then you wait till eternity for a key press :)

Now lets get to the juice of the tutorial… actually detecting the presence of quads. Once you’re through this, you’ll be able to detect the presence of complex shapes as well… like circles, squares, even something as complex as a star.

Go above the main function and add these lines:

IplImage* DetectAndDrawQuads(IplImage* img)
{
    CvSeq* contours;
    CvSeq* result;
    CvMemStorage *storage = cvCreateMemStorage(0);

This function returns an image… also takes an image as a parameter. Inside it, we first create some sequences (a sequence is roughly equal to a linked list): one for holding the various contours we’ll get. The other is for temporarily holding the points of a contour as we go through each contour. Then we create some actual storage area… for storing the contours.

Then, add these lines:

    IplImage* ret = cvCreateImage(cvGetSize(img), 8, 3);
    IplImage* temp = cvCreateImage(cvGetSize(img), 8, 1);

Then we create a blank image (of the same size as img) with 3 channels (rgb). We’ll be drawing on this image. We also create a single channeled temporary image. Finding contours works only on grayscale images (with one channel). So to preserve the original image passed to us, we create a new image which is grayscale. To actually convert the image into grayscale, we use the following command:

    cvCvtColor(img, temp, CV_BGR2GRAY);

This convert img from a BGR format into a grayscale image and stores it into temp. Now, add this line:

    cvFindContours(temp, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));

This line actually detects all the contours on the grayscale image img and stores them in contours.

Next we loop through all the contours discovered.. and try to figure out which one is a quad:

    while(contours)
    {
        result = cvApproxPoly(contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);

result now stores actual points from the image that lie on the contour. Now, the most simple logic for detecting a quad would be: if a contour has 4 points, it has a quad. As simple as that. And we implement that using the following code:

        if(result->total==4)
        {
            CvPoint *pt[4];
            for(int i=0;i<4;i++)
                pt[i] = (CvPoint*)cvGetSeqElem(result, i);
 
            cvLine(ret, *pt[0], *pt[1], cvScalar(255));
            cvLine(ret, *pt[1], *pt[2], cvScalar(255));
            cvLine(ret, *pt[2], *pt[3], cvScalar(255));
            cvLine(ret, *pt[3], *pt[0], cvScalar(255));
        }

If the number of points on a contour is 4… we take it to be a quadrilateral. Then, we get each point from result and store them into an array of points (pt[]). And then we draw the quadrilateral (joining each of the points… from 0 to 1, from 1 to 2, and so on).

To finish off this function, add these line:

        contours = contours->h_next;
    }
 
    cvReleaseImage(&temp);
    cvReleaseMemStorage(&storage);
 
    return ret;
}

We go on to the next contour. And the loop continues until all contours are checked. Once that is done, the temporary image is released.. and also the storage for contours. Note that we don’t release img… it was given to us… so we assume that the function which passed it, will manage release it… we don’t need to bother about that.

Finally, we return the image we’ve drawn.

At this point, try executing the program. You’ll get some output like this:

Detection!

Satisfactory results. Our program is somewhat able to recognize the presense of squares, and then draw an image. Notice the small quads? They aren’t really quads…but the “jaggy” edges turn into quads. To eliminate some of this “noise”, we can set a minimum limit on a contour to be considered a quad… say 20 pixels… so if a contour has more than 20 pixels inside it, it would be considered a quad… otherwise it would be taken as noise and would be ignored (no drawn).

To do this, you’d need to modify the if… change it to this line:

if(result->total==4 && fabs(cvContourArea(result, CV_WHOLE_SEQ)) > 20)

and you’re done. The cvContourArea calculates the area of a contour, and returns it. If it is less than 20, we simply skip this contour. Note that we did a fabs… a floating number absolute. The area can be negative (this depends on the orientation of the points). So we simply take the magnitude and ignore the sign.

Run this code and you should get better results:

Detection with size constraints

Another condition used to reduce noise is checking convexity. You can do that using the cvCheckContourConvexity(result) function. If it returns a non zero value, the given contour is convex. Otherwise, it is concave.

Convex vs Concave polygonsConvex / concave

Wrap up

Thats it for now. The program we’ve made isn’t flawless: it doesn’t detect the two small quads. I’ll leave it up to you to find out why and figure out a way to “detect” them (hint: maybe they’re not 4 points).

I hope you learned something from this. And if you have any suggestions or criticisms, do leave a comment!

Issues? Suggestions? Visit the Github issue tracker for AI Shack

Back to top

45 Comments

  1. Jr
    Posted August 26, 2010 at 9:09 am | Permalink

    Good day to you.
    Thank you for sharing the knowledge. I am new in opencv and MVS. I`m using MVS2008 and OpenCV2.1.
    Here is my problem, that I cannot follow your instruction. I try several times your`s contours and thresholding tutorial, but several errors always come out.
    May I have these programs code in file.txt.
    Thank you in advanced.

    • Posted August 26, 2010 at 11:07 am | Permalink

      Hi! What errors are you getting?

      • Jr
        Posted August 27, 2010 at 7:41 am | Permalink

        Hi! Thank you for responding my question.
        Here is some errors occured;

        Error 1 error C2601: ‘DetectAndDrawQuads’ : local function definitions are illegal
        Warning 2 warning C4129: ‘O’ : unrecognized character escape sequence
        Warning 3 warning C4129: ‘i’ : unrecognized character escape sequence
        Error 4 fatal error C1075: end of file found before the left brace …

        Hope you can help me.

        Thank you

        • Posted August 28, 2010 at 8:27 am | Permalink

          Seems like you’ve made some errors while typing. Did you check your code thoroughly?

  2. ferros
    Posted September 30, 2010 at 12:27 am | Permalink

    Hi and good day
    thanks for the tutorial and it’s really help a lot. btw, can i consult you a few problem?
    if i wish to detect a rectangle in a color image and then get the image cover by the rectangle, what technique should i use?
    Can u give me some guidance how to start

    • Posted October 2, 2010 at 10:12 am | Permalink
      • ferros
        Posted October 2, 2010 at 8:56 pm | Permalink

        Thanks for responding on my question, Utkarsh,

        Regards to above question. Actually i’m currently undergoing a project on photo capturing using hand gesture. User’s hand will perform a rectangle shape in front of a webcam, when this hand gesture is perform and identified by webcam, the program will then call a function that will capture the image inside the rectangle boundary.

        Now the question is can i use threshold technique to detect the hand gesture? That mean threshold out the hand image, make the background black and detect the edge of the hand boundary and then crop out the image inside the rectangle, is it the technique you recommend above still apllicable? or have other more better technique on doing this?

        sorry if my question troublesome you a lot, because i’m a new guy in this software field.

        • Posted October 9, 2010 at 6:52 pm | Permalink

          I think it could work. Calculate the area of the hand’s contours. If it is smaller than a particular value, “start dragging”. When the hand’s area is bigger than a particular size, “stop dragging”.

          It would be like close your fist to start dragging and then open your hand wide open to stop dragging.

          I hope this makes sense.

        • antony
          Posted March 18, 2011 at 7:23 pm | Permalink

          Hi ferros,

          Even I’am currently undergoing with the same project as that of yours which involves pointing gesture recognition.I could manage to segment an image and obtain binary image which differentiates skin colour from rest of the image.But further I am stuck with the project.Could you please guide me how do I move on with this. I think by this time you would have completed your project.Thank You in advance.

  3. integral
    Posted January 15, 2011 at 2:18 am | Permalink

    Hello,
    Could you please explain what moments are or make a page about them?
    Thanks

  4. TrUnx
    Posted January 21, 2011 at 9:03 pm | Permalink

    can u plz explain in detail about this function??

        cvFindContours(temp, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
  5. TrUnx
    Posted January 21, 2011 at 9:47 pm | Permalink

    sry gt it….
    jst a format problem with the browser..
    &=&
    >=>
    <=<

  6. Andy
    Posted February 26, 2011 at 12:25 am | Permalink

    Hi,

    If I need to detect squares in images that are not in grayscale, i.e. squares in different colours in a coloured background, how should the code be changed? Any hints?
    Thank you.

    • Posted March 4, 2011 at 9:51 am | Permalink

      You could convert the image into grayscale. Or you could search for square in all channels of the image and then consider all the squares together.

  7. ferros
    Posted February 27, 2011 at 7:09 pm | Permalink

    Hi Utkarsh

    If i wanna detect the white region and track i with a circle, what should i do? i dunno where should i apply the cvCircle function.
    btw, when ii i able to detect the circle, how can i determine the center of the circle?

    Thanks

  8. Saurabh
    Posted March 4, 2011 at 9:21 am | Permalink

    Hi,
    Can I have a tutorial about basic of thresholding.

  9. Rene
    Posted March 5, 2011 at 9:38 pm | Permalink

    Thankyou for the article, it helped me a lot.

    I’m programming with the c++ interface, so I had to figure out how to “translate” all those lines, it really made me understand what I was coding.

  10. Dilip
    Posted March 24, 2011 at 11:47 pm | Permalink

    Hi Utkarsh,
    I was wondering if you know of any methods to detect curves in a binary image. You can use hough for detecting shapes that have a parametric equation. But it cannot be used for curves. Thanks in advance.

    -Dilip

  11. proud to be Egyptian
    Posted March 26, 2011 at 4:29 am | Permalink

    hello Utkarsh.
    i want to thank u for ur great efforts.
    i am new to opencv and my graduation project is about have a webcam on a moving vichel and we put some objects in its path and we want the car to detect these objects and locate there positions and avoid collision with those objects and also recognize their type and using an arm the car suppose to take every object and put it in specific box acording to its type.
    so i want u to help me with an algorithm or steps to move on it
    i searched on the net and i have found many ways to detect an object (colour ,shape,moving,…) i dont know which one is simplest to use it as i just want to detect simple objects.
    and i dont know how to know the location of objects :(
    also my car will be moving and the static objects will be moving refered to it so how my car know that it come very close to the object and it must change its path…
    i mean my car will see the object small and when it come closer the object is getting larger so at what instant of time should the change its direction
    sorry for taking much of ur time.
    thank u. :)

  12. eddie
    Posted April 3, 2011 at 7:28 am | Permalink

    hi utkarsh
    im trying to use a web cam to track objects by size, how would i implement what you have here with the webcam?

  13. John
    Posted April 6, 2011 at 6:54 am | Permalink

    hi utkarsh, im having a project which needs me to detect rectangles. I tried your codes however there are errors. i’ve look thru ur codes to ensure that i did not mistype it. Could you send me a txt. file instead?
    thx.

    • Posted April 29, 2011 at 9:35 pm | Permalink

      OpenCV comes with a sample file – squares.c have a look at that.

  14. John
    Posted April 6, 2011 at 7:38 am | Permalink

    when i did a toggle break point, the erros keep appearing at these 2 lines
    IplImage* ret = cvCreateImage(cvGetSize(img), 8, 3);
    IplImage* temp = cvCreateImage(cvGetSize(img), 8, 1);

    why is this so?

  15. Jayvee
    Posted April 13, 2011 at 10:36 pm | Permalink

    thanks sir for this wonderful walk through…hope that i can ask some questions??

  16. Heba
    Posted July 5, 2011 at 5:54 am | Permalink

    Hi,
    I wanted to detect contours in an image full of separated polygons.
    I used the function of cvFindContours, the return value of the function represents the number of the detected contours in the image.
    The problems I am facing:
    1.The value returned is a huge number compared to the polygons in the image [111935 returned value compared to 10 polygons in the image]
    2.The h_prev, h_next and v_prev of the CvSeq* first_contour are not allocated
    I don’t know where is the problem.
    Do you have any suggestions to help me with that?
    Thanks a lot.

    • Posted August 9, 2011 at 6:51 pm | Permalink

      Sounds like the function is reading a garbage image somehow. Put your code on pastebin or something and we’ll figure it out.

  17. Lucía
    Posted July 29, 2011 at 1:23 pm | Permalink

    Thank you for your post. I am really new in computer vision and OpenCV and this post has helped me a lot.

  18. Esko918
    Posted August 3, 2011 at 2:09 am | Permalink

    Im Trying to figure out a way to overlap these boxes over another image. The program would use the image to find squares upon the screen and then i want to display the boxes over the original image. Also this is all in real time so its constantly going to be updating th images over and over. I have most of it done now all i have to do is take away the black screen behind the return image. Is there any concatenation function in openCv?

    • Posted August 9, 2011 at 6:52 pm | Permalink

      What you can do is OR the two images. The white boxes will be visible and the black regions won’t be visible.

  19. Madu
    Posted August 30, 2011 at 11:31 pm | Permalink

    Thank u for your tutorial. I need to detect color objects and draw rectangles on it (rectangles sizes are vary according to the sizes of each object). If you have any tutorial on this please published. Thanks.

  20. Madu
    Posted September 20, 2011 at 10:31 pm | Permalink

    Hi Utkarsh
    Do you have any tutorial regarding blobs ?

  21. Cesar
    Posted January 9, 2012 at 10:29 am | Permalink

    You are my hero! Thanks, this helped me a lot!

  22. faisal
    Posted January 16, 2012 at 4:26 pm | Permalink

    I have a question about ratio contour. How calculate ratio in contour?
    Thanks for your attention…

    • Posted January 17, 2012 at 12:41 pm | Permalink

      What do you mean by ‘ratio’ in a contour?

  23. Knighk
    Posted January 22, 2012 at 3:59 pm | Permalink

    Thank you utkarsh! This will really help me alot. :) ) A very nice tutorial! I hope even if you’ll graduate already, you’ll still find time teaching newbies like us. ^_^

One Trackback

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>