《OpenCV轻松入门》学习打卡第六天

OpenCV + Python3

  • 第十二章 图像轮廓(重点)
    • 1.查找并绘制轮廓
      • 1)查找图像轮廓:findContours()函数
      • 2)绘制图像轮廓:drawContours函数
      • 3)轮廓实例(两个函数的应用)
    • 2.矩特征
      • 1)矩的计算:moments函数
      • 2)计算轮廓的面积:contourArea函数
      • 3)计算轮廓的长度:arcLength函数
    • 3.Hu矩
      • 1)Hu矩函数
      • 2)形状匹配
    • 4.轮廓拟合
      • 1)矩形包围框retval = cv2.boundingRect(array)
      • 2)最小包围矩形框retval = cv2.minAreaRect(points)
      • 3)最小包围圆形
      • 4)最优拟合椭圆
      • 5)最优拟合直线
      • 6)最小外包三角形
      • 7)逼近多边形
    • 5.凸包
      • 1)获取凸包
      • 2)凸缺陷
      • 3)几何学测试
    • 6.利用形状场景算法比较轮廓(通过计算比较轮廓)
      • 1)计算形状场景距离
      • 2)计算Hausdorff距离
    • 7.轮廓的特征值
      • 1)宽高比
      • 2)Extent
      • 3)Solidity
      • 4)等效直径(Equivalent Diameter)
      • 5)方向
      • 6)掩模和像素点
      • 7)最大值和最小值及它们的位置
      • 8)平均颜色及平均灰度
      • 9)极点

第十二章 图像轮廓(重点)

边缘检测虽然能够检测出边缘,但边缘是不连续的,检测到的边缘并不是一个整体。而图像轮廓是指将边缘连接起来形成的一个整体,用于后续的计算。OpenCV提供了查找图像轮廓的函数cv2.findContours(),该函数能够查找图像内的轮廓信息,而函数cv2.drawContours()能够将轮廓绘制出来。图像轮廓是图像中非常重要的一个特征信息,通过对图像轮廓的操作,我们能够获取目标图像的大小,位置,方向等位置。

1.查找并绘制轮廓

1)查找图像轮廓:findContours()函数

image, contours, hierarchy = cv2.findContours(image, mode, method)
image: 与函数参数中的原始图像image一致
contours: 返回的轮廓
hierarchy: 图像的拓扑信息(轮廓层次)
image:原始图像,8位单通道图像,会被自动处理为二值图像(0和1)
mode:轮廓检索模式
method:轮廓的近似方法
第一个返回值image在OpenCV 4.X中已经被取消,在OpenCV 4.X中,该函数仅有两个返回值,语法格式为:
contours, hierarchy = cv2.findContours(image, mode, method)
1>返回值contours
该返回值返回的是一组轮廓信息,contours[i]是第i个轮廓(下标从0开始)
<1> type属性
返回值contours的type属性是list类型,list的每个元素都是图像的一个轮廓,用Numpy中的ndarray结构表示,type(contours)
<2> 轮廓的个数
len(contours)
<3> 每个轮廓的点数
len(contours[i]) # 获取每个轮廓内点的个数
<4> 轮廓内的点
contours[i]可以直接获取第i个轮廓中具体点的位置属性
2>返回值hierarchy
图像内的轮廓可能位于不同的位置。比如一个轮廓在另一个轮廓内,这种情况下,我们将外部的轮廓称为父轮廓,内部的轮廓称之为子轮廓。这样分类下来,一幅图像中所有轮廓之间就建立了父子关系
根据轮廓之间的关系,就能够确定一个轮廓与其他轮廓是如何连接的。上述关系被称为层次,返回值hierarchy就包含上述层次关系,每个contours[i]对应四个元素来说明当前轮廓的层次关系:
[Next, Previous, First_Child, Parent]
·Next:后一个轮廓的索引编号
·Previous:前一个轮廓的索引编号
·First_Child:第一个子轮廓的索引编号
·Parent:父轮廓的索引编号
(若无,则为-1)
轮廓的层次结构是由参数mode决定的,也就是说,使用不同的mode,得到轮廓的编号是不一样的,得到的hierarchy也不一样。
3>参数image
表示输入的图像,必须是8位单通道二值图像,一般情况下都是将图像处理为二值图像后,再将其作为image参数使用。
4>参数mode
该参数决定了轮廓的提取方式:
· cv2.RETR_EXTERNAL:只检测外轮廓
· cv2.RETR_LIST:对检测到的轮廓不建立等级方式
· cv2.RETR_CCOMP:检索所有轮廓并组织成两级层次结构,上面的一层为外边界,下面的一层为内孔的边界
· cv2.RETR_TREE:建立一个等级树结构的轮廓
5>参数method
该参数决定了怎么表达轮廓,可以是:
· cv2.CHAIN_APPROX_NONE:存储所有轮廓点,相邻两个点的像素位置差不超过1
· cv2.CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,一个矩形就存储4个点
· cv2.CHAIN_APPROX_TC89_L1:使用teh-Chinl chain近似算法的一种风格
· cv2.CHIAN_APPROX_TC89_KCOS:使用teh-Chinl chain近似算法的一种风格

