什么是轮廓?

轮廓可以简单地解释为一条连接所有连续点 (沿着边界)并具有相同颜色或强度的曲线。轮廓是形状分析和目标检测与识别的重要工具。

  • 为了更好的准确性,我们将使用二进制图像。因此,在找到轮廓之前,我们需要应用阈值或canny边缘检测。
  • findContours 函数可以用来修改源图像。如果我们在找到轮廓之后还想要源图像,要确保在使用函数前,把原图像信息存储到其他变量中。
  • 在OpenCV 中,寻找轮廓就像从黑色背景中寻找白色的物体。记住,要找的对象应该是白色的,而背景应该是黑色的。

让我们看看下面的代码如何找到一个二进制图像的轮廓:


# 导入包
import numpy as np
import cv2

# 读取图片
im = cv2.imread('test.jpg')
# 转换成灰度图像
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
# 设置阈值
ret,thresh = cv2.threshold(imgray,127,255,0)
# 用函数找到图像的轮廓,下面会详介
image, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)


从上面的代码可见,cv2.findContours() 函数中有三个参数,第一个是源图像,第二个是轮廓的检索模式,第三个是轮廓的近似方法。它依次输出图像,轮廓和层次结构。这里的contours 轮廓是图像中所有轮廓的Python 列表。每个单独的轮廓是物体的边界点的坐标(x,y) 的Numpy 数组。

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

如何画轮廓?

如果想画出轮廓,cv2.drawContours 函数就得使用。只要我们有任何想画的形状的边界点,该函数也可以用来绘制任何我们想要的形状。它的第一个参数是源图像,第二个参数是应该作为Python 列表传递的轮廓,第三个参数是轮廓的索引(这在我们绘制单个轮廓时很有用,如果要绘制所有的轮廓,传递-1即可),而剩余的参数则是颜色,厚度等。

在图像中画出所有的轮廓的代码:


img = cv2.drawContours(img, contours, -1, (0,255,0), 3) # 这里的-1 是关键,说明我们要全部的轮廓


如果我们只想要单独特定的轮廓,比如说第4个轮廓,代码如下:


img = cv2.drawContours(img, contours, 3, (0,255,0), 3) # 这里的3 是关键,因为计算机中0是第一个,3就代表第四个


但是多数情况,下面的代码是很有帮助的,代码如下:


cnt = contours[4]
img = cv2.drawContours(img, [cnt], 0, (0,255,0), 3)


请注意:最后两个方法的效果是相同的,但是当我们继续学下去之后,就将看到最后一个方法更有用。

轮廓近似法

轮廓近似法 Contour Approximation Method,这是cv2.findContours 函数中的第三个参数。但它实际上表示什么呢?

在文章前面,我们说过轮廓是具有相同强度的形状的边界。它存储形状边界的(x,y) 坐标。但是它是否能存储所有的坐标?这则是由轮廓近似法确定的。

如果我们在近似法中传递了cv2.CHAIN_APPROX_NONE(我们在第一个代码中所使用的),那么函数将存储所有边界点。但实际上我们经常需要所有的点吗?例如,当我们想了解一条直线的轮廓,我们需要这条直线上的所有点来表示这条直线吗?显然不是,我们只需要这条线的两个端点即可。这就是cv2.CHAIN_APPROX_SIMPLE 所表达的。它将去除所有冗余点,压缩轮廓,以此来节省内存。

下面的矩形图像演示了这种技术的原理。在等高线阵列的所有坐标上画一个小圆(用蓝色画,所有的小圆连起来就是线),第一幅图展示了我用cv2.CHAIN_APPROX_NONE(734个点) 得到的所有的点,第二张图显示的是用cv2.CHAIN_APPROX_SIMPLE(只有4个点) 得到的图像。很明显,它节省了很多内存!!!


opencv图像右侧拼接一块 opencv 两幅图像相减_opencv两个图像相减

734个点 VS 4个点