一个跟轮廓相关的最常用到的功能是如何匹配多条轮廓。我们或许需要比较两条计算好的轮廓,或者比较一条轮廓和一个抽象模板。这两种情况都会在本文讨论。
矩
- 相关介绍
- 比较两条轮廓最简洁的方法之一是比较它们的轮廓矩。轮廓矩代表了一条轮廓、一幅图像、一组点集的某些高级特征。下面的所有讨论对轮廓、图像、点集都同样适用,简便起见,将它们统称为对象。矩的数值定义如下式:
在上式中, m_{p, q} 代表对象中所有像素的总和, 其中每个像素 x, y 的像素值都乘以因子 x^{p} y^{q} , 在 m_{00} 时, 这个因子等于 1 。.
- 因此若图像为二值图(例如,所有像素都等于0或者1),则 m_{00} 代表图像上所有值非零的区域。当处理轮廓时,结果是轮廓的长度。
- 将m_{10}和m_{01}相加再除以mo,能得到整个对象的平均x值和y值。
cv2.moments
计算多边形或光栅化形状的所有矩,最高可达三阶。 官方文档
- 仅适用于来自 Python 绑定的轮廓矩计算: 注意,输入数组的 numpy 类型应该是
np.int32
或np.float32
。 - 函数使用
cv2.moments(
array[, # 单通道2D图像
binaryImage] # 如果为真,所有非零的图像像素将被视为1。该参数仅用于图像。
) ->
retval # 矩结果
- retval 包含多组矩:
矩名称 | 含义 |
---|---|
m00 | 零阶矩 |
m10, m01 | 一阶矩 |
m20, m11, m02 | 二阶矩 |
m30, m12, m21, m03 | 三阶矩 |
mu20, mu11, mu02 | 二阶中心距 |
mu30, mu21, mu12, mu03 | 三阶中心距 |
nu20, nu11, nu02 | 二阶归一化中心矩 |
nu30, nu21, nu12, nu03 | 三阶归一化中心矩 |
- 示例代码
img = 255 - mt.cv_rgb_imread('conc.png', gray=True)
contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
res = np.zeros_like(img)
cv2.drawContours(res, [contours[0]], -1, [200], 3)
mom_res = cv2.moments(res, True)
-->
mom_res
{
'm00':5991.0
'm10':1325055.0
'm01':1749761.0
'm20':344323579.0
'm11':349473277.0
'm02':669951437.0
'm30':96933608139.0
'm21':85341328483.0
'm12':127841748675.0
'm03':288309060455.0
'mu20':51255518.07110667
'mu11':-37528819.78768152
'mu02':158907611.74194622
'mu30':-1894707829.496994
'mu21':1377304228.770937
'mu12':1587438731.2496796
'mu03':-182940816.065979
'nu20':1.428045313703228
'nu11':-1.0456016687269127
'nu02':4.4273724820231575
'nu30':-0.682015038155167
'nu21':0.49577152820752285
'nu12':0.571411100966244
'nu03':-0.06585099069469698
}
- 计算第二个 Hu 不变矩测试归一化中心矩:
- 公式为:
- 示例代码:
def h2(mom):
v20 = mom['nu20']
v02 = mom['nu02']
v11 = mom['nu11']
h2 = (v20 v02) ** 2 4 * (v11 ** 2)
return h2
img = 255 - mt.cv_rgb_imread('conc.png', gray=True)
contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
res = np.zeros_like(img)
cv2.drawContours(res, [contours[0]], -1, [200], -1)
another_res = mt.image_resize(np.rot90(res), factor=0.5)
mom_res = cv2.moments(res, True)
mom_another_res = cv2.moments(another_res, True)
h2_1 = h2(mom_res)
h2_2 = h2(mom_another_res)
PIS([res, str(h2_1)], [another_res, str(h2_2)])
Hu 矩
- Hu 矩
cv2.HuMoments
函数用于计算 Hu 矩: 官方代码
- 函数使用
cv2.HuMoments(
moments[, # cv2.moments 函数的输出结果
hu]
) ->
hu # 输出 7 个 Hu 不变矩
- 示例代码
img = 255 - mt.cv_rgb_imread('conc.png', gray=True)
contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
res = np.zeros_like(img)
cv2.drawContours(res, [contours[0]], -1, [200], -1)
another_res = mt.image_resize(np.rot90(res), factor=0.5)
mom_res = cv2.moments(res, True)
mom_another_res = cv2.moments(another_res, True)
hu_res = cv2.HuMoments(mom_res)
hu_another_res = cv2.HuMoments(mom_another_res)
-->
hu_res
array([[2.65472790e-01],
[3.67973951e-02],
[4.31746085e-03],
[1.65053925e-03],
[4.40583128e-06],
[3.16616604e-04],
[4.73966676e-08]])
hu_another_res
array([[2.65294556e-01],
[3.67252753e-02],
[4.27103909e-03],
[1.62915379e-03],
[4.29714551e-06],
[3.12207937e-04],
[5.03721607e-08]])
使用Hu矩进行匹配
- 我们想要使用Hu矩比较两个物体,并判定它们是否相似。对“相似”的定义可能有很多。为了使比较过程变得简单,OpenCV的函数
cv2.matchShapes
允许我们简单提供两个物体,然后计算它们的矩,并根据我们提供的标准进行比较。
cv2.matchShapes
该函数比较两个形状,所有三个实现的方法都使用 Hu 不变量。 官方文档
- 函数使用
cv2.matchShapes(
contour1, # 第一个轮廓或灰度图像。
contour2, # 第二轮廓或灰度图像。
method, # 比对方法
parameter # 方法参数(OpenCV 4.5.5 暂时还不支持)
) ->
retval
method:ShapeMatchModes
- A, B 分别表示两个输入的物体轮廓
- 其中 h_{i}^{A}, h_{i}^{B} 为 A,B 的 Hu 不变矩
- 示例代码
img = 255 - mt.cv_rgb_imread('conc.png', gray=True)
contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
res1 = np.zeros_like(img)
res2 = np.zeros_like(img)
cv2.drawContours(res1, [contours[0]], -1, [200], -1)
cv2.drawContours(res2, [contours[6]], -1, [200], -1)
another_res = mt.image_resize(np.rot90(res1), factor=0.5)
match_res1 = cv2.matchShapes(res1, another_res, cv2.CONTOURS_MATCH_I1, None)
match_res2 = cv2.matchShapes(res1, res2, cv2.CONTOURS_MATCH_I1, None)
PIS([res1, 'source img'], [another_res, f"match score {format(match_res1, '.3f')}"], [res2, f"match score {format(match_res2, '.3f')}"])
利用形状场景方法比较轮廓
OpenCV 努力提供比矩匹配更好的形状匹配算法
https://docs.opencv.org/4.5.5/d1/d85/group__shape.html#ga1d058c5d00f6292da61422af2f3f4adc
- 在 OpenCV 4.5.5 中还没有实现,有传说在 3.5 的版本中有相关函数
源码
https://github.com/zywvvd/Python_Practise/tree/master/OpenCV/Chapter 14
参考资料
- 《学习OpenCV》 第十四章