Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
How to approximate a contour shape in an image using OpenCV Python?
The function cv2.approxPolyDP() approximates a contour shape to another shape with fewer vertices. This is useful for simplifying complex shapes or detecting specific geometric forms like rectangles, triangles, or polygons.
Syntax
approx = cv2.approxPolyDP(contour, epsilon, closed)
Parameters
contour ? The array of contour points to be approximated
epsilon ? Maximum distance from contour to approximated contour. Usually calculated as a percentage of the contour perimeter
closed ? Boolean flag indicating whether the contour is closed (True) or open (False)
Complete Example
Here's a complete example that creates a sample image and demonstrates contour approximation ?
import cv2
import numpy as np
# Create a sample image with a rectangle
img = np.zeros((300, 400, 3), dtype=np.uint8)
cv2.rectangle(img, (50, 50), (300, 200), (255, 255, 255), -1)
# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply thresholding
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# Find contours
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Get the first contour
cnt = contours[0]
# Calculate epsilon as 1% of contour perimeter
epsilon = 0.01 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
# Print results
print(f"Original contour points: {len(cnt)}")
print(f"Approximated contour points: {len(approx)}")
# Draw original contour in yellow
cv2.drawContours(img, [cnt], -1, (0, 255, 255), 2)
# Draw approximated contour in red
cv2.drawContours(img, [approx], -1, (0, 0, 255), 3)
print("Contour approximation completed")
Original contour points: 8 Approximated contour points: 4 Contour approximation completed
Effect of Different Epsilon Values
The epsilon parameter controls the approximation accuracy. Here's how different values affect the result ?
import cv2
import numpy as np
# Create a sample image with an irregular shape
img = np.zeros((300, 400, 3), dtype=np.uint8)
points = np.array([[100, 50], [200, 80], [250, 150], [180, 200], [80, 180], [50, 120]], np.int32)
cv2.fillPoly(img, [points], (255, 255, 255))
# Convert to grayscale and find contours
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
perimeter = cv2.arcLength(cnt, True)
# Test different epsilon values
epsilon_values = [0.01, 0.02, 0.05, 0.1]
print("Epsilon\tPoints\tApproximation Level")
print("------\t------\t------------------")
for eps_factor in epsilon_values:
epsilon = eps_factor * perimeter
approx = cv2.approxPolyDP(cnt, epsilon, True)
level = "High" if len(approx) <= 4 else "Medium" if len(approx) <= 6 else "Low"
print(f"{eps_factor}\t{len(approx)}\t{level}")
Epsilon Points Approximation Level ------ ------ ------------------ 0.01 7 Low 0.02 6 Medium 0.05 4 High 0.1 3 High
Comparison of Methods
| Epsilon Factor | Approximation Level | Best For |
|---|---|---|
| 0.01 - 0.02 | Low (preserves detail) | Complex shape analysis |
| 0.02 - 0.05 | Medium | General shape simplification |
| 0.05 - 0.1 | High (heavy simplification) | Basic geometric detection |
Practical Application: Shape Detection
Here's how to use contour approximation for basic shape detection ?
import cv2
import numpy as np
def detect_shape(contour):
# Approximate the contour
epsilon = 0.02 * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)
# Classify shape based on number of vertices
vertices = len(approx)
if vertices == 3:
return "Triangle"
elif vertices == 4:
return "Rectangle/Square"
elif vertices == 5:
return "Pentagon"
elif vertices > 5:
return "Circle/Ellipse"
else:
return "Unknown"
# Create sample shapes
img = np.zeros((200, 600, 3), dtype=np.uint8)
# Triangle
triangle = np.array([[50, 150], [100, 50], [150, 150]], np.int32)
cv2.fillPoly(img, [triangle], (255, 255, 255))
# Rectangle
cv2.rectangle(img, (200, 50), (350, 150), (255, 255, 255), -1)
# Circle
cv2.circle(img, (500, 100), 50, (255, 255, 255), -1)
# Process and detect shapes
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print("Shape Detection Results:")
for i, contour in enumerate(contours):
shape = detect_shape(contour)
print(f"Contour {i+1}: {shape}")
Shape Detection Results: Contour 1: Triangle Contour 2: Rectangle/Square Contour 3: Circle/Ellipse
Conclusion
The cv2.approxPolyDP() function is essential for simplifying contours and detecting geometric shapes. Choose epsilon values between 0.01-0.05 times the perimeter for optimal results in most applications.
