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.

Updated on: 2026-03-26T22:04:37+05:30

8K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements