[OpenCV implements images to find different features of contours, such as area, perimeter, centroid, bounding box, etc.

Article directory

    • summary
    • image moment
    • convex hull
    • bounding rectangle

Summary

OpenCV is a popular computer vision library that provides many image processing and analysis functions, including finding the outlines of objects in images. By finding contours, many useful features can be extracted, such as area, perimeter, centroid, bounding box, etc.

Here are several common features when using OpenCV to find contours:

Area: The area of a contour can be calculated using the cv2.contourArea() function. This function takes a contour as input and returns a floating point number representing the area of the contour.

Perimeter: The perimeter of a contour can be calculated using the cv2.arcLength() function. This function takes a contour as input and returns a floating point number representing the perimeter of the contour.

Center of mass: The center of mass of the contour can be calculated using the cv2.moments() function. This function takes a contour as input and returns a dictionary containing the various rectangles of the contour, including the contour’s centroid.

Bounding box: The bounding box of a contour can be calculated using the cv2.boundingRect() function. This function takes an outline as input and returns a tuple containing the x and y coordinates of the bounding box as well as the width and height.

Minimum area rectangle: Use the cv2.minAreaRect() function to calculate the minimum area rectangle of the outline. The function takes an outline as input and returns a tuple containing the center coordinates of the rectangle, its width and height, and its angle.

Minimum circumscribing circle: Use the cv2.minEnclosingCircle() function to calculate the minimum circumscribing circle of a contour. This function takes as input a contour and returns a tuple containing the coordinates of the center point and the radius.

These features can be used to describe contours and extract useful information. For example, area and perimeter can be used to classify and identify objects, centroids can be used to track the movement of objects, bounding boxes and minimum area rectangles can be used to determine the location and orientation of objects, and minimum circumscribed circles can be used to determine the size of an object.

Image moment

Image moments can help you calculate some features, such as the center of mass of an object, the area of an object, etc. The function cv.moments() provides a dictionary of all calculated moment values. code show as below:

import numpy as np
import cv2 as cv

img = cv.imread('star.jpg', 0)
ret, thresh = cv.threshold(img, 127, 255, 0)
contours, hierarchy = cv.findContours(thresh, 1, 2)

cnt = contours[0]
M = cv.moments(cnt)
print(M)

In this M, you can extract useful data such as area, centroid, etc. The center of mass is given by the following relationship:

code show as below:

cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])

Contour area
The contour area can be obtained through the function cv.contourArea() or M[m00’].

area = cv.contourArea(cnt)

Contour perimeter
It is also called arc length. It can be obtained by the function cv.arcLength(). The second parameter needs to specify whether the shape is closed. If closed, pass True, otherwise pass False.

perimeter = cv.arcLength(cnt, True)

contour approximation
The approxPolyDP() function in OpenCV is a contour approximation function that implements the Douglas-Peucker algorithm. This function can approximate one contour shape to another shape with a smaller number of vertices, the accuracy depends on the parameter epsilon we specify.

For example, if we want to find a square in an image, but we cannot get a perfect square outline due to noise or other factors in the image. At this time, we can use the approxPolyDP() function to approximate the contour shape, thereby obtaining results closer to the real shape. In this case, we need to choose a suitable epsilon value to control the approximation accuracy. It is usually recommended to set epsilon to a certain proportion of the original contour perimeter, such as 0.1*cv.arcLength(cnt, True).

Using the approxPolyDP() function can reduce the number of vertices of the outline, thereby improving the efficiency and accuracy of image processing and analysis.

epsilon = 0.1*cv.arcLength(cnt, True)
approx = cv.approxPolyDP(cnt, epsilon, True)

Convex hull

hull = cv.convexHull(points[, hull[, clockwise[, returnPoints]]])

Parameter details:

?points are the contours we cross.
?clockwise: direction flag. If True, the direction of the output convex hull is clockwise. Otherwise, its direction is counterclockwise.
?returnPoints: True by default. It returns the coordinates of the hull point. If False, returns the index of the contour point corresponding to the hull point.

So to get the convex hull in the image above, the following is enough:

hull = cv.convexHull(cnt)

