目录
OpenCV中的轮廓
什么是轮廓
轮廓可以简单的解释为连接具有相同颜色或强度的所有连续点(沿边界)的曲线。轮廓是用于形状分析以及对象检测和识别的有用工具。
- 为了获得更高的准确性,请使用二进制图像。在找到轮廓之前,应用阈值或canny边缘检测。
- 从OpenCV3.2开始,
cv.findContours()
不再修改源图像。 - 在OpenCV中,找到轮廓就像从黑色背景中找到白色物体。要找到的对象应该是白色,背景应该是黑色。
下面代码展示如何找到二进制图像(也称二值图像,通常用一个二维数组描述,1位非0即1的数表示一个像素,通常0表示黑色,1表示白色)的轮廓:
import cv2 as cv
import numpy as np
img = cv.imread('./OpenCV/fusi1.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(gray, 127, 255, 0)
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
cv.findcontour()
函数中有三个参数,第一个是源图像需为二值图,即黑白的(不是灰度图),可将读取的图像先转成灰度的,再转成二值图;第二个是轮廓检索模式,cv.RETR_EXTERNAL
只检测外轮廓,cv.RETR_LIST
检测的轮廓不建立等级关系,cv.RETR_CCOMP
建立两个等级的轮廓,上一层为外边界,内层为内孔的边界,如果内孔内还有连通物体,则这个物体的边界也在顶层,cv.RETR_TREE
建立一个等级树结构的轮廓;第三个是轮廓逼近方法cv.CHAIN_APPROX_NOME
存储所有的轮廓点,相邻的两个点的像素位置差不超过,cv.CHAIN_APPROX_SIMPLE
压缩水平方向、垂直方向、对角线方向的元素,只保留该方向的终点坐标。输出等高线和层次结构,轮廓是图像中所有轮廓的Python列表,每个单独的轮廓是一个(x, y)坐标的Numpy数组的边界点的对象。
如何绘制轮廓?
使用cv.drawContours()
函数绘制轮廓,只要有边界点,它也可以用来绘制任何形状。它的第一个参数是源图像,第二个参数是作为Python列表传递的轮廓,第三个参数是轮廓的索引(绘制单个轮廓时有用,要绘制所有轮廓,传递-1),其余参数是颜色、厚度等等
- 在图像中绘制所有轮廓:
cv.drawContours(img, contours, -1, (0, 255, 0), 3
) - 绘制单个轮廓,如第四个轮廓:
cv.drawContours(img, contours, 3, (0, 255, 0), 3
- 绘制单个轮廓的另一个方法,大多数情况下以下方法很有用:
cnt = contours[4
cv.drawContours(img, [cnt], 0, (0, 255, 0), 3
轮廓近似方法
这是cv.drawContours()
函数中的第三个参数。
如果参数传递的是cv.CHAIN_APPROX_NODE
,则轮廓存储所有边界点的(x, y)坐标。传递cv.CHAIN_APPROX_SIMPLE
会删除所有冗余点并压缩轮廓,从而节省内存。
下面为使用两种轮廓近似方法展示图像轮廓的代码:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('./OpenCV/dark.png')
pimg = cv.cvtColor(img, cv.COLOR_BGR2RGB)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 转换为灰度图
ret, thresh = cv.threshold(gray, 127, 255, cv.THRESH_BINARY) # 二值化灰度图
contours1, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
contours2, hierarchy2 = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
draw_simple = pimg.copy()
draw = pimg.copy()
dgray_simple = cv.merge((thresh.copy(), thresh.copy(), thresh.copy()))
dgray = cv.merge((thresh.copy(), thresh.copy(), thresh.copy()))
res1 = cv.drawContours(draw_simple, contours1, -1, (255, 0, 0), 2)
res2 = cv.drawContours(draw, contours2, -1, (255, 0, 0), 2)
res3 = cv.drawContours(dgray_simple, contours1, -1, (255, 0, 0), 2)
res4 = cv.drawContours(dgray, contours2, -1, (255, 0, 0), 2)
images = [pimg, res1, res2, res3, res4]
titles = ['Original', 'Simple__outline', 'All_outline', 'Gray_Simple', 'Gray_All']
for i in range(5):
plt.subplot(2, 3, i + 1)
plt.imshow(images[i]), plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
在二值化(或灰度)图像中是无法使用其他颜色画出图像轮廓,因为图像是单通道的,若要展示,可将三幅二值化(或灰度)图进行堆叠。
轮廓特征
1. 特征矩
特征矩可帮助计算一些特征,如物体的质心、面积等。函数cv.moment()
提供了所有计算出的矩值的字典,如下代码:
import cv2 as cv
import numpy as np
img = cv.imread('./OpenCV/hand.png')
ret, thresh = cv.threshold(cv.cvtColor(img, cv.COLOR_BGR2GRAY), 127, 255, 0)
contours, hierachy = cv.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv.moments(cnt)
print(M)
结果:
{‘m00’: 25055.0, ‘m10’: 4036943.333333333, ‘m01’: 4082197.0, ‘m20’: 702954431.1666666,
‘m11’: 654563694.75, ‘m02’: 745633429.5, ‘m30’: 129617766948.20001, ‘m21’: 113234562831.3, ‘m12’: 119268551752.56667, ‘m03’: 146615303293.9, ‘mu20’: 52508952.15870643, ‘mu11’: -3173202.655840397, ‘mu02’: 80523377.74150872, ‘mu30’: -565371477.2726593, ‘mu21’: -274852113.0884037, ‘mu12’: 163677451.77373505, ‘mu03’: -1109587190.9181519, ‘nu20’: 0.08364587675018598, ‘nu11’: -0.00505485840683985, ‘nu02’: 0.1282723850538334, ‘nu30’: -0.005689811478185491, ‘nu21’: -0.0027660693379827884, ‘nu12’: 0.0016472246677793024, ‘nu03’: -0.011166714609285085}
可使用M提取出有用的数据。如质心由以下式子计算:
C
x
=
M
10
M
00
C_{x}=\dfrac{M_{10}}{M_{00}}
Cx=M00M10 和
C
y
=
M
01
M
00
C_{y}=\dfrac{M_{01}}{M_{00}}
Cy=M00M01,代码表示如下:
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
2. 轮廓面积
由函数cv.contourArea()
或M['m00']
得到
area = cv.contourArea(cnt)
3. 轮廓周长
也称弧长,可使用函数cv.arcLength()
得到,其第二个参数用来指定形状是闭合轮廓(True)还是曲线(False)
perimeter = cv.arcLength(cnt, True)
4. 轮廓近似
使用函数cv.approxPolyDP()
,根据指定的精度,将轮廓形状近似为顶点数量较少的其他形状,是Douglas-Peucker算法的实现。假设试图在图像中找到一个正方形,但由于图像中得某些问题,没有得到一个完美的正方形,可使用此功能来近似形状。在这种情况下,第二个参数称为epsilon,它是从轮廓到近似轮廓的最大距离,是一个精度参数。正确选择epsilon才能获得正确的输出。
epsilon = 0.1 * cv.arcLength(cnt, True)
approx = cv.approxPolyDP(cnt, epsilon, True)
5. 轮廓凸包
凸包外观看起来与轮廓逼近相似,但不相似(在某些情况下两者可能提供相同的结果)。函数cv.convexHull()
检查曲线是否存在凸凹缺陷并对其进行校正;一般而言,凸曲线是始终凸出或至少平坦的曲线;如果在内部凸出,则称为凸度缺陷;
hull = cv.convexHull(points[, hull[, clockwise[, returnPoints]]])
其中点是传递到的轮廓;凸包是输出,通常忽略它;顺时针方向:方向标记,若为True,则输出凸包为顺时针方向,False为逆时针;returnPoints:默认情况下为True,返回凸包的坐标,False则返回与凸包点相对应的轮廓点的索引,可使用cnt[索引]获取对应凸度缺陷。
要获取整个图像的凸包,可使用:
hull = cv.convexHull(cnt)
6. 检查凸度
函数cv.isContourConvex()
具有检查曲线是否凸出的功能,凸出返回True,否则返回False。
k = cv.isContourConvex(cnt)
7. 边界矩形
1. 直角矩形
它是一个矩形,不考虑物体的旋转,所以这种边界矩形的面积不是最小的,由函数cv.boundingRect()
得到。令(x, y)为矩形的左上角坐标,而(w, h)为矩形的宽度和高度。
x, y, w, h = cv.boundingRect(cnt)
cv.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
2. 旋转矩形
这种边界矩形是用最小面积绘制的,考虑了旋转。使用函数cv.minAreaRect()
;返回一个Box2D结构,其中包含中心(x, y),(宽度, 高度),旋转角度;要画出这个矩形,需要矩形的四个角,由函数cv.boxPoints()
获得。
rect = cv.minAreaRect(cnt)
box = cv.boxPoints(rect)
box = np.int0(box)
cv.drawContours(img, [box], 0, (0, 0, 255), 2)
8. 最小闭合圈
使用函数cv.minEnclosingCircle()
查找对象的圆周,它是一个以最小面积完全覆盖物体的圆。
(x, y), radius =cv.minEnclosingCircle(cnt)
center = (int(x), int(y))
radius = int(radius)
cv.circle(img, center, radius, (0, 255, 0), 2)
9. 拟合一个椭圆
把一个椭圆拟合到一个物体上,返回内接椭圆的旋转矩形。
ellipse = cv.fitEllipse(cnt)
cv.ellipse(img, ellipse, (0, 255, 0), 2)
10. 拟合直线
可以将一条直线拟合到一组点,可以将下面包含的一组白点,近似一条直线。
rows, cols = img.shape[:2]
[vx, vy, x, y] = cv.fitLine(cnt, cv.DIST_L2, 0, 0.01, 0.01)
lefty = int((-x * vy / vx) + y)
righty = int(((cols - x) * vy / vx) + y)
cv.line(img, (cols - 1, rignty), (0, lefty), (0, 255, 0), 2)
各轮廓特征完整展示代码:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('./OpenCV/hand.png')
ret, thresh = cv.threshold(cv.cvtColor(img, cv.COLOR_BGR2GRAY), 127, 255, 0)
contours, hierachy = cv.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv.moments(cnt)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
area = cv.contourArea(cnt)
perimeter = cv.arcLength(cnt, True)
print('第一个轮廓质心为:({}, {}),面积为:{},周长为:{}'.format(cx, cy, area, perimeter))
epsilon = 0.1 * cv.arcLength(cnt, True)
approx = cv.approxPolyDP(cnt, epsilon, True)
draw_imgs = []
for i in range(8) :
draw_imgs.append(cv.cvtColor(img.copy(), cv.COLOR_BGR2RGB))
approx_img = cv.drawContours(draw_imgs[1], [approx], -1, [255, 0, 0], 2)
hull = cv.convexHull(cnt)
hull_img = cv.drawContours(draw_imgs[2], [hull], -1, [0, 255, 0], 2)
k = cv.isContourConvex(hull)
print('图像轮廓是否有凸出?:{}'.format(k))
x, y, w, h = cv.boundingRect(cnt)
cv.rectangle(draw_imgs[3], (x, y), (x + w, y + h), (0, 255, 0), 2)
rect = cv.minAreaRect(cnt)
box = cv.boxPoints(rect)
box = np.int0(box)
cv.drawContours(draw_imgs[4], [box], 0, (0, 0, 255), 2)
(x, y), radius =cv.minEnclosingCircle(cnt)
center = (int(x), int(y))
radius = int(radius)
cv.circle(draw_imgs[5], center, radius, (0, 255, 0), 2)
ellipse = cv.fitEllipse(cnt)
cv.ellipse(draw_imgs[6], ellipse, (0, 255, 0), 2)
rows, cols = img.shape[:2]
[vx, vy, x, y] = cv.fitLine(cnt, cv.DIST_L2, 0, 0.01, 0.01)
lefty = int((-x * vy / vx) + y)
righty = int(((cols - x) * vy / vx) + y)
cv.line(draw_imgs[7], (cols - 1, righty), (0, lefty), (0, 255, 0), 2)
images = [draw_imgs[0], approx_img, hull_img, draw_imgs[3], draw_imgs[4], draw_imgs[5], draw_imgs[6], draw_imgs[7]]
titles = ['Original', 'Approx', 'Hull', 'Rect', 'RotateRect', 'MinEnclosingCircle', 'Ellipse', 'Line']
for i in range(8):
plt.subplot(2, 4, i + 1)
plt.imshow(images[i]), plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
输出:
第一个轮廓质心为:(161, 162),面积为:25058.0,周长为:1281.484406709671
图像轮廓是否有凸出?:True
轮廓属性
学习提取一些常用的物体属性,如坚实度,等效直径,掩模图像,平均强度等(上节的质心、面积、周长等也属于这一类)。
1. 长宽比
它是对象边界矩形的宽度与高度的比值。 A s p e c t R a t i o = W i d t h H e i g h t Aspect Ratio=\dfrac{Width}{Height} AspectRatio=HeightWidth.
x, y, w, h = cv.boundingRect(cnt)
aspect_ratio = float(w) / h
2. 范围
范围是轮廓区域与边界区域的比值。
E
x
t
e
n
t
=
O
b
j
e
c
t
A
r
e
a
B
o
u
n
d
i
n
g
R
e
c
t
a
n
g
l
e
A
r
e
a
Extent=\dfrac{Object Area}{Bounding Rectangle Area}
Extent=BoundingRectangleAreaObjectArea
area = cv.contourArea(cnt)
x, y, w, h = cv.boundingRect(cnt)
rect_area = w * h
extent = float(area) / rect_area
3. 坚实度
坚实度是等高线面积与其凸包面积之比。
S
o
l
i
d
i
t
y
=
C
o
n
t
o
u
r
A
r
e
a
C
o
n
v
e
x
H
u
l
l
A
r
e
a
Solidity=\dfrac{Contour Area}{Convex Hull Area}
Solidity=ConvexHullAreaContourArea
area = cv.contourArea(cnt)
hull = cv.convexHull(cnt)
hull_area = cv.contourArea(hull)
solidity = float(area) / hull_area
4. 等效直径
等效直径是面积与轮廓面积相同的圆的直径。
E
q
u
i
v
a
l
e
n
t
D
i
a
m
e
t
e
r
=
4
×
C
o
n
t
o
u
r
A
r
e
a
π
Equivalent Diameter = \sqrt{\dfrac{4\times Contour Area}{\pi}}
EquivalentDiameter=π4×ContourArea
area = cv.contourArea(cnt)
equi_diameter = np.sqrt(4 * area / np.pi)
5. 取向
取向是物体指向的角度。
以下方法还给出了主轴和副轴的长度。
(x, y), (MA, ma), angle = cv.fitEllipse(cnt)
6. 掩码和像素点
获得构成该对象的所有点
mask = np.zeros(imgray.shape, np.uint8)
cv.drawContours(mask, [cnt], 0, 255, -1)
npixelpoints = np.transpose(np.nonzero(mask)) # numpy函数
pixelpoints = cv.findNonZero(mask) #OpenCV函数
适用numpy给出的坐标是(行, 列)格式,而使用OpenCV给出的坐标是(x, y)格式,注意row = x, column = y。
7. 最大值、最小值和它们的位置
可以使用掩码图像找到这些参数。
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(imgray, mask=mask)
8. 平均颜色或平均强度
可以找到对象的平均颜色或灰度模式下的平均强度。
mean_val = cv.mean(img, mask=mask)
9. 极端点
极点是指对象的最顶部,最底部,最右侧和最左侧的点。
leftmost = tuple(cnt[cnt[:, :, 0].argmin()][0])
rightmost = tuple(cnt[cnt[:, :, 0].argmax()][0])
topmost = tuple(cnt[cnt[:, :, 1].argmin()][0])
topmost = tuple(cnt[cnt[:, :, 1].argamx()][0])
10. 凸性缺陷
从凸包上的任何偏差都可以被认为是凸性缺陷,可使用函数cv.convexityDefects()
找到。
hull = cv.convexHull(cnt, returnPoints=False)
defects = cv.convexityDefects(cnt, hull)
注意:需在发现凸包时,传递returnPoints=False,以找到凸性缺陷。
它返回一个数组,其中每行包含这些值-[起点、终点、最远点、到最远点的近似距离];可以用图像把它形象化,可以画一条连接起点和终点的线,然后在最远处画一个圆;返回的前三个值是cnt的索引,所以得从cnt中获取这些值。
11. 点多边形测试
函数cv.pointPolygonTest()
可获得图像中一点到轮廓线的最短距离;点在轮廓线外时距离为负,在轮廓线内时为正,在轮廓线上时为零。
如检查点(50, 50):
dist = cv.pointPolygonTest(cnt, (50, 50), True)
第三个参数是measureDist,值为True时,函数返回有符号的距离;为False时,则查找该点是在轮廓线内部(函数返回1)、外部(返回-1)、之上(返回0)。找距离是一个耗时的过程,若不想找到此距离,将第三个参数设置为False可使速度提高2-3倍。
12. 形状匹配
函数cv.matchShapes()
可以比较两个形状或两个轮廓,并返回一个显示相似性的度量;结果越低,匹配越好,它是根据矩值计算出来的。
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('./OpenCV/hand.png')
rimg = cv.imread('./OpenCV/rhand.png')
limg = cv.imread('./OpenCV/lozenge.png')
ret, thresh = cv.threshold(cv.cvtColor(img, cv.COLOR_BGR2GRAY), 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
# 获取结构元素
k = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
# 开操作
thresh = cv.morphologyEx(thresh, cv.MORPH_OPEN, k)
contours, hierachy = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 在原图上绘制轮廓,以方便和凸包对比,发现凸缺陷
cv.drawContours(img, contours, -1, (0, 225, 0), 2)
pimg = img.copy()
cnt = contours[0]
for c in range(len(contours)):
# 是否为凸包
ret = cv.isContourConvex(contours[c])
if ret:
cnt = contours[c]
break
hull = cv.convexHull(cnt, returnPoints=False)
defects = cv.convexityDefects(cnt, hull)
for i in range(defects.shape[0]):
s, e, f, d = defects[i, 0]
start = tuple(cnt[s][0])
end = tuple(cnt[e][0])
far = tuple(cnt[f][0])
cv.line(img, start, end, (255, 255, 0), 2)
cv.circle(img, far, 3, (0, 0, 255), -1)
# 检查点(50, 50)与轮廓的距离或关系
dist = cv.pointPolygonTest(cnt, (50, 50), True)
print('点(50, 50)与轮廓的距离是:', dist)
ison = cv.pointPolygonTest(cnt, (50, 50), False)
if ison == 1:
print('点(50, 50)在轮廓内部')
elif ison == -1:
print('点(50, 50)在轮廓外部')
else:
print('点(50, 50)在轮廓上')
ret, rthresh = cv.threshold(cv.cvtColor(rimg, cv.COLOR_BGR2GRAY), 127, 255, 0)
ret, lthresh = cv.threshold(cv.cvtColor(limg, cv.COLOR_BGR2GRAY), 127, 255, 0)
rcontours,hierarchy = cv.findContours(rthresh, 2, 1)
cv.drawContours(rimg, rcontours, -1, (0, 225, 0), 2)
rcnt = rcontours[0]
lcontours,hierarchy = cv.findContours(lthresh, 2, 1)
cv.drawContours(limg, lcontours, -1, (0, 225, 0), 2)
lcnt = lcontours[0]
ret1 = cv.matchShapes(cnt,rcnt,1,0.0)
ret2 = cv.matchShapes(cnt,lcnt,1,0.0)
ret3 = cv.matchShapes(cnt,cnt,1,0.0)
print('pimg与rimg相似性度量为:', ret1)
print('pimg与limg相似性度量为:', ret2)
print('pimg与自身相似性度量为:', ret3)
images = [img, pimg, rimg, limg]
titles = ['img', 'pimg', 'rimg', 'limg']
for i in range(4):
plt.subplot(2, 2, i + 1)
plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB)), plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
点(50, 50)与轮廓的距离是: -202.03960007879644
点(50, 50)在轮廓外部
pimg与rimg相似性度量为: 1.7976931348623157e+308
pimg与limg相似性度量为: 0.15376293094777732
pimg与自身相似性度量为: 0.0
轮廓分层
使用函数cv.findcontour()
在图像中找到轮廓时,已经传递了一个参数,轮廓检索模式(通常使用cv.RETR_LIST
或cv.RETR_TREE
),效果很好。
层次结构是什么
使用函数cv.findcontour()
检测图像中的对象,有时对象在不同的位置;在某些情况下,某些形状在其他形状中,像嵌套的图形一样,在这种情况下,将外部的称为父类,内部的称为子类;这样,图像中的轮廓有了一定的相互关系,可以指定一个轮廓是如何相互连接的,比如,它是另一个轮廓的子轮廓,这种关系的表示称为层次结构。
下面是一个例子:
在这张图中,一些形状已经从0-5开始编号。2和2a分别表示最外层盒子的外部和内部轮廓。
这里,等高线0,1,2在外部或最外面。可以说,它们在层级-0
中,或者简单地说,它们在同一个层级中。
其次是contour-2a。它可以被认为是contour-2的子级(或者反过来说contour-2是contour-2a的父级),contour-3是contour-2的子级。
OpenCV中的分级表示
每个轮廓都有它自己的信息关于它是什么层次,谁是它的孩子,谁是它的父母等等。OpenCV将它表示为一个包含四个值的数组:[Next, Previous, First_Child, Parent]
Next表示同一层次的下一个轮廓:
在图片中取contour-0,谁是下一个同级别的等高线?这是contour-1,令Next = 1;类似地取contour-1,下一个同级别的是contour-2,所以Next = 2;取contour-2呢?同一级别上没有下一条等高线,Next = -1。
Previous表示同一层次上的先前轮廓:
contour-1之前的等值线为同级别的contour-0;对于contour-0,没有前项,所以Previous设为-1。
First_Child表示它的第一个子轮廓:
对于contour-2, child是contour-2a,First_Child得到contour-2a对应的索引值;contour-3a有两个孩子,只关注第一个孩子。它是contour-4。
Parent表示其父轮廓的索引:
对于contour-3a,它的父轮廓是contour-3。
轮廓检索模式
1、RETR_LIST
它只是检索所有的轮廓,但不创建任何亲子关系。在这个规则下,父轮廓和子轮廓是平等的,它们只是轮廓,都属于同一层级。
因此,此种模式下,数组中[Next, Previous, First_Child, Parent]
的第3和第4项总是-1。
若没有使用任何层次结构特性,此模式是使用的最佳选择。
2. RETR_EXTERNAL
使用此标志,它只返回极端外部标志。根据这项规则,每个家庭只有长子得到关注,不关心家庭的其他成员。在我们的图像中,有多少个极端的外轮廓?在等级0级?有3个,即等值线是0 1 2。
若只想提取外部轮廓,可以使用此标志。
3. RETR_CCOMP
此标志检索所有轮廓并将其排列为2级层次结构。物体的外部轮廓(即物体的边界)放在层次结构-1中。对象内部孔洞的轮廓(如果有)放在层次结构-2中。如果其中有任何对象,则其轮廓仅在层次结构1中重新放置。以及它在层级2中的漏洞等等。
只需考虑在黑色背景上的“白色的零”图像。零的外圆属于第一级,零的内圆属于第二级。
可以用一个简单的图像来解释它。用橙色标注了等高线的顺序;绿色标注了它们所属的层次,顺序与OpenCV检测等高线的顺序相同。
考虑第一个轮廓,即contour-0。这是hierarchy-1。它有两个孔,分别是等高线1和2,属于第二级。因此,对于轮廓-0,在同一层次的下一个轮廓是轮廓-3。previous也没有。在hierarchy-2中,它的第一个子结点是contour-1。它没有父类,因为它在hierarchy-1中。所以它的层次数组是[3,-1,1,-1]。
现在contour-1。它在层级-2中。相同层次结构中的下一个(在contour-1的父母关系下)是contour-2。没有previous。没有child,但是parent是contour-0。所以数组是[2,-1,-1,0]。
类似的contour-2:它在hierarchy-2中。在contour-0下,同一层次结构中没有下一个轮廓。所以没有Next。previous是contour-1。没有child,parent是contour0。所以数组是[-1,1,-1,0]。
contour-3:层次-1的下一个是轮廓-5。以前是contour-0。child是contour4,没有parent。所以数组是[5,0,4,-1]。
contour-4:它在contour-3下的层次结构2中,它没有兄弟姐妹。没有next,没有previous,没有child,parent是contour-3。所以数组是[-1,-1,-1,3]。
4. RETR_TREE
它检索所有的轮廓并创建一个完整的家族层次结构列表,谁是爷爷,父亲,儿子,孙子…
用橙色标注了等高线的顺序;绿色标注了它们所属的层次
取contour-0:它在hierarchy-0中。同一层次结构的next轮廓是轮廓-7。没有previous的轮廓。child是contour-1,没有parent。所以数组是[7,-1,1,-1]
以contour-2为例:它在hierarchy-1中。没有轮廓在同一水平。没有previous。child是contour-3。父母是contour-1。所以数组是[-1,-1,3,1]
学习来源:OpenCV-Python中文文档
参考:opencv学习—cv2.findContours()函数讲解(python)
OpenCV自学记录(5)——凸包检测和凸缺陷