Skip to content
No description, website, or topics provided.
Branch: master
Clone or download
Type Name Latest commit message Commit time
Failed to load latest commit information.
figures Add figures/README-3-LM.png Jan 5, 2015
LICENSE Add LICENSE Dec 31, 2014 Fix minor typo in Apr 26, 2015 Fix reference to `SOLVER_TYPES` Apr 26, 2015 Make number of spaces between imports and functions consistent Jan 4, 2015 Minor cosmetic updates to Jan 5, 2015


B-spline regression

This repository provides the code necessary to fit explicit uniform B-splines of any degree to unstructured 2D/3D/ND point data. In the above example, the data points (red) are approximated by a uniform cubic B-spline (blue) with 14 control points (black).

The primary purpose of this repository is to provide a general B-spline solver which is easy to use, has minimal dependencies, but is still performant. The secondary purpose is to provide a starting point for people learning about B-splines, sparse non-linear least squares optimisation, and the damped Newton and Levenberg-Marquardt algorithms in particular.

Author: Richard Stebbing

License: MIT (refer to LICENSE)


This repository is tested to work under Python 2.7 and Python 3.4.

The required dependencies are:

  • Numpy
  • Scipy
  • Sympy
  • matplotlib

Getting Started

To generate the above example problem and initialisation:

python 512 1 1e-1 3 14 Example_1.json --seed=0 --frequency=3

The arguments passed to are:

Value Argument Description
512 num_data_points The number of data points to generate.
1 w The weight applied to each squared residual.
1e-1 lambda_ The regularisation weight.
3 degree The degree of the uniform B-spline.
14 num_control_points The number of control points.
Example_1.json output_path The output path.
--seed 0 The random number generator seed (optional).
--frequency 3 The frequency of the sin (optional).


  • w can be a pair (2D) or triple (3D) so that w[i] is the weight applied to the ith dimension of each squared residual. This is useful for simulating time-series data where the uncertainty of the measurement (y-axis) is much greater than that of the reported time (x-axis).
  • Increasing lambda_ increases the "force" pulling adjacent B-spline control points together.

The output Example_1.json is a dictionary of the form:

Key Value Type Description
degree int The degree of the uniform B-spline.
num_control_points int The number of control points.
dim int The dimension of the problem.
is_closed bool True if the B-spline is closed, False otherwise.
Y array_like, shape = (num_data_points, dim) The array of data points.
w array_like, shape = (num_data_points, dim) The weight of each squared residual on each dimension.
lambda_ float The regularisation weight.
X array_like, shape = (num_control_points, dim) The array of B-spline control points.
u array_like, shape = (num_data_points, 1) The array of correspondences (preimages).
where u[i] gives the coordinate of the point on the B-spline that is closest to Y[i].

In summary, the first four keys define the structure of the B-spline, the next three keys specify the data points and problem configuration, and the final two keys define the state. In, X is initialised to a straight line and u is set approximately.

Visualising the initialisation is straightforward:

python Example_1.json --empty


where `--empty` generates the plot *without* axis labels or a title, and the correspondences are shown in orange. (All figures in this document were generated with the additional arguments `--width=8` and `--height=4` but this is omitted for brevity.)

To solve for X and u:

python Example_1.json Example_1_Output.json

and visualise the output:

python Example_1_Output.json --empty


Alternatively, to save and visualise all optimisation steps:

python Example_1.json Example_1_Output --output-all
python Example_1_Output Example_1_Output_Visualisation

Additional arguments to and can be found with --help. Further details regarding the solver implementation are provided in the docstring for UniformBSplineLeastSquaresOptimiser.minimise in

Additional Examples

  • Fitting a uniform quadratic B-spline with 5 control points and penalising errors in the x- direction more heavily:
python 256 "1e2,1" 1e-1 2 5 Example_2.json --seed=0 --frequency=0.75 --sigma=0.1 --alpha=-0.1
python Example_2.json Example_2_Output.json
python Example_2_Output.json --empty

Quadratic B-spline

Solving with Levenberg-Marquardt instead of damped Newton:

python Example_2.json Example_2_Output_LM.json lm
python Example_2_Output_LM.json --empty

Quadratic B-spline with LM

(For comparison, damped Newton converges at 4.18 whereas Levenberg-Marquardt converges at 6.96. The initial energy is 1.97e3.)

  • Fitting a uniform quintic B-spline with 9 control points to 65536 data points:
python 65536 1 1e-1 5 9 Example_3.json --seed=0 --frequency=2 --alpha=0
python Example_3.json Example_3_Output.json
python Example_3_Output.json -d u -d X --empty

Quintic B-spline

  • Fitting a uniform quartic B-spline with 10 control points in 3D:
python 128 1 1e-1 4 10 Example_4.json --seed=0 --frequency=3 --dim=3
python Example_4.json Example_4_Output.json
python Example_4_Output.json --empty

Quartic B-spline

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.