However, if you want to find convexity defects, you need to pass returnPoints=False. To understand it, we will take the rectangular image above. First I found out that its outline is cnt. Now I find its convex hull, returnPoints=True, I get the following values: [[234 202]], [[51 202]], [[51 79]], [[234 79]], which are rectangles of the four corners. Now, if returnPoints=False also does this, I get the following results: [[129], [67], [0], [142]]. These are the indices of corresponding points in the contours. For example, check the first value: cnt[129]=[[234202]], which is the same as the first result (and so on for the other values).

The syntax of the cv2.convexHull() function is as follows:

hull = cv2.convexHull(points[, hull[, clockwise[, returnPoints]]])

Among them, the details of the parameters are as follows:

? Points: Indicates that we are looking for the contour of the convex hull.

? clockwise: direction flag. If True, the direction of the output convex hull is clockwise; otherwise, its direction is counterclockwise.

? returnPoints: True by default. It returns the coordinates of the hull point. If False, returns the index of the contour point corresponding to the hull point.

So if we just need to get the convex hull of the contour, we can use the following code:

hull = cv2.convexHull(cnt)

However, if you need to find convexity defects, you need to set returnPoints to False. To better understand its meaning, consider the following rectangular image. First, we find the outline cnt of the rectangle. Next, we use the returnPoints=True option to calculate its convex hull and obtain the coordinate values of the following four points: [[234 202]], [[51 202]], [[51 79]], [[234 79]] . These coordinate values are the points at the four corners of the rectangle. Now, if we set returnPoints to False, we will get the following four point index values: [[129], [67], [0], [142]]. These are the index values of the corresponding points in the contour array. For example, the first value 129 means cnt[129]=[[234 202]], which is the same as the coordinate value of the first convex hull point obtained earlier (other index values are similar).

Check convexity
There is a function to check if a curve is convex, cv.isContourConvex(). It only returns True or False.

k = cv.isContourConvex(cnt)

Bounding rectangle

A bounding rectangle is a rectangular shape used to enclose an object or outline. There are two types of bounding rectangles:

7.a. Straight bounding rectangle:
A straight bounding rectangle is a simple rectangle that does not take into account the rotation of the object. It can be found by using the cv2.boundingRect() function. This function returns a four-tuple (x, y, w, h), where (x, y) represents the coordinates of the upper left corner of the rectangle and (w, h) represents the width and height of the rectangle.

x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)

The above code will draw a rectangle on the image img, framing the object represented by the outline cnt.

7.b. Rotate rectangle:
A rotated rectangle is a rectangle drawn based on the object’s minimum area, taking into account the object’s rotation. To get a rotated rectangle, you can use the cv2.minAreaRect() function. This function returns a Box2D structure containing the rectangle’s center coordinates (x, y), width and height (w, h), and rotation angle.

To draw a rotated rectangle, we need to get the four corner points of the rectangle. These corner points can be obtained using the cv2.boxPoints() function, which takes a rotated rectangle as a parameter and returns an array containing the coordinates of the four corner points.

rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(img, [box], 0, (0, 0, 255), 2)

The above code will draw a red bounding box on the image img, which is calculated based on the minimum area rotated rectangle of the outline cnt.
Minimum circumscribed circle

# Import OpenCV library
import cv2 as cv

# Use cv.minEnclosingCircle() to find the smallest circumscribed circle
(x, y), radius = cv.minEnclosingCircle(cnt)
center = (int(x), int(y))
radius = int(radius)

# Draw the smallest circumscribed circle on the image
cv.circle(img, center, radius, (0, 255, 0), 2)

# show image
cv.imshow('minimum circumscribed circle', img)

Fit ellipse

# Use cv.fitEllipse() to fit the ellipse to the object
ellipse = cv.fitEllipse(cnt)

# Draw the fitted ellipse on the image
cv.ellipse(img, ellipse, (0, 255, 0), 2)

# show image
cv.imshow('Fit ellipse', img)

straight line fitting

# Get the number of rows and columns of the image
rows, cols = img.shape[:2]

# Use cv.fitLine() to fit a straight line to a set of points
[vx, vy, x, y] = cv.fitLine(cnt, cv.DIST_L2, 0, 0.01, 0.01)
lefty = int((-x * vy / vx) + y)
righty = int(((cols - x) * vy / vx) + y)

# Draw a fitted straight line on the image
cv.line(img, (cols - 1, righty), (0, lefty), (0, 255, 0), 2)

# show image
cv.imshow('straight line fitting', img)