Contours in OpenCV Python: Approximation, Sorting, and Hierarchy Read it later

Rate this post

Are you interested in computer vision and image processing using OpenCV Python? If so, then you may have come across the term “contours”. Contours are a fundamental concept in OpenCV Python, used for shape analysis and object detection. In this blog, we will dive deep into the world of contours, exploring contour approximation, contour sorting, and contour hierarchy. We will also provide code examples to help you understand these concepts better.

What are contours?

Contours can be defined as the curves joining all the continuous points along the boundary of an object that has the same color or intensity. In other words, contours are the boundaries of objects in an image. The contour function in OpenCV Python detects the edges of objects in an image and creates a set of points that represent the boundary of the objects.

Canny Edge Detection

Before we delve into the concept of contours, we need to discuss an essential image processing technique called Canny edge detection. The Canny edge detector is a popular edge detection algorithm that uses a multi-stage algorithm to detect a wide range of edges in an image. The algorithm applies the following steps:

  1. Gaussian blur: The image is smoothed using a Gaussian filter to remove noise.
  2. Gradient calculation: The gradient magnitude and direction are calculated.
  3. Non-maximum suppression: Non-maximum pixels are suppressed to thin out the edges.
  4. Double thresholding: Two thresholds are applied to identify strong and weak edges.
  5. Edge tracking by hysteresis: Strong edges are connected to weak edges to form a complete edge.

Contour Detection in OpenCV Python

The OpenCV function findContours() is used to detect contours in an image. The function takes the image as input and returns the contours found in the image. The contours are represented as a list of points.

import cv2

# Load the image
img = cv2.imread('image.jpg')

