1.认识轮廓
1.1 目标
- 理解什么是轮廓
- 学习掌握找轮廓、绘制轮廓等
- 学习使用cv2.findContours()、cv2.drawContours()函数的用法
1.2 什么是轮廓
在OpenCV中,轮廓是图像中连续的边界线的曲线,具有相同的颜色或者灰度,用于表示物体的形状。轮廓在图像处理和计算机视觉中非常重要,常用于物体检测、形状分析、图像分割等任务。
提示:
- 为了使轮廓更加准确,要使用二值化图像。所以,在寻找轮之前,要进行阈值化处理或者Canny边界检测。
- 查找轮廓的函数会修改原始图像。如果你在找到轮廓之后想使用原始图像的话,你应该将原始图像存储到其他变量中。
- 在OpenCV 中,查找廓就像在黑色背景中查找白色物体。你应该记住,要找的物体应该是白色而背景应该是黑色。
在OpenCV中,可以通过以下步骤找到图像中的轮廓:
- 对图像进行预处理,如灰度化、二值化等操作。
- 使用**
cv2.findContours()
**函数找到图像中的轮廓。该函数会返回一个包含轮廓信息的列表。 - 遍历轮廓列表,可以使用**
cv2.drawContours()
**函数将轮廓绘制到图像上。 - 对轮廓进行进一步的分析和操作,如计算轮廓的面积、周长,寻找轮廓的凸包等。
**cv2.findContours()
**是一个用于查找图像中轮廓的函数。它的语法如下:
contours, hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
参数说明:
image
:输入的二值化图像,通常为灰度图像或二值图像。mode
:轮廓检索模式,指定轮廓的层级结构。常用的取值有:cv2.RETR_EXTERNAL
:只检测最外层的轮廓。cv2.RETR_LIST
:检测所有的轮廓,不建立层级关系。cv2.RETR_CCOMP
:检测所有的轮廓,并将它们组织为两层的层级结构。cv2.RETR_TREE
:检测所有的轮廓,并完整地重建轮廓之间的层级关系。
method
:轮廓近似方法。常用的取值有:cv2.CHAIN_APPROX_NONE
:保存所有的轮廓点。cv2.CHAIN_APPROX_SIMPLE
:压缩水平、垂直和对角线方向上的轮廓,仅保留终点。
contours
:输出的轮廓列表,每个轮廓由一系列点组成。hierarchy
:可选输出的层级关系,用于表示轮廓之间的层级关系。offset
:可选的偏移量,用于调整轮廓的位置。
4.0以上的版本cv2.findContours()
函数会返回两个值(OpenCV 3.0系列版本会返回3个值,多出的第一个值是图像),分别是轮廓列表和层级关系。轮廓列表是一个包含每个轮廓的Numpy数组,每个数组中的元素表示轮廓上的一个点,包含对边界点(x,y)的坐标。层级关系是一个包含每个轮廓的层级关系信息的Numpy数组,用于表示轮廓之间的层级关系,可用于进一步分析轮廓的形状和结构。以下是一个使用cv2.findContours()
函数查找轮廓的示例代码:
import cv2
# 读取图像
image = cv2.imread("image.jpg")
# 灰度化
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 二值化
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 寻找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 三个参数的返回
'''
_,contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
'''
# 绘制轮廓
cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
# 显示图像
cv2.imshow("Contours", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
代码中,首先读取了一张图像,并对其进行了灰度化和二值化处理。然后使用**cv2.findContours()
**函数找到图像中的轮廓,并将其绘制到原始图像上。最后显示图像并等待按键关闭窗口。
1.3 怎样绘制轮廓
**cv2.drawContours()**是一个用于绘制轮廓的函数,它可以根据你提供的边界点绘制任何形状。它的语法如下:
cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])
参数说明:
image
:要绘制轮廓的图像。contours
:轮廓列表,即cv2.findContours()
函数返回的轮廓列表。contourIdx
:要绘制的轮廓的索引,绘制独立轮廓时很有用,-1表示绘制所有轮廓。color
:绘制轮廓的颜色,可以是一个BGR元组或一个整数。thickness
:轮廓线的粗细,默认为1。lineType
:轮廓线的类型,默认为cv2.LINE_8
,表示8连通线。hierarchy
:层级关系,即cv2.findContours()
函数返回的层级关系。maxLevel
:绘制轮廓的最大层级,默认为0,表示只绘制当前层级的轮廓。offset
:可选的偏移量,用于调整轮廓的位置。
cv2.drawContours()
函数用于在图像上绘制轮廓。可以通过设置contourIdx
参数来指定要绘制的轮廓的索引,-1表示绘制所有轮廓。可以通过设置color
参数来指定绘制轮廓的颜色。绘制的轮廓线的粗细可以通过thickness
参数进行设置,默认为1。轮廓线的类型可以通过lineType
参数进行设置,默认为cv2.LINE_8
,表示画8连通线。
以下是一个使用cv2.drawContours()
函数绘制轮廓的示例代码:
import cv2
# 读取图像
image = cv2.imread("image.jpg")
# 灰度化
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 二值化
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 寻找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓
cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
# 显示图像
cv2.imshow("Contours", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
在上述代码中,首先读取了一张图像,并对其进行了灰度化和二值化处理。然后使用cv2.findContours()
函数找到图像中的轮廓,并使用cv2.drawContours()
函数将轮廓绘制到原始图像上。最后显示图像并等待按键关闭窗口。
绘制独立的廓,如第四个轮廓:
img = cv2.drawContour(img, contours, -1, (0,255,0), 3)
但是大多数时候下面的方法更有用:
img = cv2.drawContours(img, contours, 3, (0,255,0), 3)
**注意:**最后这两种方法结果是一样的,但是后面的知识会告诉你最后一种方法更有用。
1.4 轮廓的近似方法
上面查找轮廓的时候,提到了是函数cv2.findCountours() 有一个轮廓的近似方法参数,那么它到底代表什么意思呢?
上面我们已经提到轮廓是一个形状具有相同灰度值的边界。它会存储形状边界上所有的(x,y)坐标。但是,需要将所有的这些边界点都存储吗?这就是这个参数要告诉函数cv2.findContours 的。
这个参数如果被设置为cv2.CHAIN_APPROX_NONE,所有的边界点都会被存储。但是我们真的需要这么么多点吗?例如,当我们找的边界是一条直线时。你用需要把直线上所有的点来表示直线吗?不是的,我们只需要这条直线的两个端点而已。这就是cv2.CHAIN_APPROX_SIMPLE 要做的。它会将将廓上的冗余点去掉,压缩轮廓,从而节省内存开支。
我们用下图中的矩形来演示这个技术。在轮廓列表中的每一个坐标上画一个蓝色圆圈。第一个图显示使用cv2.CHAIN_APPROX_NONE 的效果,一共734 个点。第二个图是使用cv2.CHAIN_APPROX_SIMPLE 的结果,只有4 个点。看到他的威力了吧: