Как сгладить обводку, учитывая некоторые координаты (x, y) обводки по порядку?

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

import numpy as np
X = np.array([1, 3, 6, 8, 5])
Y = np.array([1, 8, 4, 4, 1])
plt.plot(X, Y)

Но я хочу создать сглаженную коллекцию точек, которая будет изображать это (это просто нарисованная от руки картина, я думаю, вы поняли):

Я видел этот вопрос, который работает только для функций (один х будет выводить только один у). Но мне нужен сплайн для отношения (не функция). Заранее спасибо.

3 ответа

Решение

Вы можете использовать B-spline (splprep и splev) из scipy.interpolate:

import numpy as np
from scipy.interpolate import splprep, splev
import matplotlib.pyplot as plt

X = np.array([1, 3, 6, 8, 5])
Y = np.array([1, 8, 4, 4, 1])
pts = np.vstack((X, Y))
# Find the B-spline representation of an N-dimensional curve
tck, u = splprep(pts, s=0.0)
u_new = np.linspace(u.min(), u.max(), 1000)
# Evaluate a B-spline
x_new, y_new = splev(u_new, tck)

plt.plot(x_new, y_new, 'b--')
plt.show()

Это даст вам нечто похожее на то, что вы просили:

Вы можете поиграть с параметрами splprep, чтобы изменить результат. Вы можете найти более подробную информацию в этом сообщении Stackru.

Смотрите алгоритм Чайкина.

Алгоритм Чайкина - это геометрические алгоритмы, которые работают с полигоном управления напрямую. Схема генерации кривой основана на "разрезании углов", когда алгоритм генерирует новый многоугольник управления, обрезая углы от исходного.
Рисунок ниже иллюстрирует эту идею, когда начальный контрольный многоугольник был уточнен во второй многоугольник (слегка смещен) путем обрезания углов первой последовательности.

Вот пример реализации.

"""
polygoninterpolation.py
Chaikin's Algorith for curves
http://graphics.cs.ucdavis.edu/~joy/GeometricModelingLectures/Unit-7-Notes/Chaikins-Algorithm.pdf
"""
import math
import random
from graphics import *


class MultiLine:

    def __init__(self, points=None, rgb_color=(255, 255, 255), width=1):
        self.lines = []
        if points is None:
            self.points = []
        else:
            self.points = points
            self._build_lines()
        self.rgb_color = rgb_color
        self.width = width

    def add_point(self):
        self.points.append(point)

    def _build_lines(self):
        for idx, point in enumerate(self.points[:-1]):
            self.lines.append(Line(self.points[idx], self.points[idx + 1]))

    def draw(self, win):
        for line in self.lines:
            line.setOutline(color_rgb(*self.rgb_color))
            line.setWidth(self.width)
            line.draw(win)


def get_chaikin(points, factor=4):
    new_points = []   # [points[0]]
    for idx in range(len(points) - 1):
        p1, p2 = points[idx], points[idx+1]
        p_one_qtr, p_three_qtr = get_quarter_points(p1, p2, factor)
        new_points.append(p_one_qtr)
        new_points.append(p_three_qtr)
    return new_points   # + [points[-1]]  # for a closed polygon


def get_quarter_points(p1, p2, factor=4):
    n = factor
    qtr_x = (p2.x - p1.x) / n
    qtr_y = (p2.y - p1.y) / n
    return Point(p1.x + qtr_x, p1.y + qtr_y), \
           Point(p1.x + (n-1) * qtr_x, p1.y + (n-1) * qtr_y)


win = GraphWin("My Window", 500, 500)
win.setBackground(color_rgb(0, 0, 0))


# points0 = [Point(250, 20),
#            Point(20, 400),
#            Point(480, 400)]

# points0 = [Point(20, 400),
#            Point(35, 200),
#            Point(250, 100),
#            Point(400, 150),
#            Point(450, 350),
#            Point(380, 450)]

# points0 = [Point(20, 400),
#            Point(35, 200),
#            Point(250, 100),
#            Point(400, 150),
#            Point(220, 170),
#            Point(310, 190),
#            Point(180, 270),
#            Point(450, 230),
#            Point(440, 440),
#            Point(380, 450)]

points0 = [Point(random.randrange(500), random.randrange(500)) for _ in range(random.randrange(3, 80))]

x_line0 = MultiLine(points0)
# x_line0.draw(win)

points1 = get_chaikin(points0)
x_line1 = MultiLine(points1, rgb_color=(200, 200, 200), width=1)
# x_line1.draw(win)

points2 = get_chaikin(points1)
x_line2 = MultiLine(points2, rgb_color=(200, 200, 200), width=1)
# x_line2.draw(win)

points3 = get_chaikin(points2)
x_line3 = MultiLine(points3, rgb_color=(200, 200, 200), width=1)
# x_line3.draw(win)

points4 = get_chaikin(points3)
x_line4 = MultiLine(points4, rgb_color=(200, 200, 200), width=1)
# x_line4.draw(win)

points5 = get_chaikin(points4)
x_line5 = MultiLine(points5, rgb_color=(200, 200, 200), width=1)
x_line5.draw(win)


# poly0 = Polygon(points0)
# poly0.setOutline(color_rgb(0, 255, 0))
# poly0.setWidth(1)
# poly0.draw(win)
#
# points1 = get_chaikin(points0 + [points0[0]])
# poly1 = Polygon(points1)
# poly1.setOutline(color_rgb(0, 255, 0))
# poly1.setWidth(1)
# poly1.draw(win)
#
# points2 = get_chaikin(points1 + [points1[0]])
# poly2 = Polygon(points2)
# poly2.setOutline(color_rgb(0, 255, 0))
# poly2.setWidth(1)
# poly2.draw(win)
#
# points3 = get_chaikin(points2 + [points2[0]])
# poly3 = Polygon(points3)
# poly3.setOutline(color_rgb(0, 255, 0))
# poly3.setWidth(1)
# poly3.draw(win)
#
# points4 = get_chaikin(points3 + [points3[0]])
# poly4 = Polygon(points4)
# poly4.setOutline(color_rgb(0, 255, 0))
# poly4.setWidth(1)
# poly4.draw(win)
#
# points5 = get_chaikin(points4 + [points4[0]])
# poly5 = Polygon(points5)
# poly5.setOutline(color_rgb(0, 255, 0))
# poly5.setWidth(2)
# poly5.draw(win)


print("done")


print(win.getMouse())
win.close()

Приведенные выше ответы очень элегантны, но вот попытка найти "хакерское" решение, это гораздо менее гладко

X_new = []
Y_new = []
for i in range(4):
    line1 = [X[i],Y[i]] + np.expand_dims(np.linspace(0,1,10),-1)*np.array([X[i+1] - X[i], Y[i+1] - Y[i]])

    line_normal = [- Y[i+1] + Y[i], X[i+1] - X[i]]
    line_normal = line_normal/np.sqrt(np.dot(line_normal, line_normal))

    line1_noisy = line1 + line_normal * 0.2*(np.random.rand(10,1) - 0.5)
    X_new.append(line1_noisy[:,0])
    Y_new.append(line1_noisy[:,1])
X_new = np.stack(X_new).reshape(-1)
Y_new = np.stack(Y_new).reshape(-1)
plt.plot(X_new, Y_new)

Другие вопросы по тегам