cv2传统方法-计算两个点集之间的最短距离

引言

适用于二维图像上的点集:点的数组(N, 2)、两个轮廓(M,1,2)、两个凸包等。

第一种GJK算法几乎实时,非常快。

第二种算法很常规,引用ultralytics里的函数。

后续如有其他算法,再补充。

Gilbert–Johnson–Keerthi distance algorithm

GJK距离算法:计算两个凸包(cv2.convexHull)之间最短距离及最近点坐标,判断有无交集。

###############################################################################
#
# Implementation of the Gilbert–Johnson–Keerthi distance algorithm.
# See:
#  * https://en.wikipedia.org/wiki/Gilbert%E2%80%93Johnson%E2%80%93Keerthi_distance_algorithm
#  * the original paper: A fast procedure for computing the distance between complex objects 
#    in three-dimensional space,  E.G. Gilbert ; D.W. Johnson ; S.S. Keerthi 
#  * Real-Time Collision Detection, Christer Ericson
# 
#
import numpy as np
import itertools


###############################################################################
# Support function for a convex shape:
def support(poly, direction):
    dot_products = poly.dot(direction)
    idx = np.argmax(dot_products)
    return poly[idx]


###############################################################################
# Find the closest point to the origin in the affine hull of the simplex. 
# The affine hull is much larger than the convex hull because it allows negative coefficients,
# so the closest point might not be in the simplex. Return whether or not the closest
# point is in the simplex, and the closest point.
# 
def simplex_closest_point(simplex, tol=1e-6):
    if simplex.shape[0] == 1:
        return True, simplex[0]

    x0 = simplex[0]
    d = simplex[1:] - x0

    D = d.dot(d.T)
    b = -d.dot(x0)

    a = np.linalg.solve(D, b)

    min = np.min(a)
    sum = np.sum(a)

    in_face = min > -tol and sum < 1 + tol

    p = x0 + a.dot(d)
    return in_face, p


###############################################################################
# Given a list of points, find the point on or in the simplex closest to the origin (it will only
# be inside if the simplex contains the origin).
# If the closest point is not the origin, return closest point and the the smallest subset if the origin
# simplex so that the simplex made by the subset contains the closest point.
# The original points are assumed to be a in tuple, which is preserved (this is done so
# we can track the points in the original shapes).
#
def update_simplex(simplex_list, tol=1e-6):
    nump_points = len(simplex_list)

    best_p = None

    # To find the smallest sub-simplex with the closest point, we iterate over all possible
    # subsets, starting with the smallest, and keep the one that produces the smallest magnitude.
    #
    for i in range(1, nump_points + 1):
        for sub_tuple in itertools.combinations(simplex_list, i):
            sub_simplex = np.vstack([t[0] for t in sub_tuple])
            in_face, p = simplex_closest_point(sub_simplex, tol)

            if in_face:
                d2 = p.dot(p)

                if best_p is None or d2 < best_d2:
                    best_d2 = d2
                    best_p = p
                    best_sub_tuple = sub_tuple

    # Since we test 0-d simplices (points), there should alway be a best point:
    assert (best_p is not None)
    return best_p, list(best_sub_tuple)


def support_point(direction, poly_A, poly_B):
    a = support(poly_A, direction)
    b = support(poly_B, -direction)

    return a - b, (a, b)


###############################################################################
# Test to convex shapes for intersection. Return True if they intersect.
# If they don't intersect, return the closest distance between them, along
# with a list of tuple with points in each shape that form the closest features.
# Depending on the situation there might be duplications.
#
def GJK(poly_A, poly_B, epsilon=0.05):
    dimension = poly_A.shape[1]

    initial_dir = np.ones(dimension)
    p, sups = support_point(initial_dir, poly_A, poly_B)
    simplex_list = [(p, sups)]

    while True:

        # This is the termination condition, which is not trivial to understand. See eqn 25
        # (and the proof in Appendix 1) in the original GJK paper for an
        # explanation:
        next, next_sups = support_point(-p, poly_A, poly_B)
        dp = p.dot(p) - next.dot(p)

        if dp < epsilon * epsilon:
            return False, np.linalg.norm(p), [t[1] for t in simplex_list]

        simplex_list.append((next, next_sups))
        p, new_simplex_list = update_simplex(simplex_list)

        pl2 = p.dot(p)

        if pl2 < epsilon * epsilon:
            return True, 0., None

        simplex_list = new_simplex_list


# *** 用法 ***
# 计算轮廓
jx_contour, jq_contour = cv2.findContours(...), ...
# 计算凸包
jx_hull = cv2.convexHull(jx_contour)
jq_hull = cv2.convexHull(jq_contour)
# 碰撞flag, 最近距离, 最近点集
pz_flag, dis, _ = GJK(jx_hull, jq_hull)
if dis < self.min_dis:
    jx_pz_jq_ids[idx].append(idy)

ultralytics/data/converter.py里的min_index(arr1, arr2)

YOLOv8里的源码实现,采用矩阵计算。

def min_index(arr1, arr2):
    """
    Find a pair of indexes with the shortest distance between two arrays of 2D points.

    Args:
        arr1 (np.array): A NumPy array of shape (N, 2) representing N 2D points.
        arr2 (np.array): A NumPy array of shape (M, 2) representing M 2D points.

    Returns:
        (tuple): A tuple containing the indexes of the points with the shortest distance in arr1 and arr2 respectively.
    """
    dis = ((arr1[:, None, :] - arr2[None, :, :]) ** 2).sum(-1)
    return np.unravel_index(np.argmin(dis, axis=None), dis.shape)

POJ算法题:从点集中找到距离最远的两个点

算法题:http://poj.org/problem?id=2187

https://blog.csdn.net/u010585135/article/details/41897937

  • 14
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值