OpenCV-Python接口中使用cv2.findContours()函数来查找检测物体的轮廓。
cv2.findContours(image, mode, method, contours=None, hierarchy=None, offset=None)
参数 | 描述 | 返回 |
image | 寻找轮廓的图像,注意输入的图片必须为二值图片。若输入的图片为彩色图片,必须先进行灰度化和二值化 | opencv2返回两个值:一个是轮廓本身contours,还有一个是每条轮廓对应的属性hierarchy。 opencv3返回三个值:img, countours, hierarchy |
mode | 轮廓的检索模式,有4种 | |
method | 轮廓的近似办法,有4种 | |
contours | 使用findContours检测到的轮廓数据,每个轮廓以点向量的形式存储,point类型的vector | |
hierarchy | 可选层次结构信息 | |
offset | 可选轮廓偏移参数,用制定偏移量offset=(dx, dy)给出绘制轮廓的偏移量 |
mode | 描述 |
cv2.RETR_EXTERNAL | 只检测外轮廓 |
cv2.RETR_LIST | 提取所有轮廓,并放置在list中,检测的轮廓不建立等级关系 |
cv2.RETR_CCOMP | 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。 |
cv2.RETR_TREE | 检测所有轮廓,建立完整的层次结构,建立网状轮廓结构 |
method | 描述 |
cv2.CHAIN_APPROX_NONE | 获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1 |
cv2.CHAIN_APPROX_SIMPLE | 压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息 |
cv2.CHAIN_APPROX_TC89_L1 | 使用Teh-Chini chain近似算法 |
cv2.CHAIN_APPROX_TC89_KCOS | 使用Teh-Chini chain近似算法 |
cv2.findContours()函数首先返回一个list,list中每个元素都是图像中的一个轮廓信息,list中每个元素(轮廓信息)类型为ndarray。len(contours[1]) 表示第一个轮廓储存的元素个数,即该轮廓中储存的点的个数。
该函数还可返回一个可选的hiararchy结果,这是一个ndarray,其中的元素个数和轮廓个数相同,每个轮廓contours[i]对应4个hierarchy元素hierarchy[i][0] ~hierarchy[i][3],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为-1。
img:
import cv2
img = cv2.imread('../../DAVIS-2016/Annotations/1080p/flamingo/00007.png', 0)
img = cv2.resize(img, (16, 16))
returns = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print(len(returns))
print(type(returns))
# results
3
可以看出,returns的长度为3, 说明returns接受3个值,这是由于opencv3版本中的cv2.findContours()函数的返回了3个值,都是元组类型,故以一个参数去接收,那么就会接收3个值。(opencv2版本中只返回两个值)。
以两个参数去接收返回值,会出错。
import cv2
img = cv2.imread('../../DAVIS-2016/Annotations/1080p/flamingo/00007.png', 0)
img = cv2.resize(img, (16, 16)) # 为了方便显示,调整下尺寸
# print(img.shape)
return1, return2 = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# print(returns)
# print(len(returns))
# print(type(returns))
# results
ValueError: too many values to unpack (expected 2)
可以看出,代码执行错误。ValueError: too many values to unpack (expected 2)
import cv2
img = cv2.imread('../../DAVIS-2016/Annotations/1080p/flamingo/00007.png', 0)
img = cv2.resize(img, (16, 16))
# print(img.shape)
image, contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print(image)
print(contours)
print(hierarchy)
输出结果:
[[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 255 255 0 255 255 0 0 0]
[ 0 0 0 0 0 0 0 255 255 255 0 0 0 0 0 0]
[ 0 0 0 0 0 0 255 255 255 0 255 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
[array([[[ 8, 10]]], dtype=int32), array([[[11, 5]],
[[12, 5]]], dtype=int32), array([[[ 9, 4]],
[[ 6, 7]],
[[ 8, 7]],
[[ 9, 6]],
[[10, 7]],
[[ 9, 6]]], dtype=int32)]
[[[ 1 -1 -1 -1]
[ 2 0 -1 -1]
[-1 1 -1 -1]]]
print(type(contours)) # 输出为:
print(type(contours[0])) # 输出为:
print(len(contours)) # 图像中轮廓的数量,输出为:3
print(len(contours[0])) # 轮廓0中的元素(点)的数量,输出为:1
# results
3
1
print(hierarchy)
# results
[[[ 1 -1 -1 -1]
[ 2 0 -1 -1]
[-1 1 -1 -1]]]
具体意义不做解释,可查看1.2,自行理解。
OpenCV中通过cv2.drawContours在图像上绘制轮廓。
cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None)
参数 | 描述 | 返回值 |
image | 需要绘制轮廓的目标图像,注意会改变原图 | 绘制的图像array |
contours | 轮廓点,上述函数cv2.findContours()的第一个返回值 | |
contourIdx | 轮廓的索引,表示绘制第几个轮廓,-1表示绘制所有的轮廓 | |
color | 绘制轮廓的颜色 | |
thickness | (可选参数)轮廓线的宽度,-1表示填充 | |
lineType | 可选参数)轮廓线型,包括cv2.LINE_4,cv2.LINE_8(默认),cv2.LINE_AA,分别表示4邻域线,8领域线,抗锯齿线(可以更好地显示曲线) | |
hierarchy | (可选参数)层级结构,上述函数cv2.findContours()的第二个返回值,配合maxLevel参数使用 | |
maxLevel | (可选参数)等于0表示只绘制指定的轮廓,等于1表示绘制指定轮廓及其下一级子轮廓,等于2表示绘制指定轮廓及其所有子轮廓 | |
offset | (可选参数)轮廓点的偏移量 |
import cv2
import numpy as np
# 读入的为二值图
img = cv2.imread('../../DAVIS-2016/Annotations/1080p/flamingo/00007.png')
img = cv2.resize(img, (224, 224))
# 灰度图
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 获取轮廓
image, contours, hierarchy = cv2.findContours(img_gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 创建白色幕布,画图
temp = np.ones(img_gray.shape, np.uint8) * 255
# 画轮廓:temp为白色幕布,contours为轮廓, -1表示画所有轮廓,颜色:绿色,厚度
cv2.drawContours(temp, contours, -1, (0, 255, 0), 3)
cv2.imshow('pic', img)
cv2.waitKey()
cv2.imshow('contour', temp)
cv2.waitKey()
结果:
原图:
import cv2
import numpy as np
# 读入RGB图像图像
img = cv2.imread('../../DAVIS-2016/JPEGImages/1080p/flamingo/00007.jpg')
img = cv2.resize(img, (224, 224))
# 灰度化
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化
ret, binary = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
# 获取轮廓
image, contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 白色幕布
temp = np.ones(img_gray.shape, np.uint8) * 255
# 绘制轮廓,在白色幕布上绘制,也可以在原图上绘制
cv2.drawContours(temp, contours, -1, (0, 0, 255), 3)
cv2.imshow('img', img) # 原图
cv2.waitKey()
cv2.imshow('gray', img_gray) # 灰度图
cv2.waitKey()
cv2.imshow('binary', binary) # 二值图
cv2.waitKey()
cv2.imshow('result', temp)
cv2.waitKey()
结果:
参考: