cv2.findContours 是 OpenCV 库中一个非常重要的函数,它用于在二值图像中检测轮廓。这个函数可以找到所有非零像素的边界,并将它们作为轮廓返回。以下是 cv2.findContours 函数的详细介绍:
cv2.findContours函数语法
contours, hierarchy = cv2.findContours(image, mode, method)
image
:输入的二值图像。这个图像通常是通过阈值操作或其他分割技术从灰度或彩色图像转换而来的。mode
:轮廓检索模式,它决定了哪些轮廓被检测以及它们如何被组织起来。常见的模式有:cv2.RETR_EXTERNAL
:只检测外部轮廓。cv2.RETR_LIST
:检测所有轮廓,但不建立任何层次关系。cv2.RETR_CCOMP
:检测所有轮廓,并组织成两级层次结构,其中顶部级别是外部轮廓,下一级是孔。cv2.RETR_TREE
:检测所有轮廓,并重建完整的层次结构。
method
:轮廓近似方法,它决定了如何存储轮廓。常见的方法有:cv2.CHAIN_APPROX_NONE
:存储所有轮廓点。cv2.CHAIN_APPROX_SIMPLE
:压缩水平、垂直和对角线段,只保留它们的端点。
返回值
contours
:一个 Python 列表,其中每个元素都是一个轮廓,轮廓由点的 NumPy 数组表示。hierarchy
:一个可选的输出,包含关于轮廓层次结构的信息。每个轮廓的四个值 [next, previous, first_child, parent] 表示:- next:同一层级的下一个轮廓。
- previous:同一层级的上一个轮廓。
- first_child:当前轮廓的第一个子轮廓。
- parent:当前轮廓的父轮廓。
注意事项
- cv2.findContours 函数会修改原始图像,因此如果需要保留原始图像,应该先复制一份。
- 在 OpenCV 4.x 版本中,函数只返回两个值:contours 和 hierarchy。而在 OpenCV 3.x 版本中,它返回三个值,包括修改后的原始图像。
使用findContours函数获得图像轮廓并获取边界框、最小矩形、以及最小外接圆形。
本次代码使用OpenCV 3.x 版本
import cv2
import numpy as np
img =cv2.cvtColor(cv2.imread('cs.jpeg'),cv2.COLOR_BGR2RGB)
ret, thresh = cv2.threshold(cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY), 127, 255, cv2.THRESH_BINARY)
a,contours, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# print(hier, ":hierarchy")
# findContours函数查找图像里的图形轮廓
# 函数参数thresh是图像对象
# 层次类型,参数cv2.RETR_EXTERNAL是获取最外层轮廓,cv2.RETR_TREE是获取轮廓的整体结构
# 轮廓逼近方法
# 输出的返回值,image是原图像、contours是图像的轮廓、hier是层次类型
for c in contours:
# find bounding box coordinates
# 现计算出一个简单的边界框
x, y, w, h = cv2.boundingRect(c) # 将轮廓信息转换成(x, y)坐标,并加上矩形的高度和宽度
cv2.rectangle(img, (x,y), (x+w, y+h), (255, 255, 0), 2) # 画出矩形
# find minimum area
# 计算包围目标的最小矩形区域
rect = cv2.minAreaRect(c)
# calculate coordinate of the minimum area rectangle
box = cv2.boxPoints(rect)
# normalize coordinates to integers
box =np.int0(box)
# 注:OpenCV没有函数能直接从轮廓信息中计算出最小矩形顶点的坐标。所以需要计算出最小矩形区域,
# 然后计算这个矩形的顶点。由于计算出来的顶点坐标是浮点型,但是所得像素的坐标值是整数(不能获取像素的一部分),
# 所以需要做一个转换
# draw contours
cv2.drawContours(img, [box], 0, (0, 0, 255), 2) # 画出该矩形
# calculate center and radius of minimum enclosing circle
(x, y), radius = cv2.minEnclosingCircle(c) # 会返回一个二元组,第一个元素为圆心的坐标组成的元组,第二个元素为圆的半径值。
# cast to integers
center = (int(x), int(y))
radius = int(radius)
# draw the circle
img = cv2.circle(img, center, radius, (0, 255, 0), 2)
cv2.namedWindow('Bounding Rectangles', cv2.WINDOW_NORMAL)
cv2.imshow('Bounding Rectangles', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
使用findContours函数获得图像轮廓并绘制轮廓,绘制多边形以及凸包。
import cv2
import numpy as np
img2 =cv2.cvtColor(cv2.imread('cs.jpeg'),cv2.COLOR_BGR2RGB)
ret, thresh = cv2.threshold(cv2.cvtColor(img2.copy(), cv2.COLOR_BGR2GRAY), 127, 255, cv2.THRESH_BINARY)
# black = cv2.cvtColor(np.zeros((img2.shape[0], img2.shape[1]), dtype=np.uint8), cv2.COLOR_GRAY2BGR) # 可以创建原图大小的全黑图像,将后续结果绘制在这个图像上
a,contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
# approxPolyDP参数说明:
#
# InputArray curve:输入的点集
# OutputArray approxCurve:输出的点集,当前点集是能最小包容指定点集的。draw出来即是一个多边形;
# double epsilon:指定的精度,也即是原始曲线与近似曲线之间的最大距离。
# bool closed:若为true, 则说明近似曲线是闭合的,它的首位都是相连,反之,若为false,则断开。
# PS: findContours后的轮廓信息contours可能过于复杂不平滑,可以用approxPolyDP函数对该多边形曲线做适当近似。
epsilon = 0.01 * cv2.arcLength(cnt, True) #取轮廓周长的百分之一,epsilon 的值越大,得到的多边形顶点越少,多边形越简化。
approx = cv2.approxPolyDP(cnt, epsilon, True)
hull = cv2.convexHull(cnt) # 使用 cv2.convexHull(contour) 函数来获取轮廓的凸包,它是一个包含所有轮廓点的最小凸多边形。
cv2.drawContours(black, [cnt], -1, (0, 255, 0), 2) #绘制轮廓
cv2.drawContours(black, [approx], -1, (255, 0, 0), 2) # 绘制多边形
cv2.drawContours(black, [hull], -1, (0, 0, 255), 2) # 绘制凸包
#
cv2.namedWindow('Bounding Rectangles', cv2.WINDOW_NORMAL)
cv2.imshow('Bounding Rectangles', black)
cv2.waitKey(0)
cv2.destroyAllWindows()