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






45 Comments
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.
Hi! What errors are you getting?
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
Seems like you’ve made some errors while typing. Did you check your code thoroughly?
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
Hi Ferros! Did you have a look at this http://www.aishack.in/2010/04/cropping-robotics-arena-boundaries/ ?
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.
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.
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.
Hello,
Could you please explain what moments are or make a page about them?
Thanks
can u plz explain in detail about this function??
Have a look at its documentation. The parameters of the function perform a lot of heavy duty tasks.
sry gt it….
jst a format problem with the browser..
&=&
>=>
<=<
Fixed the problem!
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.
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.
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
Detecting circles isn’t done with cvCircle. It’s done with a transform similar to the Hough transform – Circle Hough Transform
Hi,
Can I have a tutorial about basic of thresholding.
Have a look at Tracking colored objects in OpenCV and Thresholding
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.
Great!
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
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.
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?
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.
OpenCV comes with a sample file – squares.c have a look at that.
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?
Is img a valid image?
thanks sir for this wonderful walk through…hope that i can ask some questions??
Sure.
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.
Sounds like the function is reading a garbage image somehow. Put your code on pastebin or something and we’ll figure it out.
Thank you for your post. I am really new in computer vision and OpenCV and this post has helped me a lot.
Happy to help!
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?
What you can do is OR the two images. The white boxes will be visible and the black regions won’t be visible.
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.
How do you detect objects?
Using Color Object Tracking
http://www.aishack.in/2010/07/tracking-colored-objects-in-opencv/
And is there any way to draw a rectangle to it ? ( I mean to crop only that part) without using any classifiers like Haar
Hi Utkarsh
Do you have any tutorial regarding blobs ?
You are my hero! Thanks, this helped me a lot!
I have a question about ratio contour. How calculate ratio in contour?
Thanks for your attention…
What do you mean by ‘ratio’ in a contour?
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
[...] check this article http://www.aishack.in/2010/01/an-introduction-to-contours/ It talks about how the squares.c sample works. Then you’ll understand a bit about how to [...]