目录
1 什么是轮廓
轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同 的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。
- 为了更加准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理 或者 Canny 边界检测。
- 查找轮廓的函数会修改原始图像。如果你在找到轮廓之后还想使用原始图 像的话,你应该将原始图像存储到其他变量中。
- 在 OpenCV 中,查找轮廓就像在黑色背景中超白色物体。你应该记住, 要找的物体应该是白色而背景应该是黑色。
让我们看看如何在一个二值图像中查找轮廓:
cv2.findContours(image, mode, method, contours, hierachy, offset)
- image:输入图像,单通道,可以是灰度图,但更常用二值图
- mode:定义轮廓的检索模式,共四种,可参考https://blog.csdn.net/yukinoai/article/details/87900420
RETR_EXTERNAL:只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
RETR_LIST:检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,
没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,
所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1
RETR_CCOMP:检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,
若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层。
两个等级关系:顶层为连通域的外围边界,次层为孔的内层边界。
RETR_TREE:检测所有轮廓,所有轮廓建立一个等级树结构。
外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
- method:定义轮廓的近似方法
CHAIN_APPROX_NONE :保存物体边界上所有连续的轮廓点到contours向量内
CHAIN_APPROX_SIMPLE :仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,
拐点与拐点之间直线段上的信息点不予保留
CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法
CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近似算法
- contours:轮廓,是一个 Python 列表,其中存储这图像中的所有轮廓。每一个轮廓都是一个 Numpy 数组,包 含对象边界点(x,y)的坐标。
- hierachy:层析结构,后面再讲
- offset:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且Point还可以是负值!
使用方法,例如:
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
2 怎样绘制轮廓
函数 cv2.drawContours() 可以被用来绘制轮廓。它可以根据你提供的边界点绘制任何形状。
cv2.drawContours(image, contours, contourIdx, color, thickness, lineType, hierarchy, maxLevel, offset)
- image:输入图像
- contours:轮廓列表
- contourIdx:指定要绘制轮廓的编号,如果是负数,则绘制所有的轮廓
- color:轮廓颜色
- thickness:轮廓线宽
- lineType:轮廓线型
- hierarchy:层级
- maxLevel:绘制轮廓的最高级别,这个参数只有hierarchy有效的时候才有效
//maxLevel=0,绘制与输入轮廓属于同一等级的所有轮廓即输入轮廓和与其相邻的轮廓
//maxLevel=1, 绘制与输入轮廓同一等级的所有轮廓与其子节点。
//maxLevel=2,绘制与输入轮廓同一等级的所有轮廓与其子节点以及子节点的子节点
- offset:偏置
例如:
import cv2
import numpy as np
from matplotlib import pyplot as plt
src = cv2.imread('test21_1.jpg')
img = src.copy()
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
draw = cv2.drawContours(img, cnt, -1, (0, 255, 0), 10)
plt.subplot(121), plt.imshow(cv2.cvtColor(src, cv2.COLOR_BGR2RGB)), plt.title('Src')
plt.subplot(122), plt.imshow(cv2.cvtColor(draw, cv2.COLOR_BGR2RGB)), plt.title('image')
plt.show()
结果如下:
method如果被设置为 cv2.CHAIN_APPROX_NONE,所有的边界点都会被存储。但是我们真的需要这么多点吗?例如,当我们找的边界是一条直线时。你用需要直线上所有的点来表示直线吗?不是的,我们只需要这条直线 的两个端点而已。这就是 cv2.CHAIN_APPROX_SIMPLE 要做的。它会102将轮廓上的冗余点都去掉,压缩轮廓,从而节省内存开支。如下所示