2)绘制图像轮廓:drawContours函数

在OpenCV中,可以使用函数cv2.drawContours()绘制图像轮廓,该函数的语法格式是:
Image = cv2.drawContours(image, contours, contoursIdx, color, thickness, lineType, hierarchy, maxLevel, offset)
返回值image,表示为目标图像,即绘制了边缘的原始图像
参数列表:
Image: 待绘制轮廓的图像;
contours: 需要绘制的轮廓,返回值是一个list类型;
contourIdx: 需要绘制的边缘索引,如果为整数或0,表示绘制对应索引号的轮廓,如为-1,则表示绘制全部轮廓;
color: 绘制的颜色,用BGR格式表示;
thickness: 可选参数,表示绘制轮廓的画笔的粗细,如若设为-1,则表示要绘制实心轮廓;
lineType:可选参数,表示绘制轮廓所用的线型;
Hierarchy:输出的轮廓的层次信息;
maxLevel:控制所绘制的轮廓层次的深度,如果值为0,表示仅仅绘制第0层的轮廓;如果为其他的非零正数,表示绘制最高层及以下相同数量层次的轮廓;
Offset:偏移参数,该参数使轮廓偏移到不同的位置展示出来。

3)轮廓实例(两个函数的应用)

1> 绘制所有轮廓
contours, hierarchy = cv2.findContours()
img = cv2.drawContours(img, contours, -1, (0, 0, 0), 3)
2>逐一显示边缘信息
contours, hierarchy = cv2.findContours()
n = len(contours)
contoursImg = []
for i in range(n):
temp = np.zeros(img.shape, np.uint8)
contoursImg.append(temp)
contoursImg[i] = cv2.drawContours(
contoursImg[i], contours, i, (255, 255, 255), 3)
cv2.imshow()
3>用轮廓提取前景图像
将函数cv2.drawContours()的参数thickness设为-1,绘制前景图像的实心轮廓,然后和原图像按位与操作就可以实现把前景图像从原始图像中提取出来。

2.矩特征

比较两个轮廓最简单的方法是比较两者的轮廓矩。轮廓矩代表了一个轮廓,一幅图像,一组点集的全局特征。矩信息包含了对应对象不同类型的几何特征,大小,位置,角度,形状等。被广泛的应用在模式识别,图像识别上。

1)矩的计算:moments函数

