В настоящее время я работаю над проектом, в котором мне нужно создать изображение сглаженного штриха с учетом некоторых упорядоченных координат штриха. Предположим, у меня есть несколько точек
import numpy as np
X = np.array([1, 3, 6, 8, 5])
Y = np.array([1, 8, 4, 4, 1])
plt.plot(X, Y)
Но я хочу создать сглаженную коллекцию точек, которая будет отображать это (это просто нарисованная от руки картинка, я думаю, вы поняли):
Я видел вопрос это, который работает только для функций (один x выводит только один y). Но мне нужен сплайн для отношения (а не функции). Заранее спасибо.






См. алгоритм Чайкина.
Алгоритм Чайкина — это геометрический алгоритм, работающий непосредственно с управляющим полигоном. Схема генерации кривой основана на «разрезании углов», когда алгоритм генерирует новый контрольный многоугольник, обрезая углы исходного. Рисунок ниже иллюстрирует эту идею, где исходный контрольный многоугольник был преобразован во второй многоугольник (с небольшим смещением) путем срезания углов первой последовательности.
Вот пример реализации.
"""
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()
Вы можете использовать B-сплайн (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, чтобы изменить результат. Вы можете найти более подробную информацию в этом StackOverflow Почта.
Приведенные выше ответы очень элегантны, но вот попытка «хакерского» решения, которое гораздо менее гладко.
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)
Прошу прощения за плохой рисунок в моем вопросе, но выходной график не должен быть шумным. А также просто взять 3 точки для рисования линии для конкретной точки может быть не очень хорошей идеей.