OpenCV 在二值图像中查找轮廓 cv2.findContours
OpenCV 在二值图像中查找轮廓 cv2.findContours
flyfish
1. Contours
(轮廓)的含义
在图像处理中,Contours
(轮廓)指的是图像中具有相同颜色或者灰度值的连续点所构成的曲线,它可以用来表示物体的边界。简单来说,轮廓就是将物体的边缘连接起来形成的封闭曲线。轮廓在很多计算机视觉任务中都非常重要,比如物体检测、形状分析、目标识别等。通过分析轮廓的特征,如面积、周长、形状等,可以对物体进行分类和识别。
2. cv2.findContours
函数的作用
cv2.findContours
是 OpenCV 库中的一个函数,其主要作用是在二值图像中查找物体的轮廓。通常在使用这个函数之前,需要对图像进行预处理,如灰度化、二值化等操作,以便更好地提取物体的轮廓。
3. 函数语法和参数
contours, hierarchy = cv2.findContours(image, mode, method)
- 参数说明:
image
:输入的二值图像,通常是经过阈值处理或者边缘检测后的图像。mode
:轮廓检索模式,用于指定查找轮廓的方式,常见的取值有:cv2.RETR_EXTERNAL
:只检索最外层的轮廓。cv2.RETR_LIST
:检索所有的轮廓,并将它们存储在一个列表中,不建立轮廓之间的层次关系。cv2.RETR_TREE
:检索所有的轮廓,并建立完整的轮廓层次结构。
method
:轮廓近似方法,用于指定如何近似轮廓的曲线,常见的取值有:cv2.CHAIN_APPROX_NONE
:存储所有的轮廓点,即不进行近似处理。cv2.CHAIN_APPROX_SIMPLE
:只保留轮廓的端点,对于水平、垂直和对角线方向的直线段,只保留其端点,从而减少存储的点数。
- 返回值:
contours
:一个列表,包含了检测到的所有轮廓,每个轮廓是一个由点组成的 NumPy 数组。hierarchy
:一个 NumPy 数组,用于表示轮廓之间的层次关系,对于不同的轮廓检索模式,其含义不同。
4. 示例代码
以下是一个简单的示例代码,展示如何使用 cv2.findContours
函数查找图像中的轮廓:
import cv2
import numpy as np# 读取图像
image = cv2.imread('example.jpg', 0)# 进行阈值处理,得到二值图像
_, binary = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)# 查找轮廓
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)# 在原始图像上绘制轮廓
contour_image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
cv2.drawContours(contour_image, contours, -1, (0, 255, 0), 2)# 显示原始图像和绘制轮廓后的图像
cv2.imshow('Original Image', image)
cv2.imshow('Contour Image', contour_image)# 等待按键事件
cv2.waitKey(0)# 关闭所有窗口
cv2.destroyAllWindows()
可以直观地看到 cv2.findContours
函数查找图像中的轮廓,并将其绘制出来。
import cv2
import numpy as np# 读取图像
image = cv2.imread('example.jpg')
# 转换为灰度图像
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 进行高斯模糊,减少噪声
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# 进行阈值处理,得到二值图像
_, binary = cv2.threshold(blurred, 127, 255, cv2.THRESH_BINARY)# 查找轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 初始化最大面积和对应的轮廓
max_area = 0
max_contour = None# 遍历每个轮廓,找出面积最大的轮廓
for contour in contours:# 计算轮廓的面积area = cv2.contourArea(contour)if area > max_area:max_area = areamax_contour = contourif max_contour is not None:# 计算最大面积轮廓的周长perimeter = cv2.arcLength(max_contour, True)# 计算最大面积轮廓的凸包hull = cv2.convexHull(max_contour)# 计算凸包的面积hull_area = cv2.contourArea(hull)# 计算最大面积轮廓的紧致度if hull_area != 0:solidity = float(max_area) / hull_areaelse:solidity = 0# 计算最大面积轮廓的边界矩形x, y, w, h = cv2.boundingRect(max_contour)# 计算边界矩形的宽高比aspect_ratio = float(w) / h# 在原始图像上绘制最大面积的轮廓cv2.drawContours(image, [max_contour], -1, (0, 255, 0), 2)# 在原始图像上绘制边界矩形cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)# 在原始图像上显示面积信息cv2.putText(image, f'Area: {max_area:.2f}', (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)# 在原始图像上显示周长信息cv2.putText(image, f'Perimeter: {perimeter:.2f}', (x, y - 30), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)# 在原始图像上显示紧致度信息cv2.putText(image, f'Solidity: {solidity:.2f}', (x, y - 50), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)# 在原始图像上显示宽高比信息cv2.putText(image, f'Aspect Ratio: {aspect_ratio:.2f}', (x, y - 70), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)# 显示原始图像和处理后的图像
cv2.imshow('Original Image', cv2.imread('example.jpg'))
cv2.imshow('Processed Image', image)# 等待按键事件
cv2.waitKey(0)# 关闭所有窗口
cv2.destroyAllWindows()
函数
- 面积计算:
cv2.contourArea(contour)
:计算当前轮廓的面积,存储在area
变量中。
- 周长计算:
cv2.arcLength(contour, True)
:计算当前轮廓的周长,True
表示轮廓是封闭的。
- 凸包计算:
cv2.convexHull(contour)
:计算当前轮廓的凸包,凸包是指能够完全包含该轮廓的最小凸多边形。cv2.contourArea(hull)
:计算凸包的面积,存储在hull_area
变量中。
- 紧致度计算:
solidity
:紧致度,定义为轮廓面积与凸包面积的比值。如果凸包面积为 0,则紧致度设为 0。
- 边界矩形计算:
cv2.boundingRect(contour)
:计算当前轮廓的最小外接矩形,返回矩形的左上角坐标(x, y)
以及宽度w
和高度h
。aspect_ratio
:宽高比,即矩形宽度与高度的比值。
1. cv2.contourArea
:计算轮廓的面积
- 功能:
cv2.contourArea
函数用于计算给定轮廓的面积。在图像处理里,轮廓通常代表一个物体的边界,计算其面积可以帮助我们了解物体的大小。 - 语法:
area = cv2.contourArea(contour)
- 参数:
contour
:这是一个由cv2.findContours
函数返回的轮廓点集,通常是一个numpy.ndarray
类型的数组,其形状为(N, 1, 2)
,其中N
是轮廓上的点数,每个点用(x, y)
坐标表示。
- 返回值:
area
:返回的是该轮廓所围成区域的面积,数据类型为浮点数。
- 示例:
import cv2
import numpy as np# 生成一个简单的轮廓
contour = np.array([[[0, 0]], [[0, 10]], [[10, 10]], [[10, 0]]], dtype=np.int32)
area = cv2.contourArea(contour)
print(f"轮廓面积: {area}")
2. cv2.arcLength
:计算轮廓的周长
- 功能:
cv2.arcLength
函数用于计算给定轮廓的周长。在某些场景下,周长也是描述物体形状的一个重要特征。 - 语法:
perimeter = cv2.arcLength(contour, closed)
- 参数:
contour
:与cv2.contourArea
中的contour
参数含义相同,是由cv2.findContours
函数返回的轮廓点集。closed
:这是一个布尔值,用于指定轮廓是否为封闭的。如果轮廓是封闭的,应设为True
;若为开放的,则设为False
。
- 返回值:
perimeter
:返回的是轮廓的周长,数据类型为浮点数。
- 示例:
import cv2
import numpy as np# 生成一个简单的轮廓
contour = np.array([[[0, 0]], [[0, 10]], [[10, 10]], [[10, 0]]], dtype=np.int32)
perimeter = cv2.arcLength(contour, True)
print(f"轮廓周长: {perimeter}")
3. cv2.convexHull
:计算轮廓的凸包
- 功能:
cv2.convexHull
函数用于计算给定轮廓的凸包。凸包是指能够完全包含该轮廓的最小凸多边形。在处理不规则形状的物体时,凸包可以帮助我们简化物体的形状表示。 - 语法:
hull = cv2.convexHull(points, clockwise=None, returnPoints=None)
- 参数:
points
:这是输入的轮廓点集,同样是由cv2.findContours
函数返回的轮廓点集。clockwise
:这是一个可选的布尔值参数,用于指定凸包的点顺序。若为True
,凸包的点将按顺时针方向排列;若为False
或省略该参数,则按逆时针方向排列。returnPoints
:这也是一个可选的布尔值参数。若为True
(默认值),返回的是凸包上点的坐标;若为False
,返回的是凸包上点在输入轮廓点集中的索引。
- 返回值:
hull
:返回的是凸包的点集,是一个numpy.ndarray
类型的数组。
- 示例:
import cv2
import numpy as np# 生成一个简单的轮廓
contour = np.array([[[0, 0]], [[0, 10]], [[10, 10]], [[10, 0]]], dtype=np.int32)
hull = cv2.convexHull(contour)
print("凸包点集:", hull)
4. 再次使用 cv2.contourArea
计算凸包的面积
在得到轮廓的凸包后,我们可以再次使用 cv2.contourArea
函数来计算凸包的面积。具体操作是将 cv2.convexHull
返回的凸包点集作为参数传入 cv2.contourArea
函数。
- 示例:
import cv2
import numpy as np# 生成一个简单的轮廓
contour = np.array([[[0, 0]], [[0, 10]], [[10, 10]], [[10, 0]]], dtype=np.int32)
hull = cv2.convexHull(contour)
hull_area = cv2.contourArea(hull)
print(f"凸包面积: {hull_area}")
5. 计算轮廓的紧致度(面积与凸包面积的比值)
紧致度是一个描述轮廓形状紧凑程度的指标,它通过轮廓的面积与凸包的面积之比来计算。如果紧致度接近 1,说明轮廓形状接近凸多边形;若紧致度远小于 1,则表示轮廓形状较为复杂,有较多的凹陷。
- 示例:
import cv2
import numpy as np# 生成一个简单的轮廓
contour = np.array([[[0, 0]], [[0, 10]], [[10, 10]], [[10, 0]]], dtype=np.int32)
area = cv2.contourArea(contour)
hull = cv2.convexHull(contour)
hull_area = cv2.contourArea(hull)
if hull_area != 0:solidity = float(area) / hull_area
else:solidity = 0
print(f"轮廓紧致度: {solidity}")
6. cv2.boundingRect
:计算轮廓的边界矩形
- 功能:
cv2.boundingRect
函数用于计算给定轮廓的最小外接矩形。这个矩形是与坐标轴平行的,在很多场景下,它可以为我们提供物体大致的位置和尺寸信息。 - 语法:
x, y, w, h = cv2.boundingRect(contour)
- 参数:
contour
:同样是由cv2.findContours
函数返回的轮廓点集。
- 返回值:
x
和y
:表示边界矩形左上角顶点的坐标。w
和h
:分别表示边界矩形的宽度和高度。
- 示例:
import cv2
import numpy as np# 生成一个简单的轮廓
contour = np.array([[[0, 0]], [[0, 10]], [[10, 10]], [[10, 0]]], dtype=np.int32)
x, y, w, h = cv2.boundingRect(contour)
print(f"边界矩形: 左上角坐标 ({x}, {y}), 宽度 {w}, 高度 {h}")