「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战」
图像矩
在数学中,矩表示函数形状的特定定量测量。而在计算机数据领域,图像矩可以视为图像像素强度的加权平均值,其编码了图像的一些特性。因此,图像矩可用于描述检测到的轮廓的一些性质(例如,物体的质心,或物体的区域等)。
OpenCV 中 提供了 cv2.moments() 函数用于计算向量形状或栅格化形状的三阶矩,函数用法如下:
retval = cv.moments(array[, binaryImage])
因此,可以使用以下方式计算检测到的轮廓(例如,检测到的第一个轮廓)的矩:
M = cv2.moments(contours[0])
我们打印 M,以查看图像矩的信息:
{'m00': 203700.5, 'm10': 65138893.0, 'm01': 65184079.166666664, 'm20': 24157077178.583332, 'm11': 20844151188.958332, 'm02': 24186367618.25, 'm30': 9853254039349.8, 'm21': 7730082994775.5, 'm12': 7733632427205.399, 'm03': 9869218925404.75,
'mu20': 3327106799.2006187, 'mu11': -268722.3380508423, 'mu02': 3327488151.551258, 'mu30': 487977833.58203125, 'mu21': -253389.3426513672, 'mu12': -458453806.3643799, 'mu03': 1109170.4453125,
'nu20': 0.08018304628713532, 'nu11': -6.476189966458202e-06, 'nu02': 0.08019223685270128, 'nu30': 2.605672665422043e-05, 'nu21': -1.3530321224005687e-08, 'nu12': -2.448022162878717e-05, 'nu03': 5.9226770393023014e-08}
如上所示,有三种不同类型的矩,包括 m_ji,mu_ji 和 nu_ji。
m_ji 表示空间矩,其计算公式如下:
mu_ji 表示中心矩,其计算公式如下:
其中:
通过定义,可知中心矩是具有平移不变性。因此,中心矩适合描述物体的形状。然而,空间矩和中心矩的缺点是它们依赖于对象的大小,它们不具尺度不变性。
nu_jl 表示归一化中心矩,其计算公式如下:
根据定义可知,归一化中心矩是具有平移和缩放不变量。
接下来,将计算基于矩的一些对象特征(例如,中心,偏心或轮廓的区域)。
一些基于矩的对象特征
我们已经知道了,矩是从轮廓计算的特征,虽然其没有直接理解表征其几何含义,但可以根据矩计算一些几何特性。
接下来,我们首先计算检测到的轮廓的矩,然后,据此计算一些对象特征:
M = cv2.moments(contours[0])
print("Contour area: '{}'".format(cv2.contourArea(contours[0])))
print("Contour area: '{}'".format(M['m00']))
矩 m_00 给出了轮廓的区域,这等价于函数cv2.contourArea()。要计算轮廓的质心,需要使用以下方法:
print("center X : '{}'".format(round(M['m10'] / M['m00'])))
print("center Y : '{}'".format(round(M['m01'] / M['m00'])))
圆度 是测量轮廓接近完美圆轮廓的程度,轮廓圆度计算公式如下:
其中,P 是轮廓的周长,A 是轮廓的区域面积。如果轮廓为圆形,其圆度为 1; 值越高,它将越不像圆:
def roundness(contour, moments):
"""计算轮廓圆度"""
length = cv2.arcLength(contour, True)
k = (length * length) / (moments['m00'] * 4 * np.pi)
return k
偏心率(也称为伸长率)是一种衡量轮廓伸长的程度。偏心 ε 可以直接从对象的长半轴 a 和短半轴 b 计算得出:
因此,计算轮廓的偏心度的一种方法是首先计算拟合轮廓的椭圆,然后从计算出的椭圆导出 a 和 b;最后,利用上述公式计算 ε:
def eccentricity_from_ellipse(contour):
"""利用拟合的椭圆计算偏心率"""
# 拟合椭圆
(x, y), (MA, ma), angle = cv2.fitEllipse(contour)
a = ma / 2
b = MA / 2
ecc = np.sqrt(a ** 2 - b ** 2) / a
return ecc
另一种方法是通过使用轮廓矩来计算偏心率:
接下来利用轮廓矩计算偏心率:
def eccentricity_from_moments(moments):
"""利用轮廓矩计算偏心率"""
a1 = (moments['mu20'] + moments['mu02']) / 2
a2 = np.sqrt(4 * moments['mu11'] ** 2 + (moments['mu20'] - moments['mu02']) ** 2) / 2
ecc = np.sqrt(1 - (a1 - a2) / (a1 + a2))
return ecc
纵横比是轮廓边界矩形的宽度与高度的比率,可以基于 cv2.boundingRect() 计算的最小边界矩形的尺寸来计算纵横比:
def aspect_ratio(contour):
"""计算纵横比"""
x, y, w, h = cv2.boundingRect(contour)
res = float(w) / h
return res
在下图中,通过可视化脚本中计算的所有对象属性来显示轮廓分析结果:
需要注意的是:在以上示例中,仅使用二阶矩计算简单对象特征。为了更精确的描述复杂对象,应该使用高阶矩或更复杂的矩,对象越复杂,为了最大限度地减少从矩重构对象的误差,应计算的矩阶越高。