图像轮廓(一)
0 引言
🍔小小搬运工,一边搬运一边思考
参考书籍 《OpenCV轻松入门——面向Python李立宗著,电子工业出版社出版
边缘检测虽然能够检测出轮廓,但边缘是不连续的,检测到的边缘并不是一个整体。图像轮廓是指将边缘连接起来形成的整体,用于后续的计算。
OpenCV提供了查找轮廓的函数cv2.findContours(),该函数能够查找图像轮廓内的信息,而函数cv2.drawContours()函数能够将轮廓绘制出来。
图像轮廓是图像中非常重要的一个特征信息,通过对图像轮廓的操作,我们能够获取目标图像的大小、位置、方向等信息。
1 查找并绘制轮廓
1.1 查找图像轮廓:findContours函数
函数findContours用于查找图像的轮廓,并能够根据参数返回特定方式的轮廓。其语法格式为:
image,contours,hierarchy = cv2.fingContours(image,mode,method)
# 返回值image:与函数参数中的原始图像image一致
# 返回值contours:返回的轮廓
# 返回值hierarchy:图像的拓普信息(轮廓层次)
# 参数image:原始图像。8位单通道图像,所有非零值被处理为1,所有零值保持不变。也就是说灰度图像会被自动处理成二值图像。在实际操作中,可以根据需要,预先使用阈值处理等函数,将待查找轮廓的图像处理为二值图像。
# mode:轮廓检索方式
# method:轮廓的近似方法
1、返回值image
在OpenCV 4.X中,函数cv2.findContours()仅有两个返回值,其语法格式为:
contours,hierarchy = cv2.findContours(image,mode,method)
2、返回值contours
该返回值返回的是一组轮廓信息,每个轮廓都是由若干个点所构成的。例如,contour是[i]是第i个轮廓(下标从0开始),contours[i][j]是第i个轮廓内的第j个点。
(1)type属性
print(type(contours)) # 获取轮廓contours的类型
print(type(contours[0])) # 获取轮廓contours中每个元素的类型
(2)轮廓的个数
print(len(contours)) # 获取轮廓的个数
(3)每个轮廓的点数
print(len(contours[0])) # 打印第0个轮廓的长度(点的个数)
print(contours[0].shape) # 获取轮廓每个轮廓内点的shape属性
(4)轮廓内的点
print (contours[0]) # 打印第0个轮廓中的像素点
3、返回值hierarchy
图像内的轮廓位于不同的位置,将外部的轮廓称为父轮廓,内部的轮廓称为子轮廓。这样,一幅图像的轮廓就建立了父子关系。上述关系就被称为层次(组织结构)。
print(hierarchy) # 查看hierarchy的值,轮廓的层次结构由参数mode决定
[Next,Previous,First_Child,Parant] # 四个元素说明当前轮廓的层次关系
# Next:后一个轮廓的索引编号
# Previous:前一个轮廓的索引编号
# First_Child:第1个子轮廓的索引编号
# Parant:父轮廓的索引编号
如果上述参数所对应的关系为空时,则将该参数所对应的值设为"1"。
4、参数image
原始图像。8位单通道图像,所有非零值被处理为1,所有零值保持不变。也就是说灰度图像会被自动处理成二值图像。在实际操作中,可以根据需要,预先使用阈值处理等函数,将待查找轮廓的图像处理为二值图像。
5、参数mode
参数mode决定了轮廓的提取方式,具体有如下4种:
(1)cv2.RETR_EXTERNAL:只检测外轮廓
(2)cv2.RETR_LIST:对检测到的轮廓不建立等级关系
(3)cv2.RETR_CCOMP:检索所有轮廓并将它们组织成两级层次结构
(4)cv2.RETR_TREE:建立一个等级树的结构
6、参数method
参数method决定了如何表达轮廓,具体如下:
(1)cv2.CHAIN_APPROX_NONE:存储所有的轮廓点,相邻两个像素位置差不超过1。
(2)cv2.CHAIN_APPROX_SIMPLE:压缩水平方向、垂直方向、对角线方向的元素,只保留该方向的终点坐标。例如,在极端的情况下,一个矩形只需要用4个点来保存轮廓信息。
(3)cv2.CHAIN_APPROX_TC89_L1:使用teh-Chinl chain近似算法的一种风格。
(4)cv2.CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain近似算法的一种风格。
注意:
待处理的源图像必须是灰度二值图。因此,在通常情况下,都要预先对图像进行阈值分割或者边缘检测,得到满意的二值图像后再将其作为参数使用。
在OpenCV中,都是从黑色背景中查找白色对象。因此,对象必须是白色的,背景必须是黑色的。
1.2 绘制图像轮廓:drawContours函数
在OpenCV中,可以使用函数cv2.drawContours()绘制图像轮廓。该函数的语法格式是:
image = cv2.drawContours(
image,
contours,
contourIdx,
color[,
thickness[,
lineType[,
hierarchy[,
maxLevel[,
offset]]]]])
# 返回值image表示目标图像,即绘制了边缘的原始图像。
# 参数image:待绘制轮廓的图像。函数cv2.drawContours会在图像image上直接绘制轮廓,若有其他用途,需要预先复制一份,将该副本图像传递给函数cv2.drawContours()使用。
# 参数contours:需要绘制的轮廓。该参数的类型与函数cv2.findContours()的输出contours相同,都是list类型。
# 参数contourIdx:需要绘制的边缘索引,告诉函数cv2.drawContours()要绘制某一条轮廓还是全部轮廓。如果该参数是一个整数或者为0,则表示绘制对应索引号的轮廓;如果该值为负值(-1),则表示绘制全部轮廓。
# 参数color:绘制的颜色,用BGR格式表示。
# thickness:可选参数,表示绘制轮廓时所用画笔的粗细,如将该值设置为"-1",则表示要绘制实心轮廓。
# lineType:可选参数,表示绘制轮廓时所用的线型。
# hierarchy:对应函数cv2.findContours()所输出的层次信息。
# maxLevel:控制所控制的轮廓层次的深度。如果值为0,表示仅仅绘制第0层的轮廓;如果值为其他的非零正数,表示绘制最高层及以下的相同数量层级的轮廓。
# offset: 偏移参数。该参数使轮廓偏移到不同的位置展示出来。
1.3 轮廓实例
例1:绘制一幅图像内的所有轮廓。
将cv2.drawContours()的参数contourIdx的值设置为“-1”。
import cv2
o = cv2.imread("C:\\Users\\Kris\\Desktop\\timg.jpg")
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours,hierarchy = cv2.findContours(binary,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # 根据版本确定是否返回image
o = cv2.drawContours(o,contours,-1,(0,0,255),5)
# -1代表轮廓索引,BGR(0,255,255)代表红色,5代表参数thickness的轮廓粗细
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()
运行程序,结果显示如下。
例2:逐个显示一幅图像内的边缘信息。
将cv2.drawContours()的参数contourIdx的值设置为具体的索引值。
import cv2
import numpy as np
o = cv2.imread("C:\\Users\\Kris\\Desktop\\timg.jpg")
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours,hierarchy = cv2.findContours(binary,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
n = len(contours)
con