《OpenCV 轻松入门 面向Python》 学习笔记
- 边缘:上一章学习的边缘检测。边缘检测虽能过检测出边缘,但边缘不是连续的,检测到的边缘并不是一个整体。
- 轮廓:图像轮廓是指将边缘连接起来形成一个整体,用于后续的计算。
opencv 提供了查找图像轮廓的函数 cv2.findContours(),和 绘制图像轮廓的函数cv2.drawContours()
查找图像轮廓和绘制图像轮廓
查找图像轮廓 cv2.findContours()
函数原型:
image, contours, hierarchy = cv2.findContours(image, mode, method)
返回值:
- image:与函数参数中的原始图像image一致。返回的轮廓
- contours:返回的轮廓
- hierarchy:图像的拓扑信息(轮廓层次)
参数:
- image:原始图像, 8位单通道图像。所有非0值被处理为1,所有0值保持不变。也就是说灰度图像会自动被处理成二值图像。
- mode:轮廓的检索模式
- method:轮廓的近似方法
函数cv2.findContours()的返回值及参数的含义比较丰富,下面逐一作出说明。
1. 返回值 image
该返回值与参数image是一样的,就是原始输入图像。在openCV 4.X中,该返回值已经被取消,只有contours, hierarchy两个返回值。函数格式为:
contours, hierarchy = cv2.findContours(image, mode, method)
2. 返回值 contours
返回值 contours 返回的是一组轮廓信息,包含:
- n 个轮廓:第i个轮廓表示为 contours[i],数据类型是 list
- 以及每个轮廓由若干个点构成:第i个轮廓内的第j个点表示为 contours[i][j] , 数据类型是ndarray。
比如如下图像:
# 轮廓的数据类型
print(type(contours)) # <class 'list'>
# 轮廓的个数
print(len(contours)) # 3
# 轮廓中每个轮廓元素的数据类型
print(type(contours[0])) # <class 'lumpy. nndarray'>
# 组成轮廓的点的个数
print(len(contours[0])) # 4
print(len(contours[1])) # 60
print(len(contours[2])) # 184
print(contours[0].shape) # (4, 1, 2)
print(contours[0].shape) # (60, 1, 2)
print(contours[0].shape) # (184, 1, 2)
# 轮廓内的点
print(contours[0])
# [[[79, 270]]
# [[79, 383]]
# [[195, 383]]
# [[195, 270]]]
3. 返回值 hierarchy
图像的轮廓可能位于不同的位置,若果一个轮廓位于另一个轮廓的内部,我们就称外部的轮廓为父轮廓, 里面的轮廓为子轮廓。这种关系被称为层次。
每个轮廓(contours[i])对应4个元素来说明当前轮廓的层次关系:[Nest, Previous, First_Child, Parent]
Nest:后一个轮廓的索引编号
Previous:前一个轮廓的索引编号
First_Child:第一个字轮廓的索引编号
Parent:父轮廓的索引编号
如果上述各个参数所对应的关系为空时, 也就是没有对应关系时,用 -1 表示
使用打印语句 print(hierarchy) 查看hierarchy 的值。
轮廓的层次结构是由参数mode决定的,也就是说,使用不同的mode,得到的轮廓编号是不一样的,得到的hierarchy也是不一样的。
4. 参数 image
输入图像,其必须是8位单通道灰度二值图。一般情况下,都要预先对图像进行阈值分割或者边缘检测处理,得到满意的二值图像后,再将其作为参数使用。
5. 参数 mode
参数 mode 决定了轮廓的提取方式:
- cv2.RETR_EXTERNAL:只检测外轮廓。
- cv2.RETR_LIST:对检测到的外轮廓不建立等级关系
- cv2.CCOMP:检索所有轮廓,并将他们组织成两级层次结构。
- cv2.Tree:建立一个等级树结构的轮廓
6. 参数 method
参数 method 决定了如何表达轮廓:
- cv2.CHAIN_APPROX_NONE:存储所有的轮廓点
- cv2.CHAIN_APPROX_SIMPLE:压缩水平,垂直和对角线段,只留下端点。 例如矩形轮廓可以用4个点编码。
- cv2.CHAIN_APPROX_TC89_L1, cv2.CHAIN_APPROX_TC89_KCOS: 使用Teh-Chini chain近似算法
7. 注意:
(1) 待处理的原图像必须是灰度二值图。一般情况下,都要预先对图像进行阈值分割或者边缘检测处理,得到满意的二值图像后,再将其作为参数使用。
(2)在opencv中,都是从黑色背景中查找白色对象。对象必须是白的,背景必须是黑的。
(3)在openCV 4.X中,返回值image已经被取消,只有contours, hierarchy两个返回值。函数格式为:contours, hierarchy = cv2.findContours(image, mode, method)
绘制图像轮廓 cv2.drawContours()
函数原型:
image = cv2.drawContours(image,
contours,
contourIdx,
color,
thickness,
lineType,
hierarchy,
maxLevel,
offset)
参数:
- image:原图像。cv2.drawContours() 函数会直接在原图像上绘制轮廓,也就是说会改变原图。如果图像image还有其他用途的话,则需要预先复制一份,将该副本图像传递给 cv2.drawContours() 函数使用。
- 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表示只绘制contourIdx指定的轮廓,等于1表示绘制contourIdx指定轮廓及其下一级子轮廓,等于2表示绘制contourIdx指定轮廓及其所有子轮廓
- offset:(可选参数)轮廓点的偏移量
举例:
import cv2
o = cv2.imread("/Users/manmi/Desktop/contours.bmp")
cv2.imshow('original_image', o)
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
threshold, binary_img = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
image, contours, hierrarchy = cv2.findContours(binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
o = cv2.drawContours(o, contours, -1, (0, 0, 255), 5)
cv2.imshow('contours_image', o)
cv2.waitKey()
cv2.destroyAllWindows()
输出: