使用改进的 Hausdorff 距离查找形状

Finding shapes using Modified Hausdorff Distance

我有 2 张图像,它们具有相同的形状,但排列在不同的位置。我想正确匹配这些图像。


执行的步骤...

  1. 从源图像获取轮廓。
  2. 从目标图像获取轮廓。
  3. 使用修正的 Hausdorff 距离比较从源到目标的轮廓。
  4. 取最小值作为匹配。

    def modified_hausdorff(A,B): D = cdist(A,B) #euclidean distance fhd = np.mean(np.min(D,axis=0)) rhd = np.mean(np.min(D,axis=1)) return max(fhd,rhd)

源图片。

目标图片。

这是一个双向任务。

前进方向


1.翻译

对于每个轮廓,计算其 moment。然后对于该轮廓中的每个点,将其平移到时刻,即 contour.point[i] = contour.point[i] - contour.moment[i]。这会将所有轮廓点移动到原点。

PS: 你需要跟踪每个轮廓的产生力矩,因为它将在下一节中使用

2。旋转

有了新翻译的点数,计算它们的rotated rect. This will give you the angle of rotation. Depending on this angle, you would want to calculate the new angle which you want to rotate this contour by; this answer会很有帮助。

获得新角度后,计算rotation matrix。请记住,您在这里的中心将是原点,即 (0, 0)。在计算旋转矩阵时我没有考虑缩放比例(这就是金字塔发挥作用的地方)因此我通过了 1.

PS: 你需要跟踪每个轮廓的生成矩阵,因为它会在下一节中使用

使用此矩阵,您可以继续旋转轮廓中的每个点,如图所示 here*.

完成所有这些后,您可以继续计算 Hausdorff 距离并找到通过设置阈值的轮廓。


反向

第一部分中完成的所有操作都必须撤消,以便我们将有效的轮廓绘制到我们的相机画面上。


1.旋转

回想一下,每个检测到的轮廓都会产生一个旋转矩阵。您想要撤消有效轮廓的旋转。只需执行相同的旋转,但使用 inverse matrix.

For each valid contour and corresponding matrix
inverse_matrix = matrix[i].inv(cv2.DECOMP_SVD)
Use * to rotate the points but with inverse_matrix as parameter

PS:求逆时,如果生成的矩阵不是方阵,会失败。 cv2.DECOMP_SVD 将生成逆矩阵,即使原始矩阵不是正方形。

2。翻译

随着有效等高线的点向后旋转,您只需撤消之前执行的平移即可。不要减去,只需将力矩添加到每个点即可。

您现在可以继续将这些轮廓绘制到您的相机画面中。


缩放


这是图像金字塔发挥作用的地方。

您所要做的就是将模板图​​像的大小固定 size/ratio 调整到您想要的次数(称为图层)。找到的教程 here 很好地解释了如何在 OpenCV 中执行此操作。

不用说,您选择的用于调整图像大小的值和层数将并且确实对您的程序的健壮性起着巨大的作用。


全部放在一起

模板图像操作

Create a pyramid consisting of n layers
For each layer in n
    Find contours
    Translate the contour points
    Rotate the contour points

这个操作应该只执行一次并且只存储旋转点的结果。

相机馈送操作

假设

让模板图像在每一层旋转后的轮廓存储在templ_contours中。因此,如果我说 templ_contours[0],这将给我在金字塔级别 0 的旋转轮廓。

让图像的平移、旋转轮廓和力矩分别存储在transControtContmoment中。

image_contours = Find Contours
for each contour detected in image
    moment = calculate moment

for each point in image_contours
    transCont.thisPoint = forward_translate(image_contours.thisPoint)
    rotCont.thisPoint = forward_rotate(transCont.thisPoint)

for each contour_layer in templ_contours
    for each contour in rotCont
        calculate Hausdorff Distance
        valid_contours = contours_passing_distance_threshold

for each point in valid_contours
    valid_point = backward_rotate(valid_point)

for each point in valid_contours
    valid_point = backward_translate(valid_point)

drawContours(valid_contours, image)

一开始可能有点混乱,尤其是在跟踪每个轮廓各自的力矩和旋转矩阵时,但是一旦您理解了发生了什么,它确实是一个非常容易实现的算法。