In the previous blog, we learned how to find and draw contours using OpenCV. In this blog, we will discuss how to detect simple geometric shapes by approximating the contours. So, let’s first discuss what is meant by contour approximation.
This means approximating a contour shape to another shape with less number of vertices so that the distance between both the shapes is less or equal to the specified precision. The below figure shows the curve approximation for different precisions (epsilon). See how the shape is approximated to a rectangle with epsilon =10% in the below image.
This is widely used in robotics for pattern classification and scene analysis. OpenCV provides a builtin function that approximates the polygonal curves with the specified precision. Its implementation is based on the Douglas-Peucker algorithm.
1 |
approxCurve = cv2.approxPolyDP(curve, epsilon, closed) |
- “curve“: contour/polygon we want to approximate.
- “epsilon“: This is the maximum distance between the original curve and its approximation.
- “closed“: If true, the approximated curve is closed otherwise, not.
This function returns the approximated contour with the same type as that of the input curve. Now, let’s detect simple shapes using this concept. Let’s take the below image to perform shape detection.
Steps
- Load the image and convert to greyscale
- Apply thresholding and find contours
- For each contour
- First, approximate its shape using cv2.approxPolyDP()
- if len(shape) == 3; shape is Triangle
- else if len(shape) == 4; shape is Rectangle
- else if len(shape) == 5; shape is Pentagon
- else if 6< len(shape) <15; shape is Ellipse
- else; shape is circle
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import cv2 import numpy as np # Load the image img = cv2.imread("D:/downloads/cont_new.png") # Convert to greyscale img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Convert to binary image by thresholding _, threshold = cv2.threshold(img_gray, 245, 255, cv2.THRESH_BINARY_INV) # Find the contours contours, _ = cv2.findContours(threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # For each contour approximate the curve and # detect the shapes. for cnt in contours: epsilon = 0.01*cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, epsilon, True) cv2.drawContours(img, [approx], 0, (0), 3) # Position for writing text x,y = approx[0][0] if len(approx) == 3: cv2.putText(img, "Triangle", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, 0,2) elif len(approx) == 4: cv2.putText(img, "Rectangle", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, 0,2) elif len(approx) == 5: cv2.putText(img, "Pentagon", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, 0,2) elif 6 < len(approx) < 15: cv2.putText(img, "Ellipse", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, 0,2) else: cv2.putText(img, "Circle", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, 0,2) cv2.imshow("final", img) cv2.waitKey(0) |
Below is the final result.
Hope you enjoy reading.
If you have any doubts/suggestion please feel free to ask and I will do my best to help or improve myself. Good-bye until next time.