Contours - 1 : Getting Started


Contours - 1 : Getting Started

Contours - 1 : Getting Started

Hi, this article is a tutorial which try to cover all relevant functions in OpenCV dealing with Structural Analysis and Shape Descriptors, which are mainly related to contours.

Contours - 1 : Getting Started
Contours can be explained simply as a curve joining all the continuous points (along the boundary), having same color or intensity. For example, consider image at left.

Assuming it is a binary image,we can say, its contour is the curve joining the all the boundary white points.

So if we find a contour in a binary image, we are finding the boundaries of objects in an image. That is why, OpenCV doc says, "The contours are a useful tool for shape analysis and object detection and recognition".

Finding Contours:

We start with a simple image as above. First we find the contours.

import numpy as np
	import cv2

	im = cv2.imread('test.jpg')
	imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
	ret,thresh = cv2.threshold(imgray,127,255,0)
	contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

Points to remember :
  1. For better accuracy, use binary images. So before finding contours, apply threshold or canny edge detection.
  2. FindContours function modifies the source image, so 'thresh' before and after finding contours are different image. So if you want 'thresh' as such after finding contours, store it to some other variables.
  3. In OpenCV, its operation is like finding white object from black background. So remember, object to be found should be white and background in black.
What is structure of resulting contours?

The result "contours" is a Python list, where it contains all objects boundary points as separate lists. So to find number of objects, find length of list "contours", where in this case, it is one. Only one object. So we take it as "cnt".

>>> len(contours)
	>>> cnt = contours[0]
	>>> len(cnt)

Here, number of points in cnt is 244. What these points denote? They are the boundary points of the object.

But, does it include all the boundary? Not exactly. The points are selected such that, contours can be drawn as straight line joining these points. So, if object is a horizontal or vertical line, only end points are stored. ie length of cnt = 2. If object is a rectangle, only 4 vertices are stored.

Contours - 1 : Getting Started
Contour points for a rectangle

Thus in our image, there are no direct horizontal or vertical lines. So most of the points will be stored. To visualise it as above, you can draw circles for each value in cnt.

How to draw the contours?

For this, there is a function, cv2.drawContours(). Let's try it:


This draws a 3-pixel wide green outline of the object. If you want to fill the object with a particular color, pass value of -1 to line thickness.


Contours - 1 : Getting Started
Contours drawn filled
Contours - 1 : Getting Started
Contours drawn 3 px wide

Also, the third argument in cv2.drawContours() is also to be noted. Suppose, you want to draw only fourth contour(not here), third argument should be set to 3. If it is -1, all contours are drawn.

Now you want to draw "cnt" only. It can be done as follows:


Note the square bracket around "cnt". Third argument set to 0, means only that particular contour is drawn.

Now we end after one more important concept, called Mask.

Mask : What and Why?

Mask can be considered as a binary image where only our desired area is white and all others are blacked out. They are used to isolate a part of image and do operations on that part only without affecting or operating on other parts of the image. This can also be considered as a ROI (Region of Interest) which can have any shape.

