Найдите минимальное расстояние от точки до сложной кривой

У меня есть сложная кривая, определенная как набор точек в такой таблице (полная таблица находится здесь):

#  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)

введите здесь описание изображения

Любые идеи/указатели будут высоко оценены.


person Gabriel    schedule 30.09.2013    source источник
comment
Эта проблема не является выпуклой (en.wikipedia.org/wiki/Convex_optimization)... это означает, что методы оптимизации, как правило, не гарантируют получение глобального минимума. Тем не менее, имитация отжига — это один из вариантов попытки найти глобальные оптимумы в несовершенном мире.   -  person Him    schedule 21.03.2018


Ответы (4)


Если вы готовы использовать для этого библиотеку, взгляните на shapely: https://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 все равно найдет ближайшую точку, на дисплее она будет выглядеть не совсем правильно:

введите здесь описание изображения

person Joe Kington    schedule 08.11.2013
comment
Я не знаю, как я пропустил этот ответ до сих пор. Это самый удивительный ответ, который я получил в StackOverflow. Вы не только ответили на мой вопрос, но и показали мне, как сделать интерактивный сюжет. Я не могу отблагодарить тебя, Джо. - person Gabriel; 26.11.2013
comment
@Габриэль - Спасибо! Рад помочь! - person Joe Kington; 26.11.2013
comment
Если бы я мог спросить, будет ли это возвращать минимальное расстояние до кривой (любой точки на кривой) или минимальное расстояние до существующей точки (похоже, что это последнее)? - person pceccon; 03.03.2020
comment
Как это сделать в 3D (рассчитать расстояние от 3D точки до 3D сплайна/кривой/линии)? - person dany; 17.05.2021

Кривая по своей природе является параметрической, т. е. для каждого 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 равным 1 и len(x_data).

person prgao    schedule 30.09.2013
comment
Не могли бы вы уточнить, что такое fminsearch? Кроме того, что вы говорите о создании двух интерполяций, одной для x и одной для y, разве это не было тем, что я пробовал последним в своем вопросе, который вызвал у меня беспорядок? - person Gabriel; 01.10.2013
comment
не сортируйте, начальные порядки x и y уже в правильной последовательности. - person prgao; 01.10.2013
comment
также это «fminbound», «fminsearch» является эквивалентностью Matlab. он находит минимум скалярной функции между двумя указанными границами. см. docs.scipy.org/doc/scipy/reference /сгенерировано/ - person prgao; 01.10.2013
comment
Кривая довольно четко представляет разрывы, я бы вообще не стал интерполировать, если только вы не собираетесь интерполировать только в пределах непрерывных частей, а автоматизировать это нетривиально. - person gg349; 01.10.2013

Вы можете попробовать реализовать вычисление расстояния от точки до линии по возрастающим парам точек на кривой и найти этот минимум. Это внесет небольшую ошибку в нарисованную кривую, но она должна быть очень маленькой, так как точки расположены относительно близко друг к другу.

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

person Chris Bonnell    schedule 30.09.2013
comment
Более того, кривая определяется только точками, а не конкретным интерполянтом точек. Таким образом, если мы не предполагаем, что конкретная интерполирующая функция является правильной, мы не можем даже обсуждать допущенную ошибку. - person gg349; 01.10.2013
comment
О, хороший улов! Да, тогда особой ошибки в этом методе нет. - person Chris Bonnell; 01.10.2013
comment
У меня была такая же идея, но на самом деле это не сработает: вы должны проверить, что проекция вашей точки принадлежит прямой. Но этого может не произойти при определенных обстоятельствах. Пример: ваша точка находится чуть выше изогнутой под шляпой кривой. Тогда минимальное расстояние будет между вашей точкой и верхней точкой шляпы, но вы не сможете найти его по ортогональному расстоянию к линии. - person ; 01.10.2013
comment
Если точка на линии ближе, чем любая из двух рассматриваемых точек, вы должны отклонить расстояние. - person Chris Bonnell; 01.10.2013

Вы можете легко использовать пакет trjtrypy в PyPI: https://pypi.org/project/trjtrypy/

Все необходимые расчеты и визуализации доступны в этом пакете. Вы можете получить ответ в строке кода, например:

чтобы получить минимальное расстояние, используйте: trjtrypy.basedists.distance(points, curve)

для визуализации кривой и точек используйте: trjtrypy.visualizations.draw_landmarks_trajectory(точки, кривая)

person John    schedule 09.07.2021