===================================================================
vspline - generic C++ code to create and evaluate uniform B-splines
===================================================================

---------------------
vspline in a nutshell
---------------------

Impatient? Here's a quick example of code to set up a 1D cubic b-spline
over three knot points, and to evaluate it at several coordinates:

::

  #include <iostream>
  #include "vspline/vspline.h"

  int main ( int argc , char * argv[] )
  {
    auto spline = vspline::bspline < double , 1 > ( 3 ) ;
    auto coefficients = spline.core ;

    coefficients [ 0 ] = 0.0 ;
    coefficients [ 1 ] = 1.0 ;
    coefficients [ 2 ] = 0.0 ;

    spline.prefilter() ;

    auto evaluator = vspline::make_safe_evaluator ( spline ) ;

    for ( double x = -1.0 ; x < 3.3 ; x += .3 )
      std::cout << x << " -> " << evaluator ( x ) << std::endl ;
  }


This only scratches the surface, but it demonstrates a few points:

- a vspline::bspline object holds the spline's coefficients
- prefiltering the spline has to be done explicitly
- to evaluate the spline, a specific evaluation functor is created

Beyond the single-value operation in this simple example, vspline offers
sophisticated processing of data in multidimensional arrays of arbitrary
dimensionality, shape and strides. Splines can hold fundamentals or
'xel' data, like pixels, and evaluation code has SIMD overloads,
which optionally use Vc. Spline degree can be up to 45.

------------
Introduction
------------

vspline provides a free, comprehensive and fast library for uniform B-splines
and their use with n-dimensional raster data, using multithreading and SIMD processing.

Uniform B-splines are a method to provide a 'smooth' interpolation over a set of
uniformly sampled data points. They are commonly used in signal processing as they
have several 'nice' qualities - an in-depth treatment and comparison to other
interpolation methods can be found in the paper 'Interpolation Revisited' [CIT2000]_
by Philippe Thévenaz, Member, IEEE, Thierry Blu, Member, IEEE, and Michael Unser,
Fellow, IEEE.

While there are several freely available packets of B-spline code, I failed to find
one which is comprehensive, efficient and generic at once. vspline attempts to be
all that, making use of generic programming in C++11, and of common, but often underused
hardware features in modern processors. Overall, there is an emphasis on speed, even
if this makes the code more complex. I tried to eke as much performance out of the
hardware at my disposal as possible, only compromising when the other design goals
would have been compromised.

Some of the code is quite low-level, but there are reasonably high-level mechanisms
to interface with vspline, allowing easy access to it's functionality without requiring
users to familiarize themselves with the internal workings. High-level approach is
provided via class 'bspline' defined in bspline.h, 'evaluator' objects from eval.h
and via the remap/transform functions defined in transform.h. This high-level code
allows using fast, high-quality interpolation with just a few lines of code. It should
be especially attractive to vigra users, since the data handling is done with vigra
data types.

While I made an attempt to write code which is portable, vspline is only tested with
g++ and clang++. An installation of Vigra_ is needed to compile, installation
of Vc_ is optional but recommended. Some Linux distros offer both vigra and Vc versions
suitable for vspline, alternatively you can build either from source. A vspline debian
package_ is now available.

Note: in November 2017, with help from Bernd Gaucke, vspline's companion program pv, which uses vspline heavily, was successfully compiled with 'Visual Studio Platform toolset V141'.

vspline has been in use for a few years now, and the code seems robust enough to warrant a 1.X version number. I think it's fit for production use.

I have made efforts to cover 'reasonable' use cases, but there may be
corner cases and unexpected scenarios where my code fails. The code is not
well shielded against inappropriate parameters. The intended audience is
developers rather than end users.

vspline is useful for sound and image processing, volume slicing, and other
tasks needing efficient, precise interpolation. It comes with example code, and
if you want to see vspline used in a 'real world' program, have a look at
pv_, my image and panorama viewer, which uses vspline for rendering.

vspline goes beyond the 'standard' features one might expect from a b-spline
library, namely coefficient generation and evaluation at some coordinate: it
also provides code to process n-dimensional arrays of data with multithreaded
code and to use hardware vectorization with SIMD code. This makes the code base
large, and for maximum benefit you'll have to get familiar with these 'additional'
features. I hope you'll find this is worth your while.

-----
Scope
-----

There are (at least) two different approaches to tackle B-splines as a mathematical problem. The first one is to look at them as a linear algebra problem. Calculating the B-spline coefficients is done by solving a set of equations, which can be codified as banded diagonal matrices with slight disturbances at the top and bottom, resulting from boundary conditions. The mathematics are reasonably straightforward and can be efficiently coded (at least for lower-degree splines), but I found it hard to generalize to B-splines of higher order.

The second approach to B-splines comes from signal processing, and it's the one which I found most commonly used in the other implementations I studied. It generates the B-spline coefficients by applying a forward-backward recursive digital filter to the data and usually implements boundary conditions by picking appropriate initial causal and anticausal coefficients. Once I had understood the process, I found it elegant and beautiful - and perfectly general, lending itself to the implementation of a body of generic code with the scope I envisioned.

Evaluation of a b-spline requires picking a subset of the coefficients and forming a weighted sum of this subset. So there are two components involved: getting the coefficients from memory and the formation of the weighted sum. vspline is designed to do these tasks as quickly as possible, and a large part of it's code is dedicated to the evaluation process.

As it turns out, merely having a fast evaluation function is not enough, and a good part of vspline deals with moving data around, parceling them so that a set of independent threads can process them concurrently with vector operations, and putting the results back to memory. The actual evaluation is done by a functor, which I think of as a tool. I think of applying this tool to data as 'wielding' it, so I call the code to 'roll out' the functors 'wielding code'. This code goes beyond what one might usually expect in a b-spline package and might even be put into a separate library. For now it's part of vspline, and vspline's remap, transform and apply functions use it extensively.

vspline can handle

-  splines over real and integer data types and their aggregates:
-  all 'xel' data, arbitrary number of channels (template argument)
-  single, double precision and long doubles supported (template argument)
-  a reasonable selection of boundary conditions
-  arbitrary dimensionality of the spline (template argument)
-  spline degree up to 45 (runtime argument)
-  specialized code for 1D data
-  multithreaded code (pthread)
-  using the CPU's vector units if possible (like SSE, AVX/2)

On the evaluation side it provides

-  evaluation of the spline at point locations in the defined range
-  evaluation of vectorized arguments
-  evaluation of the spline's derivatives
-  factory functions to create evaluation functors
-  specialized code for degrees 0 and 1 (nearest neighbour and n-linear)
-  mapping of arbitrary coordinates into the defined range
-  evaluation of nD arrays of coordinates ('remap' function)
-  discrete-coordinate-fed remap function ('index_remap')
-  generalized functor-based 'apply' and 'transform' functions
-  restoration of the original data from the spline coefficients

To produce maximum performance, vspline has a fair amount of collateral code,
and some of this code may be helpful beyond vspline:

-  multithreading with a thread pool
-  efficient processing of nD arrays with multiple threads
-  functional constructs using vspline::unary_functor
-  nD forward-backward n-pole recursive filtering
-  nD separable convolution
-  efficient access to the b-spline basis functions
-  precise precalculated constants (made with GNU GSL, BLAS and GNU GMP)
-  many examples, ample comments

The code at the very core of my B-spline prefiltering code evolved from the code by Philippe Thévenaz which he published here_, with some of the boundary condition treatment code derived from formulae which he communicated to me. Next I needed code to handle multidimensional arrays in a generic fashion in C++. I chose to use Vigra_. Since my work has a focus on signal (and, more specifically image) processing, it's an excellent choice, as it provides a large body of code with precisely this focus and has well-thought-out, reliable support for multidimensional arrays and small zero-overhead aggregates. vigra's MultiArray and MultiArrayView types are similar to Numpy_ arrays.

Once I had a prototype up and running, I looked out for further improvements to it's speed. While using GPUs is tempting, I chose not to go down this path. Instead I chose to stick with CPU code and use vectorization. Again, I did some research and found Vc_. Vc allowed me to write generic code for vectorization for a reasonably large sets of targets. I found the performance gain for some data types so enticing that I chose to make my code optionally use Vc. The use of Vc has to be activated by a compile-time flag (USE_VC). When Vc can't be used - or for data types Vc can't vectorize - vspline uses SIMD emulation: data are aggregated to small vector-friendly parcels and the expectation is that processing of such aggregates will trigger the compiler's autovectorization. I call this technique 'goading' and found it to work surprisingly well. This technique works especially well for vspline's digital filters, while the b-spline evaluation code with it's more complex arithmetics and frequent data-driven memory access does benefit less.

With the 'core' b-spline functionality established, I wrote the 'wielding' code and extended the code base to handle 'corner cases' like 1D data, which need some trickery to be efficiently processed with multithreading and SIMD instructions, or integer-based splines, which need auxilliary code to exploit the integer dynamic range. Using vspline with other projects, I established a small set of useful helper types, methods for functor composition and factory functions to facilitate using vspline without having to be too concerned about template arguments.

I did all my programming on a Kubuntu_ system, running on an intel(R) Core (TM) i5-4570 CPU, and used GNU gcc_ and clang_ to compile the code in C++11 dialect. While I am confident that the code runs on other CPUs, I have not tested it much with other compilers or operating systems.

.. _here: http://bigwww.epfl.ch/thevenaz/interpolation/
.. _Vigra: http://ukoethe.github.io/vigra/
.. _Vc: https://github.com/VcDevel/Vc
.. _Kubuntu: http://kubuntu.org/
.. _gcc: https://gcc.gnu.org/
.. _clang: http://http://clang.llvm.org/
.. _package: https://tracker.debian.org/pkg/vspline

.. [CIT2000] Interpolation Revisited by Philippe Thévenaz, Member,IEEE, Thierry Blu, Member, IEEE, and Michael Unser, Fellow, IEEE in IEEE TRANSACTIONS ON MEDICAL IMAGING, VOL. 19, NO. 7, JULY 2000, which is available online_.

.. _online: http://bigwww.epfl.ch/publications/thevenaz0002.pdf

.. [1] I use 'aggregate' here to mean a collection of identical elements, in contrast to what C++ defines as an aggregate type. So aggregates would be pixels, vigra TinyVectors, and, also, complex types.

-------------
Documentation
-------------

vspline uses doxygen to create documentation.
You can access the documentation online here:

documentation_

I have made an effort to comment my code very extensively. There are probably more comments in vspline than actual code, and this is appropriate, since the code is in large parts complex template metacode, in an attempt to make it as generic as possible.

vspline comes with a fair amount of examples, which at times are a bit rough-and-ready, and less well-groomed than the library code. Nevertheless the examples should help in understanding how vspline can be put to use. I've also published my bootstrapping code, producing precalculated b-spline prefilter pole values and basis function values in high-precision arithmetic using GSL, BLAS and GNU GMP. Some of the examples have self-testing code to establish that an installation of vspline runs correctly. The precalculated values allow vspline to provide very fast access to the b-spline basis functions and their derivatives, and creation of evaluation functors is near-instantaneous even for high degree splines.

-----
Speed
-----

While performance will vary widely from system to system and between different compiles, I'll quote some measurements from my own system. I include benchmarking code (roundtrip.cc in the examples folder). Here are some measurements done with "roundtrip", working on a full HD (1920*1080) RGB image, using single precision floats internally - the figures are averages of several runs, using a binary
compiled with clang++ v.10:

::

    testing bc code MIRROR    spline degree 3 using SIMD emulation
    avg 32 x prefilter:............................ 11.0 ms
    avg 32 x transform with ready-made bspline:.... 27.2 ms
    avg 32 x classic remap:........................ 39.9 ms
    avg 32 x restore original data: ............... 10.8 ms

    testing bc code MIRROR    spline degree 3 using Vc
    avg 32 x prefilter:............................ 9.2 ms
    avg 32 x transform with ready-made bspline:.... 19.0 ms
    avg 32 x classic remap:........................ 32.2 ms
    avg 32 x restore original data: ............... 10.7 ms
    difference original data/restored data:

As can be seen from these test results, using Vc on my system speeds evaluation up a good deal. When it comes to prefiltering, a lot of time is spent buffering data to make them available for fast vector processing. The time spent on actual calculations is much less. Therefore prefiltering for higher-degree splines is not much different:

::

    testing bc code MIRROR spline degree 5 using Vc
    avg 32 x prefilter:............................ 10.3 ms

    testing bc code MIRROR spline degree 7 using Vc
    avg 32 x prefilter:............................ 10.7 ms

Using double precision arithmetics, vectorization doesn't help so much, and prefiltering is actually slower on my system when using Vc. Doing a complete roundtrip run on your system should give you an idea about which mode of operation best suits your needs. These speed measurements are a bit of a moving target, since the code base is still under development - some modifications make some things faster and other things slower. But the figures above should give you an idea what to expect.

Since vspline can make heavy demands on memory bandwidth and CPU usage, it also lends itself to benchmarking a given system, running it full throttle on all cores with SIMD instructions.

vspline is written to auto-vectorize well, so even if Vc is not used, if the relevant compiler switches are set (-march=native, -mavx2, -O3 or -Ofast etc) the code will use SIMD instructions if the compiler autovectorizes it, so vspline can also serve to check how well different compilers autovectorize. The compilers' autovectorization capabilites differ, but seem to gradually improve, so the need for Vc lessens. I hope to exploit the upcoming C++ standard for SIMD processing, which is actually quite similar to Vc (Matthias Kretz, Vc's author, has contributed a great deal to the standardization process) - so porting from Vc to std::simd should not be too hard. For the time being I'll stick with Vc, though, especially because of Vc's good gather/scatter support and fast interleave/deinterleave routines based on load/shuffle and shuffle/store combinations, which are not part of the simd standard (yet). Some special Vc features go beyond what can be expected from autovectorization, so I still recommend it.

----------
History
----------

Some years ago, I needed uniform B-splines for a project in python. I looked for code in C which I could easily wrap with cffi_, as I intended to use it with pypy_, and found K. P. Esler's libeinspline_. I proceeded to code the wrapper, which also contained a layer to process Numpy_ nD-arrays, but at the time I did not touch the C code in libeinspline. The result of my efforts is still available from the repository_ I set up at the time. I did not use the code much and occupied myself with other projects, until my interest in B-splines was rekindled sometime in late 2015. I had a few ideas I wanted to try out which would require working with the B-spline C code itself. I started out modifying code in libeinspline, but after some initial progress I felt constricted by the code base in C, the linear algebra approach, the limitation to cubic splines, etc. Being a C++ programmer with a fondness for generic programming, I first re-coded the core of libeinspline in C++ (to continue using my python interface), but then, finally, I decided to start afresh in C++, abandon the link to libeinspline and not have a python interface (for now). vspline is the result of my work. I have chosen the name vspline, because I rely on two libraries staring with 'V', namely Vigra_ and Vc_. You can also think of it as a shorthand for 'vectorized splines'.

The code has been under development for severeal years now. Once the initial design turned out to be workable I went on to go through all the components cyclically, trying to make everything rock solid and precise.

vspline is in constant use via pv_, my image and panorama viewer, where it is used for interpolation and for efficient handling of rendering pipelines. Interaction with this program drove vspline development and helped in keeping it well-groomed.

While my initial way to use b-splines in python used 'classical' python tools like CFFI, I have recently coded a python interface to vspline which provides most of it's functionality to python. This interface is based on cppyy_, a novel way to integrate python and C++ without the need for interface definitions. My new b-spline python module is there_. It's still quite rough-and-ready, but it sports a pythonization layer and can handle NumPy ndarrays.

.. _cffi: https://cffi.readthedocs.org/en/latest/
.. _pypy: http://pypy.org/
.. _libeinspline: http://einspline.sourceforge.net/
.. _Numpy: http://www.numpy.org/
.. _repository: https://bitbucket.org/kfj/python-bspline
.. _documentation: https://kfj.bitbucket.io
.. _pv: https://bitbucket.org/kfj/pv
.. _cppyy: https://bitbucket.org/wlav/cppyy
.. _there: https://bitbucket.org/kfj/python-vspline