OpenCV提供了函数cv2.moments()来获取图像的moments特征
retval = cv2.moments(array, binaryImage)
该函数的返回值retval是矩特征,主要包括:
1>空间矩
· 零阶矩:m00
· 一阶矩:m10, m01
· 二阶矩:m20, m11, m02
· 三阶矩:m30, m21, m12, m03
2>中心矩
· 二阶中心矩:mu20, mu11, mu02
· 三阶中心距:mu30, mu21, mu12, m03
3>归一化中心距
· 二阶Hu矩:nu20, nu11, nu02
· 三阶Hu矩:nu30, nu21, nu12, nu03
很明显,如果两个轮廓的矩是一样的,那么这两个轮廓就是一致的,其中m00表示轮廓的面积。无论两个轮廓出现在图像的任何位置,通过函数cv2.moments()的m00矩判断其面积是否一致。而当位置变化时,虽然轮廓的周长,面积等特征不变,但是更高阶的特征会随着位置变化而变化。在很多情况下,我们都希望比较两个不同位置的两个对象的一致性,解决这一问题的办法就是引入中心矩。中心矩可以获取平移不变性,可以忽略两个对象的位置关系,帮助我们比较不同位置上的两个对象的一致性。除了考虑平移不变性以外,我们还希望经过缩放后的大小不一致的对象的一致性,也就是说希望图像在缩放前后拥有相同的特征值,归一化中心矩可以获得缩放不变性,通过计算上述计算提取对象的归一化中心矩属性值,该属性值不仅具有平移不变性,还具有缩放不变性。

2)计算轮廓的面积:contourArea函数

函数cv2.contourArea()用于计算轮廓的面积,语法格式为:
retval = cv2.contourArea(contour, oriented)
式中返回值retval是面积值,两个参数:
· contour是轮廓
· oriented是布尔型值,为True时,返回的值包含正负号,用来表示轮廓是顺时针的还是逆时针的,默认为False,返回的是一个绝对值

3)计算轮廓的长度:arcLength函数

函数cv2.arcLength()用来计算轮廓的长度,其语法格式为:
retval = cv2.arcLength(curve, closed)
式中返回值retval是轮廓的周长,两个参数:
· curve是轮廓
· closed是布尔值,表示该路面阔是否封闭,该值为True时表示轮廓封闭

3.Hu矩

Hu矩是归一化中心矩的线性组合。Hu矩在图像旋转、缩放、平移等操作后,仍能保持矩形的不变性,所以经常会使用Hu矩来识别图像的特征。在OpenCV中,使用函数cv2.HuMoments()可以得到Hu矩,该函数使用cv2.moments()函数的返回值作为参数,返回7个Hu矩值。

1)Hu矩函数

函数cv2.HuMoments()的语法格式为:
hu = cv2.HuMoments(m)
式中返回值hu,表示返回的Hu矩值;参数m,是由函数cv2.moments()计算得到矩特征值。Hu矩是归一化中心矩的线性组合,每一个矩都是通过归一化中心矩的组合运算得到的,cv2.moments()函数返回的归一化中心矩中包含:
· 二阶Hu矩:nu20, nu11, nu02
· 三阶Hu矩:nu30, nu21, nu12, nu03
为了表述上的方便,将上述字母“nu”表示为字母“v”,则归一化中心矩为:
· 二阶Hu矩:v20, v11, v02
· 三阶Hu矩:v30, v21, v12, v03

2)形状匹配

我们通过Hu矩来判断两个对象的一致性,为了更直观方便的比较Hu矩值,OpenCV提供了函数cv2.matchShapes(),对两个对象的Hu矩进行比较。该函数允许我们提供两个对象,对二者的Hu矩进行比较。这两个对象可以是轮廓,也可以是灰度图。不管是什么,函数cv2.matchShapes()都会提前计算好对象的Hu矩值
retval = cv2.matchShapes(contours1, contours2, method, parameter)
式中retval是返回值,contours1是第一个轮廓或者灰度图像,contours2是第二个轮廓或者灰度图像,method是比较两个对象的Hu矩的方法
· cv2.CONTOURS_MATCH_I1
· cv2.CONTOURS_MATCH_I2
· cv2.CONTOURS_MATCH_I3
parameter是应用method的特定参数,该参数是扩展参数,目前不支持因此设置为0.

4.轮廓拟合

在计算轮廓时,我们可能并不需要实际的轮廓,而仅需要一个接近于轮廓的近似多边形,OpenCV提供了多种计算轮廓近似多边形的方法。

1)矩形包围框retval = cv2.boundingRect(array)

cv2.rectangle(o, (x, y), (x + w, y + h), (255, 255, 255), 2)
retval表示返回的矩形边界的左上角顶点的坐标值及矩形边界的宽度和高度

2)最小包围矩形框retval = cv2.minAreaRect(points)

retval的结构为(最小外接矩形的中心(x, y), (宽度, 高度), 旋转角度)
这里需要注意,返回值retval的结构不符合函数cv2.drawContours()的参数结构要求,因此要转换一下。函数cv2.boxPoints()能够将上述返回值retval转换为符合要求的结构:points = cv2.boxPoints(box),返回值points是符合参数结构的,参数box是函数cv2.minAreaRect()的返回值。

3)最小包围圆形

函数cv2.minEnclosingCircle()通过迭代算法构造一个对象的面积最小包围圆形,该函数语法格式为:center, radius = cv2.minEnclosingCircle(points)
center是圆心,radius是半径,参数points是轮廓

4)最优拟合椭圆

函数cv2.fitEllipse()可以用来构造最优拟合椭圆,语法格式为:
retval = cv2.fitEllipse(points)
返回值retval是RotatedRect类型的值,因为该函数返回的是拟合椭圆的外接矩形,retval中包括外接矩形的质心,宽,高,旋转角度等参数信息,这些信息刚好与椭圆的中心点,轴长度,旋转角度等信息吻合。

5)最优拟合直线

在OpenCV中,函数cv2.fitLiine()用来构造最优拟合直线,语法格式为:
line = cv2.fitLine(points, distType, param, reps, aeps)
Line是返回值,是返回的最优拟合直线的直线参数;points是轮廓;distType是距离类型,在拟合直线时,要使输入点到拟合直线的距离之和最小;param是距离参数,设为0时,该函数会自动选择最优值;reps用来表示拟合直线所需要的径向精度,通常设为0.01;aeps用来表示拟合直线所需要的角度精度,通常设为0.01

6)最小外包三角形

函数cv2.minEnclosingTriangle()用来构造最小外包三角形,语法格式为:
retval, triangle = cv2.minEnclosingTriangle(points)
retval是最小外包三角形的面积,triangle是最小外包三角形的三个顶点集

7)逼近多边形

函数cv2.approxPolyDP()用来构造指定精度的逼近多边形曲线,语法格式为:
approxCurve = cv2.approxPolyDP(curve, epsilon, closed)
返回值approxCurve为逼近多边形的点集;curve是轮廓;epsilon是精度,原始轮廓的边界点与逼近多边形边界之间的最大距离;closed是布尔值,为True时,逼近多边形是封闭的,否则不封闭。

5.凸包

逼近多边形是轮廓的高度近似,但有时候,我们希望用一个多边形的凸包来简化它,凸包跟逼近多边形很像,不过它是凸的。

1)获取凸包

OpenCV提供函数cv2.convexHull()用于获取轮廓的凸包,语法格式为:
hull = cv2.convexHull(points, clockwise, returnPoints)
返回值hull为凸包角点;points是轮廓,clockwise是布尔值,为True时,凸包角点将按顺时针方向排列,反之逆时针;returnPoints是布尔值,默认True,函数返回凸包角点的x/y轴坐标,False时返回轮廓中凸包角点的索引

2)凸缺陷

凸包与轮廓之间的部分,称为凸缺陷。
在OpenCV中使用函数cv2.convexityDefects()获取凸缺陷,语法格式为:
convexityDefects = cv2.convexityDefects(contour, convexhull)
返回值convexityDefects为凸缺陷点集,是一个数组,每一行包含的值是[起点,终点,轮廓上距离凸包最远的点,最远点到凸包的近似距离],需要注意的是这里前三个值是轮廓点的索引,需要到轮廓点中去寻找它们;contour是轮廓,convexhull是凸包。需要注意的是,用cv2.convexityDefects()计算凸缺陷时,要使用凸包作为参数,在查找该凸包时,所使用的函数cv2.convexHull()的参数returnPoints的值必须是False。

3)几何学测试

1> 测试轮廓是否是凸形的
用函数cv2.isContourConvex()来判断轮廓是否是凸形的,语法格式为:
retval = cv2.isContourConvex(contour)
2>点到轮廓的距离
用函数cv2.pointPolygonTest()来计算点到多边形(轮廓)的最短距离,这个计算过程又称点和多边形的关系测试,语法格式为:
retval = cv2.pointPolygonTest(contour, pt, measureDist)
返回值retval与参数measureDist的值有关;contour为轮廓;pt为待判定的点,measureDist为布尔值,表示距离的判定方式:
· 为True时,表示计算点到轮廓的距离,如果点在轮廓外部,返回值为负数,在轮廓上为0,在轮廓里返回值为正。
· 为False时,不算距离,只返回-1, 0, 1,分别表示点在轮廓外部,轮廓上,轮廓里。

6.利用形状场景算法比较轮廓(通过计算比较轮廓)

用矩比较形状是一种非常有效的方法,不过从OpenCV 3开始,有了专有模块shape,该模块中的形状场景算法能够更高效的比较形状。

1)计算形状场景距离

OpenCV提供了使用“距离”作为形状比较的度量标准,这是因为形状之间的差异值和距离有相似之处,函数cv2.createShapeContextDistanceExtractor(),用于计算形状场景距离,其使用的“形状上下文算法”在计算距离时,在每个点附加一个“形状上下文”描述符,让每个点都能够捕获剩余点相对它的分布特征,从而提供全局鉴别特征,语法格式为:
Retval = cv2.createShapeContextDistanceExtractor(
[, nAngularBins[,
nRadialBins[,
innerRadius[,
outerRadius[,
iterations[,
comparer[,
Transformer]]]]]]])
返回值retval返回结果
该结果可以通过函数cv2.ShapeDistanceExtractor.computeDistance()计算两个不同形状之间的距离,此函数语法格式为:
Retval = cv2.ShapeDistanceExtractor.computeDistance(contour1, contour2)
式中contour1和contour2是不同的轮廓
函数cv2.createShapeContextDistanceExtractor()的参数都是可选参数:
· nAngularBins:为形状匹配中使用的形状上下文描述符建立的角容器的数量
· nRadialBins:为形状匹配中使用的形状上下文描述符建立的径向容器的数量
· innerRadius:形状上下文描述符的内半径
· outerRadius:形状上下文描述符的外半径
· iterations:迭代次数
· comparer:直方图代价提取算子,该函数使用了直方图代价提取仿函数,可以直接采用直方图代价提取仿函数的算子作为参数
· transformer:形状变换参数

2)计算Hausdorff距离

Hausdorff距离的计算方法是:
1> 针对图像A内的每一个点,寻找其距离图像B的最短距离,将这个最短距离作为Hausdorff直接距离D1
2> 针对图像B内的每一个点,寻找其距离图像A的最短距离,将这个最短距离作为Hausdorff直接距离D2
3> 将上述D1, D2中的较大的那个作为Hausdorff距离
用数学公式为:
在这里插入图片描述
其中,在这里插入图片描述
式中,||·||表示点a和点b的某种范数,通常是欧氏距离
OpenCV提供了函数cv2.createHausdorffDistanceExtractor([, distanceFlag[, rankProp]])
返回值retval,distanceFlag为距离标记,是可选参数,rankProp为一个比例值,范围在0到1之间,也是可选参数

7.轮廓的特征值

轮廓自身的一些属性特征及轮廓所包围对象的特征对于描述图像具有重要意义,接下来介绍几个轮廓自身的属性特征以及轮廓所包围对象的特征。

1)宽高比

可以使用宽高比来描述轮廓,例如矩形轮廓的宽高比为:宽高比=宽度/高度
我们可以先获取其轮廓的宽和高,然后做除法即可

2)Extent

可以使用轮廓面积与矩形边界(矩形包围框,矩形轮廓)面积之比Extent来描述图像及其轮廓特征。计算方法为:
在这里插入图片描述

3)Solidity

可以使用轮廓面积与凸包面积之比Solidity来衡量图像、轮廓及凸包的特征,计算方法为:
在这里插入图片描述

4)等效直径(Equivalent Diameter)

可以用等效直径来衡量轮廓的特征值,该值是与轮廓面积相等的圆形的直径。其计算公式为:
在这里插入图片描述

5)方向

在OpenCV中,函数cv2.fitEllipse()可以用来构造最优拟合椭圆,还可以返回椭圆的中心点、轴长、旋转角度等信息,函数cv2.fitEllipse()返回各个属性值的语法格式为:
(x, y), (Ma, ma), angle = cv2.fitEllipse(cnt)
· (x, y):椭圆的中心点
· (Ma, ma):椭圆水平方向轴和垂直方向轴的长度
· angle:椭圆的旋转角度

6)掩模和像素点

有时,我们想获取某对象的掩模图像及其对应的点,我们将函数cv2.drawContours()的轮廓宽度参数thickness设为-1,就可以绘制实心轮廓,也就能得到掩模。另外,我们可能还希望能获取轮廓像素点的具体位置信息,一般情况下,轮廓是图像内非零的像素点,可以通过Numpy函数或者OpenCV函数获取像素点的位置信息。
1>使用Numpy函数获取轮廓像素点
Numpy.unzeros()函数能找出数组中非零元素的位置,但是返回值为行,列,使用函数Numpy.transpose()处理这些行列值,可得到这些点的(x, y)形式的坐标
2>使用OpenCV函数获取轮廓点
OpenCV提供了函数cv2.findNonZero()用于查找非零元素的索引,语法格式为:
idx = cv2.findNonZero(src)
式中idx为返回值,表示非零元素的索引位置,需要注意的是,在返回的索引中,每个元素对应的是(列好,行号)的格式。src为参数,表示要查找非零元素的图像

7)最大值和最小值及它们的位置

OpenCV提供了函数cv2.minMaxLoc(),用于在指定的对象内查找最大值、最小值及其位置,语法格式为:
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray, mask = mask)
· min_val:最小值
· max_val:最大值
· min_loc:最小值位置
· max_loc:最大值位置
· imgray:单通道图像
· mask:掩模,通过使用掩模图像,可以得到掩模指定区域内的最值信息

8)平均颜色及平均灰度

OpenCV提供了函数cv2.mean(),用于计算一个对象的平均颜色或平均灰度,语法格式为:
mean_val = cv2.mean(im, mask = mask)
返回值mean_val,表示返回的平均值,im为原图像,mask为掩模

9)极点

有时,我们希望获取某个对象内的极值点,例如最左端,最右端,最上端,最下端的四个点,OpenCV提供了相应的函数来找出这些点,通常语法格式:
Leftmost = tuple(cnt [cnt[:, :, 0].argmin()] [0])
Rightmost = tuple(cnt [cnt[:, :, 0].argmax()] [0])
Topmost = tuple(cnt [cnt[:, :, 1].argmin()] [0])
Bottommost = tuple(cnt [cnt[:, :, 1].argmax()] [0])

若有侵权,请联系删除

本读书笔记来源于教材。1


  1. 李立宗. OpenCV轻松入门: 面向python[M].北京. 电子工业出版社: 李立宗. 2019.5 ↩︎

你可能感兴趣的:(OpenCV学习,python,计算机视觉,opencv)