How to Detect Shapes in Python using OpenCV?

By | September 25, 2021

Detecting shapes from an image is one of the coolest things you can perform with Python and OpenCV. There are many methods and techniques available in OpenCV to detect shapes in an image, and for most of them we first find out the edges in the image and then detect its shape.

We can either use the canny edge or contours methods to finds edges in an image then according to the edge detection we can name the shape. In this Python tutorial, I will walk you through the different Python scripts to detect shapes in an image.

In this tutorial, we will use the Python OpenCV library and use its contour edge detection to detect the shapes in the image.

So let’s start with installing the OpenCV library for our Python environment.

Install Python OpenCV

OpenCV is one of the most popular Python image processing libraries. We can easily install this library for our Python environment using the Python pip install terminal command.

Vamware
pip install opencv-python

For this tutorial, I will be using the following image and detect the shape from this image only.

shape.png

Vamware

Detect Circle in an image using Python OpenCV

import cv2 as cv
#import image
image = cv.imread("shape.png")

#convert image into greyscale mode
gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)

#find threshold of the image
_, thrash = cv.threshold(gray_image, 240, 255, cv.THRESH_BINARY)
contours, _ = cv.findContours(thrash, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

for contour in contours:
    shape = cv.approxPolyDP(contour, 0.01*cv.arcLength(contour, True), True)
    x_cor = shape.ravel()[0]
    y_cor = shape.ravel()[1]-15
    
    if len(shape) >12:
        cv.drawContours(image, [shape], 0, (0,0,255), 4)
        cv.putText(image, "Circle", (x_cor, y_cor), cv.FONT_HERSHEY_COMPLEX, 0.5, (0,0,255))
        
cv.imshow("Shape", image)
cv.waitKey(0)
cv.destroyAllWindows()

Output

Behind the code

  • On the first line, we imported the OpenCV cv2 module as cv.
  • The imread() image loads the "shape.png" image in our script.
  • cv.cvtColor(image, cv.COLOR_BGR2GRAY) statement converts the loaded BGR image into a grayscale image because we do not need color intensities to detect shapes.
  • The cv.threshold(gray_image, 240, 255, cv.THRESH_BINARY) function find out the threshold frequency of the grayscale image gray_image for further image processing. 240 is the threshold value, and 255 is the maximum threshold value.
  • cv.findContours(thrash, cv.RETR_TREE, cv.CHAIN_APPROX_NONE) the function will find out all the contours present in the grayscale image based on the thresholds. To know more about the OpenCV counter click here.
  • After finding all the contours, we loop over every contour and detect shape.
  • The cv.approxPolyDP() function returns all the polygons curve based on the contour with precision. Both the True parameters specify the close contour and curve.
  • The ravel()[0] and ravel()[1] function returns the x and y coordinates of the contour, and we will use these two coordinates to write the shape name.
  • TheapproxPolyDP() function returns the approximate curves, using the len() function we can find out the total number of curves present in that close loop. A circle can have an infinite number of curved edges as arcs, but for this tutorial, I have specified that if the shape has more than 12 curves or edges it should be treated as a circle.
  • The drawContours(image, [shape], 0, (0,0,255), 4) function will draw the contours over the original image (0,0,255) color code and 4 border thickness.
  • The putText() function will write the shape name over the detected shape.

Detect Square and Rectangle in an image using Python OpenCV

Similar to the above program we can find out the square and rectangle shape in an image. Both squares and rectangles have the same number of edges, but using the aspect ratio we can find out whether the shape is a square or rectangle.

import cv2 as cv
image = cv.imread("shape.png")
#convert image into greyscale mode
gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)

#find threshold of the image
_, thrash = cv.threshold(gray_image, 240, 255, cv.THRESH_BINARY)
contours, _ = cv.findContours(thrash, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

for contour in contours:
    shape = cv.approxPolyDP(contour, 0.01*cv.arcLength(contour, True), True)
    x_cor = shape.ravel()[0]
    y_cor = shape.ravel()[1]
    
    if len(shape) ==4:
        #shape cordinates
        x,y,w,h = cv.boundingRect(shape)

        #width:height
        aspectRatio = float(w)/h
        cv.drawContours(image, [shape], 0, (0,255,0), 4)
        if aspectRatio >= 0.9 and aspectRatio <=1.1:
            cv.putText(image, "Square", (x_cor, y_cor), cv.FONT_HERSHEY_COMPLEX, 0.5, (0,0,0))
        else:
            cv.putText(image, "Rectangle", (x_cor, y_cor), cv.FONT_HERSHEY_COMPLEX, 0.5, (255,0,0))
        
cv.imshow("Shape", image)
cv.waitKey(0)
cv.destroyAllWindows()

Output

Behind the code

The above square and rectangle shape detection code is similar to the Circle detection, the only difference is, here I have detected the shape if it has len(shape)==4. And to differentiate square from rectangle I had found out the width and height aspect ratio. Although for a perfect square the aspect ratio should be 1, here I have considered some noises and treated the shape as a square if its aspact ratio lay between 0.9 to 1.1.

Detect Triangle and Polygon in an image using Python OpenCV

Now let’s detect the polygon and triangle shapes in an Image. The code will remain the same we only need to specify a if...elif condition where if the shape length is 3 it would be a triangle and if the shape length is 5 it would be a polygon.

import cv2 as cv
image = cv.imread("shape.png")
#convert image into greyscale mode
gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)

#find threshold of the image
_, thrash = cv.threshold(gray_image, 240, 255, cv.THRESH_BINARY)
contours, _ = cv.findContours(thrash, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)

for contour in contours:
    shape = cv.approxPolyDP(contour, 0.01*cv.arcLength(contour, True), True)
    x_cor = shape.ravel()[0]
    y_cor = shape.ravel()[1]

    #For triangle
    if len(shape) ==3:
        cv.drawContours(image, [shape], 0, (0,255,0), 4)
        cv.putText(image, "Triangle", (x_cor, y_cor), cv.FONT_HERSHEY_COMPLEX, 0.5, (0,0,0))

    #for polygon
    if len(shape) ==5:
        cv.drawContours(image, [shape], 0, (0,255,0), 4)
        cv.putText(image, "Polygon", (x_cor, y_cor), cv.FONT_HERSHEY_COMPLEX, 0.5, (0,0,0))

        
cv.imshow("Shape", image)
cv.waitKey(0)
cv.destroyAllWindows()

Output

Conclusion

In this Python OpenCV tutorial, we used the approxPolyDP() function to find out the closed shape in an image. This method will work fine if the image, as well as all the shapes present in the image, are clear. There is another popular shape detection called the Hough Transform technique also present in OpenCV but it is limited to the circle. The approxPolyDP()technique is emerging as a better alternative when we want to detect different shapes in an image with ease.

People are also reading: 

Author: Vinay

I am a Full Stack Developer with a Bachelor's Degree in Computer Science, who also loves to write technical articles that can help fellow developers.

Leave a Reply

Your email address will not be published.