OpenCV shape detection

Click here to download the source code to this post.

shape_detection_results

This tutorial is the second post in our three part series on shape detection and analysis.

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

Today, we are going to leverage contour properties to actually label and identify shapes in an image, just like in the figure at the top of this post.

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

OpenCV shape detection

Before we get started with this tutorial, let’s quickly review our project structure:

As you can see, we have defined a pyimagesearch  module. Inside this module we have shapedetector.py  which will store our implementation of the ShapeDetector  class.

Finally, we have the detect_shapes.py  driver script that we’ll use to load an image from disk, analyze it for shapes, and then perform shape detection and identification via the ShapeDetector  class.

Before we get started, make sure you have the imutils package installed on your system, a series of OpenCV convenience functions that we’ll be using later in this tutorial:

Defining our shape detector

The first step in building our shape detector is to write some code to encapsulate the shape identification logic.

Let’s go ahead and define our ShapeDetector . Open up the shapedetector.py  file and insert the following code:

Line 4 starts the definition of our ShapeDetector  class. We’ll skip the __init__  constructor here since nothing needs to be initialized.

We then have our detect  method on Line 8 which requires only a single argument, c , the contour (i.e., outline) of the shape we are trying to identify.

In order to perform shape detection, we’ll be using contour approximation.

As the name suggests, contour approximation is an algorithm for reducing the number of points in a curve with a reduced set of points — thus the term approximation.

This algorithm is commonly known as the Ramer-Douglas-Peucker algorithm, or simply the split-and-merge algorithm.

Contour approximation is predicated on the assumption that a curve can be approximated by a series of short line segments. This leads to a resulting approximated curve that consists of a subset of points that were defined by the original cruve.

Contour approximation is actually already implemented in OpenCV via the cv2.approxPolyDP  method.

In order to perform contour approximation, we first compute the perimeter of the contour (Line 11), followed by constructing the actual contour approximation (Line 12).

Common values for the second parameter to cv2.approxPolyDP  are normally in the range of 1-5% of the original contour perimeter.

Note: Interested in a more in-depth look at contour approximation? Be sure to check out the PyImageSearch Gurus course where I discuss computer vision and image processing fundamentals such as contours and connected-component analysis in detail.

Given our approximated contour, we can move on to performing shape detection:

It’s important to understand that a contour consists of a list of verticesWe can check the number of entries in this list to determine the shape of an object.

For example, if the approximated contour has three vertices, then it must be a triangle (Lines 15 and 16).

If a contour has four vertices, then it must be either a square or a rectangle (Line 20). To determine which, we compute the aspect ratio of the shape, which is simply the width of the contour bounding box divided by the height (Lines 23 and 24). If the aspect ratio is ~1.0, then we are examining a square (since all sides have approximately equal length). Otherwise, the shape is a rectangle.

If a contour has five vertices, we can label it as a pentagon (Line 31 and 32).

Otherwise, by process of elimination (in context of this example, of course), we can make the assumption that the shape we are examining is a circle (Lines 35 and 36).

Finally, we return the identified shape to the calling method.

Shape detection with OpenCV

Now that our ShapeDetector  class has been defined, let’s create the detect_shapes.py  driver script:

We start off on Lines 2-5 by importing our required packages. Notice how we’re importing our implementation of the ShapeDetector  class from the shapedetector  sub-module of pyimagesearch .

Lines 8-11 handle 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.

Next up, let’s pre-process our image:

First, we load our image from disk on Line 15 and resize it on Line 16. We then keep track of the ratio  of the old height to the new resized height on Line 17 — we’ll find out exactly why we do this later in the tutorial.

From there, Lines 21-23 handle converting the resized image to grayscale, smoothing it to reduce high frequency noise, and finally thresholding it to reveal the shapes in the image.

After thresholding, our image should look like this:

Figure 1: Thresholding reveals the shapes in our image.

Figure 1: Thresholding reveals the shapes in our image.

Notice how our image has been binarized — the shapes appear as a white foreground against a black background.

Lastly, we find contours in our binary image, handle grabbing the correct tuple value from cv2.findContours  based on our OpenCV version, and finally initialize our ShapeDetector  (Lines 27-30).

The last step is to identify each of the contours:

On Line 33 we start looping over each of the individual contours. For each of them, we compute the center of the contour, followed by performing shape detection and labeling.

Since we are processing the contours extracted from the resized image (rather than the original image), we need to multiply the contours and center (x, y)-coordinates by our resize ratio  (Lines 43-45). This will give us the correct (x, y)-coordinates for both the contours and centroid of the original image.

Lastly, we draw the contours and the labeled shape on our image (Lines 44-48), followed by displaying our results (Lines 51 and 52).

To see our shape detector in action, just execute the following command:

Figure 2: Performing shape detection with OpenCV.

Figure 2: Performing shape detection with OpenCV.

As you can see from the animation above, our script loops over each of the shapes individually, performs shape detection on each one, and then draws the name of the shape on the object.

Summary

In today’s post blog, we learned how to perform shape detection with OpenCV and Python.

To accomplish this, we leveraged contour approximation, the process of reducing the number of points on a curve to a more simple approximated version.

Then, based on this contour approximation, we examined the number of vertices each shape has. Given the vertex count, we were able to accurately label each of the shapes.

This lesson is part of a three part series on shape detection and analysis. Last week we covered how to compute the center of a contour. Today we covered shape detection with OpenCV. And next week we’ll discuss how to label the actual color of a shape using color channel statistics.

Be sure to enter your email address in the form below to be notified when the next post goes live — you won’t want to miss it!

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 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning. Inside you'll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL! Sound good? If so, enter your email address and I’ll send you the code immediately!

, , , ,