Contours - 1 : Getting StartedConsider a scenario, where you are asked to find average colors of each shapes in the image at right. So simply threshold the image to binarize it (please don't ask me if white ball can be detected using thresholding, it is just an example). Find contours in the binary image, then for each contour, create a mask image of that shape. ie, if first ball is cosidered, the region of that ball in mask image will be white, while all other shapes and backgrounds are blacked out. Now if you can find the mean color of that shape only. So for every shapes.

(OK, just for this case, I will do it in this image, not on our original image at the beginning)

First we find the contours as we did before. (Adjust the threshold value to detect all). Now we will see how to do it:

First create a mask image where all elements are zero (ie just a black image) with size same as source, but single channel (ie grayscale).

Then for each contour, we draw it on the mask image filled with white color. Then we find mean using mean() function, taking our mask as operating mask.

for h,cnt in enumerate(contours):
	    mask = np.zeros(imgray.shape,np.uint8)
	    mean = cv2.mean(im,mask = mask)

Contours - 1 : Getting Started
Mask Images

See the result at left side.

(All the resulting images are animated to a single image)

I think it is sufficient for now. Keep these three in mind, ie Find Contours, Draw Contours and Mask Image. Now we can find some contour features in next post.



40 Comments on OpenCV-Python: Contours - 1 : Getting Started

  • Anonymous
    on June 22, 2012 | 17:35 Anonymoussaid :
    "Nice tut.."
  • Nirmal Balasooriya
    on July 13, 2012 | 11:39 Nirmal Balasooriyasaid :
    "Can you please explain how to access the edges of a polygon. I mean x,y values of edges."
  • abidk
    on July 13, 2012 | 14:24 abidksaid :
    "Sorry, Are you asking about sides of polygon or its vertices? For example, for a triangle ABC, are you asking about vertices (A,B,C) or sides (AB,BC,AC)? Also read the part-2, part-3, part-4 of this series on contours for more info."
  • Anonymous
    on October 09, 2012 | 01:51 Anonymoussaid :
    "Hi! When I try to draw the contours using cv2.drawContours, I get a TypeError: is not a numpy array.

    My function call looks like:

    fcontours = cv2.findContours(edges_arr2,
    mode = cv2.RETR_TREE,
    method = cv2.CHAIN_APPROX_SIMPLE)


    cv2.drawContours(image = edges_arr,
    contours = fcontours,
    contourIdx = -1,
    color = (0,255,0)

    where edges_arr2 is a copy of edges_arr, my binary edge image (just a simple rectangle!!!)

    Any idea?

  • HuMaH
    on November 10, 2012 | 13:58 HuMaHsaid :
    "@abid rahman, i've text and shapes in an image, when i try to get the triangle out- my text is also considered as triangle- i'm using if contours==3, any ideaS?"
  • abidk
    on November 10, 2012 | 21:47 abidksaid :
    "It may be because, when you approximate the contour, it contains only 3 points. Just check it out. You will have to adjust the parameter in cv2.approxPolyDP function to get a good result. (Check reducing its value). I can't tell more without the image you are working with"
  • Anonymous
    on November 16, 2012 | 14:39 Anonymoussaid :

    is not working for me i am getting this traceback
    Traceback (most recent call last):
    File "", line 19, in
    TypeError: contours data type = 5 is not supported

    what am I doing wrong?"
  • mmolano
    on November 16, 2012 | 14:59 mmolanosaid :
    "Hi! Congrats for this amazing blog!
    I've got a little problem with 'drawContours' function. It actually does not give any error, but it does not do anything!!
    Any hint?

  • Domagoj
    on November 16, 2012 | 16:03 Domagojsaid :
    "The code you posted here does not work with 2.4.3 version


    By downgrading to 2.4.2 it works :/"
  • abidk
    on November 17, 2012 | 01:45 abidksaid :
    "Me too get the same error. Problem is with version 2.4.3. I have filed a bug report to OpenCV. Meanwhile, you have to work with version 2.4.2."
  • abidk
    on November 17, 2012 | 01:45 abidksaid :
    "Me too get the same error. Problem is with version 2.4.3. I have filed a bug report to OpenCV. Meanwhile, you have to work with version 2.4.2."
  • wade
    on November 30, 2012 | 01:11 wadesaid :
    "Thanks for these great tutorials.
    I get error:
    OpenCV Error: Assertion failed (scn == 3 || scn == 4) in unknown function, file ..\..\.

    at line:
    imgray=cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)

    Any ideas???
    Thank you!"
  • abidk
    on November 30, 2012 | 10:48 abidksaid :
    "Input image should be 3-channel, ie RGB etc. And it should be loaded correctly. just print im.shape, and check if it is a valid numpy array and shape is (rows,cols,3) where rows and cols are number of rows and columns of your image."
  • Anonymous
    on January 09, 2013 | 00:20 Anonymoussaid :
    "Do you have any idea why:

    does not produce any output for me? It runs without errors, but does not do anything. Everything else works to that point. Thank you. I am doing this in Spyder."
  • abidk
    on January 09, 2013 | 16:45 abidksaid :
    "I don't know really. Never came across such a problem. But which version of OpenCV do you use?"
  • Anonymous
    on January 16, 2013 | 18:03 Anonymoussaid :
    "How can i see binary images? if i execute: cv2.imshow('im',im) show up only a gray window!"
  • abidk
    on January 16, 2013 | 22:43 abidksaid :
    "Of course you can view a binary image with imshow function. make sure, it has only 1 channel and of datatype= uint8. (you can use img.shape and img.dtype for that)"
  • Anonymous
    on January 20, 2013 | 06:22 Anonymoussaid :
    "My image have white background and a 3 drawed signs find_contour finds 100 hundred objects instead of only 3! I can change any setting or maybe i could change method to retrieve contours? it's better binary images or canny algorithm as first step (i used binary images)?"
  • abidk
    on January 21, 2013 | 10:31 abidksaid :
    "I can't say it without seeing the image. Anyway, for finding contours, it is best to have a black background and white objects. So invert the image. Also, apply threshold before finding contours,especially if image is jpg format."
  • Unknown
    on January 25, 2013 | 17:18 Unknownsaid :
    "Hi, I have the same problem, cv2.drawContours(im,contours,-1,(0,255,0),3) does not produce any output, i have installed OpenCV 2.4.2 and Python 2.7..... "
  • abidk
    on January 25, 2013 | 19:00 abidksaid :
    "Hi, I am not sure why it happens. Can you please send me your code and image to my mail : ? "
  • ChrisM
    on February 05, 2013 | 18:53 ChrisMsaid :
    "Hi, thanks for the great tutorial! I also have the same issue. It seems like none of the drawing functions produce output. I'm also working with opencv 2.4.2, python 2.7 (32 bit) and Eclipse. Any luck with what this is?"
  • ChrisM
    on February 06, 2013 | 15:23 ChrisMsaid :
    "I think I found a solution for my problem. I needed to use cv2.namedWindow, and then cv2.imshow("windowname", im) for the results of the draw functions to display. Thanks again for the tutorial!"
  • ChrisM
    on February 06, 2013 | 15:28 ChrisMsaid :
    "...and also a cv2.waitkey(0) at the end."
  • Anonymous
    on February 08, 2013 | 13:52 Anonymoussaid :
    "what does the last line (mean = cv2.mean(im,mask=mask) does?"
  • abidk
    on February 08, 2013 | 16:18 abidksaid :
    "It finds the mean color inside the shape specified by the mask image. ie it calculates the mean of a region in image 'im', where region is specified by the mask image."
  • Anonymous
    on February 12, 2013 | 14:46 Anonymoussaid :
    "ok this is clear, then how to display this portion both from the binarized image and from the intensity image? cv2.imshow() does not work. i'm missing something..."
  • abidk
    on February 12, 2013 | 16:37 abidksaid :
    "cv2.imshow() should work. Check my last animated image. It is created by this way only. Once you created the mask image,use imshow() to display it. Also, use bitwise_and() to perform AND operation between mask and intensity image. It will give you the only shape you want. Display that also."
  • Anonymous
    on February 12, 2013 | 17:47 Anonymoussaid :
    "thank you. in the end bitwise_and(src,mean,mask = mask) worked perfectly."
  • abidk
    on February 12, 2013 | 19:24 abidksaid :
    "I think it should be bitwise_and(src,src,mask=mask)"
  • Anonymous
    on February 12, 2013 | 20:37 Anonymoussaid :
    "then why compute the mean?"
  • abidk
    on February 12, 2013 | 21:02 abidksaid :
    "Read my statement in italics. My goal was to find the average color of each shape. so cv2.mean() gives you R,G,B values of each shape. In order to show a shape in particular (as in my animated image), use bitwise_and() and imshow()."
  • Anonymous
    on February 13, 2013 | 17:40 Anonymoussaid :
    "ok, sorry for that! btw, great tutorial :)"
  • Anonymous
    on February 18, 2013 | 22:35 Anonymoussaid :
    "I copied and pasted your first block of code and set cnt = contours[0]. Why am i getting a different data type?? len(contours) gives 995 and len(cnt) gives 1. Please help thank you!
  • abidk
    on February 18, 2013 | 23:26 abidksaid :
    "I don't understand why you got like that? You used the same image as above? Then you should get the opposite way, actually.
  • Anonymous
    on February 25, 2013 | 13:27 Anonymoussaid :
    "@Abid Rahman Nice. I have a question though. If I have many blobs(some have holes while some do not). It will go through a blobs elimination process. How can I retrieve back the blobs with the holes as well?"
  • abidk
    on February 25, 2013 | 19:25 abidksaid :
    "I am sorry, I didn't get your question."
  • arjun
    on March 23, 2013 | 01:29 arjunsaid :
    "thanks for the question though,the findcontours function returns hierarchy.I checked the documentation and could not comprehend what you could hierarchy for."
  • abidk
    on March 23, 2013 | 17:52 abidksaid :
  • Daniel
    on April 06, 2013 | 05:53 Danielsaid :
    "I had the same trouble with this
    TypeError: contours data type = 5 is not supported"
    Error on python 2.7.3. with opencv 2.4.3.

    So I installed the newest version 2.4.5. of opencv.
    Now very thing is working fine :>

Contours - 1 : Getting Started

Report "Contours - 1 : Getting Started"

Are you sure you want to report this post for ?