1. Environment
The usage environment of this article is:
- Windows10
- Python 3.9.17
- opencv-python 4.8.0.74
2. Principle
2.1 Function interface
The findContours function in OpenCV is used to detect contours in images. Contours are contiguous sets of points in an image, and they usually represent the edges or shapes of objects. In computer vision and image processing, contour analysis is a common task such as object detection, shape recognition, etc.
The basic syntax of the findContours function is as follows:
contours, hierarchy = cv.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])
Parameter Description:
image
: Input image, usually a binary image.mode
: Contour search mode. This parameter determines how the function returns the contour. Common patterns are:cv2.RETR_EXTERNAL
: Only the outermost outline is retrieved.cv2.RETR_LIST
: Retrieve all contours and save them to a list.cv2.RETR_CCOMP
: Retrieve all contours and organize them into two different hierarchies (for example, exterior and interior).cv2.RETR_TREE
: Retrieve all contours and organize them into a hierarchy.
method
: Approximation method of contour. Common ones are:cv2.CHAIN_APPROX_SIMPLE
: Compress horizontal, vertical and diagonal segments.cv2.CHAIN_APPROX_NONE
: Store all segments (4 points).cv2.CHAIN_APPROX_SIMPLE
andcv2.CHAIN_APPROX_DP
: Use dynamic programming to compress the contour.
contours
(optional): Output parameter to return the found contour.hierarchy
(optional): Output parameter that returns information about the relationship between contours.offset
(Optional): Offset, used to adjust the position of the outline.
return value:
- If the
contours
parameter is specified, the found contour is returned. - If the
hierarchy
parameter is specified, returns information about the relationships between contours.
2.2 Principle understanding
In the OpenCV library, the function cv2.findContours() is an important tool for finding the contours of objects in images. The main parameters of this function include: input image, contour retrieval mode and approximation method, etc.
First, the input image is usually a binary single-channel image, where black represents the background and white represents the target object. Such images are usually processed by edge detection operators such as Canny or Laplacian.
Second, the contour retrieval mode determines how contours in the image are processed. For example, cv2.RETR_EXTERNAL only detects the outer contour; the contours detected by cv2.RETR_LIST do not establish a hierarchical relationship; cv2.RETR_CCOMP establishes two levels of contours, the upper layer is the outer boundary, and the inner layer is the boundary of the inner hole; and cv2.RETR_TREE Then establish an outline of a hierarchical tree structure.
Finally, the approximation method determines how to simplify the contour. For example, cv2.CHAIN_APPROX_SIMPLE means using as few pixels as possible to represent the outline.
The return value of the function consists of two parts: contours and hierarchy. Among them, contours is an array containing all detected contour information, and each contour is composed of several points; while hierarchy is an array containing the hierarchical relationship between contours.
3. Complete code
from __future__ import print_function
import cv2 as cv
import numpy as np
import argparse
import random as rng
rng.seed(12345)
def thresh_callback(val):
threshold = val
# 使用canny检测边缘
canny_output = cv.Canny(src_gray, threshold, threshold * 2)
# 查找轮廓
contours, _ = cv.findContours(canny_output, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# 这里在分配空间
contours_poly = [None]*len(contours)
boundRect = [None]*len(contours)
centers = [None]*len(contours)
radius = [None]*len(contours)
# 依据canny检测出来的边缘,下面查找边缘的轮廓、边缘的外接圆、外接矩形
for i, c in enumerate(contours):
contours_poly[i] = cv.approxPolyDP(c, 3, True) # 轮廓
boundRect[i] = cv.boundingRect(contours_poly[i]) # 外接矩形
centers[i], radius[i] = cv.minEnclosingCircle(contours_poly[i]) # 外接圆
# 搞一张黑色的图,用于绘制
drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8)
# 绘制轮廓、矩形、圆
for i in range(len(contours)):
color = (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256))
cv.drawContours(drawing, contours_poly, i, color)
cv.rectangle(drawing, (int(boundRect[i][0]), int(boundRect[i][1])), \
(int(boundRect[i][0]+boundRect[i][2]), int(boundRect[i][1]+boundRect[i][3])), color, 2)
cv.circle(drawing, (int(centers[i][0]), int(centers[i][1])), int(radius[i]), color, 2)
cv.imshow('Contours', drawing)
parser = argparse.ArgumentParser(description='Code for Creating Bounding boxes and circles for contours tutorial.')
parser.add_argument('--input', help='Path to input image.', default='data/stuff.jpg')
args = parser.parse_args()
# 读取图片
src = cv.imread(cv.samples.findFile(args.input))
if src is None:
print('Could not open or find the image:', args.input)
exit(0)
# 彩色图转灰度图
src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# 图片平滑
src_gray = cv.blur(src_gray, (3,3))
# 显示原彩色图
source_window = 'Source'
cv.namedWindow(source_window)
cv.imshow(source_window, src)
# 创建滑条,控制canny查找边缘的阈值
max_thresh = 255
thresh = 100 # canny初始化阈值
cv.createTrackbar('Canny thresh:', source_window, thresh, max_thresh, thresh_callback)
thresh_callback(thresh)
cv.waitKey()