OpenCV 4.12.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 列表。 每个单独的轮廓都是对象边界点的 (x,y) 坐标的 Numpy 数组。

注意
稍后我们将详细讨论第二个和第三个参数以及层级。 在此之前,代码示例中给它们的值对于所有图像都可以正常工作。

如何绘制轮廓?

要绘制轮廓,请使用 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 个点)得到的点。 看看它节省了多少内存!!!

image