OpenCV 4.11.0
开源计算机视觉库
加载中…
搜索中…
无匹配项
轮廓:入门

下一个教程: 轮廓特征

目标

什么是轮廓?

轮廓可以简单地解释为连接所有连续点(沿边界)的曲线,这些点具有相同的颜色或强度。轮廓是用于形状分析和对象检测与识别的有用工具。

  • 为了获得更高的精度,请使用二值图像。因此,在查找轮廓之前,请应用阈值或 Canny 边缘检测。
  • 从 OpenCV 3.2 开始,findContours() 不再修改源图像。
  • 在 OpenCV 中,查找轮廓就像从黑色背景中查找白色物体一样。因此请记住,要查找的对象应该是白色的,背景应该是黑色的。

让我们看看如何查找二值图像的轮廓

import numpy as np
import cv2 as cv
im = cv.imread('test.jpg')
assert im is not None, "文件无法读取,请使用 os.path.exists() 检查"
imgray = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(imgray, 127, 255, 0)
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
从文件中加载图像。
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0, AlgorithmHint hint=cv::ALGO_HINT_DEFAULT)
将图像从一种颜色空间转换为另一种颜色空间。
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)
将固定级别的阈值应用于每个数组元素。
void findContours(InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
在二值图像中查找轮廓。

请看,cv.findContours() 函数中有三个参数,第一个是源图像,第二个是轮廓检索模式,第三个是轮廓逼近方法。它输出轮廓和层次结构。轮廓是图像中所有轮廓的 Python 列表。每个单独的轮廓都是一个 Numpy 数组,包含对象边界点的 (x,y) 坐标。

注意
我们稍后将详细讨论第二个和第三个参数以及层次结构。在此之前,代码示例中给出的值将适用于所有图像。

如何绘制轮廓?

要绘制轮廓,可以使用 cv.drawContours 函数。如果您拥有其边界点,它也可以用于绘制任何形状。它的第一个参数是源图像,第二个参数是轮廓(应作为 Python 列表传递),第三个参数是轮廓的索引(在绘制单个轮廓时有用。要绘制所有轮廓,请传递 -1),其余参数是颜色、粗细等。

  • 要绘制图像中的所有轮廓
    cv.drawContours(img, contours, -1, (0,255,0), 3)
    void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar &color, int thickness=1, int lineType=LINE_8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point())
    绘制轮廓轮廓或填充轮廓。
  • 要绘制单个轮廓,例如第 4 个轮廓
    cv.drawContours(img, contours, 3, (0,255,0), 3)
  • 但大多数情况下,以下方法将很有用
    cnt = contours[4]
    cv.drawContours(img, [cnt], 0, (0,255,0), 3)
注意
最后两种方法相同,但是当您继续前进时,您会发现最后一种方法更有用。

轮廓逼近方法

这是 cv.findContours 函数中的第三个参数。它究竟表示什么?

上面,我们说过轮廓是具有相同强度的形状的边界。它存储形状边界的 (x,y) 坐标。但是它是否存储所有坐标?这是由这种轮廓逼近方法指定的。

如果您传递 cv.CHAIN_APPROX_NONE,则会存储所有边界点。但是实际上我们需要所有点吗?例如,您找到了直线的轮廓。您是否需要直线上的所有点来表示该直线?不需要,我们只需要该直线的两个端点。这就是 cv.CHAIN_APPROX_SIMPLE 所做的。它删除所有冗余点并压缩轮廓,从而节省内存。

下面的矩形图像演示了此技术。只需在轮廓数组中的所有坐标上绘制一个圆圈(以蓝色绘制)。第一张图像显示了我使用 cv.CHAIN_APPROX_NONE(734 个点)获得的点,第二张图像显示了使用 cv.CHAIN_APPROX_SIMPLE(只有 4 个点)获得的点。看看它节省了多少内存!!!

图像