OpenCV 轮廓 —— 轮廓匹配

2022-08-09 16:29:32 浏览数 (1)

一个跟轮廓相关的最常用到的功能是如何匹配多条轮廓。我们或许需要比较两条计算好的轮廓,或者比较一条轮廓和一个抽象模板。这两种情况都会在本文讨论。

  • 相关介绍
  • 比较两条轮廓最简洁的方法之一是比较它们的轮廓矩。轮廓矩代表了一条轮廓、一幅图像、一组点集的某些高级特征。下面的所有讨论对轮廓、图像、点集都同样适用,简便起见,将它们统称为对象。矩的数值定义如下式:
m_{p, q}=sum_{i=1}^{N} Ileft(x_{i}, y_{i}right) x^{p} y^{q}

在上式中, 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.int32np.float32
  • 函数使用
代码语言:javascript复制
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

三阶归一化中心矩

  • 示例代码
代码语言:javascript复制
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 不变矩测试归一化中心矩:
  • 公式为:
h_2=(v_{20} v_{02})^2 4v_{11}^2
  • 示例代码:
代码语言:javascript复制
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 矩: 官方代码

  • 函数使用
代码语言:javascript复制
cv2.HuMoments(	
	moments[, 				# cv2.moments 函数的输出结果
	hu]	
	) ->	
	hu						# 输出 7 个 Hu 不变矩

  • 示例代码
代码语言:javascript复制
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 不变量。 官方文档

  • 函数使用
代码语言:javascript复制
cv2.matchShapes(	
	contour1, 			# 第一个轮廓或灰度图像。
	contour2, 			# 第二轮廓或灰度图像。
	method, 			# 比对方法
	parameter			# 方法参数(OpenCV 4.5.5 暂时还不支持)
	) ->	
	retval

method:ShapeMatchModes

  • A, B 分别表示两个输入的物体轮廓

  • 其中 h_{i}^{A}, h_{i}^{B} A,B 的 Hu 不变矩
  • 示例代码
代码语言:javascript复制
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》 第十四章

0 人点赞