文章目录
【youcans 的 OpenCV 例程200篇】197.轮廓的基本特征
2. 轮廓的特征描述
在对实际图像进行轮廓查找时,得到的轮廓数量很多。获取轮廓后,通常基于轮廓的特征进行筛选、识别和处理。例如,基于轮廓的周长和面积对轮廓进行筛选,然后绘制筛选的目标轮廓或其最小外接矩形。
常用的轮廓特征,包括图像距、轮廓周长、轮廓近似、凸包、边界矩形、拟合图形等特征。
2.2 轮廓的面积、周长、质心和近似多边形
面积、周长、质心是常用的轮廓特征。
2.2.1 轮廓的面积
几何矩实质是面积或质量。函数 cv.moments() 的返回值 Moments[‘m00’] 表示轮廓面积。
轮廓的面积也可以使用函数 cv2.contourArea() 计算。
函数说明:
cv.contourArea(contour[, oriented] ) → retval
- contour:顶点构成的二维向量组(如轮廓列表 contours 中的一个轮廓)
- oriented:定向区域标志,默认值为 False,返回面积的绝对值,Ture 时则根据轮廓方向返回带符号的数值
2.2.2 轮廓的周长
轮廓的周长可以使用函数 cv2.arcLength() 计算。
函数说明:
cv.arcLength(curve, closed=True) → retval
- curve:以顶点构成的二维向量组表示的曲线(如轮廓列表 contours 中的一个轮廓)
- closed:曲线闭合标志,True 表示闭合曲线
2.2.3 轮廓的质心
轮廓的质心 (Cx,Cy) 可以通过一阶矩计算:
C
x
=
M
10
/
M
00
C
y
=
M
01
/
M
00
Cx = M_{10} / M_{00} \\ Cy = M_{01} / M_{00}
Cx=M10/M00Cy=M01/M00
2.2.4 轮廓的近似
轮廓的近似是用顶点数量较少的多边形对轮廓进行近似,可以使用函数 cv2. approxPolyDP() 实现。近似多边形的边数取决于设定的最大近似距离。
函数说明:
cv.approxPolyDP(curve, epsilon, closed[, approxCurve]) → approxCurve
- curve:以顶点构成的二维向量组表示的曲线(如轮廓列表 contours 中的一个轮廓)
- approxCurve:近似多边形顶点坐标 (x,y) 的二维向量组
- epsilon:近似精度,浮点数,原始曲线与近似多边形之间的最大距离
- closed:曲线闭合标志,True 表示近似曲线是闭合的
2.2.5 轮廓的凸包(凸壳)
物体的凸包(凸壳)是指包含该物体的最小凸面体。在二维图像中凸壳可以想象为一条刚好包着所有点的橡皮圈。在凸壳与物体边缘之间的部分称为凸陷(Convexity defect)。
OpenCV 中的函数 cv.isContourConvex() 测试轮廓是否为凸面体,函数 cv.convexHull() 获取轮廓的凸壳 。
函数说明:
cv.convexHull(points[, hull[, clockwise[, returnPoints]]]) → hull
cv.isContourConvex( contour) → retval
参数说明:
- points:二维点向量集
- contour:二维点向量集(如轮廓列表 contours 中的一个轮廓)
- hull:输出凸包,凸包顶点的索引向量,或凸包顶点坐标 (x,y) 的二维向量组
- clockwise:方向标志,默认值 False 表示逆时针方向输出凸包,True 为是顺时针
- returnPoints:操作标志,默认值 True 表示返回凸包点集, False 返回凸包点的索引
注意事项:
函数 cv.isContourConvex() 测试轮廓是否为凸面,输入的轮廓必须没有自交叉线。
函数 cv.convexHull() 与函数 cv.drawContours() 配合,可以用来检测物体是否存在缺陷。
例程 12.4:轮廓的面积、周长、质心和近似多边形
# 12.4 轮廓的面积、周长、质心和近似多边形
img = cv2.imread("../images/seagull01.png", flags=1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度图像
plt.figure(figsize=(9, 6))
plt.subplot(231), plt.axis('off'), plt.title("Origin")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
# HSV 色彩空间图像分割
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 将图片转换到 HSV 色彩空间
lowerBlue, upperBlue = np.array([100, 43, 46]), np.array([124, 255, 255]) # 蓝色阈值
segment = cv2.inRange(hsv, lowerBlue, upperBlue) # 背景色彩图像分割
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # (5, 5) 结构元
binary = cv2.dilate(cv2.bitwise_not(segment), kernel=kernel, iterations=3) # 图像膨胀
plt.subplot(232), plt.axis('off'), plt.title("Tree contour")
plt.imshow(binary, 'gray')
# 寻找二值化图中的轮廓
# binary, contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # OpenCV3
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # OpenCV4~
print("len(contours) = ", len(contours)) # 所有轮廓的列表
# # 绘制全部轮廓,contourIdx=-1 绘制全部轮廓
for i in range(len(contours)): # 绘制第 i 个轮廓
if hierarchy[0][i][3]==-1: # 最外层轮廓
rect = cv2.minAreaRect(contours[i]) # 最小外接矩形
x, y = int(rect[0][0]), int(rect[0][1]) # 最小外接矩形的中心(x,y)
text = "{}:({},{})".format(i, x, y)
img = cv2.drawContours(img, contours, i, (255, 255, 255), -1) # 绘制第 i 个轮廓, 内部填充
img = cv2.putText(img, text, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255))
# print("i=", i, ",contours[i]:", contours[i].shape, ",hierarchy[0][i] =", hierarchy[0][i], "text=", text)
plt.subplot(233), plt.axis('off'), plt.title("Contours")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
# 轮廓的特征矩
cnt = contours[0] # 第 0 个轮廓
moments = cv2.moments(cnt) # 返回字典,几何矩 mpq, 中心矩 mupq 和归一化矩 nupq
huM = cv2.HuMoments(moments) # 计算 Hu 不变矩
# 轮廓面积
area = cv2.contourArea(cnt)
print("area by moments['m00']: ", moments['m00'])
print("area of contour: ", area)
# 轮廓的质心 (Cx, Cy)
if moments['m00'] > 0:
cx = round(moments['m10'] / moments['m00'])
cy = round(moments['m01'] / moments['m00'])
print("centroid of contour: ({}, {})".format(cx, cy))
cv2.circle(img, (cx, cy), 5, (0, 0, 255), -1) # 在轮廓的质心上绘制圆点
plt.subplot(234), plt.axis('off'), plt.title("Contour centroid")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
else:
print("moments['m00'] = 0")
# 轮廓的周长
perimeter = cv2.arcLength(cnt, True) # True 表示输入是闭合轮廓
print("centroid of perimeter: {:.1f}".format(perimeter))
# 轮廓近似多边形
epsilon = 0.01 * cv2.arcLength(contours[1], True)
approx = cv2.approxPolyDP(contours[1], epsilon, True)
approx.shape: (15, 1, 2)
cv2.polylines(img, [approx], True, (0, 0, 255), 2) # 绘制多边形
plt.subplot(235), plt.axis('off'), plt.title("Approximate polygon")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
# 检查轮廓是否为凸面体
isConvex = cv2.isContourConvex(contours[2]) # True 凸面体, False 非凸
print("contours[2] ContourConvex?", isConvex)
# 获取轮廓的凸壳
hull = cv2.convexHull(contours[2], returnPoints=True) # 返回轮廓凸壳顶点坐标
print("hull.shape: ", hull.shape) # 凸壳顶点坐标 (x,y), (24, 1, 2)
cv2.polylines(img, [hull], True, (0, 0, 255), 2) # 绘制多边形
plt.subplot(236), plt.axis('off'), plt.title("Contour convex")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.tight_layout()
plt.show()
运行结果:
len(contours) = 6
area by moments['m00']: 15523.0
area of contour: 15523.0
centroid of contour: (394, 380)
centroid of perimeter: 783.5
approx.shape: (15, 1, 2)
contours[2] ContourConvex? False
hull.shape: (24, 1, 2)
(本节完)
版权声明:
youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/125023990)
Copyright 2022 youcans, XUPT
Crated:2022-5-31
欢迎关注 『youcans 的 OpenCV 例程 200 篇』 系列,持续更新中
欢迎关注 『youcans 的 OpenCV学习课』 系列,持续更新中【youcans 的 OpenCV 例程200篇】194.寻找图像轮廓(cv.findContours)
【youcans 的 OpenCV 例程200篇】195.绘制图像轮廓(cv.drawContours)
【youcans 的 OpenCV 例程200篇】196.图像的矩和不变矩(cv.moments)
【youcans 的 OpenCV 例程200篇】197.轮廓的基本特征
【youcans 的 OpenCV 例程200篇】198.基于不变矩的形状相似性检测
【youcans 的 OpenCV 例程200篇】199.轮廓的外接边界框
【youcans 的 OpenCV 例程200篇】200.轮廓的基本属性
更多内容,请见:
【OpenCV 例程200篇 总目录-202206更新】