OpenCV  4.10.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, "file could not be read, check with 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)
从文件中加载图像。
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0)
将图像从一个颜色空间转换成另一个颜色空间。
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 个点)获得的点。看看这节省了多少内存!!!

图像

其他资源

练习