OpenCV center of contour

center_of_contour_results

Today, we are going to start a new 3-part series of tutorials on shape detection and analysis.

Throughout this series, we’ll learn how to:

  1. Compute the center of a contour/shape region.
  2. Recognize various shapes, such as circles, squares, rectangles, triangles, and pentagons using only contour properties.
  3. Label the color of a shape.

While today’s post is a bit basic (at least in context of some of the more advanced concepts on the PyImageSearch blog recently), it still addresses a question that I get asked a lot:

“How do I compute the center of a contour using Python and OpenCV?

In today’s post, I’ll answer that question.

And in later posts in this series, we’ll build upon our knowledge of contours to recognize shapes in images.

Looking for the source code to this post?
Jump right to the downloads section.

OpenCV center of contour

Figure 1: An example image containing a set of shapes that we are going to compute the center of the contour for.

Figure 1: An example image containing a set of shapes that we are going to compute the center of the contour for.

In above image, you can see a variety of shapes cut out from pieces of construction paper. Notice how these shapes are not perfect. The rectangles aren’t quite rectangular — and the circles are not entirely circular either. These are human drawn and human cut out shapes, implying there is variation in each shape type.

With this in mind, the goal of today’s tutorial is to (1) detect the outline of each shape in the image, followed by (2) computing the center of the contour — also called the centroid of the region.

In order to accomplish these goals, we’ll need to perform a bit of image pre-processing, including:

  • Conversion to grayscale.
  • Blurring to reduce high frequency noise to make our contour detection process more accurate.
  • Binarization of the image. Typically edge detection and thresholding are used for this process. In this post, we’ll be applying thresholding.

Before we start coding, make sure you have the imutils Python package installed on your system:

From there, we can go ahead and get started.

Open up a new file, name it center_of_shape.py , and we’ll get coding:

We start off on Lines 2-4 by importing our necessary packages, followed by parsing our command line arguments. We only need a single switch here, --image , which is the path to where the image we want to process resides on disk.

We then take this image, load it from disk, and pre-process it by applying grayscale conversion, Gaussian smoothing using a 5 x 5 kernel, and finally thresholding (Lines 14-17).

The output of the thresholding operation can be seen below:

Figure 2: Thresholding our image returns a binary image, where the shapes appear as white on a black foreground.

Figure 2: Thresholding our image returns a binary image, where the shapes appear as white on a black foreground.

Notice how after applying thresholding the shapes are represented as a white foreground on a black background.

The next step is to find the location of these white regions using contour detection:

A call to cv2.findContours  on Lines 20 and 21 returns the set of outlines (i.e., contours) that correspond to each of the white blobs on the image. Line 22 then grabs the appropriate tuple value based on whether we are using OpenCV 2.4 or OpenCV 3. You can read more about how the return signature of cv2.findContours  changed between OpenCV versions in this post.

We are now ready to process each of the contours:

On Line 25 we start looping over each of the individual contours, followed by computing image moments for the contour region on Line 27.

In computer vision and image processing, image moments are often used to characterize the shape of an object in an image. These moments capture basic statistical properties of the shape, including the area of the object, the centroid (i.e., the center (x, y)-coordinates of the object), orientation, along with other desirable properties.

Here we are only interested in the center of the contour, which we compute on Lines 28 and 29.

From there, Lines 32-34 handle:

  • Drawing the outline of the contour surrounding the current shape by making a call to cv2.drawContours .
  • Placing a white circle at the center (cX, cY) -coordinates of the shape.
  • Writing the text center  near the white circle.

To execute our script, just open up a terminal and execute the following command:

Your results should look something like this:

Figure 3: Looping over each of the shapes individually and then computing the center (x, y)-coordinates for each shape.

Figure 3: Looping over each of the shapes individually and then computing the center (x, y)-coordinates for each shape.

Notice how each of the shapes are successfully detected, followed by the center of the contour being computed and drawn on the image.

Summary

In this lesson, we learned how to compute the center of a contour using OpenCV and Python.

This post is the first in a three part series on shape analysis.

In next week’s post, we’ll learn how to identify shapes in an image.

Then, two weeks from now, we’ll learn how to analyze the color of each shape and label the shape with a specific color (i.e., “red”, “green”, “blue”, etc.).

To be notified when these posts go live, be sure to enter your email address using the form below!

Downloads:

If you would like to download the code and images used in this post, please enter your email address in the form below. Not only will you get a .zip of the code, I’ll also send you a FREE 11-page Resource Guide on Computer Vision and Image Search Engines, including exclusive techniques that I don’t post on this blog! Sound good? If so, enter your email address and I’ll send you the code immediately!

, , , ,

41 Responses to OpenCV center of contour

  1. Luis Jose February 1, 2016 at 1:41 pm #

    Hi Adrian!

    Great job. I’m looking forward to completing this series of posts.

    Thanks for sharing your knowledge with the world!!!

    • Adrian Rosebrock February 2, 2016 at 10:32 am #

      Thanks Luis!

  2. Marco February 1, 2016 at 2:28 pm #

    Hi Adrian, another great Tutorial, but how to run the script into the Python Idle directly?

    • Adrian Rosebrock February 2, 2016 at 10:31 am #

      You would have to copy and paste each command into IDLE one-by-one. If you like using IDLE, you should also look into using IPython Notebooks as they are a bit more user friendly.

  3. Harley Mackenzie February 1, 2016 at 6:59 pm #

    There seems to be good support for opencv for shapes and finding centroids but are there equivalent routines for line detection. I have found this to be quite challenging especially discriminating between lots of small noise lines and what I think should be dominant significant lines.

    • Adrian Rosebrock February 2, 2016 at 10:29 am #

      Line detection is much, much more challenging for a variety of reasons. The “standard” method to perform line detection is to use the Hough Lines transform. But for noisy images, you’ll often get mixed results.

  4. Boško February 2, 2016 at 8:06 am #

    Hello Andrian,

    I got “ZeroDivisionError: float division by zero”, because all “m” values are 0. Why? Where I wrong? I trying solve it but do not have luck. I use Python 2.7 and openCV 3.1.

    Thanks,
    Best Regards,
    Boško

    • Adrian Rosebrock February 2, 2016 at 10:24 am #

      It seems like both you and Ruttunenn are getting the same error message. It seems like the segmentation may not be perfect and there is some noise left over in the thresholding. A simple check would be to use:

      Where you can set MIN_THRESH to be a suitable value to filter out small contour regions.

      • Boško February 2, 2016 at 3:17 pm #

        Thanks! It’s work now

        • Adrian Rosebrock February 4, 2016 at 9:23 am #

          Awesome, I’m happy it worked for you! 🙂

      • Amir April 19, 2017 at 9:50 am #

        Hi Adrian, where should i put this command? And what is the range of the MIN_THRESH?

        Thanks.

        • Adrian Rosebrock April 19, 2017 at 12:43 pm #

          You would typically define MIN_THRESH at the top of your file, but you can place it anywhere that you think is good from a code organization perspective. The actual range of MIN_THRESH will vary on your application and will have to be experimentally determined.

    • leena February 9, 2016 at 3:49 am #

      I also got the same and resolved with following:

      if (M[“m00”] == 0):
      M[“m00”]=1

      regards

      • ESPLondon February 26, 2016 at 9:33 am #

        Thanks leena, that worked

      • Ashay December 16, 2016 at 11:33 am #

        Can also resolve as following:

        if(M[“m00”]!=0):
        #find centroid
        else:
        cX,cY=0,0

  5. Ruttunenn February 2, 2016 at 10:21 am #

    Hi,

    Just run to a minor glitch in the example as I was getting zeros on the M = cv2.moments(c) on the first iteration, leading to float division by zero. A simple work around was to implement a check for 0.0 results.

    Cheers for awesome tutorials anyway.

  6. David February 3, 2016 at 10:20 pm #

    Hi, excellent post Adrian!!!

    Could you please explain a bit more why on the pre-processing stage you slightly blur the image???

    Thanks,
    David Darias

    • Adrian Rosebrock February 4, 2016 at 9:13 am #

      Blurring (also called “smoothing”) is used to smooth high frequency noise in the image. Simply put, this allows us to ignore the details in the image and focus on what matters — the shapes. So by blurring, we smooth over less interesting regions of the image, allowing the thresholding and contour extraction phase to be more accurate.

  7. Ken Doman February 9, 2016 at 6:42 pm #

    Great post, Adrian!

    It may be a little off topic, but I’m curious how the tool to find the center would fair against crescent-shaped features. Would the centroid be inside the shape, or in the middle possibly blank area?

    • Adrian Rosebrock February 10, 2016 at 4:41 pm #

      Great question. It would still be inside the shape, in the center, but towards the rim. An example can be found here. Keep in mind that only non-zero pixels are included in the calculation of the centroid.

  8. gary February 11, 2016 at 4:43 pm #

    Hi Adrian,
    I have a question about the value of cX and cY. As i want to know what is the pixel value at the point (cX, cY), i tried to print it by image[cX,cY]. However, I got error like:
    IndexError: index 1040 is out of bounds for axis 0 with size 1024
    which means that cX and cY is outside of range of the image size. Therefore, I want to ask how can i find out the pixel coordinate at point (cX, cY)?
    Thanks!

    • Adrian Rosebrock February 12, 2016 at 3:19 pm #

      When accessing pixel values in OpenCV + NumPy, you actually specify them in (y, x) order rather than (x, y) order. Thus, you need to use: image[cY, cX]

  9. thecanadiran October 12, 2016 at 4:15 pm #

    Thanks for a great tutorial Adrian.
    Could you please explain here or in another tutorial how to use image moments to characterize the other shape and statistical properties of an object?

  10. Rock January 30, 2017 at 1:52 pm #

    After running the code it gives me the following error: –

    usage: center_of_shape.py [-h] -i IMAGE
    center_of_shape.py: error: argument -i/–image is required

    What i simply did was that i just downloaded the code and ran it and the above error occured.

    Also could you suggest me a book which i can use to learn open CV with python from scratch. I know python but i don’t have any clue about open CV.

    • Adrian Rosebrock January 30, 2017 at 4:09 pm #

      Hey Rock — you error is coming from not supplying the image path via command line argument. You should be executing the command line this:

      $ python center_of_shape.py --image shapes_and_colors.png

      If you want to learn OpenCV + Python from scratch, I would highly recommend that you take a look at my book, Practical Python and OpenCV.

      • Rock January 31, 2017 at 12:29 pm #

        Thank you for your reply. i shall definitely refer that book.

        Could you please elaborate i still am not able to figure out what i have to do. Where do i have to make changes in the code.

        • Rock January 31, 2017 at 12:51 pm #

          Just wanted to add that i’m running the exact same code on IDLE and tis is what it shows as error. Do i have to run it on CMD as admin?

          • Rock January 31, 2017 at 1:02 pm #

            Thank you so much for your help. I finally figured it out! :p

  11. alcreek February 21, 2017 at 8:56 am #

    thanks Adrian! I do not use to leave comments. But you saved me! Pleasure to read you again!

    • Adrian Rosebrock February 22, 2017 at 1:38 pm #

      I’m happy to hear it! 🙂

  12. Jonathan April 1, 2017 at 10:38 am #

    If I am doing this in a Jupiter notebook, and what to display the results using matplotlib, how would I do so for the very last step as you do with:

    # show the image
    cv2.imshow(“Image”, image)
    cv2.waitKey(0)
    I’ve tried placing: plt.imshow(image) inside of the for loop as I thought this would work. It will run the cell with no error but not display any image.

    • Adrian Rosebrock April 3, 2017 at 2:09 pm #

      If you’re using Jupyter notebooks make sure you declare matplotlib to be inline:

      %matplotlib inline

      From there, follow this tutorial on displaying images with matplotlib.

  13. Amit April 13, 2017 at 7:43 am #

    Adrian will these help me in detecting shapes in real time images also ,or will it throw errors in them

    • Adrian Rosebrock April 16, 2017 at 9:04 am #

      You can use this code to detect shapes in real-time as well.

  14. Abhinav April 18, 2017 at 12:04 pm #

    Sir can you provide me with what changes to make in shape detector program so that i can take object from webcam feed and classify it ,it will be very helpful if you can provide with the code modification

    • Adrian Rosebrock April 19, 2017 at 12:49 pm #

      You should use this post as a starting point to access your webcam.

  15. MOHAMED AWNI HAMED April 21, 2017 at 7:49 am #

    I can’t understand why you made this line
    cnts = cnts[0] if imutils.is_cv2() else cnts[1]

    • Adrian Rosebrock April 21, 2017 at 10:41 am #

      The cv2.findContours function in OpenCV 2.4 returns a 2-tuple while in OpenCV 3 it returns a 3-tuple. You can read more about the change in this blog post.

Trackbacks/Pingbacks

  1. OpenCV shape detection - PyImageSearch - February 8, 2016

    […] Last week we learned how to compute the center of a contour using OpenCV. […]

  2. Determining object color with OpenCV - PyImageSearch - February 15, 2016

    […] Compute the center of a contour […]

Leave a Reply