From here:
splrep
enables to compute B-Spline knots, coefficients and degree from a path and a smooth factorsplev
enables interpolation using the resulting B-SplineBSpline
enables to build a spline directly from knots, coefficients and degreeThen, I should be allowed to perform:
import numpy as np
from scipy.interpolate import splev, splprep, BSpline
path = [(2077.0, 712.0, 1136.6176470588234), (2077.0004154771536, 974.630482962754, 1313.735294117647), (2077.1630960823995, 1302.460574562254, 1490.8529411764707), (2078.1944091179635, 1674.693193015173, 1667.9705882352941), (2080.5096120056783, 2086.976611915444, 1845.0882352941176), (2085.1051468332066, 2711.054258877495, 2022.2058823529412), (1477.0846185328733, 2803.6223679691457, 2199.323529411765), (948.4693105162195, 2802.0390667447105, 2376.4411764705883), (383.8615403256207, 2804.843424134807, 2553.5588235294117), (-41.6669725172834, 2497.067373170676, 2730.676470588235), (-37.94311919744064, 1970.5155845437525, 2907.794117647059), (-35.97395938535092, 1576.713103381243, 3084.9117647058824), (-35.125016151504795, 1214.2319876178394, 3262.029411764706), (-35.000550767864524, 893.3910350913443, 3439.1470588235297), (-35.0, 631.2108462417168, 3616.264705882353), (-35.0, 365.60545190581837, 3793.3823529411766), (-35.0, 100.00005756991993, 3970.5)]
p = [[x for x,y,z in path], [y for x,y,z in path], [z for x,y,z in path]]
tck, u = splprep(p, k=3)
t, c0, k = tck
sp = BSpline(t, k, c0)
The goal is to be able to tune the B-Spline. But BSpline
is not happy with my arguments:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/lalebarde/anaconda3/lib/python3.7/site-packages/scipy/interpolate/_bsplines.py", line 184, in __init__
self.k = operator.index(k)
TypeError: 'list' object cannot be interpreted as an integer
If I check the shapes and types of the variables:
type(t)
<class 'numpy.ndarray'>
type(c0)
<class 'list'>
type(k)
<class 'int'>
t.shape
(21,)
np.array(c0).shape
(3, 17)
My usage of BSpline fails, from the documentation:
class scipy.interpolate.BSpline(t, c, k, extrapolate=True, axis=0)
t: ndarray, shape (n+k+1,) --> knots
c: ndarray, shape (>=n, …) --> spline coefficients - At least k+1 coefficients are required for a spline of degree k, so that n >= k+1. Additional coefficients, c[j] with j > n, are ignored.
k: int --> B-spline order
except for the coefficients c
which should be a one dimension vector of the same length as my path p
.
For example, sp = BSpline(t, c0[0], k)
executes with no error, as with c0[1]
or c0[2]
, but of course, I expect all coefficient computed by splprep
to be used.
From here, it appears that the scipy interpolate manual is confusing:
tck[1]: x and y coordinates of the relocated control points
The manual says:
(t,c,k) a tuple containing the vector of knots, the B-spline coefficients, and the degree of the spline
Eventually, I performed a missuse of BSpline by mis interpreting its spline coefficients parameter.
So, how can I build a BSpline from the knots and coefficients returned by splprep
with BSpline
or with another function?
BSpline(t, k, c0)
should be BSpline(t, c0, k)
EDIT. In fact, there is one more problem: splprep returns the list of arrays and it's inconsistent with BSpline
.
Note the difference between splrep and splprep:
Basically, splrep/splev are consistent, splrep/BSpline are consistent, but splprep/BSpline are not. It's a known wart and cannot be fixed in a backwards compatible way.
If you want to use them together, you'll need to transpose the c
array.
Basing off your OP example:
In [1]: import numpy as np
...: from scipy.interpolate import splev, splprep, BSpline
...: path = [(2077.0, 712.0, 1136.6176470588234), (2077.0004154771536, 974.6
...: 30482962754, 1313.735294117647), (2077.1630960823995, 1302.460574562254,
...: 1490.8529411764707), (2078.1944091179635, 1674.693193015173, 1667.97058
...: 82352941), (2080.5096120056783, 2086.976611915444, 1845.0882352941176),
...: (2085.1051468332066, 2711.054258877495, 2022.2058823529412), (1477.08461
...: 85328733, 2803.6223679691457, 2199.323529411765), (948.4693105162195, 28
...: 02.0390667447105, 2376.4411764705883), (383.8615403256207, 2804.84342413
...: 4807, 2553.5588235294117), (-41.6669725172834, 2497.067373170676, 2730.6
...: 76470588235), (-37.94311919744064, 1970.5155845437525, 2907.794117647059
...: ), (-35.97395938535092, 1576.713103381243, 3084.9117647058824), (-35.125
...: 016151504795, 1214.2319876178394, 3262.029411764706), (-35.0005507678645
...: 24, 893.3910350913443, 3439.1470588235297), (-35.0, 631.2108462417168, 3
...: 616.264705882353), (-35.0, 365.60545190581837, 3793.3823529411766), (-35
...: .0, 100.00005756991993, 3970.5)]
...: p = [[x for x,y,z in path], [y for x,y,z in path], [z for x,y,z in path]
...: ]
...: tck, u = splprep(p, k=3, s=0) # ADDED s=0 for clarity
...:
In [2]: t, c, k = tck
In [3]: c1 = np.asarray(c)
In [4]: spl = BSpline(t, c1.T, k) # Note the transpose
In [5]: spl(u) - path # these should match, and they do
Out[5]:
array([[ -4.54747351e-13, -1.13686838e-13, -4.54747351e-13],
[ 0.00000000e+00, -1.13686838e-13, 0.00000000e+00],
[ -4.54747351e-13, 0.00000000e+00, 0.00000000e+00],
[ 0.00000000e+00, -2.27373675e-13, -2.27373675e-13],
[ -4.54747351e-13, 0.00000000e+00, 4.54747351e-13],
[ -4.54747351e-13, 0.00000000e+00, -6.82121026e-13],
[ 2.27373675e-13, 0.00000000e+00, 0.00000000e+00],
[ -1.13686838e-13, -4.54747351e-13, -4.54747351e-13],
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 4.26325641e-14, -9.09494702e-13, 0.00000000e+00],
[ 1.42108547e-14, -4.54747351e-13, 0.00000000e+00],
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 7.10542736e-15, 0.00000000e+00, -4.54747351e-13],
[ 0.00000000e+00, -3.41060513e-13, 0.00000000e+00],
[ -7.10542736e-15, -1.13686838e-13, 0.00000000e+00],
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00],
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00]])
This answer is based on https://github.com/scipy/scipy/issues/10389.
The general suggestion over there applies: if you want interpolation, prefer make_interp_spline
to splrep
and splprep
. If you want smoothing, there's only FITPACK at the moment, either splrep (which is BSpline compatible) or splprep (where you need to transpose manually).