OpenCV-Python教程:22.轮廓层级

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV-Python教程:22.轮廓层级相关的知识,希望对你有一定的参考价值。

参考技术A 理论

在前面的关于轮廓的几节里,我们介绍了轮廓相关的一些函数。但当我们用cv2.findContours()函数来找轮廓的时候,我们传入了一个参数,Contour Retrieval Mode。我们一般传的是cv2.RETR_LIST或者cv2.RETR_TREE这样就可以了。但是这个参数实际是什么意思呢?

并且在输出时我们得到了三个数组,第一个是图像,第二个是我们的轮廓,第三个输出名字是hierarchy。但是我们一直没用这个。

什么是层级?

一般来说我们用cv2.findContours()函数来检测图像里的目标,有时候目标在不同的地方,但是在有些情况下,有些图形在别的图形里面,就像图形嵌套,在这种情况下,我们把外面那层图形叫做parent,里面的叫child。这样图形里的轮廓之间就有了关系。我们可以指定一个轮廓和其他之间的是如何连接的,这种关系就是层级。

看下面的例子:

在这个图像里,不同的图形我标注了0-5,2和2a表示了最外层盒子的外部和内部轮廓。

这里轮廓0,1,2是外部的。我们可以说他们是hierarchy-0,或者他们是同层级的。

接下来是contour-2a,可以认为是轮廓-2的孩子,或者反过来,contour-2是contour-2a的父亲,所以它在hierarchy-1里。类似的contour-3是contour-2的孩子,在下一层级。最后contour4,5是contour-3a的孩子,它们在最后的层级。

OpenCV里的层级表示

每个轮廓有他自己的关于层级的信息,谁是他的孩子,谁是他的父亲等。OpenCV用一个包含四个值得数组来表示:[Next, Previous, First_Child, Parent]

"Next表明同一层级的下一个轮廓"

比如,在我们的图片里的contour-0,水上hi他相同层级的下一个轮廓?是contour-1,所以Next=1,对于Contour-1,下一个是contour-2,所以Next=2

那对于contour-2呢?没有同层级的下一个轮廓,所以Next=-1。那么对于contour-4呢?同层级的下一个是contour-5,所以下一个轮廓是contour-5.Next=5

"Previous指同层级的前一个轮廓"

和上面一样,contour-1的前一个是contour-0.contour-2的前一个contour-1.对于contour-0没有前序,所以-1

"First_Child指它的第一个孩子轮廓"

不用解释,对于contour-2,孩子是contour-2a,所以这里是contour-2a的索引,contour-3a有两个孩子,但我们只取第一个,是contour-4,所以First_Child=4.

"Parent指它的父轮廓索引"

和First_Child相反,contour-4和contour-5的parent都是contour-3a,对于contour-3a,是contour-3

注意:
如果没有孩子或者父亲,就为-1

我们知道了层级,现在来看OpenCV里的轮廓获取模式,四个标志cv2.RETR_LIST, cv2.RETR_TREE, cv2.RETR_CCOMP, cv2.RETR_EXTERNAL表示啥?

轮廓获取模式

1.RETR_LIST

这是最简单的一个,它获取所有轮廓,但是不建立父子关系,他们都是一个层级。

所以,层级属性第三个和第四个字段(父子)都是-1,但是Next和Previous还是有对应值。

下面是结果,每行是对应轮廓的层级信息。

>>> hierarchy

2.RETR_EXTERNAL

如果用这个模式,它返回最外层的。所有孩子轮廓都不要,我们可以说在这种情况下,只有家族里最老的会被照顾,其他都不管。

所以在我们的图像里,有多少最外层的轮廓呢,有3个,contours 0,1,2

3.RETR_CCOMP

这个模式获取所有轮廓并且把他们组织到一个2层结构里,对象的轮廓外边界在等级1里,轮廓内沿(如果有的话)放在层级2里。如果别的对象在它里面,里面的对象轮廓还是放在层级1里,它的内沿在层级2.

看下面的例子,轮廓的顺序为红色,他们的层级是绿色,

看第一个轮廓,contour-0,他的层级是1,他有两个洞,contours1和2,他们都属于层级2,所以对于contour-0,Next是contour-3,没有前序,他的第一个孩子是contour-1,没有parent,所以层级数组是[3,-1,1,-1]

看contour-1,他在层级2里,Next是contour-2,没有前序,没有孩子,parent是contour-0,所以数组是[2,-1,-1,0]

同样对于contour-2,也在层级2里,没有next,前序是contour-1,没有孩子,parent是contour-0,所以[-1,1,-1,0]。

contour-3:next是contour-5,Previous是contour-0,Child是contour-4,没有parent,所以[5,0,4,-1]

contour-4:在层级2里,没有兄弟,所以没有Next,没有Previous,没有孩子,parent是contour-3,[-1,-1,-1,3]

