如何比较两条折线是否相等?
How to compare two poly lines for equality?
我有两条折线(路径),每条线都由一个二维点数组表示。我想计算两者之间的距离或相似度分数。每个阵列中可能有不同数量的点。如果您要绘制折线并且它们直接在彼此之上,则距离应该为零。
p1 = np.array([[0,0], [5,5], [9,9]])
p2 = np.array([[0,0], [3,3], [6,6], [9,9]])
p3 = np.array([[0,0], [3,4], [6,7], [9,9]])
p4 = np.array([[0,0], [0,9]])
polyline_dist(p1, p2) # Should be 0 since the plots are identical
polyline_dist(p1, p3) # Should be small since the plots are close
polyline_dist(p1, p4) # Should be larger since the plots are much different
我尝试了一种方法,计算数组 1 中的每个点到数组 2 中的线段的距离并取最小距离,然后取所有点的平均值。这行得通,但对于具有数百个点的较长数组来说速度很慢。
欢迎提出任何建议!
您可以尝试找出每个绘图投射到 x 轴和 y 轴上的区域,然后比较该区域的交集。与具有两条曲线 f(x) 和 g(x) 的微积分类似,您可以使用从 (f(x) - g(x)) 的下限到上限的积分找到曲线之间的区域dx。如果您的行没有重叠 domains/codomains,您可能需要添加一些惩罚并从 p1[0] 和 p2[0] 的最大值开始,到 p1[-1] 和 p2[ 的最小值结束-1]。相同的多段线彼此之间的距离为 0,因此每条多段线之间的面积为 0.You 将检查 x 轴和 y 轴以检查多段线区域高度垂直的情况。我写了下面的代码减去 sample_at_point 部分,因为它来晚了,所有这部分需要做的是找到给定轴的 p1 中 p 的上下边界点,然后定义一个线方程并找到在哪里通过的点 p 位于该线方程和 return 值上。我明天会编辑这个答案,但我想我会 post 我现在拥有的。此解决方案适用于您在上面显示的多段线,但如果多段线与基本曲线不相似(例如:circle/any 曲线,其中 f(x) 可能有两个不同的值),它将失败。
def polydist(p1, p2):
x_area = area_between_squared(p1, p2, axis=0, value=1)
y_area = area_between_squared(p1, p2, axis=1, value=0)
return (x_area + y_area)**0.5
def sample_at_point(p1, p, axis, value):
"""
# There should be a fast way to do this,
# Finnd the bounding points that p is between,
# the bounding points are the max point < p and the min point > p
# these points form a line,
# find the value of p applied to that line formula.
"""
def area_between_squared(p1, p2, axis, value):
start = max(p1[0][axis], p2[0][axis])
start_penalty = start - min(p1[0][axis], p2[0][axis])
end = min(p1[-1][axis], p2[-1][axis])
end_penalty = max(p1[-1][axis], p2[-1][axis]) - end
# Getting late, may edit this later tomorrow with the code for this to work but feel free to implement the following idea below:
axis_points = [x[axis] for x in p1]
axis_points.extend([x[axis] for x in p2])
axis_points.sort()
prev_p1_v = sample_at_point(p1, axis_points[0], axis, value)
prev_p2_v = sample_at_point(p2, axis_points[0], axis, value)
prev_axis_point = axis_points[0]
axis_points.pop(0) # remove the first item
sum = 0
for e in axis_points:
p1_v = sample_at_point(p1, e, axis, value)
p2_v = sample_at_point(p2, e, axis, value)
point_c = p1_v - p2_v
point_b = prev_p1_v - prev_p2_v
area = 0.5 * (point_c + point_b) * (e - prev_axis_point)
sum += area
prev_p1_v = p1_v
prev_p2_v = p2_v
prev_axis_point = e
sum *= sum # square the result value, add the penalties since this difference should be positive
sum += start_penalty + end_penalty
return sum
感谢您的评论和回答。他们实际上引导我找到了一个使用 OpenCV 函数 pointPolygonTest
的好解决方案,它比我写得不好的 numpy 代码优化得更多。
方法是通过以相反顺序附加点将多段线变成等高线,然后遍历第一个数组的点并调用 pointPolygonTest
来获取距离。我发现平方距离有助于放大较小的差异。然后我取所有点的平均值。
代码如下:
import cv2
import numpy as np
def point_to_polyline_dist(p, polyline):
cnt = np.concatenate((polyline, polyline[::-1]))
return np.abs(cv2.pointPolygonTest(cnt, (int(p[0]),int(p[1])), True))
def polyline_dist(polyline1, polyline2):
return np.mean([point_to_polyline_dist(p, polyline2)**2 for p in polyline1])
p1 = np.array([[0,0], [5,5], [9,9]])
p2 = np.array([[0,0], [3,3], [6,6], [9,9]])
p3 = np.array([[0,0], [3,4], [6,7], [9,9]])
p4 = np.array([[0,0], [0,9]])
print(polyline_dist(p1, p2)) # 0.0
print(polyline_dist(p1, p3)) # 0.1666666666666667
print(polyline_dist(p1, p4)) # 35.333333333333336
这对我来说效果很好,因为我的折线有很多短线段。计算两个阵列之间的距离并添加距离可能更稳健,如下所示:
def polyline_dist_better(polyline1, polyline2):
dist_1to2 = np.mean([point_to_polyline_dist(p, polyline2)**2 for p in polyline1])
dist_2to2 = np.mean([point_to_polyline_dist(p, polyline1)**2 for p in polyline2])
return dist_1to2 + dist_2to1
希望对外面的人有所帮助!
我有两条折线(路径),每条线都由一个二维点数组表示。我想计算两者之间的距离或相似度分数。每个阵列中可能有不同数量的点。如果您要绘制折线并且它们直接在彼此之上,则距离应该为零。
p1 = np.array([[0,0], [5,5], [9,9]])
p2 = np.array([[0,0], [3,3], [6,6], [9,9]])
p3 = np.array([[0,0], [3,4], [6,7], [9,9]])
p4 = np.array([[0,0], [0,9]])
polyline_dist(p1, p2) # Should be 0 since the plots are identical
polyline_dist(p1, p3) # Should be small since the plots are close
polyline_dist(p1, p4) # Should be larger since the plots are much different
我尝试了一种方法,计算数组 1 中的每个点到数组 2 中的线段的距离并取最小距离,然后取所有点的平均值。这行得通,但对于具有数百个点的较长数组来说速度很慢。
欢迎提出任何建议!
您可以尝试找出每个绘图投射到 x 轴和 y 轴上的区域,然后比较该区域的交集。与具有两条曲线 f(x) 和 g(x) 的微积分类似,您可以使用从 (f(x) - g(x)) 的下限到上限的积分找到曲线之间的区域dx。如果您的行没有重叠 domains/codomains,您可能需要添加一些惩罚并从 p1[0] 和 p2[0] 的最大值开始,到 p1[-1] 和 p2[ 的最小值结束-1]。相同的多段线彼此之间的距离为 0,因此每条多段线之间的面积为 0.You 将检查 x 轴和 y 轴以检查多段线区域高度垂直的情况。我写了下面的代码减去 sample_at_point 部分,因为它来晚了,所有这部分需要做的是找到给定轴的 p1 中 p 的上下边界点,然后定义一个线方程并找到在哪里通过的点 p 位于该线方程和 return 值上。我明天会编辑这个答案,但我想我会 post 我现在拥有的。此解决方案适用于您在上面显示的多段线,但如果多段线与基本曲线不相似(例如:circle/any 曲线,其中 f(x) 可能有两个不同的值),它将失败。
def polydist(p1, p2):
x_area = area_between_squared(p1, p2, axis=0, value=1)
y_area = area_between_squared(p1, p2, axis=1, value=0)
return (x_area + y_area)**0.5
def sample_at_point(p1, p, axis, value):
"""
# There should be a fast way to do this,
# Finnd the bounding points that p is between,
# the bounding points are the max point < p and the min point > p
# these points form a line,
# find the value of p applied to that line formula.
"""
def area_between_squared(p1, p2, axis, value):
start = max(p1[0][axis], p2[0][axis])
start_penalty = start - min(p1[0][axis], p2[0][axis])
end = min(p1[-1][axis], p2[-1][axis])
end_penalty = max(p1[-1][axis], p2[-1][axis]) - end
# Getting late, may edit this later tomorrow with the code for this to work but feel free to implement the following idea below:
axis_points = [x[axis] for x in p1]
axis_points.extend([x[axis] for x in p2])
axis_points.sort()
prev_p1_v = sample_at_point(p1, axis_points[0], axis, value)
prev_p2_v = sample_at_point(p2, axis_points[0], axis, value)
prev_axis_point = axis_points[0]
axis_points.pop(0) # remove the first item
sum = 0
for e in axis_points:
p1_v = sample_at_point(p1, e, axis, value)
p2_v = sample_at_point(p2, e, axis, value)
point_c = p1_v - p2_v
point_b = prev_p1_v - prev_p2_v
area = 0.5 * (point_c + point_b) * (e - prev_axis_point)
sum += area
prev_p1_v = p1_v
prev_p2_v = p2_v
prev_axis_point = e
sum *= sum # square the result value, add the penalties since this difference should be positive
sum += start_penalty + end_penalty
return sum
感谢您的评论和回答。他们实际上引导我找到了一个使用 OpenCV 函数 pointPolygonTest
的好解决方案,它比我写得不好的 numpy 代码优化得更多。
方法是通过以相反顺序附加点将多段线变成等高线,然后遍历第一个数组的点并调用 pointPolygonTest
来获取距离。我发现平方距离有助于放大较小的差异。然后我取所有点的平均值。
代码如下:
import cv2
import numpy as np
def point_to_polyline_dist(p, polyline):
cnt = np.concatenate((polyline, polyline[::-1]))
return np.abs(cv2.pointPolygonTest(cnt, (int(p[0]),int(p[1])), True))
def polyline_dist(polyline1, polyline2):
return np.mean([point_to_polyline_dist(p, polyline2)**2 for p in polyline1])
p1 = np.array([[0,0], [5,5], [9,9]])
p2 = np.array([[0,0], [3,3], [6,6], [9,9]])
p3 = np.array([[0,0], [3,4], [6,7], [9,9]])
p4 = np.array([[0,0], [0,9]])
print(polyline_dist(p1, p2)) # 0.0
print(polyline_dist(p1, p3)) # 0.1666666666666667
print(polyline_dist(p1, p4)) # 35.333333333333336
这对我来说效果很好,因为我的折线有很多短线段。计算两个阵列之间的距离并添加距离可能更稳健,如下所示:
def polyline_dist_better(polyline1, polyline2):
dist_1to2 = np.mean([point_to_polyline_dist(p, polyline2)**2 for p in polyline1])
dist_2to2 = np.mean([point_to_polyline_dist(p, polyline1)**2 for p in polyline2])
return dist_1to2 + dist_2to1
希望对外面的人有所帮助!