# Convert the image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Find the contours in the image
contours, hierarchy = cv2.findContours(gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

In the above code, we loaded an image and converted it to grayscale using the cvtColor() function. Then we used the findContours() function to detect the contours in the image. The function returns two outputs, contours and hierarchy. contours contains the list of points representing the contours, and hierarchy represents the hierarchy of contours.

Contour approximation

Now that we have a basic understanding of Canny edge detection and Contour detection, we can move on to contour approximation. Contour approximation is the process of approximating a contour with a simpler polygon, while still retaining the essential features of the contour. The approximated contour can then be used for shape analysis and object detection.

OpenCV Python provides two methods for contour approximation: the Douglas-Peucker algorithm and the Ramer-Douglas-Peucker algorithm. The Douglas-Peucker algorithm is a classic algorithm that reduces the number of points in a curve, while the Ramer-Douglas-Peucker algorithm is an optimized version of the Douglas-Peucker algorithm that uses the minimum-perimeter polygon instead of the minimum number of points.

The approxPolyDP() function in OpenCV is used to approximate the contours to reduce the number of points. It takes two arguments, the contour and the epsilon value. The epsilon value is the maximum distance between the original contour and its approximation. The function returns the approximated contour.

# Approximate the contour
epsilon = 0.01 * cv2.arcLength(contours[0], True)
approx = cv2.approxPolyDP(contours[0], epsilon, True)

In the above code, we used the approxPolyDP() function to approximate the first contour in the list contours. The epsilon value is set to 1% of the perimeter of the contour. The True parameter indicates that the contour is a closed shape.

Here is the full code of to use the Douglas-Peucker algorithm for contour approximation:

import cv2

# Load the image
img = cv2.imread('shapes.png')

# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Apply Canny edge detection
edges = cv2.Canny(gray, 50, 150)

# Find contours
contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Approximate each contour
for contour in contours:
    epsilon = 0.01 * cv2.arcLength(contour, True)
    approx = cv2.approxPolyDP(contour, epsilon, True)
    cv2.drawContours(img, [approx], 0, (0, 255, 0), 3)

# Display the image
cv2.imshow('Contours', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

In this example, we first load an image and convert it to grayscale. We then apply Canny edge detection to detect the edges of the objects in the image. We then use the findContours function to find the contours of the objects. Finally, we use the approxPolyDP function to approximate each contour with a polygon and draw the polygon on the image.

Contour sorting

Once we have detected the contours, we may want to sort them based on their properties such as size, position, or orientation. Sorting contours can be helpful in object detection, where we want to identify specific objects based on their characteristics.

The sorted() function in Python is used to sort the contours in a specific order. The function takes two arguments, the contours and the sorting method. The sorting method can be either by area, by perimeter, or by any other criteria.

# Sort the contours by area
sorted_contours = sorted(contours, key=cv2.contourArea, reverse=True)

In the above code, we sorted the contours by area in descending order using the cv2.contourArea function. The reverse=True parameter indicates that we want the contours in descending order.

Here is the full code:

import cv2

# Load the image
img = cv2.imread('shapes.png')

# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Apply Canny edge detection
edges = cv2.Canny(gray, 50, 150)

# Find contours
contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Sort contours by area
contours = sorted(contours, key=cv2.contourArea, reverse=True)

# Draw the contours
for contour in contours:
    cv2.drawContours(img, [contour], 0, (0, 255, 0), 3)

# Display the image
cv2.imshow('Contours', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

In this example, we first load an image and convert it to grayscale. We then apply Canny edge detection to detect the edges of the objects in the image. We then use the findContours function to find the contours of the objects. We then sort the contours based on their area using the sorted function and the cv2.contourArea function. Finally, we draw the sorted contours on the image.

Contour Hierarchy

Contour hierarchy is a useful concept in OpenCV Python that describes the relationship between the contours in an image. Contour hierarchy is represented as a tree-like structure, where each contour can have a parent, child, or sibling contour. The hierarchy information can be used to identify the different components of an image and to understand the relationship between them.

The findContours() function in OpenCV returns the hierarchy of contours along with the contours. The hierarchy is represented as a numpy array of shape (1, N, 4), where N is the number of contours. Each contour has four values in the hierarchy array, [Next, Previous, First_Child, Parent].

  • Next: Index of the next contour at the same hierarchical level.
  • Previous: Index of the previous contour at the same hierarchical level.
  • First_Child: Index of the first child contour.
  • Parent: Index of the parent contour.

The hierarchy information is useful in many applications, such as detecting holes in an object and separating objects in an image.

# Get the hierarchy of contours
contours, hierarchy = cv2.findContours(gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Print the hierarchy of the first contour
print(hierarchy[0])

In the above code, we used the findContours() function to get the hierarchy of contours along with the contours. We printed the hierarchy information of the first contour using the hierarchy[0] command.

OpenCV Python provides two methods for retrieving contour hierarchy information: RETR_LIST and RETR_TREE. The RETR_LIST method retrieves all the contours without any hierarchical information, while the RETR_TREE method retrieves all the contours with their hierarchical information.

Here is an example of how to use the RETR_TREE method to retrieve contour hierarchy information:

import cv2

# Load the image
img = cv2.imread('shapes.png')

# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Apply Canny edge detection
edges = cv2.Canny(gray, 50, 150)

# Find contours with hierarchy information
contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Draw the contours with hierarchy information
for i in range(len(contours)):
    # Draw the contour
    cv2.drawContours(img, contours, i, (0, 255, 0), 3)
    # Draw the hierarchy information
    if hierarchy[0][i][3] != -1:
        cv2.line(img, tuple(contours[i][0][0]), tuple(contours[hierarchy[0][i][3]][0][0]), (0, 0, 255), 2)

# Display the image
cv2.imshow('Contours', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

In this example, we first load an image and convert it to grayscale. We then apply Canny edge detection to detect the edges of the objects in the image. We then use the findContours function with the RETR_TREE method to find the contours and their hierarchical information. We then draw the contours on the image and the hierarchy information as lines between the contours. The hierarchy information is drawn using the cv2.line function, which connects the centroid of the child contour with the centroid of its parent contour.

References:

  • OpenCV documentation: https://docs.opencv.org/
  • “Contours in OpenCV”, by Satya Mallick:
  • “Contour Features”, by OpenCV-Python Tutorials:
Was This Article Helpful?

Leave a Reply

Your email address will not be published. Required fields are marked *