4.RETR_TREE

最后,Mr.Perfect。它取回所有的轮廓并且创建完整的家族层级列表,它甚至能告诉你谁是祖父,父亲,儿子,孙子。。

比如把上面的图形用cv2.RETR_TREE,

对于contour-0:层级是0,Next是contour-7,没有previous,孩子是contour-1,没有parent,所以[7,-1,1,-1]

contour-1:在层级1里,没有同级的其他轮廓,没有previous,孩子是contour-2,所以[-1,-1,2,0]

OpenCV里的直方图

OpenCV-Python教程:19.轮廓属性

参考技术A 1图像矩

帮你计算一些属性,比如重心,面积等。

函数cv2.moments()会给你一个字典,包含所有矩值

你可以从这个里面得到有用的数据比如面积,重心等。重心可以用下面的式子得到:

2.轮廓面积

轮廓面积由函数cv2.contourArea()得到或者从矩里得到M['m00']

3.轮廓周长

可以用cv2.arcLength()函数得到。第二个参数指定形状是否是闭合的轮廓(如果传True)。或者只是一个曲线。

4.轮廓近似

这会把轮廓形状近似成别的边数少的形状,边数由我们指定的精确度决定。这是Douglas-Peucker算法的实现。

要理解这个,假设你试图找一个图像里的方块,但是由于图像里的一些问题,你得不到一个完美的方块,只能得到一个“坏方块”。现在你可以使用这个函数来近似,第二个参数叫epsilon,是从轮廓到近似轮廓的最大距离。是一个准确率参数,好的epsilon的选择可以得到正确的输出。

在下面第二个图像里,绿线显示了epsilon = 10% of arc length 的近似曲线。第三个图像显示了epsilon = 1% of the arc length。第三个参数指定曲线是否闭合。

5.凸形外壳

凸形外壳和轮廓近似类似,但是还不一样(某些情况下两个甚至提供了同样的结果)。这儿,cv2.convexHull()函数检查凸面曲线缺陷并修复它。一般来说,凸面曲线总是外凸的,至少是平的,如果它内凹了,这就叫凸面缺陷。比如下面这张图,红线显示了手的凸形外壳。双向箭头显示了凸面缺陷,是轮廓外壳的最大偏差。

参数详情:

·points 是我们传入的轮廓
·hull 是输出,一般我们不用传
·clockwise: 方向标示,如果是True,输出凸形外壳是顺时针方向的。否则,是逆时针的。
·returnPoints:默认是True。然后会返回外壳的点的坐标。如果为False,它会返回轮廓对应外壳点的索引。

所以要获得凸形外壳,下面

但是如果你想找到凸面缺陷,你需要传入returnPoints = False。我们拿上面的矩形图形来说,首先我找到他的轮廓cnt,现在用returnPoints = True来找他的凸形外壳,我得到下面的值:[[[234 202]], [[51 202]], [51 79]], [[234 79]]]  是四个角的点。如果你用returnPoints = False,我会得到下面的结果:[[129], [67], [0], [142]].  这是轮廓里对应点的索引,比如cnt[129] = [234, 202]],这和前面结果一样。

6.检查凸面

有一个函数用来检查是否曲线是凸面, cv2.isContourConvex().它返回True或False。

7.边界矩形

有两种边界矩形

7.a.正边界矩形

这个矩形不考虑对象的旋转,所以边界矩形的面积不是最小的,函数是cv2.boundingRect()。

假设矩形左上角的坐标是(x,y), (w, h)是它的宽和高

7.b.渲染矩形

这个边界矩形是用最小面积画出来的,所以要考虑旋转。函数是cv2.minAreaRect()。它返回一个Box2D结构,包含了(左上角(x,y),(width, height),旋转角度)。但是要画这个矩形我们需要4个角。这四个角用函数cv2.boxPoints()得到

8.最小闭包圆

我们找一个目标的外接圆可以用函数cv2.minEnclosingCircle().这个圆用最小面积完全包围目标。

9.椭圆

用一个椭圆来匹配目标。它返回一个旋转了的矩形的内接椭圆

10. 直线

类似的我们可以匹配一根直线,下面的图像包含一系列的白色点,我们可以给它一条近似的直线。

END

以上是关于OpenCV-Python教程:22.轮廓层级的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV-Python:轮廓

OpenCV-Python17.OpenCV的图像轮廓——轮廓特征

Opencv-python 找到图像轮廓并绘制,cv2.findContours()函数,求轮廓外接矩形,cv2.boundingrect()

OpenCV-Python实战(11)——OpenCV轮廓检测相关应用

OpenCV-Python实战(10)——详解 OpenCV 轮廓检测

[OpenCV-Python] OpenCV 中的图像处理 部分 IV (四)