Skip to main content

番外篇: 凸包及更多轮廓特征

计算凸包及更多轮廓特征。图片等可到文末引用处下载。

多边形逼近

前面我们学习过最小外接矩和最小外接圆,那么可以用一个最小的多边形包围物体吗?当然可以:

import cv2
import numpy as np

# 1.先找到轮廓
img = cv2.imread('unregular.jpg', 0)
_, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
image, contours, hierarchy = cv2.findContours(thresh, 3, 2)
cnt = contours[0]

# 2.进行多边形逼近,得到多边形的角点
approx = cv2.approxPolyDP(cnt, 3, True)

# 3.画出多边形
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
cv2.polylines(image, [approx], True, (0, 255, 0), 2)

其中cv2.approxPolyDP()的参数 2(epsilon)是一个距离值,表示多边形的轮廓接近实际轮廓的程度,值越小,越精确;参数 3 表示是否闭合。

凸包

凸包跟多边形逼近很像,只不过它是物体最外层的"凸"多边形:集合 A 内连接任意两个点的直线都在 A 的内部,则称集合 A 是凸形的。如下图,红色的部分为手掌的凸包,双箭头部分表示凸缺陷(Convexity Defects),凸缺陷常用来进行手势识别等:

# 1.先找到轮廓
img = cv2.imread('convex.jpg', 0)
_, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
image, contours, hierarchy = cv2.findContours(thresh, 3, 2)
cnt = contours[0]

# 2.寻找凸包,得到凸包的角点
hull = cv2.convexHull(cnt)

# 3.绘制凸包
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
cv2.polylines(image, [hull], True, (0, 255, 0), 2)

其中函数cv2.convexHull()有个可选参数 returnPoints,默认是 True,代表返回角点的 x/y 坐标;如果为 False 的话,表示返回轮廓中是凸包角点的索引,比如说:

print(hull[0])  # [[362 184]](坐标)
hull2 = cv2.convexHull(cnt, returnPoints=False)
print(hull2[0]) # [510](cnt中的索引)
print(cnt[510]) # [[362 184]]

当使用cv2.convexityDefects()计算凸包缺陷时,returnPoints 需为 False,详情可参考:Convexity Defects

另外可以用下面的语句来判断轮廓是否是凸形的:

print(cv2.isContourConvex(hull))  # True

点到轮廓距离

cv2.pointPolygonTest()函数计算点到轮廓的最短距离(也就是垂线),又称多边形测试:

dist = cv2.pointPolygonTest(cnt, (100, 100), True)  # -3.53

其中参数 3 为 True 时表示计算距离值:点在轮廓外面值为负,点在轮廓上值为 0,点在轮廓里面值为正;参数 3 为 False 时,只返回-1/0/1 表示点相对轮廓的位置,不计算距离。

更多轮廓特征,如当量直径、平均强度等,我目前也没用到过,以后用到再写吧,感兴趣的可以参看:Contour PropertiesContours Hierarchy

引用