ev-br's answerを延長すると、BPoly.from_derivatives
を使用して、nディメンションの点間を補間する使用例を示すサンプルコードです。
import numpy as np
from scipy import interpolate
def sampleCubicSplinesWithDerivative(points, tangents, resolution):
'''
Compute and sample the cubic splines for a set of input points with
optional information about the tangent (direction AND magnitude). The
splines are parametrized along the traverse line (piecewise linear), with
the resolution being the step size of the parametrization parameter.
The resulting samples have NOT an equidistant spacing.
Arguments: points: a list of n-dimensional points
tangents: a list of tangents
resolution: parametrization step size
Returns: samples
Notes: Lists points and tangents must have equal length. In case a tangent
is not specified for a point, just pass None. For example:
points = [[0,0], [1,1], [2,0]]
tangents = [[1,1], None, [1,-1]]
'''
resolution = float(resolution)
points = np.asarray(points)
nPoints, dim = points.shape
# Parametrization parameter s.
dp = np.diff(points, axis=0) # difference between points
dp = np.linalg.norm(dp, axis=1) # distance between points
d = np.cumsum(dp) # cumsum along the segments
d = np.hstack([[0],d]) # add distance from first point
l = d[-1] # length of point sequence
nSamples = int(l/resolution) # number of samples
s,r = np.linspace(0,l,nSamples,retstep=True) # sample parameter and step
# Bring points and (optional) tangent information into correct format.
assert(len(points) == len(tangents))
data = np.empty([nPoints, dim], dtype=object)
for i,p in enumerate(points):
t = tangents[i]
# Either tangent is None or has the same
# number of dimensions as the point p.
assert(t is None or len(t)==dim)
fuse = list(zip(p,t) if t is not None else zip(p,))
data[i,:] = fuse
# Compute splines per dimension separately.
samples = np.zeros([nSamples, dim])
for i in range(dim):
poly = interpolate.BPoly.from_derivatives(d, data[:,i])
samples[:,i] = poly(s)
return samples
この機能の使用方法を示すために、ポイントと接線を指定します。この例では、接線の「大きさ」が変更された場合の効果をさらに示しています。
# Input.
points = []
tangents = []
resolution = 0.2
points.append([0.,0.]); tangents.append([1,1])
points.append([3.,4.]); tangents.append([1,0])
points.append([5.,2.]); tangents.append([0,-1])
points.append([3.,0.]); tangents.append([-1,-1])
points = np.asarray(points)
tangents = np.asarray(tangents)
# Interpolate with different tangent lengths, but equal direction.
scale = 1.
tangents1 = np.dot(tangents, scale*np.eye(2))
samples1 = sampleCubicSplinesWithDerivative(points, tangents1, resolution)
scale = 2.
tangents2 = np.dot(tangents, scale*np.eye(2))
samples2 = sampleCubicSplinesWithDerivative(points, tangents2, resolution)
scale = 0.1
tangents3 = np.dot(tangents, scale*np.eye(2))
samples3 = sampleCubicSplinesWithDerivative(points, tangents3, resolution)
# Plot.
import matplotlib.pyplot as plt
plt.scatter(samples1[:,0], samples1[:,1], marker='o', label='samples1')
plt.scatter(samples2[:,0], samples2[:,1], marker='o', label='samples2')
plt.scatter(samples3[:,0], samples3[:,1], marker='o', label='samples3')
plt.scatter(points[:,0], points[:,1], s=100, c='k', label='input')
plt.axis('equal')
plt.title('Interpolation')
plt.legend()
plt.show()
これは、次のグラフのような結果になります。
3つのことに注意する:
- 次も二つ以上のディメンションに適用することができます。
- サンプル間の間隔は固定されていません。等間隔サンプリングを達成する1つの簡単な方法は、this postで論じられているように、返された
samples
の間を直線的に補間することです。
- 接線の指定は任意ですが、
BPoly.from_derivatives
は、この位置でスプライン間のスムーズな移行を保証しません。上記のサンプルの例tangents[1]
ためNone
、sampleCubicSplinesWithDerivative(points, tangents, resolution)
に設定されている場合、結果は次のようになります。