“This is the 27th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

preface

In computer vision, an outline is usually a series of points at the boundary of an object in an image. Therefore, contour usually describes the key information of object boundary and contains the main information about object shape, which can be used for shape analysis and object detection and recognition. We have introduced how to detect and draw contours in “OpenCV Contour Detection In Detail”. In this article, we will continue to learn how to use the obtained contours for shape analysis and object detection and recognition.

Outline drawing

In OpenCV Image Moments, we show how to derive contour properties (e.g., center of mass, area, roundness, or eccentricity) from image moments calculations. In addition, OpenCV provides several other functions to further describe contours.

Cv2.boundingrect () returns the smallest bounding rectangle containing all points of the contour:

x, y, w, h = cv2.boundingRect(contours[0])
Copy the code

Cv2.minarearect () returns the minimum rotation (if necessary) rectangle containing all points of the contour:

rotated_rect = cv2.minAreaRect(contours[0])
Copy the code

To extract the four points of the rotated rectangle, we can use the cv2.boxpoints () function, which returns the four vertices of the rotated rectangle:

box = cv2.boxPoints(rotated_rect)
Copy the code

Cv2.minenclosingcircle () returns the smallest circle containing all the points of the contour (this function returns the center and radius) :

(x, y), radius = cv2.minEnclosingCircle(contours[0])
Copy the code

Cv2.fitellipse () returns an ellipse containing all points of the contour (with least square error) :

ellipse = cv2.fitEllipse(contours[0])
Copy the code

Cv2.approxpolydp () returns the contour approximation of the given contour based on the given precision. This function uses douglas-Peucker algorithm:

approx = cv2.approxPolyDP(contours[0], epsilon, True)
Copy the code

The Epsilon parameter is used to determine the accuracy, the maximum distance between the original curves and its approximation. Thus, the resulting contour is a compressed contour similar to the given contour.

Next, we use contour-related OpenCV functions to calculate the outer endpoints of a given contour. When we walk through the code, we first look at the resulting image to better understand the above functions:

First write extreme_points() to calculate the four outer endpoints that define a given contour:

def extreme_points(contour) :
    """ Detecting extreme point of contour """

    extreme_left = tuple(contour[contour[:, :, 0].argmin()][0])
    extreme_right = tuple(contour[contour[:, :, 0].argmax()][0])
    extreme_top = tuple(contour[contour[:, :, 1].argmin()][0])
    extreme_bottom = tuple(contour[contour[:, :, 1].argmax()][0])

    return extreme_left, extreme_right, extreme_top, extreme_bottom
Copy the code

Np.argmin () returns the smallest index along the axis and the first occurrence of multiple minima; And np.argmax() returns the index of the maximum value. Once the index index is calculated, the index can be used to obtain the corresponding element of the array (for example, contour[index] — [[40 320]]). To access the first element, use contour[index][0] — [40 320]; Finally, we converted it to a tuple: tuple(Contour [index][0] — (40,320), which was used to draw contour points.

def array_to_tuple(arr) :
    """ Convert a list to a tuple """
    return tuple(arr.reshape(1, -1) [0])

def draw_contour_points(img, cnts, color) :
    """ Draw all detected contour points """
    for cnt in cnts:
        squeeze = np.squeeze(cnt)
        for p in squeeze:
            pp = array_to_tuple(p)
            cv2.circle(img, pp, 10, color, -1)
    return img

def draw_contour_outline(img, cnts, color, thickness=1) :
    """ Draw all the contours """
    for cnt in cnts:
        cv2.drawContours(img, [cnt], 0, color, thickness)

def show_img_with_matplotlib(color_img, title, pos) :
    """ Image visualization ""
    img_RGB = color_img[:, :, ::-1]

    ax = plt.subplot(2.3, pos)
    plt.imshow(img_RGB)
    plt.title(title, fontsize=8)
    plt.axis('off')

# Load the image and convert it to grayscale image
image = cv2.imread("example.png")
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Threshold processing is converted into binary images
ret, thresh = cv2.threshold(gray_image, 60.255, cv2.THRESH_BINARY)

# Using binary image to detect the contour in the image
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

# display the number of contours detected
print("detected contours: '{}' ".format(len(contours)))
Create a copy of the original image to perform the visualization
boundingRect_image = image.copy()
minAreaRect_image = image.copy()
fitEllipse_image = image.copy()
minEnclosingCircle_image = image.copy()
approxPolyDP_image = image.copy()

# 1. cv2.boundingRect()
x, y, w, h = cv2.boundingRect(contours[0])
cv2.rectangle(boundingRect_image, (x, y), (x + w, y + h), (0.255.0), 5)

# 2. cv2.minAreaRect()
rotated_rect = cv2.minAreaRect(contours[0])
box = cv2.boxPoints(rotated_rect)
box = np.int0(box)
cv2.polylines(minAreaRect_image, [box], True, (0.0.255), 5)

# 3. cv2.minEnclosingCircle()
(x, y), radius = cv2.minEnclosingCircle(contours[0])
center = (int(x), int(y))
radius = int(radius)
cv2.circle(minEnclosingCircle_image, center, radius, (255.0.0), 5)

# 4. cv2.fitEllipse()
ellipse = cv2.fitEllipse(contours[0])
cv2.ellipse(fitEllipse_image, ellipse, (0.255.255), 5)

# 5. cv2.approxPolyDP()
epsilon = 0.01 * cv2.arcLength(contours[0].True)
approx = cv2.approxPolyDP(contours[0], epsilon, True)
draw_contour_outline(approxPolyDP_image, [approx], (255.255.0), 5)
draw_contour_points(approxPolyDP_image, [approx], (255.0.255))

# Detect the extreme point of the contour
left, right, top, bottom = extreme_points(contours[0])
cv2.circle(image, left, 20, (255.0.0), -1)
cv2.circle(image, right, 20, (0.255.0), -1)
cv2.circle(image, top, 20, (0.255.255), -1)
cv2.circle(image, bottom, 20, (0.0.255), -1)

# visualization
show_img_with_matplotlib(image, "image and extreme points".1)
show_img_with_matplotlib(boundingRect_image, "cv2.boundingRect()".2)
show_img_with_matplotlib(minAreaRect_image, "cv2.minAreaRect()".3)
show_img_with_matplotlib(minEnclosingCircle_image, "cv2.minEnclosingCircle()".4)
show_img_with_matplotlib(fitEllipse_image, "cv2.ellipse()".5)
show_img_with_matplotlib(approxPolyDP_image, "cv2.approxPolyDP()".6)
plt.show()
Copy the code

We can also test the effect on other images:

A link to the

OpenCV contour detection in detail

OpenCV image moments in detail

OpenCV Hu invariant moment in detail