【问题标题】:Find minimum distance from point to complicated curve找到点到复杂曲线的最小距离
【发布时间】:2026-02-06 02:55:01
【问题描述】:

我有一条复杂的曲线,定义为表格中的一组点,如下所示(完整表格为here):

#  x   y
1.0577  12.0914
1.0501  11.9946
1.0465  11.9338
...

如果我使用以下命令绘制此表:

plt.plot(x_data, y_data, c='b',lw=1.)
plt.scatter(x_data, y_data, marker='o', color='k', s=10, lw=0.2)

我得到以下信息:

我手动添加了红点和线段。我需要的是一种为每个点计算这些段的方法,即:找到从这个 2D 空间中的给定点到插值曲线的最小距离的方法

我不能使用到数据点本身的距离(生成蓝色曲线的黑点),因为它们的间隔不相等,有时它们很近,有时它们相距很远,这深深影响了我的结果更进一步。

由于这不是一条表现良好的曲线,我不太确定我能做什么。我尝试用UnivariateSpline 对其进行插值,但它返回的拟合度很差:

# Sort data according to x.
temp_data = zip(x_data, y_data)
temp_data.sort()
# Unpack sorted data.
x_sorted, y_sorted = zip(*temp_data)

# Generate univariate spline.
s = UnivariateSpline(x_sorted, y_sorted, k=5)
xspl = np.linspace(0.8, 1.1, 100)
yspl = s(xspl)

# Plot.
plt.scatter(xspl, yspl, marker='o', color='r', s=10, lw=0.2)

我也尝试增加插值点的数量,但结果一团糟:

# Sort data according to x.
temp_data = zip(x_data, y_data)
temp_data.sort()
# Unpack sorted data.
x_sorted, y_sorted = zip(*temp_data)

t = np.linspace(0, 1, len(x_sorted))
t2 = np.linspace(0, 1, 100)    
# One-dimensional linear interpolation.
x2 = np.interp(t2, t, x_sorted)
y2 = np.interp(t2, t, y_sorted)
plt.scatter(x2, y2, marker='o', color='r', s=10, lw=0.2)

任何想法/指针将不胜感激。

【问题讨论】:

  • 这个问题不是凸的 (en.wikipedia.org/wiki/Convex_optimization)... 这意味着优化技术通常不能保证给出全局最小值。也就是说,模拟退火是尝试在不完美世界中找到全局最优值的一种选择。

标签: python numpy scipy interpolation spline


【解决方案1】:

如果您愿意为此使用库,请查看shapelyhttps://github.com/Toblerity/Shapely

举个简单的例子(points.txt 包含您在问题中链接到的数据):

import shapely.geometry as geom
import numpy as np

coords = np.loadtxt('points.txt')

line = geom.LineString(coords)
point = geom.Point(0.8, 10.5)

# Note that "line.distance(point)" would be identical
print(point.distance(line))

作为一个交互式示例(这也绘制了您想要的线段):

import numpy as np
import shapely.geometry as geom
import matplotlib.pyplot as plt

class NearestPoint(object):
    def __init__(self, line, ax):
        self.line = line
        self.ax = ax
        ax.figure.canvas.mpl_connect('button_press_event', self)

    def __call__(self, event):
        x, y = event.xdata, event.ydata
        point = geom.Point(x, y)
        distance = self.line.distance(point)
        self.draw_segment(point)
        print 'Distance to line:', distance

    def draw_segment(self, point):
        point_on_line = line.interpolate(line.project(point))
        self.ax.plot([point.x, point_on_line.x], [point.y, point_on_line.y], 
                     color='red', marker='o', scalex=False, scaley=False)
        fig.canvas.draw()

if __name__ == '__main__':
    coords = np.loadtxt('points.txt')

    line = geom.LineString(coords)

    fig, ax = plt.subplots()
    ax.plot(*coords.T)
    ax.axis('equal')
    NearestPoint(line, ax)
    plt.show()

请注意,我添加了ax.axis('equal')shapely 在数据所在的坐标系中运行。如果没有等轴图,视图将被扭曲,虽然 shapely 仍会找到最近的点,但它在显示中看起来不太正确:

【讨论】:

  • 直到现在我才知道我是怎么错过这个答案的。这是迄今为止我在 * 中得到的最令人惊奇的答案。您不仅回答了我的问题,还向我展示了如何制作交互式情节。乔,我非常感谢你。
  • @Gabriel - 谢谢!很高兴为您提供帮助!
  • 如果我可能会问,这将返回到曲线的最小距离(曲线中的任何点)还是到现有点的最小距离(似乎是后者)?跨度>
  • 如何在 3D 中进行(计算从 3D 点到 3D 样条/曲线/线的距离)?
【解决方案2】:

您可以在 PyPI 中轻松使用包 trjtrypy:https://pypi.org/project/trjtrypy/

此软件包中提供了所有需要的计算和可视化。您可以在如下代码行中得到答案:

获取最小距离使用:trjtrypy.basedists.distance(points, curve)

可视化曲线和点使用:trjtrypy.visualizations.draw_landmarks_trajectory(points, curve)

【讨论】:

    【解决方案3】:

    曲线本质上是参数化的,即对于每个 x,都不需要唯一的 y,反之亦然。所以你不应该插入一个 y(x) 或 x(y) 形式的函数。相反,您应该进行两次插值,x(t) 和 y(t),其中 t 是对应点的索引。

    然后你使用scipy.optimize.fminbound 找到最优的 t 使得 (x(t) - x0)^2 + (y(t) - y0)^2 是最小的,其中 (x0, y0) 是红色的第一个图中的点。对于 fminsearch,您可以将 t 的最小/最大界限指定为 1len(x_data)

    【讨论】:

    • 您介意澄清一下fminsearch 是什么吗?还有你所说的做两个插值,一个用于 x,一个用于 y,这不是我在我的问题中最后尝试的让我一团糟吗?
    • 不要排序,x和y的初始顺序已经在正确的顺序了。
    • 也是 'fminbound','fminsearch' 是 matlab 等价的。它找到两个指定边界之间的标量函数的最小值。见docs.scipy.org/doc/scipy/reference/generated/…
    • 曲线很清楚地呈现出不连续性,我根本不会插值,除非您只在非不连续部分内插值,这对于自动化来说并非易事。
    【解决方案4】:

    您可以尝试在曲线上的增量点对上计算点到线的距离并找到最小值。这会从绘制的曲线中引入一点点误差,但应该非常小,因为点相对靠近。

    http://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line

    【讨论】:

    • 此外,曲线仅由点定义,而不是由点的特定插值定义。因此,除非我们假设特定的插值函数是正确的,否则我们甚至无法讨论所犯的错误。
    • 哦,好收获!是的,那么这个方法并没有什么特别的错误。
    • 我也有同样的想法,但实际上它不起作用:你必须检查你的点的投影是否属于线。但这在某些情况下可能不会发生。示例:您的点就在“帽子”角度曲线的上方。那么最小距离将是您的点和帽子的上点之间的距离,但您将无法通过与直线的正交距离找到它。
    • 如果线上的点比有问题的两个点中的任何一个更近,你应该拒绝距离。