本教程将学习如下内容:
- 了解轮廓是什么
- 学习寻找轮廓、绘制轮廓等
- 介绍两个关键函数:
cv2.findContours()
、cv2.drawContours()
理论基础
轮廓可以简单地解释为连接所有连续点(沿边界)的曲线,具有相同的颜色或强度。 轮廓是形状分析和物体检测与识别的有用工具。
- 为了获得更好的准确性,请使用二值图像。因此在找到轮廓之前,应用阈值或精明的边缘检测。
findContours
函数修改源图像。如果想在找到轮廓后得到源图像,就已经将其存储到其他变量中了。- 在OpenCV中,查找轮廓就像从黑色背景中查找白色对象。所以记住,要找到的对象应该是白色的,背景应该是黑色的。
接下来看一下如何找到二值图像的轮廓:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import cv2
# im = cv2.imread('test.jpg')
im = cv2.imread('/data/cvdata/messi5.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)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
# image, contours = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.findContours()
出现错误:
ValueError: not enough values to unpack (expected 3, got 2)
出现错误原因:如果用的是cv 4.0版本,findContours
返回的是两个参数,旧版的返回的则是三个参数。
解决方法:移除第一个参数赋值:
将 image, cnts, hierarchy = cv2.findContours(diff.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
改为 cnts, hierarchy = cv2.findContours(diff.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
。
cv2.findContours()
函数中有三个参数,第一个是源图像,第二个是轮廓检索模式,第三个是轮廓近似方法。
并输出图像、轮廓和层次结构。轮廓是图像中所有轮廓的Python列表。
每个单独的轮廓都是对象边界点的(x,y)坐标的Numpy数组。
注意: 稍后我们将详细讨论第二和第三个论点以及层次结构。在此之前,代码示例中给出的值对所有图像都适用。
如何绘制轮廓
要绘制轮廓,请使用cv2.drawContours
函数。
只要有它的边界点,也可以用来绘制任何形状。
第一个参数是源图像,第二个参数是应该作为Python列表传递的轮廓,
第三个参数是轮廓索引(在绘制单个轮廓时很有用。要绘制所有轮廓,请传递 -1
),其余参数是颜色、厚度等。
要绘制图像中的所有轮廓,请执行以下操作:
img = cv2.drawContours(im, contours, -1, (0,255,0), 3)
plt.imshow(img)
<matplotlib.image.AxesImage at 0x7fd47468eb70>
要绘制单个轮廓,假设第四个轮廓:
img = cv2.drawContours(im, contours, 3, (0,255,0), 3)
plt.imshow(img)
<matplotlib.image.AxesImage at 0x7fd47468d3a0>
但大多数时候,以下方法会很有用:
cnt = contours[4]
img = cv2.drawContours(im, [cnt], 0, (0,255,0), 3)
plt.imshow(img)
<matplotlib.image.AxesImage at 0x7fd47456b6b0>
注意: 最后两种方法是相同的,但当实际应用时会发现最后一种方法更实用。
轮廓近似法
这是cv2.findContours
函数中的第三个参数。它实际上意味着什么?
轮廓是由相同强度像素构成的形状边界,通常以一系列 (x,y) 坐标点的形式存储。
是否存储所有边界点取决于所选的轮廓近似方法:使用cv2.CHAIN_APPROX_NONE
会保留完整轮廓的所有点,
但在实际应用中,我们往往只需要能够表征形状特征的关键点而非全部坐标点。
在轮廓检测中,如果只需要用一条直线的两个端点来表示该直线,而不是存储所有中间点,可以使用 cv2.CHAIN_APPROX_SIMPLE
方法。
它会压缩轮廓,删除冗余的中间点,仅保留关键点(如直线的起点和终点),从而减少内存占用并提高计算效率。
下面矩形图像的示例直观展示了不同轮廓近似方法的效果。
我们在轮廓的所有坐标点上绘制蓝色圆点进行可视化:第一张图使用cv2.CHAIN_APPROX_NONE
方法检测到734个点,
而第二张图采用cv2.CHAIN_APPROX_SIMPLE
方法仅保留了4个关键点(矩形的四个角点)。
通过对比可以看出,后者大幅减少了97%以上的数据存储量,显著优化了内存使用效率。