Ответ 1
Мне удалось воссоздать пример Mathematica, о котором я спросил в предыдущем сообщении, используя Python/scipy. Здесь результат:
B-сплайн, апериодический
Хитрость заключалась в том, чтобы либо перехватить коэффициенты, т.е. элемент 1 кортежа, возвращаемый scipy.interpolate.splrep
, и заменить их значениями контрольной точки, прежде чем передать их в scipy.interpolate.splev
, или, если вы в порядке с созданием узлы, вы также можете обойтись без splrep
и создать весь кортеж самостоятельно.
Однако это странно, так как согласно руководству splrep
возвращает (и splev
ожидает) кортеж, содержащий, среди прочего, вектор сплайн-коэффициентов с одним коэффициентом за узел. Однако, по всем найденным мною источникам, сплайн определяется как взвешенная сумма сплайнов базиса N_control_points, поэтому я ожидаю, что вектор коэффициентов будет иметь столько элементов, сколько контрольных точек, а не узлов.
Фактически, при поставке splrep
результата кортеж с вектором коэффициентов, измененным, как описано выше, на scipy.interpolate.splev
, оказывается, что первые N_control_points этого вектора фактически являются ожидаемыми коэффициентами для сплайнов базиса N_control_points. Последняя степень + 1 элемент этого вектора, по-видимому, не имеет никакого эффекта. Я в тупике, почему это так. Если кто-нибудь сможет это прояснить, это будет здорово. Здесь источник, который генерирует приведенные выше графики:
import numpy as np
import matplotlib.pyplot as plt
import scipy.interpolate as si
points = [[0, 0], [0, 2], [2, 3], [4, 0], [6, 3], [8, 2], [8, 0]];
points = np.array(points)
x = points[:,0]
y = points[:,1]
t = range(len(points))
ipl_t = np.linspace(0.0, len(points) - 1, 100)
x_tup = si.splrep(t, x, k=3)
y_tup = si.splrep(t, y, k=3)
x_list = list(x_tup)
xl = x.tolist()
x_list[1] = xl + [0.0, 0.0, 0.0, 0.0]
y_list = list(y_tup)
yl = y.tolist()
y_list[1] = yl + [0.0, 0.0, 0.0, 0.0]
x_i = si.splev(ipl_t, x_list)
y_i = si.splev(ipl_t, y_list)
#==============================================================================
# Plot
#==============================================================================
fig = plt.figure()
ax = fig.add_subplot(231)
plt.plot(t, x, '-og')
plt.plot(ipl_t, x_i, 'r')
plt.xlim([0.0, max(t)])
plt.title('Splined x(t)')
ax = fig.add_subplot(232)
plt.plot(t, y, '-og')
plt.plot(ipl_t, y_i, 'r')
plt.xlim([0.0, max(t)])
plt.title('Splined y(t)')
ax = fig.add_subplot(233)
plt.plot(x, y, '-og')
plt.plot(x_i, y_i, 'r')
plt.xlim([min(x) - 0.3, max(x) + 0.3])
plt.ylim([min(y) - 0.3, max(y) + 0.3])
plt.title('Splined f(x(t), y(t))')
ax = fig.add_subplot(234)
for i in range(7):
vec = np.zeros(11)
vec[i] = 1.0
x_list = list(x_tup)
x_list[1] = vec.tolist()
x_i = si.splev(ipl_t, x_list)
plt.plot(ipl_t, x_i)
plt.xlim([0.0, max(t)])
plt.title('Basis splines')
plt.show()
B-сплайн, периодический
Теперь, чтобы создать замкнутую кривую, подобную следующей, что является другим примером Mathematica, который можно найти в Интернете,
необходимо установить параметр per
в вызове splrep
, если вы его используете. После заполнения списка контрольных точек со степенью + 1 значения в конце, это, кажется, работает достаточно хорошо, как показывают изображения.
Следующая особенность здесь, однако, состоит в том, что элементы первой и последней степени в векторе коэффициентов не влияют, что означает, что контрольные точки должны быть помещены в вектор, начиная со второй позиции, то есть положению 1. Только тогда это результаты ok. Для степеней k = 4 и k = 5 эта позиция даже изменяется в положение 2.
Здесь источник генерации замкнутой кривой:
import numpy as np
import matplotlib.pyplot as plt
import scipy.interpolate as si
points = [[-2, 2], [0, 1], [-2, 0], [0, -1], [-2, -2], [-4, -4], [2, -4], [4, 0], [2, 4], [-4, 4]]
degree = 3
points = points + points[0:degree + 1]
points = np.array(points)
n_points = len(points)
x = points[:,0]
y = points[:,1]
t = range(len(x))
ipl_t = np.linspace(1.0, len(points) - degree, 1000)
x_tup = si.splrep(t, x, k=degree, per=1)
y_tup = si.splrep(t, y, k=degree, per=1)
x_list = list(x_tup)
xl = x.tolist()
x_list[1] = [0.0] + xl + [0.0, 0.0, 0.0, 0.0]
y_list = list(y_tup)
yl = y.tolist()
y_list[1] = [0.0] + yl + [0.0, 0.0, 0.0, 0.0]
x_i = si.splev(ipl_t, x_list)
y_i = si.splev(ipl_t, y_list)
#==============================================================================
# Plot
#==============================================================================
fig = plt.figure()
ax = fig.add_subplot(231)
plt.plot(t, x, '-og')
plt.plot(ipl_t, x_i, 'r')
plt.xlim([0.0, max(t)])
plt.title('Splined x(t)')
ax = fig.add_subplot(232)
plt.plot(t, y, '-og')
plt.plot(ipl_t, y_i, 'r')
plt.xlim([0.0, max(t)])
plt.title('Splined y(t)')
ax = fig.add_subplot(233)
plt.plot(x, y, '-og')
plt.plot(x_i, y_i, 'r')
plt.xlim([min(x) - 0.3, max(x) + 0.3])
plt.ylim([min(y) - 0.3, max(y) + 0.3])
plt.title('Splined f(x(t), y(t))')
ax = fig.add_subplot(234)
for i in range(n_points - degree - 1):
vec = np.zeros(11)
vec[i] = 1.0
x_list = list(x_tup)
x_list[1] = vec.tolist()
x_i = si.splev(ipl_t, x_list)
plt.plot(ipl_t, x_i)
plt.xlim([0.0, 9.0])
plt.title('Periodic basis splines')
plt.show()
B-сплайн, периодический, высшая степень
Наконец, есть эффект, который я тоже не могу объяснить, и это при переходе к степени 5, есть небольшой разрыв, который появляется в сплайсированной кривой, см. верхнюю правую панель, которая является крупным планом это "полумесяц с носовой формой". Исходный код, который производит это, приведен ниже.
import numpy as np
import matplotlib.pyplot as plt
import scipy.interpolate as si
points = [[-2, 2], [0, 1], [-2, 0], [0, -1], [-2, -2], [-4, -4], [2, -4], [4, 0], [2, 4], [-4, 4]]
degree = 5
points = points + points[0:degree + 1]
points = np.array(points)
n_points = len(points)
x = points[:,0]
y = points[:,1]
t = range(len(x))
ipl_t = np.linspace(1.0, len(points) - degree, 1000)
knots = np.linspace(-degree, len(points), len(points) + degree + 1).tolist()
xl = x.tolist()
coeffs_x = [0.0, 0.0] + xl + [0.0, 0.0, 0.0]
yl = y.tolist()
coeffs_y = [0.0, 0.0] + yl + [0.0, 0.0, 0.0]
x_i = si.splev(ipl_t, (knots, coeffs_x, degree))
y_i = si.splev(ipl_t, (knots, coeffs_y, degree))
#==============================================================================
# Plot
#==============================================================================
fig = plt.figure()
ax = fig.add_subplot(231)
plt.plot(t, x, '-og')
plt.plot(ipl_t, x_i, 'r')
plt.xlim([0.0, max(t)])
plt.title('Splined x(t)')
ax = fig.add_subplot(232)
plt.plot(t, y, '-og')
plt.plot(ipl_t, y_i, 'r')
plt.xlim([0.0, max(t)])
plt.title('Splined y(t)')
ax = fig.add_subplot(233)
plt.plot(x, y, '-og')
plt.plot(x_i, y_i, 'r')
plt.xlim([min(x) - 0.3, max(x) + 0.3])
plt.ylim([min(y) - 0.3, max(y) + 0.3])
plt.title('Splined f(x(t), y(t))')
ax = fig.add_subplot(234)
for i in range(n_points - degree - 1):
vec = np.zeros(11)
vec[i] = 1.0
x_i = si.splev(ipl_t, (knots, vec, degree))
plt.plot(ipl_t, x_i)
plt.xlim([0.0, 9.0])
plt.title('Periodic basis splines')
plt.show()
Учитывая, что b-сплайны являются повсеместными в научном сообществе, и этот scipy является таким всеобъемлющим набором инструментов и что я не смог много узнать о том, что я прошу здесь в Интернете, заставляет меня поверить, что я на неправильном пути или что-то не замечаешь. Любая помощь будет оценена.