174 Responses to OpenCV shape detection

  1. leena February 9, 2016 at 5:59 am #

    Why it is scanning and labeling from bottom to top?

    How to to scan and label top to bottom?

    • Adrian Rosebrock February 9, 2016 at 3:54 pm #

      That is how the cv2.findContours method is implemented. If you would like to sort contours, see this post.

      • leena February 17, 2016 at 4:57 am #

        Thanks Adrian. It worked and I am able to scan from top to bottom.

        Please help me in identifying lines connected with two shapes like in a flowchart, 2 boxes are connected with line/arrow

        with regards.

  2. leena February 9, 2016 at 6:11 am #

    I have done the same with

    shape factor= area / (peri * peri)

    if shapefactor >= 0.06 and shapefactor = 0.0484 and shapefactor = 0.050 and shapefactor = 0.032 and shapefactor = 0.95 and ar <= 1.05 else "rectangle"

    • Adrian Rosebrock February 9, 2016 at 3:55 pm #

      Is there a particular reason you are taking the ratio of the area to the perimeter squared? It seems to make the rule more complicated.

      • leena February 10, 2016 at 11:11 pm #

        Actually I do not know the reason, just it got solved my problem, so I took it. You or somebody can help me understanding this and the better solution .
        Thanks

        • bitflip June 21, 2016 at 11:30 am #

          Given you have following triangle:

          The bounding rect of it would have aspect-ratio about 1:1.
          So, better take the area() of the contour and compare it to width*height of the bounding rect. when the error is too hight –> rectangle.

  3. Vincent February 18, 2016 at 3:41 pm #

    Hi Adrian,

    First and foremost, thank you for this excellent tutorial. Very useful and informative.

    I have used the logic here to detect red triangles in a webcam feed. I have also tweaked the shapedetector class to identify only triangles. I am able to successfully identify a red triangle.
    http://imgur.com/6Z9CnBA

    I’ve noticed that sometimes a very messy contour will be classified incorrectly as a triangle.
    http://imgur.com/4a06psM

    What would be a good way to tweak this?
    http://pastie.org/10727912
    http://pastie.org/10727915

    • Adrian Rosebrock February 18, 2016 at 4:05 pm #

      Keep in mind that the code is only as good as the images that you put into it. The code detailed in this post assumes simple shapes that can be recognized utilizing contour properties. For more advanced shapes, or shapes that have substantial variances in how they appear (such as noisy contours), you might need to train your own custom object detector.

      Anyway, the reason sometimes even messy contours get classified differently is due to the contour approximation. Play around with the percentage used in cv2.approxPolyDP and you’ll be able to see the differences.

  4. Peng March 4, 2016 at 7:49 pm #

    Hi Adrian,

    Thank you for making this. A little feedback on the image file.

    I notice that if using a .jpg file as the source, the moment(cnt) will not get a correct value.

    It report an error :

    cntX = int(M[“m10”] / M[“m00”])
    ZeroDivisionError: float division by zero

    Any ideas on this?

    Thanks

    • Adrian Rosebrock March 6, 2016 at 9:20 am #

      Version version of OpenCV and Python are you using?

      In either case, you can resolve the issue by doing:

      This if statement will take care of the divide by zero error.

      Alternatively, you can add a tiny value to the moment to ensure there is no divide by zero problem:

      cX = (M["c10"] / (M["m00"] + 1e-7))

      • saurabh November 18, 2017 at 4:27 am #

        can you please elaborate this .. i didn’t get it..

        ====================================
        cX = int((M[“m10”] / M[“m00”] + 1e-7) * ratio)
        ZeroDivisionError: float division by zero
        ====================================
        showing this error

        • Adrian Rosebrock November 18, 2017 at 8:07 am #

          My original comment is missing a parenthesis:

          M["c10"] / (M["m00"] + 1e-7)

          Notice how the addition is done before the divide.

  5. Euan March 10, 2016 at 10:00 pm #

    Hi Adrian,

    Firstly thanks for a great tutorial and site. I’m a mechanical engineer and noob to openCV, python and linux and have managed to get openCV 3.0 and python2.7 setup thanks to your tutorial here…. https://www.pyimagesearch.com/2015/06/22/install-opencv-3-0-and-python-2-7-on-ubuntu/

    In order to get this code running on my setup, I needed to modify “float” on line 17 to “int” as it was causing cast problems on line 43 “c *=ratio”. I believe this is probably due to an update of how python works from how it worked when you wrote this tutorial. Is this the case?

    • Adrian Rosebrock March 13, 2016 at 10:29 am #

      Interesting, Brandon mentioned this issue in a comment above. Which version of OpenCV and NumPy are you using?

  6. brandon March 11, 2016 at 4:20 pm #

    Adrian, great stuff. I’ve learned a lot from your blog, and plan on purchasing the book soon. I was working through this post for now, and I’m getting the following traceback error:

    • Adrian Rosebrock March 13, 2016 at 10:21 am #

      I personally haven’t seen this error message before. Can you let me know which OpenCV and NumPy versions you are using?

      • Brandon March 14, 2016 at 12:28 pm #

        numpy is 1.10.4, and it happens with both OpenCV 2.4.12 and 3.1.0 (in a virtualenv, thanks to another of your tutorials) under Python 2.7 on OS X 10.11.3

        The workaround was simply to adjust data types pre and post multiplication:

        c=c.astype(np.float_)
        c *= ratio
        c=c.astype(np.int32)

        It works now.

        • Adrian Rosebrock March 14, 2016 at 3:15 pm #

          Thanks for the tip Brandon! I’ll be sure to dive into this more. I’m using NumPy 1.9.3, so perhaps this is an issue with NumPy 1.10.X.

  7. Ahmed Abdeldaim March 24, 2016 at 10:30 am #

    Great work Mr. Adrian
    but is there a way to make the selection more softer, for example by reduce point size ??
    or this is the best result??

    • Adrian Rosebrock March 24, 2016 at 5:10 pm #

      Absolutely, you just need to apply contour approximation first. I detail contour approximation in a few blog posts, but I would start with this one.

      • Ahmed Abdeldaim March 26, 2016 at 4:05 pm #

        Thanks for your help.

  8. darshan March 26, 2016 at 2:09 am #

    how to install imutils module
    I used pip install imutils I’m getting error

    • Adrian Rosebrock March 27, 2016 at 9:14 am #

      Please see the “OpenCV shape detection” section of this blog post. You just need to use pip

      $ pip install imutils

  9. firoz khan April 23, 2016 at 10:42 am #

    hi adrian it is only detcting one pentagon and nothing else

    • Adrian Rosebrock April 25, 2016 at 2:09 pm #

      Make sure you click on the window and press any key on your keyboard — this will advance the script. Right now a keypress is required after each detection.

  10. Diego Fernando Barrios April 29, 2016 at 5:08 pm #

    Good afthernoon!

    Thanks very much for this tutotrial, you´re doing a great and util work.

    Friend, I have a problem with contour detection, when I change the image, the project don’t work (I’m using a black background, I take the image from USB camera). I not have problem with the image path.

    The python scripts should recognize (9 “nine” rectangles”) but just one is recognized

    Sorry for the writing, my english is not so good.

    I would like that you can help me. I’m working in my work grade.

    Thanks very much!

    • Adrian Rosebrock April 30, 2016 at 3:55 pm #

      Depending on your image, this could be an issue with segmentation and/or the contours. Make sure that after thresholding your 9 rectangles have been clearly segmented. From there, move on to the contour approximation and see how many points are returned by the approximation. You might need to tweak the cv2.approxPolyDP parameters.

  11. itai May 8, 2016 at 4:24 am #

    Hey Adrian,
    I was wondering why did you use the Ramer-Douglas-Peucker algorithm to reduce the set of points, instead of using the convex hull ?

    Thanks

    • Adrian Rosebrock May 8, 2016 at 8:12 am #

      The contour approximation algorithm and Convex Hull algorithm are used for two separate purposes. As the name implies, contour approximation is used to reduce the number of points along a contour by “simplifying” the contour based on a percentage of the perimeter. Your resulting contour approximation is this a simplification of the shape by utilizing points that are already part of the shape.

      The convex hull on the other hand is the smallest convex set that contains all points along the contour — it is, by definition, not a simplification of the contour shape and the resulting convex hull actually contains points that are not part of the original shape.

      In this case, I used contour approximation because I wanted to reduce the number of (x, y)-coordinates that comprise the contour, while ensuring that all points in the resulting approximation were also part of the original shape.

  12. Armin June 30, 2016 at 2:56 pm #

    Hello Adrian

    thanks for tutorial

    I want to show detected shapes in seperate windows( each shape on each window), what should I do?
    also tried cropping them (using ROI) but I didn’t able to work it out.

    tnx

    • Adrian Rosebrock July 1, 2016 at 2:59 pm #

      Hey Armin — you’re on the right track. You should be applying array slicing to extract the ROI, then using the cv2.imshow function on each ROI. An example of ROI slicing can be found in this blog post as well as Practical Python and OpenCV.

  13. Alex Hopper July 24, 2016 at 5:04 pm #

    Hello,
    I’m new openCV-Python user.. I have a question about it:
    I have a database containing pre-processed images by kinect, and I need use Deep Learning to analyse these images. Is there a best way to start?

    https://www.pyimagesearch.com/2016/02/08/opencv-shape-detection/

    Can I use it to start?

    Thanks.

    • Adrian Rosebrock July 27, 2016 at 2:36 pm #

      What type of images are you working with? You mentioned they were pre-processed by the Kinect. Are they depth images? RGB images?

  14. Neal July 27, 2016 at 8:58 am #

    hi

    I’m looking for advice in shape detection. I want to use a camera to detect different kinds of shapes on a microcontroller. What would be the best method to approach this?

    Your help will be much appreciated.
    Thanks

    • Adrian Rosebrock July 27, 2016 at 1:55 pm #

      Hey Neal — to start, you need to segment each of the components of the microcontroller. Do you have any example images that you’re working with?

      • Neal August 2, 2016 at 8:23 am #

        well i’m going to be using different shape cut outs of wooden blocks as my different objects.

        • Adrian Rosebrock August 2, 2016 at 2:57 pm #

          I would use a similar approach as detailed in this blog post. Cut out your wooden blocks and place the camera such that its “looking down” on the blocks. Apply thresholding or edge detection to find the blocks. And from there, you can use the contour approximation technique to label the shapes.

  15. Anupam September 4, 2016 at 3:40 am #

    Can you please help me out with detecting overlapping shapes ?

  16. Leena October 6, 2016 at 8:25 am #

    How can we use the shapedetector to classify polygon as rectangle/diamond(decision box)/ parallelogram….
    please help

  17. Poehe October 31, 2016 at 11:09 am #

    Hi Adrian, thank you so much for the tutorial, it’s a great starting point for me to dive into OpenCV.

    I noticed when processing images using your code that in pictures with a white background the engine also shows the contours/edges of the whole input picture as being a shape, while in images with a black background (as in your example) the engine ignores the outside contours of the whole input pic and only shows the contours of the objects within the input pic itself (which is the way it should work, I suppose).

    Could you think of a solution that makes the engine not classify the picture edges as contours?

    Your help will be much appreciated!

    Cheers

    • Adrian Rosebrock November 1, 2016 at 8:59 am #

      So if I understand your question correctly, you are using a background that is lighter than the shapes themselves? And after thresholding your shapes appear as “black” on a “white” background? Am I understanding that correctly? If so, simply invert the threshold step to make the shapes “white” on a “black” background.

  18. Megha Maheshwari November 21, 2016 at 12:42 am #

    Hi Adrian

    How can we differentiate between rectangle and trapezoid. I have detected that the polygon contains 4 corners and hence is either a square, rectangle or trapezoid as per my image. However, for square can easily check the width and height, but how do i differentiate between rectangle and trapezoid.

    • Adrian Rosebrock November 21, 2016 at 12:27 pm #

      There are many ways to do this. I would consider computing the extent (contour area / bounding box area). A perfect rectangle will have an extent of near 1.0 while trapezoid will have an extent much less than 1.0. You can also compute the angle between each corner of the shape. A rectangle would have near perfect 90 degree angles. Either one will work.

  19. Mohamad November 23, 2016 at 8:12 am #

    Hi Great Man. Mr. Adrian
    I guess your offer in this tutorial use the webcam for raspberry pi. its Ok? other question is that for detect other shapes such as (H) similar or (L) similar or … How doing it? Is this method work with edge or point detection?

    • Adrian Rosebrock November 23, 2016 at 8:30 am #

      You can certainly use a webcam or Raspberry Pi camera module to perform shape detection. You would just need to read the frame from the camera and process it. I provide tutorials on how to access webcams here.

      As for detecting an “H” or “L” you can do that using contour properties (extent, solidity, etc.), template matching, or image descriptors such as Histogram of Oriented Gradients. I would suggest taking a look at Practical Python and OpenCV along with the PyImageSearch Gurus for more advanced demonstrates of recognizing objects in images.

  20. Luís Serrador November 25, 2016 at 5:48 am #

    Hi Adrian,

    I tried two of your tutorials (this one and ‘OpenCV center of contour’) and when I execute the command to run the .py file my result is not the same as you show at the end of the tutorials. My final image only recognize the first shape/center, and doesn’t recognize more shapes. Is there anything that could be wrong?

    • Adrian Rosebrock November 28, 2016 at 10:41 am #

      Hi Luís — what versions of OpenCV and Python are you using?

      • Luís Serrador November 28, 2016 at 11:34 am #

        Hi Adrian! I’m using OpenCV 3.0 and Python 2.7. Any mistake of mine?

        • Adrian Rosebrock November 28, 2016 at 2:42 pm #

          Nope, the Python and OpenCV version shouldn’t be an issue. I was just curious about the setup to confirm it wasn’t an outlier situation. I personally haven’t ran into this problem when executing the code. I assume you downloaded the code using the “Downloads” section of this post rather than copying and pasting the code? Sometimes that causes problems with reader’s code as well.

          • Thomas November 30, 2017 at 7:46 am #

            Hi Luis,

            you should make an empty file of __init__.py , to solve you issue for not detect all the shape from the *.png file.

  21. Jan December 4, 2016 at 1:08 am #

    Hi Adrian,
    Thanks for the tutorial,
    I did the same, but for certain cicrles the vertices were shown as 4 and hence were displayed as squares , can you suggest a way to increase the number of detected vertices in the picture.
    Thanks

    • Adrian Rosebrock December 5, 2016 at 1:31 pm #

      You’ll want to play with the following line:

      approx = cv2.approxPolyDP(c, 0.04 * peri, True)

      The smaller the value passed in for peri, the more vertices you’ll obtain.

      • Preethi December 23, 2016 at 6:16 am #

        Hi Adrian,
        From Shape detection i should detect circle alone. even though rectangle,square and etc present. In your Same example i need this modification

        • Adrian Rosebrock December 23, 2016 at 10:50 am #

          For circle detection, take a look at this blog post. Otherwise, you can use this code to determine a circle as well. A circle will have many more approximated contour vertices than all other shapes. You basically need to create an if statement for this.

  22. Niel January 5, 2017 at 12:52 am #

    Hi, Adrian i’m mechatronic students and now i build an obstacle avoidance robot i like to ask how can i create a detection square box in OpenCV for detect colour so it will give feedback to th robot to move or to stop

    • Adrian Rosebrock January 7, 2017 at 9:41 am #

      Hey Niel — can you elaborate on what you mean by a “detection square box”? I’m not sure what you mean.

  23. Chandu January 18, 2017 at 1:23 am #

    Hi Adrian

    i’m getting an error

    Traceback (most recent call last):
    File “”, line 2, in
    from pyimagesearch.shapedetector import ShapeDetector
    ImportError: No module named pyimagesearch.shapedetector

    • Adrian Rosebrock January 18, 2017 at 7:08 am #

      Hey Chandu — make sure you download the source code to this blog post using the “Downloads” section. It’s likely that your project directory structure does not match mine (perhaps missing a __init__.py file. Please download my code and compare it to yours.

  24. Arturo January 19, 2017 at 9:48 pm #

    Hi, i´m having a problem with the code, Sorry for my ignorance but where do I have to put the path of the image?
    I can´t understand the lines 9-12 where you say that we have to place the path, the (– image) part

  25. tal January 25, 2017 at 12:14 pm #

    Hi, i´m having a problem with the code, i have insert the path into the
    ap.add_argument(“-i”, “–image”, required=True, help=”rec.jpg”)
    and i’m getting this error.

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

    • Adrian Rosebrock January 26, 2017 at 8:21 am #

      You do not have to modify the code at all. You just need to supply the --image switch to the Python script via command line argument:

      $ python detect_shapes.py --image shapes_and_colors.png

      Please read up on command line arguments before continuing.

  26. BKumar March 2, 2017 at 2:07 am #

    Hey Adrian,

    I was wondering about how to find out the number of different shapes in such an example. Like if there are 4 squares, 2 rectangles etc., how can you label them in the image as Square #1, Square #2 or something. Or display the number of instances of each shape in the image.

    Thanks in advance.

    • Adrian Rosebrock March 2, 2017 at 6:41 am #

      I would use a Python built-in dictionary type and simply count the number of shapes as you loop over them. Your pseudocode might look something like:

      This would give you a dictionary of shape counts.

  27. Milán Vincze March 5, 2017 at 1:42 pm #

    Hello!

    A get an error when I run the program:
    ImportError: No module named ‘imutils’

    I installed imutils before. Could you please help?

    • Adrian Rosebrock March 6, 2017 at 3:42 pm #

      The first part of this blog post discusses exactly how to solve this error message:

      $ pip install imutils

      • Milán Vincze March 6, 2017 at 4:42 pm #

        Yes I know I ran that before I start the program. Maybe the problam is that the raspberry want to run it in python3. How can I run in python2 if I installed python2 and python 3 too?
        I thought that is the problem because the installation put the imutils in python2.7 library.

        • Milán Vincze March 6, 2017 at 4:46 pm #

          I also copied the imutils folder to python3.1 folder

          • Milán Vincze March 6, 2017 at 4:49 pm #

            Sorry, I copied the imutils to the shape detector folder and it worked, sorry to bother you.

        • Adrian Rosebrock March 8, 2017 at 1:15 pm #

          For what it’s worth, you could also just install imutils for your Python 3 as well:

          $ pip3 install imutils

  28. Milán Vincze March 12, 2017 at 6:46 am #

    Hello! Adrian

    First thank you for the exellent tutorial! I am interesting in a real-time shape detector with the picamera. Maybe there is a tutorial for it that you made?

    • Adrian Rosebrock March 13, 2017 at 12:15 pm #

      I would suggest using the cv2.VideoCapture class or the VideoStream class covered in this blog post. My book, Practical Python and OpenCV also covers the basics of working with video streams and video files — this would help you port the code over to a video stream rather than a single image.

      • Tyler April 16, 2017 at 1:13 pm #

        Adrian can u suggest what changes to make in this so that we can detect shapes in real time using laptop camera feed ,I would be very greatful

        • Adrian Rosebrock April 19, 2017 at 1:06 pm #

          The comment you replied to has a link to a blog post + book that I recommend that you read so you can access your laptop webcam. Take the time to study the basics of OpenCV first, then it will be easy to implement this method for real-time applications.

  29. PANJI March 14, 2017 at 3:48 pm #

    Sir i have problem error

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

    anyone help me?

    • Adrian Rosebrock March 15, 2017 at 8:50 am #

      Please read the comments before you submit your own. I answered your question in reply to “Arturo” above.

  30. Open The CV March 19, 2017 at 11:38 pm #

    Hi, how can I find the angle between a triangle and the horizontal axe ?

    • Adrian Rosebrock March 21, 2017 at 7:24 am #

      I’m not sure what you mean. Do you have an example image of what you’re working with?

      • Open The CV March 21, 2017 at 7:36 am #

        Problem solved with some trigonometry formulas. Thanks

  31. sandra May 8, 2017 at 12:44 pm #

    Hello, how can i detect certain rectangle which is the border of some text area in an image ?

    • Adrian Rosebrock May 8, 2017 at 12:46 pm #

      Hi Sandra — I’m not sure I understand your question. It would be helpful if you had an example image of what you’re working with.

  32. Abu May 11, 2017 at 6:51 pm #

    Hello Adrain,

    Awesome tutorial, thank you.

    I was wondering if there is a way to detect rectangles or squares to exact approximately. Basically, I have an image, and it has shapes and text in it. I only want to detect the shapes and ignore all the text in the image. This tutorial really helped me. But, I am still detecting squares and rectangles in the text of the image. is there a way, I can completely ignore that?.

    Thanks.

    • Adrian Rosebrock May 15, 2017 at 9:02 am #

      I would compute the solidity of the shape which is the area of the contour divided by the convex hull area. Text will have a lower solidity than a rectangle which should be equal to one.

  33. Laura May 30, 2017 at 2:46 pm #

    Hello Adrian,

    Thanks a lot for the awesome tutorial.

    I am using the shape detection to get the coordinates from where rectangular elements are located in an image that I am getting from my phone camera. For some specific layouts the code works perfectly, but when I start to place elements in the same row, the coordinates of an specific element are organised in a different order.

    However, when I try the same layout from an image I am building in photoshop (not a picture) it works correctly. When I compared both shape detection processes, I realised that the order in which the shapes are sorted is also changing. I tried altering the lightning and contrast of the pictures, but somehow there is something that is altering the order of how the elements are detected and the order of how the coordinates are organised.

    Image from camera: http://imgur.com/a/xHaGn

    Digital image: http://imgur.com/a/NNsv5

    Do you have any idea of what could be the problem?

    • Adrian Rosebrock May 31, 2017 at 1:09 pm #

      The cv2.findContours function will not return contours in a specific order. You should sort your contours if you expect them to be in a given order.

    • Martin June 14, 2017 at 10:21 am #

      Hi Laura,
      I am working on the same topic –> finding squares on picture taken by my phone camera.
      Could you share your project with us?
      Thanks

  34. jandi May 30, 2017 at 7:43 pm #

    Thanks for this tutorial …
    I have a question. In my image, I have a square and lozenge shape and I want to distinguish between them, how can I do that?

    • Adrian Rosebrock May 31, 2017 at 1:06 pm #

      I would suggest using either:

      1. Contour properties, such as extent, solidity, and aspect ratio.
      2. Features, such Hu Moments or Zernike Moments.

  35. Thailynn June 19, 2017 at 12:35 pm #

    Hi Adrian,
    Is there a way to save the results of the image classification into a new image? For example, identifying all the squares and saving a new image with just the squares. I am trying to do some feature extraction of satellite imagery.

    Thank you very much for any insight you can offer!

    • Adrian Rosebrock June 20, 2017 at 10:55 am #

      Hi Thailynn — if you are trying to extract each individual square, I would compute the bounding box and extract it using array slicing. You can then write the region to disk using the cv2.imwrite function. I cover this in more detail inside Practical Python and OpenCV.

  36. Cuningan July 23, 2017 at 12:55 pm #

    Hello Adrian, i ahve bought you curse and now i am seeking this tutorial.
    I am using RPI with with OpenCV3.2 compiled like you described on you post.

    My problem is that also, python2 and 3 can not find imutils, i have tryed to install it with pip but failed with a large crash…

    Any idea about??

    • Adrian Rosebrock July 24, 2017 at 3:33 pm #

      Which tutorial did you use to install OpenCV? Also, what is the error you are getting when trying to install imutils? Without knowing the error, I cannot provide any suggestions.

  37. disheet August 8, 2017 at 3:46 am #

    Hello Sir,

    I am getting below error.Please give me the solution

    File “/usr/local/lib/python2.7/dist-packages/imutils/convenience.py”, line 69, in resize
    (h, w) = image.shape[:2]
    AttributeError: ‘NoneType’ object has no attribute ‘shape’

    • disheet August 8, 2017 at 3:48 am #

      And i am using logitech c170 webcamera.

    • Adrian Rosebrock August 10, 2017 at 8:55 am #

      It sounds like OpenCV cannot access your webcam. I discuss NoneType errors (and how to resolve them) in detail inside this post.

  38. disheet August 11, 2017 at 1:57 am #

    Hello Sir,

    How can i do shape detection using webcam?

    • Adrian Rosebrock August 14, 2017 at 1:22 pm #

      You apply the exact same algorithm detailed in this post, only to every frame of a video. You can access the frames of a video using this post or reading through Practical Python and OpenCV.

  39. morejump September 10, 2017 at 12:49 am #

    Hi Adrian,
    the rectange is shape which has 4 vertices, and also has three of them is 90 degree. cause your algorithm dose not work every time

  40. Doson September 26, 2017 at 4:29 am #

    I don’t think it is strict to recognize the sharp just by the number of the ‘approx’.For example, a rhomboid have for ‘approx’ but it is neither a square or rectangle.I suggest that more judgement such as checking the degree in each ‘approx’ .If more than three degree was close to 90,maybe 91 or 89,we can believe it is a square or rectangle.

  41. David October 11, 2017 at 5:08 am #

    Just wanted to say that these tutorials have been amazing! Thanks for publishing these

    • Adrian Rosebrock October 13, 2017 at 8:57 am #

      Thanks David, I really appreciate that 🙂

  42. KansaiRobot October 23, 2017 at 5:08 am #

    Very nice Blog, I have just discovered it. I am going to be browsing through it tomorrow but let me ask you a question. In this post you covered shape detection when the objects are quite separated from each other- therefore you can find the true center.

    How about detecting shapes when one shape is touching another or even slightly overlapping it?

  43. Enko October 27, 2017 at 5:56 am #

    Hi Adrian,
    Thank’s for your excellent tutorial, I want to detect circle accurately. I have tried to use HoughCircles in circle detection part:
    else:
    # shape = “circle”
    img = np.zeros(image.shape, image.dtype)
    cv2.drawContours(img, [c], -1, (255, 255, 255), -1)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1.2, 100)
    if circles is not None:
    shape = “circle”
    but the “circles” is always None. Can you help me?

  44. KansaiRobot October 29, 2017 at 8:15 pm #

    Part 1) I would like to ask if watershed can be effectively used for adjacent objects that are not circles. In particular tablet-like. (I tried the method and it does not work)

    • Adrian Rosebrock October 31, 2017 at 7:55 am #

      In general, yes, the watershed algorithm can be used for these types of objects provided you can obtain a reasonable segmentation.

      • KansaiRobot October 31, 2017 at 8:31 pm #

        Yes, segmentation is not a problem. I used a discriminant analysis method for segmentation. I can send you the original (non segmented) images or the binarized ones (just purely black and white)

  45. KansaiRobot October 29, 2017 at 8:16 pm #

    An example of my problems can be found here
    https://stackoverflow.com/questions/46107628/problems-with-segmenting-pills

    I would like to hear your advice on how to tackle this problem. Thanks in advance

    • Adrian Rosebrock October 31, 2017 at 7:56 am #

      Do you have the original example image(s)? I would consider writing a blog post on this topic.

      • KansaiRobot October 31, 2017 at 8:28 pm #

        Thanks. I have some sample images obtained with backward ilumination. (Therefore basically dark shapes on white background). I can send some to you for the post you are considering.

        • Adrian Rosebrock November 2, 2017 at 2:36 pm #

          Sure, that would be cool to take a look at. Send me a note here or if you already know my email address you can send the images and reference this blog post.

  46. KansaiRobot October 29, 2017 at 8:17 pm #

    Part2) You seem to be using scikit watershed not opencv. Any reason for that?

    • Adrian Rosebrock October 31, 2017 at 7:55 am #

      At the time the watershed blog post was published the watershed + connected-component analysis functions in OpenCV were not as easy to use in OpenCV versus scikit-image. That has changed now and I would recommend using whichever one you are more comfortable with.

  47. Kanwal November 21, 2017 at 9:14 am #

    Hello Sir,

    Can you help how to solve the problem of occlusion.

  48. Sameer November 26, 2017 at 5:14 am #

    Hi Adrian,

    When I run the program, only single object is being contoured. What is wrong?

    • Adrian Rosebrock November 27, 2017 at 1:09 pm #

      Make sure you click the active window and press any key on your keyboard. This will advance the detection.

  49. Yadnyesh December 1, 2017 at 10:32 am #

    Hello Adrian,
    I have slight problem
    The code works fine with images which have a black background it detects all the shapes
    the problem comes when an image has a white background.
    It recognizes the frame of image as a rectangle and none of the shapes within it.
    Can you help me with this?
    Thank you in advance

    • Yadnyesh December 1, 2017 at 11:31 am #

      Sorry for this I read your reply for a comment
      So all I have to do this
      replace
      thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY)[1]
      with
      thresh = cv2.threshold(blurred, 255, 60, cv2.THRESH_BINARY)[1]

  50. Priyam Vora December 20, 2017 at 2:30 am #

    Hi Ardrian,
    How can we detect a cricket bat in a image? Please provide me a detailed idea.

    Thank You

  51. Caner Yagci December 21, 2017 at 11:12 am #

    Hi Adrian, What should I do if I want to detect only the circles ?

    Thank you

  52. james December 23, 2017 at 12:50 pm #

    hello can you help me to detect an custom object like “H” key word real time ???? how we can do that with countours or somthing else

    • Adrian Rosebrock December 26, 2017 at 4:31 pm #

      Hey James — do you have any example images of what you’re working with?

  53. Emre uysal December 24, 2017 at 9:12 am #

    hi adrian
    how can i detect resistor shape or undefined shape?
    thanks

    • Adrian Rosebrock December 26, 2017 at 4:30 pm #

      I’m not sure what you mean by “undefined shape”, but I would suggest training a custom object detector. The Histogram of Oriented Gradients method would be a great starting point.

  54. Oshada January 8, 2018 at 7:33 am #

    How to apply this technique for unfilled shapes?

    • Adrian Rosebrock January 8, 2018 at 2:36 pm #

      By “unfilled” do you mean just the outline of a shape?

      • Oshada January 9, 2018 at 10:31 am #

        Yeah. It works now. But, the problem is I have to change the threshold when a different image is used. I want to convert a hand-drawn flow chart into a stored procedure. How do I solve this issue?

        • Adrian Rosebrock January 10, 2018 at 12:56 pm #

          Take a look at “adaptive thresholding”. Adaptive thresholding works by thresholding on local areas. It might work well for you in this situation. Also take a look at Otsu’s thresholding. I cover both inside Practical Python and OpenCV.

  55. Mustafa Ismail January 9, 2018 at 4:10 pm #

    Hello, thank you for the awesome tutorial man!
    I’m tryna detect a shape like this https://drive.google.com/open?id=11D4SiYQ84TYDA0aZxaEDm5MDlY6lWWaW
    and as you can see the shape on the right is perfectly detected by following your tutorial and tuning some parameters. However i’m having a problem on detecting half colored shapes like the shape on the left, as you can see the thresholding technique classifies the blue part as black.
    So any ideas on how to solve this? I need to detect the whole object as a triangle

    • Adrian Rosebrock January 10, 2018 at 12:50 pm #

      You will need to play with the thresholding parameters (or try adaptive thresholding) or try Canny edge detection instead. Secondly, make sure there is sufficient contrast between the foreground (the shape you want to detect) and the background. If there is not enough contrast, thresholding and edge detection methods will not work.

  56. Oshada February 1, 2018 at 2:25 am #

    Hi Adrian, I tried to identify shapes in hand-drawn flowcharts like this.

    https://ibb.co/d10sEG

    The case is sometimes because these are hand-drawn diagrams, there can be unclosed shapes(specially in corners) which mess up the detection. So I used closing operation and it resolved the problem. But, when gap between shapes and arrows and text is less, everything tends to linked and it create messed up contours. Is there are way to get rid of this problem without putting condition like users have to put at least this much of gap between items. Also, I can’t get rid of the closing operation.

    • Adrian Rosebrock February 3, 2018 at 11:11 am #

      There are a few ways to approach this problem, but have you considered training an object detector to recognize each of these shapes? HOG + Linear SVM would be a good starting point. This would alleviate the new to rely on image processing operations and make your algorithm more extendible.

      • Oshada February 3, 2018 at 8:19 pm #

        The problem is my supervisor has highly recommended me not to use any ML or DL approaches first. That is why I have been trying to use contour analysis, mathematical relationships among different shapes and etc. I have done detection parts and everything putting that restriction I mentioned. And I want to get rid of it. Can you give me some tips to do that without going for ML.

        • Adrian Rosebrock February 6, 2018 at 10:31 am #

          You’ll need to continue to fiddle with the parameters to your morphological operations if you want to continue using non-ML solutions. That’s perfectly fine from a education standpoint but keep in mind that your method won’t be as robust.

  57. Juhi March 5, 2018 at 12:35 pm #

    ModuleNotFoundError: No module named ‘pyimagesearch’

    Please help.

    • Adrian Rosebrock March 7, 2018 at 9:21 am #

      You need to use the “Downloads” section of the blog post to download the code. Unzip the .zip archive. Change directory to where the code is stored. Then execute it via the command line.

  58. Darshan kumar March 17, 2018 at 3:35 am #

    Hi Adrian! I am looking for a code which has the same properties for a real time video and also to find the co ordinates on a plane. And may be midpoints as well.

  59. Andrew R March 18, 2018 at 9:58 am #

    Hi Adrian

    Thanks for posting this – and your other examples – really clear and super helpful.

    • Adrian Rosebrock March 19, 2018 at 5:12 pm #

      Thanks Andrew, I’m glad you enjoyed the post! 🙂

  60. Tom S March 22, 2018 at 10:18 pm #

    Hi Adrian,

    Thanks for this and all the other awesome posts!

    I’m really new to OpenCV and have been trying recently to implement it into my capstone project for undergrad. I’m using a number of your tutorials for help in this. The problem I’m running into with this one is identifying shapes that are really close together. I need to get the shapes of each individual Lego in an image so I can translate it to code using a syntax my team created. A sample of the code can be seen here:

    https://imgur.com/a/OjwE5

    So far it’s only detecting the entire block structure like so:

    https://imgur.com/a/8cvRP

    Any help you could offer in finding shapes that are closer together would be greatly appreciated. Thank you in advance!

    Best,
    Tom

    • Adrian Rosebrock March 27, 2018 at 6:42 am #

      Keep in mind that you’ll only be able to find individual contours for each lego piece if you can cleanly segment each piece via thresholding, edge detection, etc. In this case you’re detecting all of the legos at once. It’s impossible for the cv2.findContours function to know that you’re trying to extract each individual lego. Instead, I would recommend you use color thresholding methods to help you segment each set of legos.

      • Tom S March 27, 2018 at 2:14 pm #

        Ignore the previous comment, I did end up trying the color detection first and then using shape detection. It worked like a charm, thanks!

        • Adrian Rosebrock March 30, 2018 at 7:30 am #

          Awesome! Congrats, Tom 🙂

  61. Tom S March 27, 2018 at 2:12 pm #

    I decided to detect the shapes after each color had been detected and that worked well!

  62. Kay March 28, 2018 at 3:03 am #

    Hi Adrian,

    I have two rectangular to be detected but it only detect one and from the previous reply of yours, I need to pressed any key on the keyboard but it closed the program and does not advance the detection. How to make them show all the detect shape in one go?

    • Adrian Rosebrock March 30, 2018 at 7:25 am #

      It sounds like the second rectangle is:

      1. Not being detected by the threshold process
      2. If it is being detected, the contour approximation is not returning 4 coordinates

      You will need to debug both of these. I would suggest starting with the “thresh” map (you can debug it using cv2.imshow).

      Additionally, if you are new to working with OpenCV and Python I would recommend reading through Practical Python and OpenCV to help you get up to speed. I hope that helps point you in the right direction!

  63. kasun April 2, 2018 at 8:59 pm #

    i used this approch to identify the er digram shpes.up to now i identify the few shapes but i need to identify the double rectangle (Week entity)double elipse(multi value attribute) dotted elipse(derived attribute )can you dive mee tip for identiy those shapes

  64. kasun April 2, 2018 at 9:11 pm #

    those er digrams are hand drawn ones so up to now i didn’t use any ml or dl methos.but tell me can do this easyly using ml or dl .plese tell me easyest way to so this?

    • Adrian Rosebrock April 4, 2018 at 12:19 pm #

      Do you have any example images of the hand drawn diagrams you are working with?

  65. kasun April 5, 2018 at 9:01 pm #

    ya i have .this is link for that
    https://drive.google.com/open?id=1JEJ_RySZ04FYIVYl3Zf1jjryAR5OmYPU
    only threre you can see double rectangle and double diamond also have double elipse and doted elipse too.

    • Adrian Rosebrock April 6, 2018 at 8:47 am #

      There are a few things you can do for this. You either extract the contour hierarchy for the double rectangle/double diamond or you can extract the outer shape and then do another series of contour detections to detect any inner shapes.

  66. Manuel April 12, 2018 at 10:59 pm #

    works great ! but with 200×150 resolution not working. I have camera 1 meter above the floor. What can i do ? Thank you !

    • Adrian Rosebrock April 13, 2018 at 6:39 am #

      Can you increase the resolution of your input images?

  67. Mitch April 24, 2018 at 4:18 pm #

    Thanks for the great tutorials. Can you explain why you initially resize the image (“load the image and resize it to a smaller factor so that the shapes can be approximated better”) and its implication about improved performance? Thank you.

    • Adrian Rosebrock April 25, 2018 at 5:31 am #

      While high resolution images are appealing to the human eye, they can be a problem to computer vision algorithms. The larger an image is, the more data there is to process, and therefore the algorithm will run slower. Secondly, reducing an image size reduces the amount of detail in an image, which can be seen as a “noise reduction”, allowing us to focus on the objects/components in an image that actually matter.

      I hope that helps and if you’re interested in learning more about the fundamentals of image processing and computer vision be sure to refer to Practical Python and OpenCV. I’m confident the book will help you get up to speed quickly.

  68. ali May 14, 2018 at 5:31 am #

    Hi Adrian
    i have a image with green bounding box, i want to get coordinates of bounding box. please guide me.

    • Adrian Rosebrock May 14, 2018 at 11:47 am #

      I’m a little confused here. Typically we create computer vision algorithms to detect the (x, y)-coordinates of an object in an image and then draw a bounding box surrounding it. It sounds like you have the opposite workflow — you have a green bounding box already in an image and you want to detect the location of it?

      • ali May 15, 2018 at 12:20 am #

        Yes, precisely, I want to get the position of the bounding box, and then I draw it in another image in the same place.

        • Adrian Rosebrock May 17, 2018 at 7:12 am #

          Got it, I understand now. There are two options here.

          1. Apply color thresholding to find green regions. Then process the contours to filter out only the box itself.
          2. Train an object detector to find only squares.

          The second option would be more accurate but would require significantly more work.

  69. Yadira May 22, 2018 at 2:10 pm #

    Hi Adrian,

    When I try to run the code I keep getting “the following arguments are required: -i/–image”
    it may have the simplest solution in the world, but I’m not sure what to change

  70. Dan June 2, 2018 at 2:39 pm #

    Dear Adrian,

    Awesome work! It is quite useful.
    I have a question regarding the edge effect.

    The contour approximation goes wrong when I tested on some shapes with jagged edges.
    Does this mean that the method works perfectly, assuming the edge is smooth?

    I wonder whether you have published a paper about this?
    I’m not a computer vision newbie and many thanks in advance for your explanation.

    cheers,

    • Dan June 2, 2018 at 2:40 pm #

      I mean I am a newbie in computer vision ….. ahh

      • Adrian Rosebrock June 5, 2018 at 8:04 am #

        Yes, this method does assume that you can cleanly segment the foreground from the background. If you cannot obtain a clean segmentation than this method will likely fail.

        Additionally, if you are new to OpenCV/computer vision I would definitely recommend working through my book, Practical Python and OpenCV. I wrote this book for readers new to computer vision as you are. Give it a try, I’m confident that once you go through it you’ll be able to more easily understand the concepts discussed on this blog.

  71. Dan June 2, 2018 at 3:27 pm #

    Hi Adrian,

    Can this method detect other shapes such as an octagon, hexagon, etc?

    Many thanks!

    • Adrian Rosebrock June 5, 2018 at 8:02 am #

      Yes, you would just need to check the number of vertices in the approximated contour (as we have done in this example).

  72. Geetanjali Sahoo June 13, 2018 at 2:23 am #

    hi Adrian,

    how to know the connector between two shape ? like an arrow between rectangle and square?

    • Adrian Rosebrock June 13, 2018 at 5:25 am #

      There are a few ways to do this, some easier than others. For an arrow you could use the contour approximation technique discussed here or you could compute the solidity and extent of the contour and use them to help determine if a particular shape is an arrow.

  73. Henrique July 21, 2018 at 10:46 pm #

    Hi Adrian !
    I’m working on computer vision applied to drones, and I need to calculate the area of a shape on the ground, seen from a camera on the copter. I’ve seen this tutorial and “Measuring size of objects in an image” also. Is there any implementation in which both could return the area of the shape ? Thank you, appreciate your work a lot !

    • Adrian Rosebrock July 25, 2018 at 8:27 am #

      Hey Henrique — your project is a very specific and you’ll need more advanced techniques here. I would suggest first calibrating the camera via an intrinsic/extrinsic calibration computation. From there you’ll be able to compute a more accurate measurement of the area.

Leave a Reply