[vspline] 02/72: Initial commit of vspline's source code and examples
Kay F. Jahnke
kfj-guest at moszumanska.debian.org
Sun Jul 2 09:02:37 UTC 2017
This is an automated email from the git hooks/post-receive script.
kfj-guest pushed a commit to branch master
in repository vspline.
commit ac366e41ae127b574f7ee324c1e8240db1b09f14
Author: Kay F. Jahnke <kfjahnke at gmail.com>
Date: Wed Oct 12 17:56:13 2016 +0200
Initial commit of vspline's source code and examples
---
LICENSE | 27 +
basis.h | 236 ++
brace.h | 542 +++++
bspline.h | 534 +++++
common.h | 530 +++++
doxy.h | 300 +++
eval.h | 1099 ++++++++++
example/eval.cc | 132 ++
example/gradient.cc | 90 +
example/pano_extract.cc | 776 +++++++
example/roundtrip.cc | 405 ++++
example/times.txt | 5502 +++++++++++++++++++++++++++++++++++++++++++++++
mapping.h | 1555 ++++++++++++++
poles.cc | 660 ++++++
prefilter.h | 1915 +++++++++++++++++
prefilter_poles.cc | 174 ++
remap.h | 883 ++++++++
vspline.doxy | 2303 ++++++++++++++++++++
vspline.h | 38 +
19 files changed, 17701 insertions(+)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d4ebd66
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+vspline - generic C++ code for creation and evaluation
+ of uniform b-splines
+
+ Copyright 2015, 2016 by Kay F. Jahnke
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/basis.h b/basis.h
new file mode 100644
index 0000000..d517d9f
--- /dev/null
+++ b/basis.h
@@ -0,0 +1,236 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/*! \file basis.h
+
+ \brief Code to calculate the value B-spline basis function
+ and it's derivatives.
+
+ There are several variants in here. First, there is a perfectly general
+ routine, using the Cox-de Boor recursion. While this is 'nice to have',
+ vspline does not actually use it (except as a reference in unit testing).
+
+ vspline only needs evaluation of the B-spline basis function at multiples
+ of 0.5. With these values it can construct it's evaluators which in turn
+ are capable of evaluating the spline at real coordinates.
+
+ So next is a specialized routine using an adapted version of the recursion
+ to calculate the basis function's value for integral operands. This isn't
+ used in vspline either - instead vspline uses a third version which abbreviates
+ the recursion by relying on precomputed values for the basis function with
+ derivative 0, which the recursion reaches after as many levels as the
+ requested derivative, so seldom deeper than 2. That makes it very fast.
+
+ For comparison there is also a routine calculating an approximation of the
+ basis function's value (only derivative 0) by means of a gaussian. This
+ routine isn't currently used in vspline.
+
+ for a discussion of the b-spline basis function, have a look at
+ http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-basis.html
+*/
+
+#ifndef VSPLINE_BASIS_H
+#define VSPLINE_BASIS_H
+
+// poles.cc has precomputed basis function values sampled at n * 1/2
+
+#include <vspline/poles.cc>
+
+namespace vspline {
+
+/// Implementation of the Cox-de Boor recursion formula to calculate
+/// the value of the bspline basis function. This code is taken from vigra
+/// but modified to take the spline degree as a parameter. This makes it
+/// easier to handle, since we don't need a vigra::BSpline object of a specific
+/// order to call it. This code is quite expensive for higer spline orders
+/// because the routine calls itself twice recursively, so the performance is
+/// N*N with the spline's degree. Luckily there are ways around using this routine
+/// at all - whenever we need the b-spline basis function value in vspline, it is
+/// at multiples of 1/2, and poles.cc has precomputed values for all spline
+/// degrees covered by vspline. I leave the code in here for reference purposes.
+
+template < class real_type >
+real_type gen_bspline_basis ( real_type x , int degree , int derivative )
+{
+ if ( degree == 0 )
+ {
+ if ( derivative == 0 )
+ return ( x < real_type(0.5) && real_type(-0.5) <= x )
+ ? real_type(1.0)
+ : real_type(0.0) ;
+ else
+ return real_type(0.0);
+ }
+ if ( derivative == 0 )
+ {
+ real_type n12 = real_type((degree + 1.0) / 2.0);
+ return ( ( n12 + x )
+ * gen_bspline_basis<real_type> ( x + real_type(0.5) , degree - 1 , 0 )
+ + ( n12 - x )
+ * gen_bspline_basis<real_type> ( x - real_type(0.5) , degree - 1 , 0 )
+ )
+ / degree;
+ }
+ else
+ {
+ --derivative;
+ return gen_bspline_basis<real_type> ( x + real_type(0.5) , degree - 1 , derivative )
+ - gen_bspline_basis<real_type> ( x - real_type(0.5) , degree - 1 , derivative ) ;
+ }
+}
+
+/// this routine is a helper routine to cdb_bspline_basis, the
+/// modified Cox-de Boor recursion formula to calculate the b-spline basis function
+/// for integral operands, operating in int as long as possible. This is achieved by
+/// working with 'x2', the doubled x value. Since in the 'real' recursion, the next
+/// iteration is called with x +/- 1/2, we can call the 'doubled' version with x +/- 1.
+/// This routine recurses 'all the way down to degree 0, So the result is, disregarding
+/// arithmetic errors, the same as the result obtained with the general routine.
+
+template < class real_type >
+real_type cdb_bspline_basis_2 ( int x2 , int degree , int derivative )
+{
+ if ( degree == 0 )
+ {
+ if ( derivative == 0 )
+ return ( x2 < 1 && -1 <= x2 )
+ ? real_type(1.0)
+ : real_type(0.0) ;
+ else
+ return real_type(0.0);
+ }
+ if ( derivative == 0 )
+ {
+ int n122 = degree + 1 ;
+ return ( ( n122 + x2 )
+ * cdb_bspline_basis_2<real_type> ( x2 + 1 , degree - 1 , 0 )
+ + ( n122 - x2 )
+ * cdb_bspline_basis_2<real_type> ( x2 - 1 , degree - 1 , 0 )
+ )
+ / ( 2 * degree ) ;
+ }
+ else
+ {
+ --derivative;
+ return cdb_bspline_basis_2<real_type> ( x2 + 1 , degree - 1 , derivative )
+ - cdb_bspline_basis_2<real_type> ( x2 - 1 , degree - 1 , derivative ) ;
+ }
+}
+
+/// modified Cox-de Boor recursion formula to calculate the b-spline basis function
+/// for integral operands, delegates to the 'doubled' routine above
+
+template < class real_type >
+real_type cdb_bspline_basis ( int x , int degree , int derivative = 0 )
+{
+ return cdb_bspline_basis_2<real_type> ( x + x , degree , derivative ) ;
+}
+
+/// see bspline_basis() below!
+///
+/// this helper routine works with the doubled value of x, so it can capture calls equivalent
+/// to basis ( x + .5 ) or basis ( x - .5 ) as basis2 ( x + 1 ) and basis2 ( x - 1 )
+/// having precalculated the basis function at .5 steps, we can therefore avoid
+/// using the general recursion formula. This is a big time-saver for high degrees.
+
+template < class real_type >
+real_type bspline_basis_2 ( int x2 , int degree , int derivative )
+{
+ if ( degree == 0 )
+ {
+ if ( derivative == 0 )
+ return ( x2 < 1 && -1 <= x2 )
+ ? real_type(1.0)
+ : real_type(0.0) ;
+ else
+ return real_type(0.0);
+ }
+ if ( derivative == 0 )
+ {
+ if ( abs ( x2 ) > degree )
+ return real_type ( 0 ) ;
+ // for derivative 0 we have precomputed values:
+ const long double * pk = precomputed_basis_function_values [ degree ] ;
+ return pk [ abs ( x2 ) ] ;
+ }
+ else
+ {
+ --derivative;
+ return bspline_basis_2<real_type> ( x2 + 1 , degree - 1 , derivative )
+ - bspline_basis_2<real_type> ( x2 - 1 , degree - 1 , derivative ) ;
+ }
+}
+
+/// bspline_basis produces the value of the b-spline basis function for
+/// integral operands, the given degree 'degree' and the desired derivative.
+/// It turns out that this is all we ever need inside vspline, the calculation
+/// of the basis function at arbitrary points is performed via the matrix
+/// multiplication in the weight generating functor, and this functor sets
+/// it's internal matrix up with bspline basis function values at integral
+/// locations.
+///
+/// bspline_basis delegates to bspline_basis_2 above, which picks precomputed
+/// values as soon as derivative becomes 0. This abbreviates the recursion
+/// a lot, since usually the derivative requested is 0 or a small integer.
+/// all internal calculations in vspline accessing b-spline basis function
+/// values are currently using this routine, not the general routine.
+///
+/// Due to the precalculation with long double arithmetic, the precomputed
+/// values aren't precisely equal to the result of running the recursive
+/// routines above on the same arguments.
+
+template < class real_type >
+real_type bspline_basis ( int x , int degree , int derivative = 0 )
+{
+ return bspline_basis_2<real_type> ( x + x , degree , derivative ) ;
+}
+
+/// Gaussian approximation to B-spline basis function. This routine
+/// approximates the basis function of degree spline_degree for real x.
+/// I checked for all degrees up to 20. The partition of unity quality of the
+/// resulting reconstruction filter is okay for larger degrees, the cumulated
+/// error over the covered interval is quite low. Still, as the basis function
+/// is never actually evaluated in vspline (whenever it's needed, it is needed
+/// at n * 1/2 and we have precomputed values for that) there is not much point
+/// in having this function around. I leave the code in for now.
+
+template < typename real_type >
+real_type gaussian_bspline_basis_approximation ( real_type x , int degree )
+{
+ real_type sigma = ( degree + 1 ) / 12.0 ;
+ return real_type(1.0)
+ / sqrt ( real_type(2.0 * M_PI) * sigma )
+ * exp ( - ( x * x ) / ( real_type(2.0) * sigma ) ) ;
+}
+
+} ; // end of namespace vspline
+
+#endif // #define VSPLINE_BASIS_H
diff --git a/brace.h b/brace.h
new file mode 100644
index 0000000..409cae4
--- /dev/null
+++ b/brace.h
@@ -0,0 +1,542 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/*! \file brace.h
+
+ \brief This file provides code for 'bracing' the spline coefficient array.
+
+ Inspired by libeinspline, I wrote code to 'brace' the spline coefficients. The concept is
+ this: while the IIR filter used to calculate the coefficients has infinite support (though
+ arithmetic precision limits this in real-world applications), the evaluation of the spline
+ at a specific location only looks at a small window of coefficients (compact, finite support).
+ This fact can be exploited by taking note of how large the support area is and providing
+ a few more coefficients in a frame around the 'core' coefficients to allow the evaluation
+ to proceed without having to check for boundary conditions. While the difference is not
+ excessive (the main computational cost is the actual evaluation itself), it's still
+ nice to be able to code the evaluation without boundary checking, which makes the code
+ very straightforward and legible.
+
+ There is another aspect to bracing: In my implementation of vectorized evaluation,
+ the window into the coefficient array used to pick out coefficients to evaluate at
+ a specific location is coded as a set of offsets from it's 'low' corner. This way,
+ several such windows can be processed in parallel. This mechanism can only function
+ efficiently in a braced coefficient array, since it would otherwise have to give up
+ if any of the windows accessed by the vector of coordinates had members outside the
+ (unbraced) coefficient array and submit the coordinate vector to individual processing.
+ I consider the logic to code this and the loss in performance too much of a bother
+ to go down this path; all my evaluation code uses braced coefficient arrays. Of course
+ the user is free to omit bracing, but then they have to use their own evaluation
+ code.
+
+ What's in the brace? Of course this depends on the boundary conditions chosen.
+ In vspline, I offer code for several boundary conditions, but most have something
+ in common: the original, finite sequence is extrapolated into an infinite periodic
+ signal. With straight PERIODIC boundary conditions, the initial sequence is
+ immediately followed and preceded by copies of itself. The other boundary conditions
+ mirror the signal in some way and then repeat the mirrored signal periodically.
+ Using boundary conditions like these, both the extrapolated signal and the
+ coefficients share the same periodicity and mirroring.
+
+ There are two ways of
+ arriving at a braced coeffcient array: We can start from the extrapolated signal,
+ pick a section large enough to make margin effects vanish (due to limited arithmetic
+ precision), prefilter it and pick out a subsection containing the 'core' coefficients
+ and their support. Alternatively, we can work only on the core coefficients, calculate
+ suitable initial causal and anticausal coeffcients (where the calculation considers
+ the extrapolated signal, which remains implicit), apply the filter and *then* surround
+ the core coefficient array with more coeffcients (the brace) following the same
+ extrapolation pattern as we imposed on the signal, but now on the coefficients
+ rather than on the initial knot point values.
+
+ The bracing can be performed without any solver-related maths by simply copying
+ (possibly trivially modified) slices of the core coefficients to the margin area.
+
+ Following the 'implicit' scheme, my default modus operandi braces after the
+ prefiltering. Doing so, it is posible to calculate the inital causal and anticausal
+ coefficient for the prefilter exactly. But this exactness is still, eventually,
+ subject to discretization and can only be represented after quantization. If,
+ instead, we prefilter a suitably extrapolated signal, now with arbitrary boundary
+ conditions, the margin effects will vanish towards the center (due to the characteristics
+ of the filter), and the 'core' coefficients will end up the same as in the first
+ approach. So we might as well extrapolate the signal 'far enough', pick any boundary
+ conditions we like (even zero padding), prefilter, and discard the margin outside the
+ area which is unaffected by margin effects. The result is, within arithmetic precision,
+ the same. Both approaches have advantages and disadvantages:
+
+ Implicit extrapolation needs less memory - we only need to provide storage for the
+ core coeffcients, which is just as much as we need for the original signal, so we can
+ operate in-place. The disadvantage of the implicit scheme is that we have to capture
+ the implicit extrapolation in code to calculate the initial causal/anticausal coefficients,
+ which is non-trivial and requires separate routines for each case, as can be seen in my
+ prefiltering code. And if, after prefiltering, we want to brace the core coeffcients
+ for efficient evaluation, we still need additional memory, which, if it hasn't been
+ allocated around the core before prefiltering, even requires us to copy the data out
+ into a larger memory area.
+
+ Explicit extrapolation needs more memory. A typical scheme would be to anticipate the
+ space needed for the explicit extrapolation, allocate enough memory for the extrapolated
+ signal, place the same into the center of the allocated memory, perform the extrapolation
+ and then prefilter. The advantage is that we can run the prefilter with arbitrary initial
+ causal/anticausal coefficients. No matter what the extrapolation looks like, we can always
+ use the same code. And we can extrapolate in any way we see fit, without having to produce
+ code to deal with our choice. If we pick the frame of extrapolated values large enough,
+ we can even pick out the 'braced' coefficient array from the result of the filter.
+
+ Obviously, there is no one 'right' way of doing this. Offered several choices
+ of implicit extrapolation, the user can choose between the implicit and explicit scheme.
+ The code in this file is useful for both choices: for the implicit scheme, bracing is
+ applied after prefiltering to enable evaluation with vspline. For the explicit scheme,
+ bracing may be used on the original data before prefiltering with arbitrary boundary
+ conditions, if the user's extrapolation scheme is covered by the code given here.
+
+ When using the higher-level access methods (via bspline objects), using the explicit or
+ implicit scheme becomes a matter of passing in the right flag, so at this level, a deep
+ understanding of the extrapolation mechanism isn't needed at all. I use the implicit scheme
+ as the default, because it needs slightly less memory and CPU cycles.
+
+ Since the bracing mainly requires copying data or trivial maths we can do the operations
+ on higher-dimensional objects, like slices of a volume. To efficiently code these operations
+ we make use of vigra's multi-math facility and it's bindAt array method, which makes
+ these subarrays easily available.
+
+ TODO: while this is convenient, it's not too fast, as it's neither multithreaded nor
+ vectorized. Still in most 'normal' scenarios the execution time is negligible...
+
+ TODO: there are 'pathological' cases where one brace is larger than the other brace
+ and the width of the core together. These cases can't be handled for all bracing modes
+ and will result in an exception.
+*/
+
+#ifndef VSPLINE_BRACE_H
+#define VSPLINE_BRACE_H
+
+#include <vigra/multi_array.hxx>
+#include <vigra/multi_iterator.hxx>
+#include <vigra/multi_math.hxx>
+#include "common.h"
+
+namespace vspline {
+
+using namespace vigra::multi_math;
+
+/// class bracer encodes the entire bracing process. It also gives the metrics
+/// for the size of the braces expected by the evaluation code.
+
+template < class view_type >
+struct bracer
+{
+ typedef typename view_type::value_type value_type ;
+ typedef typename ExpandElementResult<value_type>::type ele_type ;
+ enum { dimension = view_type::actual_dimension } ;
+ typedef typename view_type::difference_type shape_type ;
+
+ /// calculates the size of the left brace for a given spline degree. In most cases,
+ /// this is the size of the support of the reconstruction filter, rounded up to the
+ /// next integer. So for an even spline, we get the same left brace size as for the
+ /// odd spline one degree higher.
+ ///
+ /// reflect boundary conditions work slightly differently: we somehow have to access
+ /// the area around the point of reflection - coordinates -1 .. 0 and M-1 .. M, which
+ /// are part of the extrapolated signal, so we need a wider brace - 0.5 at either end.
+ /// For an even spline, this results in the same value as for other boundary conditions,
+ /// since the even spline has smaller support (0.5 precisely). But for odd splines,
+ /// we need another coefficient on either end.
+
+ static int left_brace_size ( int spline_degree , bc_code bc )
+ {
+ if ( bc == REFLECT || bc == SPHERICAL )
+ return ( spline_degree + 1 ) / 2 ;
+ else
+ return spline_degree / 2 ;
+ }
+
+/// The right handside bracing differs between periodic and mirrored splines, due to
+/// the amount of initial data: when specifying knot point data for a periodic spline,
+/// the first repetition is omitted (as it is the same value as at coordinate 0), but
+/// for evaluation, it has to be present, so the bracing code will produce it.
+///
+/// initially I was using the minimal bracing possible by coding:
+/// return left_brace_size ( spline_degree , bc ) + ( bc == PERIODIC ? 1 : 0 ) ;
+/// This has the disadvantage that for odd splines it requires checking if incoming
+/// coordinates are precisely at the right end of the defined range and splitting these
+/// coordinates to M-2, 1.0 instead of M-1, 0.0. Being more generous with the right brace
+/// and adding another layer makes this check unnecessary. Since this check is inner loop
+/// stuff where every clock cycle counts, I now use the more generous bracing.
+/// Note that for the periodic case I assume silently that incoming
+/// coordinates won't ever be M-1, as these can be mapped to 0.0 (due to the periodicity,
+/// this is equivalent) - so here I need an extended brace to capture the last unit spacing
+/// of the spline, but I don't need the additional extension to safeguard against v == M-1.
+/// If you use foreign evaluation routines you may want an additional coefficient here.
+
+ static int right_brace_size ( int spline_degree , bc_code bc )
+ {
+ return left_brace_size ( spline_degree , bc )
+ + ( ( ( spline_degree & 1 ) || ( bc == PERIODIC ) ) ? 1 : 0 ) ;
+ }
+
+/// this method gives the shape of the braced array, given the unbraced array's shape,
+/// the BC codes and the spline degree. For the shapes involved, this relation holds true:
+/// target_shape = left_corner + core_shape + right_corner
+/// So. for the implicit scheme, to evaluate from a braced spline, target_shape is the minimal
+/// coefficient array size needed by vspline's evaluation code. For the explicit scheme, this
+/// is the section of the coeficient array the evaluation code will look at.
+
+ static shape_type target_shape ( shape_type source_shape ,
+ TinyVector < bc_code , dimension > bcv ,
+ int spline_degree )
+ {
+ shape_type target_shape ;
+ for ( int d = 0 ; d < dimension ; d++ )
+ target_shape[d] = source_shape[d]
+ + left_brace_size ( spline_degree , bcv[d] )
+ + right_brace_size ( spline_degree , bcv[d] ) ;
+ return target_shape ;
+ }
+
+/// convenience variant of the previous routine using the same BC for all axes
+
+ static shape_type target_shape ( shape_type source_shape ,
+ bc_code bc ,
+ int spline_degree )
+ {
+ TinyVector < bc_code , dimension > bcv ( bc ) ;
+ return target_shape ( source_shape , bcv , spline_degree ) ;
+ }
+
+/// this method gives the left offset to the 'core' subarray (array minus bracing),
+/// given the BC codes and the spline degree
+
+ static shape_type left_corner ( TinyVector < bc_code , dimension > bcv ,
+ int spline_degree )
+ {
+ shape_type target_offset ;
+ for ( int d = 0 ; d < dimension ; d++ )
+ target_offset[d] = left_brace_size ( spline_degree , bcv[d] ) ;
+ return target_offset ;
+ }
+
+/// this method gives the right offset to the 'core' subarray (array minus bracing),
+/// given the BC codes and the spline degree
+
+ static shape_type right_corner ( TinyVector < bc_code , dimension > bcv ,
+ int spline_degree )
+ {
+ shape_type target_offset ;
+ for ( int d = 0 ; d < dimension ; d++ )
+ target_offset[d] = right_brace_size ( spline_degree , bcv[d] ) ;
+ return target_offset ;
+ }
+
+/// given a braced array, return the size of it's 'core', the array without applied bracing
+
+ static shape_type core_shape ( view_type& a ,
+ TinyVector < bc_code , dimension > bcv ,
+ int spline_degree )
+ {
+ return a.subarray ( a.shape()
+ - ( right_corner ( bcv , spline_degree )
+ + left_corner ( bcv , spline_degree ) ) ) ;
+ }
+
+/// produce a view to the core
+
+ static view_type core_view ( view_type& a ,
+ TinyVector < bc_code , dimension > bc ,
+ int spline_degree )
+ {
+ return a.subarray ( left_corner ( bc , spline_degree ) ,
+ a.shape() - right_corner ( bc , spline_degree ) ) ;
+ }
+
+ /// for spherical images, we require special treatment for two-dimensional
+ /// input data, because we need to shift the values by 180 degrees, or half
+ /// the margin's width. But to compile, we also have to give a procedure
+ /// for the other cases (not 2D), so this is first:
+
+ template < typename value_type > shift_assign ( value_type target , value_type source )
+ {
+ // should not ever get used, really...
+ }
+
+ /// specialized routine for the 2D case (the slice itself is 1D)
+
+ template < typename value_type > shift_assign ( MultiArrayView < 1 , value_type > target ,
+ MultiArrayView < 1 , value_type > source )
+ {
+ // bit sloppy here, with pathological data (very small source.size()) this will
+ // be imprecise for odd sizes, for even sizes it's always fine. But then full
+ // sphericals always have size 2N * N, so odd sizes should not occur at all for dim 0
+ auto si = source.begin() + source.size() / 2 ;
+ auto se = source.end() ;
+ for ( auto& ti : target )
+ {
+ ti = *si ;
+ ++si ;
+ if ( si >= se )
+ si = source.begin() ;
+ }
+ }
+
+/// apply the bracing to the array, performing the required copy/arithmetic operations
+/// to the 'frame' around the core. This routine performs the operation along axis dim.
+/// This variant takes the sizes of the left and right brace without any reference to
+/// a spline's degree, so it can be fed arbitrary values. This is the most general bracing
+/// routine, which is used by the routines below which derive the brace's size from the
+/// spline's degree. It's also the routine to be used for explicitly extrapolating a signal:
+/// you place the data into the center of a larger array, and pass in the sizes of the 'empty'
+/// space which is to be filled with the extrapolated data.
+///
+/// the bracing is done one-left-one-right, to avoid corner cases as best as posible.
+
+ void apply ( view_type & a , // containing array
+ bc_code bc , // boundary condition code
+ int lsz , // space to the left which needs to be filled
+ int rsz , // ditto, to the right
+ int axis ) // axis along which to apply bracing
+ {
+ int w = a.shape ( axis ) ; // width of containing array along axis 'axis'
+ int m = w - ( lsz + rsz ) ; // width of 'core' array
+
+ if ( m < 1 ) // has to be at least 1
+ throw shape_mismatch ( "combined brace sizes must be at least one less than container size" ) ;
+
+ if ( ( lsz > m + rsz )
+ || ( rsz > m + lsz ) )
+ {
+ // not enough data to fill brace
+ if ( bc == PERIODIC || bc == NATURAL || bc == MIRROR || bc == REFLECT )
+ throw std::out_of_range ( "each brace must be smaller than the sum of it's opposite brace and the core's width" ) ;
+ }
+
+ int l0 = lsz - 1 ; // index of innermost empty slice on the left; like begin()
+ int r0 = lsz + m ; // ditto, on the right
+
+ int lp = l0 + 1 ; // index of leftmost occupied slice (p for pivot)
+ int rp = r0 - 1 ; // index of rightmost occupied slice
+
+ int l1 = -1 ; // index one before outermost empty slice to the left; like end()
+ int r1 = w ; // index one after outermost empty slice on the right
+
+ int lt = l0 ; // index to left target slice
+ int rt = r0 ; // index to right target slice ;
+
+ int ls , rs ; // indices to left and right source slice, will be set below
+
+ int ds = 1 ; // step for source index, +1 == forẃard, used for all mirroring modes
+ // for periodic bracing, it's set to -1.
+
+ switch ( bc )
+ {
+ case PERIODIC :
+ {
+ ls = l0 + m ;
+ rs = r0 - m ;
+ ds = -1 ; // step through source in reverse direction
+ break ;
+ }
+ case NATURAL :
+ case MIRROR :
+ {
+ ls = l0 + 2 ;
+ rs = r0 - 2 ;
+ break ;
+ }
+ case CONSTANT :
+ case SPHERICAL :
+ case REFLECT :
+ {
+ ls = l0 + 1 ;
+ rs = r0 - 1 ;
+ break ;
+ }
+ case ZEROPAD :
+ {
+ break ;
+ }
+ case IGNORE :
+ case IDENTITY :
+ {
+ // these modes perform no bracing, return prematurely
+ return ;
+ }
+ default:
+ {
+ cerr << "bracing for BC code " << bc_name[bc] << " is not supported" << endl ;
+ break ;
+ }
+ }
+
+ for ( int i = max ( lsz , rsz ) ; i > 0 ; --i )
+ {
+ if ( lt > l1 )
+ {
+ switch ( bc )
+ {
+ case PERIODIC :
+ case MIRROR :
+ case REFLECT :
+ {
+ // with these three bracing modes, we simply copy from source to target
+ a.bindAt ( axis , lt ) = a.bindAt ( axis , ls ) ;
+ break ;
+ }
+ case NATURAL :
+ {
+ // here, we subtract the source slice from twice the 'pivot'
+ // easiest would be:
+ // a.bindAt ( axis , lt ) = a.bindAt ( axis , lp ) * value_type(2) - a.bindAt ( axis , ls ) ;
+ // but this fails in 1D TODO: why?
+ auto target = a.bindAt ( axis , lt ) ; // get a view to left targte slice
+ target = a.bindAt ( axis , lp ) ; // assign value of left pivot slice
+ target *= ele_type(2) ; // double that
+ target -= a.bindAt ( axis , ls ) ; // subtract left source slice
+ break ;
+ }
+ case CONSTANT :
+ {
+ // here, we repeat the 'pivot' slice
+ a.bindAt ( axis , lt ) = a.bindAt ( axis , lp ) ;
+ break ;
+ }
+ case ZEROPAD :
+ {
+ // fill with 0
+ a.bindAt ( axis , lt ) = 0 ;
+ break ;
+ }
+ case SPHERICAL : // needs special treatment
+ {
+ shift_assign ( a.bindAt ( axis , lt ) , a.bindAt ( axis , ls ) ) ;
+ break ;
+ }
+ default :
+ // default: leave untouched
+ break ;
+ }
+ --lt ;
+ ls += ds ;
+ }
+ if ( rt < r1 )
+ {
+ // essentially the same, but with rs instead of ls, etc.
+ switch ( bc )
+ {
+ case PERIODIC :
+ case MIRROR :
+ case REFLECT :
+ {
+ // with these three bracing modes, we simply copy from source to target
+ a.bindAt ( axis , rt ) = a.bindAt ( axis , rs ) ;
+ break ;
+ }
+ case NATURAL :
+ {
+ // here, we subtract the source slice from twice the 'pivot'
+ // the easiest would be:
+ // a.bindAt ( axis , rt ) = a.bindAt ( axis , rp ) * value_type(2) - a.bindAt ( axis , rs ) ;
+ // but this fails in 1D TODO: why?
+ auto target = a.bindAt ( axis , rt ) ; // get a view to right targte slice
+ target = a.bindAt ( axis , rp ) ; // assign value of pivot slice
+ target *= ele_type(2) ; // double that
+ target -= a.bindAt ( axis , rs ) ; // subtract source slice
+ break ;
+ }
+ case CONSTANT :
+ {
+ // here, we repeat the 'pivot' slice
+ a.bindAt ( axis , rt ) = a.bindAt ( axis , rp ) ;
+ break ;
+ }
+ case ZEROPAD :
+ {
+ // fill with 0
+ a.bindAt ( axis , rt ) = 0 ;
+ break ;
+ }
+ case SPHERICAL : // needs special treatment
+ {
+ shift_assign ( a.bindAt ( axis , rt ) , a.bindAt ( axis , rs ) ) ;
+ break ;
+ }
+ default :
+ // default: leave untouched
+ break ;
+ }
+ ++rt ;
+ rs -= ds ;
+ }
+ }
+ }
+
+/// This variant of apply braces along all axes in one go.
+
+ static void apply ( view_type& a , ///< target array, containing the core and (empty) frame
+ TinyVector < bc_code , dimension > bcv , ///< boundary condition codes
+ TinyVector < int , dimension > left_corner , ///< sizes of left braces
+ TinyVector < int , dimension > right_corner ) ///< sizes of right braces
+ {
+ for ( int dim = 0 ; dim < dimension ; dim++ )
+ apply ( a , bcv[dim] , left_corner[dim] , right_corner[dim] , dim ) ;
+ }
+
+/// apply the bracing to the array, performing the required copy/arithmetic operations
+/// to the 'frame' around the core. This routine performs the operation along axis dim.
+/// Here, the size of the brace is derived from the spline degree. This is a convenience
+/// variant which saves you the explicit calls to left_brace_size and right_brace_size.
+
+ void operator() ( view_type& a , ///< target array, containing the core and (empty) frame
+ bc_code bc , ///< boundary condition code
+ int spline_degree , ///< degree of the spline
+ int dim ) ///< axis along which to brace
+ {
+ // calculate brace sizes
+ int lsz = left_brace_size ( spline_degree , bc ) ;
+ int rsz = right_brace_size ( spline_degree , bc ) ;
+
+ // delegate to apply()
+ apply ( a , bc , lsz , rsz , dim ) ;
+ }
+
+/// This variant braces along all axes, deriving brace sizes from the spline's degree
+
+ void operator() ( view_type& a , ///< target array, containing the core and (empty) frame
+ bc_code bc , ///< boundary condition codes
+ int spline_degree ) ///< degree of the spline
+ {
+ for ( int dim = 0 ; dim < dimension ; dim++ )
+ (*this) ( a , bc , spline_degree , dim ) ;
+ }
+} ;
+
+
+} ; // end of namespace vspline
+
+#endif // VSPLINE_BRACE_H
diff --git a/bspline.h b/bspline.h
new file mode 100644
index 0000000..6efe450
--- /dev/null
+++ b/bspline.h
@@ -0,0 +1,534 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/*! \file bspline.h
+ \brief defines class bspline
+
+ class bspline is the most convenient access to vspline's functionality.
+ It attempts to do 'the right thing' by automatically creating suitable helper
+ objects and parametrization so that the spline does what it's supposed to do.
+ Most users will not need anything else, and using class bspline is quite
+ straightforward. It's quite possible to have a b-spline up and running with
+ a few lines of code without even having to make choices concerning it's
+ parametrization, since there are sensible defaults for everything. At the same
+ time, pretty much everything *can* be parametrized even at this level.
+ bspline objects can be used without any knowledge of their internals,
+ e.g. as parameters to the remap functions.
+
+ class bspline handles several views to the coefficients it operates on, these
+ are realized as vigra::MultiArrayViews, and they all share the same storage:
+
+ - the 'core', which is a view to an array of data precisely the same shape as
+ the knot point data over which the spline is calculated.
+
+ - 'coeffs', which is a view to the core, plus 'bracing' needed to evaluate
+ the spline with vspline's evaluation code. 'coeffs' contains 'core'.
+ I refer to this view as the 'braced coefficients' as well.
+
+ - 'container', which contains the two views above plus an additional frame
+ of coefficients used for the 'explicit' scheme of extrapolation before
+ prefiltering.
+
+ Using class bspline, there is a choice of 'strategy'. The simplest strategy is
+ 'UNBRACED'. With this strategy, after putting the knot point data into the bspline's 'core'
+ area and calling prefilter(), the core area will contain the b-spline coefficients.
+ The resulting b-spline object can't be evaluated with the code in eval.h, this
+ mode of operation is intended for users who want to do their own processing of the
+ coefficients and don't need the code in eval.h. prefiltering is done using an
+ implicit scheme as far as the border conditions are concerned.
+
+ The 'standard' strategy is 'BRACED'. Here, after prefiltering, the view 'coeffs'
+ in the bspline object will contain the b-spline coefficients, surrounded by a 'brace'
+ of coefficients which allows code in eval.h to process them without special treatment
+ for values near the border (the brace covers what support is needed by marginal
+ coefficients). Again, an implicit scheme is used.
+
+ The third strategy, 'EXPLICIT', extrapolates the knot point data in the 'core' area
+ sufficiently far to suppress margin effects when the prefiltering is performed without
+ initial coefficient calculation. If the 'frame' of extrapolated data is large enough,
+ the result is just the same. The inner part of the frame is taken as the brace, so no
+ bracing needs to be performed explicitly. The resulting b-spline object will work with
+ vspline's evaluation code. Note that the explicit scheme uses 'IGNORE' boundary conditions
+ on the (framed) array, which is equivalent to zero-padding.
+
+ Also note that the additional memory needed for the 'frame' will be held throughout the bspline
+ object's life, the only way to 'shrink' the coefficient array to the size of the braced or core
+ coefficients is by copying them out to a smaller array.
+
+ The fourth strategy, 'MANUAL', is identical to 'EXPLICIT', except that automatic extrapolation
+ of the core data to the frame is not performed. Instead, this strategy relies on the user to
+ fill the frame with extrapolated data. This is to allow for the user to apply custom
+ extrapolation schemes. The procedure would be to create the bspline object, fill the core,
+ apply the extrapolation, then call prefilter.
+
+ Probably the most common scenario is that the source data for the spline are available from
+ someplace like a file. Instead of reading the file's contents into memory first and passing
+ the memory to class bspline, there is a more efficient way: a bspline object is set up
+ first, with the specification of the size of the incoming data and the intended mode of
+ operation. The bspline object allocates the memory it will need for the purpose, but
+ doesn't do anything else. The 'empty' bspline object is then 'filled' by the user
+ by putting data into it's 'core' area. Subsequently, prefilter() is called, which converts
+ the data to b-spline coefficients. This way, only one block of memory is used throughout,
+ the initial data are overwritten by the coefficients, operation is in-place and most efficient.
+
+ If this pattern can't be followed, there are alternatives:
+
+ - if a view to an array at least the size of the container array is passed into bspline's
+ constructor, this view is 'adopted' and all operations will use the data it refers to.
+ The caller is responsible for keeping these data alive while the bspline object exists,
+ and relinquishes control over the data, which may be changed by the bspline object.
+ Note that there is a convenience method, 'container_size', which can calculate the
+ shape of a container suitable for the purpose.
+
+ - if data are passed to prefilter(), they will be taken as containing the knot point data,
+ rather than expecting the knot point data to be in the bspline oject's memory already.
+ This can also be used to reuse a bspline object with new data. The data passed in will
+ not be modified. This is most efficient when using an implicit scheme; when used together
+ with EXPLICIT, the data are (automatically) copied into the core area before prefiltering,
+ which is unnecessary with the implicit schemes - they can 'pull in' data in the course
+ of their operation.
+
+ While there is no explicit code to create a 'smoothing spline' - a b-spline evaluating
+ the source data without prefiltering them - this can be achieved simply by creating a b-spline
+ object with spline degree 0 and 'shifting' it to the desired degree for evaluation. Note that
+ you'll need the EXPLICIT strategy for the purpose, because otherwise the spline won't have
+ enough 'headroom' for shifting.
+
+ With shifting, you can also create a 'poor man's pyramid'. While using no additional storage,
+ you can extract smoothed data from the spline by shifting it up. This only goes so far, though,
+ because even a degree-20 b-spline reconstruction kernel's equivalent gaussian doesn't have a very
+ large standard deviation, and evaluation times become very long. From the gaussian approximation
+ of the b-spline basis function, it can be seen that the equivalent gaussian's standard deviation is
+ ( degree + 1 ) / 12.0, so a quintic spline will have a standard deviation of 0.5 only.
+*/
+
+#ifndef VSPLINE_BSPLINE_H
+#define VSPLINE_BSPLINE_H
+
+#include "prefilter.h"
+#include "brace.h"
+
+namespace vspline {
+
+/// struct bspline is a convenience class which bundles a coefficient array (and it's creation)
+/// with a set of metadata describing the parameters used to create the coefficients and the
+/// resulting data. I have chosen to implement class bspline so that there is only a minimal
+/// set of template arguments, namely the spline's data type (like pixels etc.) and it's dimension.
+/// All other parameters relevant to the spline's creation are passed in at construction time.
+/// This way, if explicit specialization becomes necessary (like, to interface to code which
+/// can't use templates) the number of specialzations remains manageable. This design decision
+/// pertains specifically to the spline's degree, which can also be implemented as a template
+/// argument, allowing for some optimization by making some members static. Yet going down this
+/// path requires explicit specialization for every spline degree used and the performance gain
+/// I found doing so was hardly measurable, while automatic testing became difficult and compilation
+/// times grew.
+///
+/// I chose making bspline a struct for now, but messing with the data inside is probably
+/// not a good idea...
+
+template < class value_type , int _dimension >
+struct bspline
+{
+ /// pull the template arg into an enum
+ enum { dimension = _dimension } ;
+ /// if the coefficients are owned, this array holds the data
+ typedef vigra::MultiArray < dimension , value_type > array_type ;
+ /// data are read and written to vigra MultiArrayViews
+ typedef vigra::MultiArrayView < dimension , value_type > view_type ;
+ /// multidimensional index type
+ typedef typename view_type::difference_type shape_type ;
+ /// nD type for one boundary condition per axis
+ typedef TinyVector < bc_code , dimension > bcv_type ;
+
+ /// type for pointer to a prefiltering method
+ typedef void (*p_solve) ( view_type& ,
+ view_type& ,
+ bcv_type ,
+ int ,
+ int ) ;
+
+ /// elementary type of value_type, float or double
+ typedef typename ExpandElementResult < value_type >::type real_type ;
+
+private:
+
+ array_type _coeffs ;
+ prefilter_strategy strategy ;
+
+public:
+
+ view_type container ; ///< view to container array
+ view_type coeffs ; ///< view to the braced coefficient array
+ view_type core ; ///< view to the core part of the coefficient array
+ int spline_degree ; ///< degree of the spline (3 == cubic spline)
+ bcv_type bcv ; ///< boundary condition, see common.h
+ bool braced ; ///< whether coefficient array is 'braced' or not
+ int horizon ; ///< additional frame size for explicit scheme
+ shape_type left_brace ; ///< width(s) of the left handside bracing
+ shape_type right_brace ; ///< width(s) of the right handside bracing
+ shape_type left_frame ; ///< width(s) of the left handside bracing
+ shape_type right_frame ; ///< width(s) of the right handside bracing
+ shape_type container_shape ; ///< shape of the container array
+ shape_type core_shape ; ///< shape of the core coefficient array
+ shape_type braced_shape ; ///< shape of the coefficient array + bracing
+
+ /// setup_metrics determines the sizes of the three views and any braces/frames
+ /// needed with the given parameters
+
+ void setup_metrics()
+ {
+ switch ( strategy )
+ {
+ case UNBRACED:
+ // UNBRACED is simple: all internal views are the same. prefiltering will
+ // be done using an implicit scheme.
+ container_shape = braced_shape = core_shape ;
+ left_brace = right_brace = left_frame = right_frame = shape_type() ;
+ braced = false ;
+ break ;
+ case BRACED:
+ // again an implicit prefiltering scheme will be used, but here we add
+ // a 'brace' to the core data, which makes the resulting bspline object
+ // suitable to work with vspline's evaluation code. The container array's
+ // size is the same as the braced core's size.
+ braced_shape = bracer<view_type>::target_shape ( core_shape , bcv , spline_degree ) ;
+ left_brace = bracer<view_type>::left_corner ( bcv , spline_degree ) ;
+ right_brace = bracer<view_type>::right_corner ( bcv , spline_degree ) ;
+ left_frame = left_brace ;
+ right_frame = right_brace ;
+ container_shape = braced_shape ;
+ braced = true ;
+ break ;
+ case EXPLICIT:
+ // here we prepare for an explicit extrapolation. This requires additional
+ // space, namely the 'frame', around the core data, into which the extrapolated
+ // data are put before prefiltering the lot. This frame is applied in excess of
+ // the bracing, to make sure all coefficients inside the brace meet the precision
+ // requirements expressed by the choice of 'horizon'.
+ braced_shape = bracer<view_type>::target_shape ( core_shape , bcv , spline_degree ) ;
+ left_brace = bracer<view_type>::left_corner ( bcv , spline_degree ) ;
+ right_brace = bracer<view_type>::right_corner ( bcv , spline_degree ) ;
+ left_frame = left_brace + horizon ;
+ right_frame = right_brace + horizon ;
+ container_shape = core_shape + left_frame + right_frame ;
+ braced = true ;
+ break ;
+ }
+ }
+
+ /// this method calculates the size of container needed by a bspline object with
+ /// the given parameters. This is a helper routine for use cases where the memory for
+ /// the bspline object is allocated externally and passed into the bspline object.
+
+ static shape_type container_size ( shape_type core_shape , ///< shape of knot point data
+ int spline_degree = 3 , ///< spline degree with reasonable default
+ bcv_type bcv = bcv_type ( MIRROR ) , ///< boundary conditions and common default
+ prefilter_strategy strategy = BRACED , ///< default strategy is the 'implicit' scheme
+ int horizon = sizeof(real_type) * 3 ) ///< width of frame for explicit scheme (heuristic)
+ {
+ switch ( strategy )
+ {
+ case UNBRACED:
+ return core_shape ;
+ break ;
+ case BRACED:
+ return bracer<view_type>::target_shape ( core_shape , bcv , spline_degree ) ;
+ break ;
+ case EXPLICIT:
+ shape_type braced_shape = bracer<view_type>::target_shape ( core_shape , bcv , spline_degree ) ;
+ braced_shape += 2 * horizon ;
+ return braced_shape ;
+ break ;
+ }
+ }
+
+ /// construct a bspline object with appropriate storage space to contain and process an array
+ /// of knot point data with shape core_shape. Depending on the strategy chosen and the other
+ /// parameters passed, more space than core_shape may be allocated. Once the bspline object
+ /// is ready, it has to be filled with the knot point data and then the prefiltering needs
+ /// to be done. This sequence assures that the knot point data are present in memory only once,
+ /// the prefiltering is done in-place. So the user can create the bspline, fill in data (like,
+ /// from a file), prefilter, and then evaluate.
+ ///
+ /// It's possible to pass in a view to an array providing space for the coefficients,
+ /// or even the coefficients themselves. This is done via the parameter _space. This has
+ /// to be an array of the same or larger shape than the container array would end up having
+ /// given all the other parameters. This view is then 'adopted' and subsequent processing
+ /// will operate on it's data. container_size can be used to get the precise shape of the memory
+ /// needed with the given parameters.
+ ///
+ /// with the EXPLICIT scheme, the horizon is set by default to a value which is
+ /// deemed to be 'sufficiently large' to keep the error 'low enough'. the expression
+ /// used here produces a frame which is roughly the size needed to make any margin
+ /// effects vanish by the time the prefilter hits the core, but it's a bit 'rule of thumb'.
+
+ // TODO: when bracing/framing is applied, we might widen the array size to a
+ // multiple of the Vc:Vector's Size for the given data type to have better-aligned
+ // access. This may or may not help, has to be tested. We might also want to position
+ // the origin of the brace to an aligned position, since evaluation is based there.
+
+ bspline ( shape_type _core_shape , ///< shape of knot point data
+ int _spline_degree = 3 , ///< spline degree with reasonable default
+ bcv_type _bcv = bcv_type ( MIRROR ) , ///< boundary conditions and common default
+ prefilter_strategy _strategy = BRACED , ///< default strategy is the 'implicit' scheme
+ int _horizon = -1 , ///< width of frame for explicit scheme
+ view_type _space = view_type() ///< coefficient storage to 'adopt'
+ )
+ : core_shape ( _core_shape ) ,
+ spline_degree ( _spline_degree ) ,
+ bcv ( _bcv ) ,
+ strategy ( _strategy )
+ {
+ // heuristic horizon for reasonable precision - we assume that no one in their right
+ // minds would want a negative horizon ;)
+
+ if ( _horizon < 0 )
+ horizon = log2 ( max ( spline_degree , 1 ) ) * 3 * sizeof ( real_type ) ;
+ else
+ horizon = _horizon ; // whatever the user specifies
+
+ // first, calculate all the various shapes and sizes used internally
+ setup_metrics() ;
+
+ // now either adopt external memory or allocate memory for the coefficients
+ if ( _space.hasData() )
+ {
+ // caller has provided space for the coefficient array. This space has to
+ // be at least as large as the container_shape we have determined
+ // to make sure it's compatible with the other parameters
+ if ( ! ( allGreaterEqual ( _space.shape() , container_shape ) ) )
+ throw shape_mismatch ( "the intended container shape does not fit into the shape of the storage space passed in" ) ;
+ // if the shape matches, we adopt the data in _space;
+ // since 'container' was default-constructed, assignment results in a view
+ // to the data in _space, not in copying the data. This means that if the data
+ // _space refers to change or are deallocated, the bspline will become invalid
+ // as well. We take a view to the container_shape-sized subarray only.
+ // _coeffs remains uninitialized.
+ container = _space.subarray ( shape_type() , container_shape ) ;
+ }
+ else
+ {
+ // _space was default-constructed and has no data.
+ // in this case we allocate a container array
+ array_type help ( container_shape ) ;
+ // and swap with the empty default-constructed array _coeffs
+ // so that the memory is automatically deallocated when the bspline
+ // object is destroyed
+ _coeffs.swap ( help ) ;
+ container = _coeffs ;
+ }
+ // finally we set the views to the braced core area and the core area
+ coeffs = container.subarray ( left_frame - left_brace ,
+ left_frame + core_shape + right_brace ) ;
+ core = coeffs.subarray ( left_brace , left_brace + core_shape ) ;
+ } ;
+
+ /// prefilter converts the knot point data in the 'core' area into b-spline
+ /// coefficients. Depending on the strategy chosen in the b-spline object's
+ /// constructor, bracing/framing may be applied.
+ ///
+ /// If data are passed in, they have to have precisely the shape
+ /// we have set up in core (_core_shape passed into the constructor).
+ /// These data will then be used in place of any data present in the
+ /// bspline object.
+
+ void prefilter ( bool use_vc = true , ///< use Vc by default
+ int nthreads = ncores , ///< number of threads to use
+ view_type data = view_type() ) ///< view to knot point data to use instead of 'core'
+ {
+ if ( data.hasData() )
+ {
+ // if the user has passed in data, they have to have precisely the shape
+ // we have set up in core (_core_shape passed into the constructor).
+ // This can have surpising effects if the container array isn't owned by the
+ // spline but constitutes a view to data kept elsewhere (by passing _space to the
+ // constructor).
+ if ( data.shape() != core_shape )
+ throw shape_mismatch ( "when passing data to prefilter, they have to have precisely the core's shape" ) ;
+ if ( strategy == EXPLICIT )
+ {
+ // the explicit scheme requires the data and frame to be together in the
+ // containing array, so we have to copy the data into the core.
+ core = data ;
+ }
+ // the other strategies can move the data from 'data' into the spline's memory
+ // during coefficient generation, so we needn't copy them in first.
+ }
+ else
+ {
+ // otherwise, we assume data are already in 'core' and we operate in-place
+ // note, again, the semantics of the assignment here: since 'data' has no data,
+ // the assignment results in 'adopting' the data in core rather than copying them
+ data = core ;
+ }
+
+ // we call the solver via a function pointer
+ p_solve solve ;
+
+ // for simplicity's sake, if USE_VC isn't defined we use solve_vigra
+ // always and simply ignore the flag use_vc.
+
+#ifdef USE_VC
+ // we have two variants, one is using Vc and the other doesn't
+ if ( use_vc )
+ solve = & solve_vc < view_type , view_type > ;
+ else
+ solve = & solve_vigra < view_type , view_type > ;
+#else
+ solve = & solve_vigra < view_type , view_type > ;
+#endif
+
+ // per default the output will be braced. This does require the output
+ // array to be sufficiently larger than the input; class bracer has code
+ // to provide the right sizes
+
+ bracer<view_type> br ;
+
+ // for the explicit schemes, we use bc code IGNORE
+ bcv_type explicit_bcv ( IGNORE ) ;
+
+ switch ( strategy )
+ {
+ case UNBRACED:
+ // only call the solver, don't do any bracing
+ solve ( data ,
+ core ,
+ bcv ,
+ spline_degree ,
+ nthreads
+ ) ;
+ break ;
+ case BRACED:
+ // solve first, passing in BC codes to pick out the appropriate functions to
+ // calculate the initial causal and anticausal coefficient, then brace result
+ solve ( data ,
+ core ,
+ bcv ,
+ spline_degree ,
+ nthreads
+ ) ;
+ for ( int d = 0 ; d < dimension ; d++ )
+ br ( coeffs , bcv[d] , spline_degree , d ) ;
+ break ;
+ case EXPLICIT:
+ // apply bracing with BC codes passed in, then solve with BC code IGNORE
+ // this automatically fills the brace, as well, since it's part of the frame.
+ for ( int d = 0 ; d < dimension ; d++ )
+ br.apply ( container , bcv[d] , left_frame[d] , right_frame[d] , d ) ;
+ solve ( container ,
+ container ,
+ explicit_bcv ,
+ spline_degree ,
+ nthreads
+ ) ;
+ break ;
+ case MANUAL:
+ // like EXPLICIT, but don't apply a frame, assume a frame was applied
+ // by external code. process whole container with IGNORE BC. For cases
+ // where the frame can't be costructed by applying any of the stock bracing
+ // modes. Note that if any data were passed into this routine, in this case
+ // they will be silently ignored (makes no sense overwriting the core after
+ // having manually framed it in some way)
+ solve ( container ,
+ container ,
+ explicit_bcv ,
+ spline_degree ,
+ nthreads
+ ) ;
+ break ;
+ }
+ }
+
+ /// shift will change the interpretation of the data in a bspline object.
+ /// d is taken as a difference to add to the current spline degree. The coefficients
+ /// remain the same, but creating an evaluator from the shifted spline will make
+ /// the evaluator produce data *as if* the coefficients were those of a spline
+ /// of the changed order. Shifting with positive d will efectively blur the
+ /// interpolated signal, shifting with negative d will sharpen it.
+ /// For shifting to work, the spline has to have enough 'headroom', meaning that
+ /// spline_degree + d, the new spline degree, has to be greater or equal to 0
+ /// and smaller than the largest supported spline degree (lower twenties)
+ /// This is a quick-shot solution to the problem of scaled-down interpolated
+ /// results; it may be faster in some situations to shift the spline up and
+ /// evaluate than to apply smoothing to the source data.
+ /// TODO: look into the transfer function
+
+ int shift ( int d )
+ {
+ int new_degree = spline_degree + d ;
+ if ( new_degree < 0 )
+ return 0 ;
+
+ bracer<view_type> br ;
+ shape_type new_left_brace = br.left_corner ( bcv , new_degree ) ;
+ shape_type new_right_brace = br.right_corner ( bcv , new_degree ) ;
+ if ( ! ( allLessEqual ( new_left_brace , left_frame )
+ && allLessEqual ( new_right_brace , right_frame ) ) )
+ return 0 ;
+
+ spline_degree = new_degree ;
+ left_brace = new_left_brace ;
+ right_brace = new_right_brace ;
+ braced_shape = core_shape + left_brace + right_brace ;
+
+ shape_type coefs_offset = left_frame - new_left_brace ;
+ coeffs.reset() ;
+ coeffs = container.subarray ( coefs_offset , coefs_offset + braced_shape ) ;
+ return d ;
+ }
+
+ /// helper function to << a bspline object to an ostream
+
+ friend ostream& operator<< ( ostream& osr , const bspline& bsp )
+ {
+ osr << "dimension:.................. " << bsp.dimension << endl ;
+ osr << "degree:..................... " << bsp.spline_degree << endl ;
+ osr << "boundary conditions:........" ;
+ for ( auto bc : bsp.bcv )
+ osr << " " << bc_name [ bc ] ;
+ osr << endl ;
+ osr << "shape of container array:... " << bsp.container.shape() << endl ;
+ osr << "shape of braced coefficients " << bsp.coeffs.shape() << endl ;
+ osr << "shape of core:.............. " << bsp.core.shape() << endl ;
+ osr << "braced:..................... " << ( bsp.braced ? std::string ( "yes" ) : std::string ( "no" ) ) << endl ;
+ osr << "left brace:................. " << bsp.left_brace << endl ;
+ osr << "right brace:................ " << bsp.right_brace << endl ;
+ osr << "left frame:................. " << bsp.left_frame << endl ;
+ osr << "right frame:................ " << bsp.right_frame << endl ;
+ osr << ( bsp._coeffs.hasData() ? "bspline object owns data" : "data are owned externally" ) << endl ;
+ return osr ;
+ }
+
+} ;
+
+} ; // end of namespace vspline
+
+#endif // VSPLINE_BSPLINE_H
\ No newline at end of file
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..49e7c0b
--- /dev/null
+++ b/common.h
@@ -0,0 +1,530 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/*! \file common.h
+ \brief definitions common to all files in this project, utility code
+
+ Currenly there isn't much code here - the only thing used throughout is the definition
+ of the boundary condition codes/mapping codes, which are lumped together for now in one
+ enum. I have also started putting some utility code here.
+
+ TODO The exceptions need some work.
+*/
+
+#ifndef VSPLINE_COMMON
+#define VSPLINE_COMMON
+
+#include <vigra/multi_array.hxx>
+#include <thread>
+
+#ifdef USE_VC
+#include <Vc/Vc>
+#endif
+
+namespace vspline {
+
+/// dimension-mismatch is thrown if two arrays have different dimensions
+/// which should have the same dimensions.
+
+struct dimension_mismatch
+: std::invalid_argument
+{
+ dimension_mismatch ( const char * msg )
+ : std::invalid_argument ( msg ) { } ;
+} ;
+
+/// shape mismatch is the exception which is thrown if the shapes of an input array and
+/// an output array do not match.
+
+struct shape_mismatch
+: std::invalid_argument
+{
+ shape_mismatch ( const char * msg )
+ : std::invalid_argument ( msg ) { } ;
+} ;
+
+/// exception which is thrown if an opertion is requested which vspline does not support
+
+struct not_supported
+: std::invalid_argument
+{
+ not_supported ( const char * msg )
+ : std::invalid_argument ( msg ) { } ;
+} ;
+
+/// exception which is thrown by mapping mode REJECT for out-of-bounds coordinates
+/// this exception is left without a message, it only has a very specific application,
+/// and there it may be thrown often, so we don't want anything slowing it down.
+
+struct out_of_bounds
+{
+} ;
+
+/// This enumeration is used for codes connected to boundary conditions. There are
+/// three aspects to boundary conditions: During prefiltering, if the implicit scheme is used,
+/// the initial causal and anticausal coefficients have to be calculated in a way specific to
+/// the chosen boundary conditions. Bracing, both before prefiltering when using the explicit
+/// scheme, and after prefiltering when using the implicit scheme, also needs these codes to
+/// pick the appropriate extrapolation code to extend the knot point data/coefficients beyond
+/// the core array. Finally, mapping of coordinates into the defined range uses some of the
+/// same codes.
+
+typedef enum { PERIODIC, ///< periodic boundary conditions / periodic mapping
+ NATURAL, ///< natural boundary conditions / uses constant mapping
+ MIRROR , ///< mirror on the bounds, both bc and mapping
+ REFLECT , ///< reflect, so that f(-1) == f(0), both bc and mapping
+ CONSTANT , ///< used for framing, with explicit prefilter scheme, and for mapping
+ ZEROPAD , ///< used for boundary condition, bracing
+ IGNORE , ///< used for boundary condition, bracing
+ IDENTITY , ///< used as solver argument, mostly internal use
+ SPHERICAL , ///< intended use for spherical panoramas, needs work
+ REJECT , ///< mapping mode, throws out_of_bounds for out-of-bounds coordinates
+ LIMIT , ///< mapping mode, maps out-of-bounds coordinates to left/right boundary
+ RAW ///< mapping mode, processes coordinates unchecked, may crash the program
+} bc_code;
+
+/// This enumeration is used by the convenience class 'bspline' to determine the prefiltering
+/// scheme to be used.
+
+typedef enum { UNBRACED , ///< implicit scheme, no bracing applied
+ BRACED , ///< implicit scheme, bracing will be applied
+ EXPLICIT , ///< explicit scheme, frame with extrapolated signal, brace
+ MANUAL ///< like explicit, but don't frame before filtering
+} prefilter_strategy ;
+
+/// bc_name is for diagnostic output of bc codes
+
+std::vector < std::string > bc_name =
+{
+ "PERIODIC",
+ "NATURAL",
+ "MIRROR" ,
+ "REFLECT" ,
+ "CONSTANT" ,
+ "ZEROPAD" ,
+ "IGNORE" ,
+ "IDENTITY" ,
+ "SPHERICAL" ,
+ "REJECT" ,
+ "LIMIT" ,
+ "RAW"
+} ;
+
+/// number of CPU cores in the system; per default, multithreading routines
+/// will perform their task with as many threads.
+
+const int ncores = std::thread::hardware_concurrency() ;
+
+/// split_array_to_chunks partitions an array. This is used for multithreading:
+/// It's possible to distribute the processing to several threads by splitting
+/// the array to chunks so that the axis which is processed is not broken up.
+/// That's what the 'forbid' parameter is for: it keeps the routine from wrongly
+/// splitting the array along a specific axis. With the default of -1, all axes
+/// are considered valid candidates for splitting.
+/// The return value indicates into how many chunks the input array was actually
+/// split. Splitting sounds like an expensive operation, but it is not. All processing
+/// is done with views, the data aren't copied.
+
+template < class view_t >
+int split_array_to_chunks ( view_t & a , ///< view to array to be split n-ways
+ std::vector < view_t > & res , ///< vector to accomodate the chunks
+ int n , ///< intended number of chunks
+ int forbid = -1 ) ///< axis which shouldn't be split
+{
+ const int dim = view_t::actual_dimension ;
+ typedef typename view_t::difference_type shape_t ;
+
+ // find outermost dimension which can be split n-ways
+ shape_t shape = a.shape() ;
+
+ // find the outermost dimension that can be split n ways, and it's extent
+ int split_dim = -1 ;
+ int max_extent = -1 ;
+ for ( int md = dim - 1 ; md >= 0 ; md-- )
+ {
+ if ( md != forbid
+ && shape[md] > max_extent
+ && shape[md] >= n )
+ {
+ max_extent = shape[md] ;
+ split_dim = md ;
+ break ;
+ }
+ }
+
+ // if the search did not yet succeed:
+ if ( max_extent == -1 )
+ {
+ // repeat process with relaxed conditions: now the search will also succeed
+ // if there is an axis which can be split less than n ways
+ for ( int md = dim - 1 ; md >= 0 ; md-- )
+ {
+ if ( md != forbid
+ && shape[md] > 1 )
+ {
+ max_extent = shape[md] ;
+ split_dim = md ;
+ break ;
+ }
+ }
+ }
+
+ if ( split_dim != -1 ) // we have found a dimension for splitting
+ {
+ int w = shape [ split_dim ] ; // extent of the dimension we can split
+ n = std::min ( n , w ) ; // just in case, if that is smaller than n
+
+ int cut [ n ] ; // where to chop up this dimension
+
+ for ( int i = 0 ; i < n ; i++ )
+ cut[i] = ( (i+1) * w ) / n ; // roughly equal chunks, but certainly last cut == a.end()
+
+ shape_t start , end = shape ;
+
+ for ( int i = 0 ; i < n ; i++ )
+ {
+ end [ split_dim ] = cut [ i ]; // apply the cut locations
+ res.push_back ( a.subarray ( start , end ) ) ;
+ start [ split_dim ] = end [ split_dim ] ;
+ }
+ }
+ else // no luck, fill the vector with the initial array as it's sole member
+ {
+ res.push_back ( a ) ;
+ n = 1 ;
+ }
+ return n ; // return the number of chunks
+}
+
+/// divide_and_conquer tries to split an array into chunks and processes each chunk
+/// in a separate thread with the functor passed as 'func'. This functor takes a reference
+/// to a chunk of data and it's offset in the original array. There's a variant of run()
+/// taking a point function and a variant taking a vectorized point function
+
+template < class view_type > // type of input array
+struct divide_and_conquer
+{
+ enum { dim = view_type::actual_dimension } ;
+ typedef typename view_type::value_type value_type ;
+ enum { channels = vigra::ExpandElementResult < value_type > :: size } ;
+ typedef typename vigra::ExpandElementResult < value_type > :: type ele_type ;
+ typedef typename view_type::difference_type shape_type ;
+
+ /// callable chunk_func is used to process each individual chunk in it's
+ /// separate thread. Alternatively, if splitting fails, it is used on the whole
+ /// data set. The additional shape which is passed in can be used by the chunk
+ /// processing function to determine it's position inside the 'whole' array.
+
+ typedef std::function < void ( view_type & , // chunk of data to process
+ shape_type ) // offset of chunk in 'whole' data set
+ > chunk_func ;
+
+ /// run tries to split 'data' into 'nthread' views. If 'forbid' refers to an axis
+ /// of 'data', this axis will not be split. If splitting fails, 'func' is applied
+ /// to 'data', otherwise, 'func' is applied to all chunks that resulted from splitting,
+ /// concurrently, in separate threads.
+
+ static void run ( view_type & data , // array of data to divide (into chunks)
+ chunk_func func , // functor to apply to each chunk
+ int forbid = -1 , // axis that should not be split (default: no restriction)
+ int nthreads = ncores )
+ {
+ if ( nthreads > 1 )
+ {
+ // we split the result array into equal chunks
+ // to process them by one thread each
+
+ std::vector<view_type> dn ; // space for chunks
+ nthreads = split_array_to_chunks <view_type> ( data , dn , nthreads , forbid ) ;
+ std::thread * t[nthreads] ;
+ shape_type offset ;
+
+ for ( int s = 0 ; s < nthreads ; s++ )
+ {
+ t[s] = new std::thread ( func , std::ref(dn[s]) , offset ) ;
+ for ( int d = 0 ; d < dim ; d ++ )
+ {
+ if ( dn[s].shape ( d ) != data.shape(d) )
+ offset[d] += dn[s].shape(d) ;
+ }
+ }
+ for ( int s = 0 ; s < nthreads ; s++ )
+ {
+ t[s]->join() ;
+ delete t[s] ;
+ }
+ }
+ else // if we're single-threaded we call func in this thread
+ {
+ func ( data , shape_type() ) ;
+ }
+ }
+
+ /// next we have a few methods for applying point functors to a MultiArrayView.
+ /// this is done by building a suitable chunk_func from a point functor and an
+ /// 'applicator' routine, and then passing this chunk_func to run() above, to
+ /// have it applied in multiple threads. First we define the mechanism for
+ /// point functors operating on value_type:
+ ///
+ /// callable point_func 'treats' a value_type passed in by reference
+
+ typedef std::function < void ( value_type & ) > point_func ;
+
+ /// apply is a helper function that applies 'f' to each value in 'data'.
+ /// By binding it's first agument to a specific point function, we receive
+ /// a functor that satisfies the chunk_func signature.
+
+ static void apply ( point_func f , view_type & data , shape_type offset )
+ {
+ for ( auto& value : data )
+ f ( value ) ;
+ }
+
+ /// overload of 'run' applying a point function to all values in all chunks
+ /// this is where the chunk_func needed for run() above is built and used
+
+ static void run ( view_type & data , // array of data to divide (into chunks)
+ point_func func , // point functor to apply to each value
+ int nthreads = ncores )
+ {
+ using namespace std::placeholders ;
+
+ // we use bind to make a chunk_func by binding the point function argument
+ // to apply() to 'func'. The resulting two-argument functor satisfies the
+ // chunk_func signature and can be passed to run() above
+
+ chunk_func treat = std::bind ( apply , // use apply to apply
+ func , // this point function
+ _1 , // to this chunk of data
+ _2 ) ; // located at this offset inside 'data'
+
+ // the resulting functor is passed to the version of run() taking a chunk_func
+
+ run ( data , treat , -1 , nthreads ) ; // delegate processing to run variant above
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::Vector < ele_type > value_v ;
+ enum { vsize = value_v::Size } ;
+
+ /// now we repeat the pattern for vectorized operation.
+ /// callable v_point_func 'treats' a value_v passed in by reference.
+
+ typedef std::function < void ( value_v & ) > v_point_func ;
+
+ /// again we have a helper function. We'll use bind on the first
+ /// argument to generate a functor satisfying the chunk_func signature.
+ /// vapply reads individual elements into a vector-sized buffer and calls
+ /// the vectorized point function vf on them.
+
+ static void vapply ( v_point_func vf ,
+ view_type & data ,
+ shape_type offset )
+ {
+ int aggregates = data.elementCount() / vsize ; // number of full vectors
+ int leftovers = data.elementCount() - aggregates * vsize ; // and of any leftover single values
+ value_v buffer ;
+ auto it = data.begin() ;
+
+ if ( data.isUnstrided() )
+ {
+ typedef Vc::SimdArray < int , vsize > ic_v ;
+ typedef vigra::TinyVector < ic_v , channels > mc_ic_v ;
+
+ // if data is unstrided, we can process it's contents by gather/scatter
+ // operations to vectors which we process with the functor vf. This is
+ // about as fast as we can go.
+
+ mc_ic_v mc_interleave ;
+ for ( int ch = 0 ; ch < channels ; ch++ )
+ {
+ mc_interleave[ch] = ic_v::IndexesFromZero() ;
+ mc_interleave[ch] *= channels ;
+ mc_interleave[ch] += ch ;
+ }
+
+ value_type * destination = data.data() ;
+
+ for ( int a = 0 ; a < aggregates ; a++ )
+ {
+ for ( int ch = 0 ; ch < channels ; ch++ )
+ {
+ buffer.gather ( (ele_type*) destination , mc_interleave [ ch ] ) ;
+ vf ( buffer ) ;
+ buffer.scatter ( (ele_type*) destination , mc_interleave [ ch ] ) ;
+ }
+ destination += vsize ;
+ }
+ it += aggregates * vsize ;
+ }
+ else
+ {
+ // if the data are strided, we 'manually' fill a vector at a time
+ for ( int a = 0 ; a < aggregates ; a++ )
+ {
+ for ( int ch = 0 ; ch < channels ; ch++ )
+ {
+ for ( int e = 0 ; e < vsize ; e++ ) // fill vector
+ buffer[e] = it[e][ch] ;
+ vf ( buffer ) ; // call functor
+ for ( int e = 0 ; e < vsize ; e++ )
+ it[e][ch] = buffer[e] ; // unpack vector
+ }
+ it += vsize ;
+ }
+ }
+
+ // process leftovers, if any
+
+ if ( leftovers )
+ {
+ for ( int ch = 0 ; ch < channels ; ch++ )
+ {
+ int e ;
+ for ( e = 0 ; e < leftovers ; e++ )
+ buffer[e] = it[e][ch] ; // fill in leftover values
+
+ for ( ; e < vsize ; e++ ) // just in case no aggregates were processed previously:
+ buffer[e] = it[0][ch] ; // fill buffer up with suitable values
+
+ vf ( buffer ) ; // process buffer
+
+ for ( e = 0 ; e < leftovers ; e++ )
+ it[e][ch] = buffer[e] ; // write back processed values
+ }
+ }
+ }
+
+ /// overload of 'run' applying a vectorized point function to all elements in all chunks
+
+ static void run ( view_type & data , // array of data to divide (into chunks)
+ v_point_func vfunc , // vectorized point functor
+ int nthreads = ncores )
+ {
+ using namespace std::placeholders ;
+
+ // again we use bind to create a functor satisfying the chunk_func signature
+ // by using bind - this time on vapply, where we bind the first two arguments
+ // to 'vfunc' and 'func'.
+
+ chunk_func treat = std::bind ( vapply , // use vapply to apply
+ vfunc , // this vectorized point functor
+ _1 , // to this chunk of data
+ _2 ) ; // which is at this offset inside 'data'
+
+ // the resulting functor is delegated to the version of run() taking a chunk_func
+
+ run ( data , treat , -1 , nthreads ) ;
+ }
+
+
+#endif
+
+} ;
+
+/// divide_and_conquer_2 works just like divide_and_conquer, but operates on
+/// two arrays synchronously.
+
+template < class view_type , // type of first array
+ class view_type_2 > // type of second array
+struct divide_and_conquer_2
+{
+ typedef typename view_type::value_type value_type ;
+ enum { dim = view_type::actual_dimension } ;
+ typedef typename view_type::difference_type shape_type ;
+
+ // run() takes two MultiArrayViews of the same shape, splits them
+ // in the same way, and applies a functor to each resulting pair of chunks. This is
+ // needed for functions like remap().
+ // TODO: we might even formulate a general manifold by accepting a vector of arrays,
+ // but currently there is no code in vspline which uses more than two views together.
+
+ typedef std::function < void ( view_type & , // chunk of data to process
+ view_type_2 & , // chunk of data2 to process
+ shape_type ) // offset of chunk in 'whole' data set
+ > chunk_func_2 ;
+
+ static void run ( view_type & data , // array of data to divide (into chunks)
+ view_type_2 & data2 , // second array to coprocess with data
+ chunk_func_2 func , // functor to apply to each pair of chunks
+ int forbid = -1 , // axis that should not be split (default: no restriction)
+ int nthreads = ncores )
+ {
+ if ( view_type_2::actual_dimension != dim )
+ throw ( dimension_mismatch ( "both views must have the same dimension" ) ) ;
+
+ // make sure that data and data2 are shape-compatible
+ // we silently assume that we can coiterate both arrays in scan order.
+ if ( data.shape() != data2.shape() )
+ {
+ throw shape_mismatch ( "both views must have the same shape" ) ;
+ }
+
+ if ( nthreads > 1 )
+ {
+ // we split both arrays into equal chunks
+ // to coprocess them by one thread each
+
+ std::vector<view_type> dn ; // space for chunks of data
+ nthreads = split_array_to_chunks <view_type> ( data , dn , nthreads , forbid ) ;
+
+ std::vector<view_type_2> d2n ; // space for chunks of data2
+ nthreads = split_array_to_chunks <view_type_2> ( data2 , d2n , nthreads , forbid ) ;
+
+ std::thread * t[nthreads] ;
+ shape_type offset ;
+
+ for ( int s = 0 ; s < nthreads ; s++ )
+ {
+ t[s] = new std::thread ( func , std::ref(dn[s]) , std::ref(d2n[s]), offset ) ;
+ for ( int d = 0 ; d < dim ; d ++ )
+ {
+ if ( dn[s].shape ( d ) != data.shape(d) )
+ offset[d] += dn[s].shape(d) ;
+ }
+ }
+ for ( int s = 0 ; s < nthreads ; s++ )
+ {
+ t[s]->join() ;
+ delete t[s] ;
+ }
+ }
+ else // if we're single-threaded we call func in this thread
+ {
+ func ( data , data2 , shape_type() ) ;
+ }
+ }
+} ;
+
+} ; // end of namespace vspline
+
+#endif // VSPLINE_COMMON
\ No newline at end of file
diff --git a/doxy.h b/doxy.h
new file mode 100644
index 0000000..f42dcfb
--- /dev/null
+++ b/doxy.h
@@ -0,0 +1,300 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+// This header doesn't contain any code, only the text for the main page of the documentation.
+
+/*! \mainpage
+
+ \section intro_sec Introduction
+
+ vspline is a header-only generic C++ library for the creation and processing of uniform B-splines.
+ It aims to be as comprehensive as feasibly possible, yet at the same time producing code
+ which performs well, so that it can be used in production.
+
+ vspline was developed on a Linux system using gcc. It has not been tested with other
+ systems or compilers, and as of this writing I am aware that the code probably isn't portable;
+ My code uses elements from the C++11 standard (mainly the auto keyword and range-based for loops).
+
+ vspline relies heavily on two other libraries:
+
+ - <a href="http://ukoethe.github.io/vigra/">VIGRA</a>, mainly for handling of multidimensional
+ arrays and general signal processing
+
+ - <a href="https://compeng.uni-frankfurt.de/index.php?id=vc">Vc</a>, for the use of the CPU's
+ vector units
+
+ I find VIGRA indispensible, omitting it from vspline is not really an option. It is possible
+ not to use Vc: either it's use can be disabled at compile time (see 'Compilation' below), or
+ the higher-level routines can be called with the flag use_vc set to false, which will prevent
+ vector code from being used even if it has been compiled in. This is for situations where
+ the binary can't be modified but the vectorized code doesn't work on the target system.
+
+ I have made an attempt to generalize the code so that it can handle
+
+ - arbitrary real data types and their aggregates
+
+ - a reasonable selection of boundary conditions
+
+ - prefiltering with implicit and explicit extrapolation schemes
+
+ - arbitrary spline orders
+
+ - arbitrary dimensions of the spline
+
+ - in multithreaded code
+
+ - using the CPU's vector units if possible
+
+On the evaluation side I provide
+
+ - evaluation of the spline at point locations in the defined range
+
+ - evaluation of the spline's derivatives
+
+ - mapping of arbitrary coordinates into the defined range
+
+ - evaluation of nD arrays of coordinates (generalized remap() function)
+
+ - transformation functor based remap function
+
+ \section install_sec Installation
+
+ vspline is header-only, so it's sufficient to place the headers where your code can access them.
+ VIGRA and Vc are supposed to be installed in a location where they can be found so that includes
+ along the lines of #include <vigra/...> succeed.
+
+ \section compile_sec Compilation
+
+ To compile software using vspline, I use this g++ call:
+
+ g++ -D USE_VC -pthread -O3 -march=native -fabi-version=6 --std=c++11 your_code.cc -lVc -lvigraimpex
+
+ where the -lvigraimpex can be omitted if vigraimpex (VIGRA's image import/export library)
+ is not used.
+
+ The -fabi-version=6 suppresses certain issues with Vc
+
+ Please note that an executable using Vc produced on your system may likely not work on
+ a machine with another CPU. It's best to compile on the intended target. Alternatively,
+ the target architecture can be passed explicitly to gcc, please refer to gcc's manual.
+ 'Not work' in this context means that it may as well crash due to an illegal instruction
+ or wrong alignment.
+
+ If you can't use Vc, the code can be made to compile without Vc by omitting -D USE_VC
+ and other flags relevant for Vc:
+
+ g++ -pthread -O3 --std=c++11 your_code.cc -lvigraimpex
+
+ All access to Vc in the code is inside #define USE_VC .... #endif statements, so not
+ defining USE_VC will effectively prevent it's use.
+
+ For simplicity's sake, even if the code isn't compiled to use Vc, the higher level code
+ will still accept the common use_vc flag in the call signatures, but it's value wont have an
+ effect. The documentation is built to contain text for vectorized operation, if this
+ is unwanted, change the doxy file.
+
+ \section license_sec License
+
+ vspline is free software, licensed under this license:
+
+ vspline - a set of generic tools for creation and evaluation
+ of uniform b-splines
+
+ Copyright 2015, 2016 by Kay F. Jahnke
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+
+ \section quickstart_sec Quickstart
+
+ TODO slightly out of data, bspline constructs differently now
+
+ If you stick with the high-level code, using class bspline or the remap function,
+ most of the parametrization is easy. Here are a few examples what you can do.
+
+ Let's suppose you have data in a 2D vigra MultiArray 'a'. vspline can handle float
+ and double values, and also their 'aggregates', meaning data types like pixels or
+ vigra's TinyVector. But for now, let's assume you have plain float data. Creating
+ the bspline object is easy:
+
+ typedef vspline::bspline < float , 2 > spline_type ; // fix the type of the spline
+
+ spline_type bspl ( a.shape() ) ; // create bspline object 'bspl' suitable for your data
+
+ bspl.core = a ; // copy the source data into the bspline object's 'core' area
+
+ bspl.prefilter() ; // run prefilter() to convert original data to b-spline coefficients
+
+ Now obviously many things have been done by default here: The default spline degree
+ was used - it's 3, for a cubic spline. Also, boundary treatment mode 'MIRROR' was
+ used per default. Further default parameters cause the spline to be 'braced' so that
+ it can be evaluated with vspline's evaluation routines, Vc (if compiled in) was
+ used for prefiltering, and the code used as many threads as your system has physical cores.
+ You could have passed different values for all the parameters I have mentioned to the
+ constructor - the only template arguments are the value type and the number of dimensions,
+ which have to be known at compile time.
+
+ while the sequence of operations indicated here looks a bit verbose (why not create the
+ bspline object by a call like bspl ( a ) ?), in 'real' code you can use bspl.core straight
+ away as the space to contain your data - you might get the data from a file or by some other
+ process.
+
+ Next you may want to evaluate the spline at some pair of coordinates x, y.
+ To do so, you can use this idiom:
+
+ typedef evaluator_type < 2 , float , float > eval_type ; // get the appropriate evaluator type
+
+ eval_type ev ( bspl ) ; // create the evaluator
+
+ Now, assuming you have float x and y:
+
+ float result = ev ( { x , y } ) ; // evaluate at (x,y)
+
+ the braces are needed because the evaluator expects a 'multi_coordinate' which is a
+ vigra::TinyVector of as many real values as the spline has dimensions. You will encounter
+ parameters of this type throughout: vspline's code is dimension-agnostic, and parameters
+ passed in often have to have as many components as there are dimensions in the concrete
+ object you are handling.
+
+ Again, some things have happened by default. The evaluator was constructed with a
+ bspline object, making sure that the evaluator is compatible. vspline can also
+ calculate the spline's derivatives. The default is plain evaluation, but you can pass
+ a request for production of derivatives to the evaluator's constructor. Let's assume you
+ want the first derivative along axis 0 (the x axis):
+
+ eval_type eval_dx ( bsp , { 1 , 0 } ) ; // ask for an evaluator producing dx
+
+ float dx = eval_dx ( { x , y } ) ; // use the evaluator
+
+ For every constellation of derivatives you'll have to create a distinct evaluator.
+ This is not an expensive operation - the same coefficients are used in all cases, only
+ the weight functors used internally differ. Calculating the spline's derivatives is even
+ slightly faster than plain evaluation, since there are less multiplications to perform.
+
+ What about the remap functions? The little introduction demonstrated how you can evaluate
+ the spline at a single location. Most of the time, though, you'll require evaluation at
+ many coordinates. This is what remap does. Instead of a single multi_coordinate, you pass
+ a whole vigra::MultiArrayView full of multi_coordinates to it - and another MultiArrayView
+ of the same dimension and shape to accept the results of evaluating the spline at every
+ multi_coordinate in the first array. Here's a simple example, using the same array 'a' as above:
+
+ // create a 1D array containing (2D) coordinates into 'a'
+ vigra::MultiArray < 1 , vigra::TinyVector < float , 2 > > coordinate_array ( 3 ) ;
+
+ ... // fill in the coordinates
+
+ // create an array to accomodate the result of the remap operation
+ vigra::MultiArray < 1 , float > target_array ( 3 ) ;
+
+ // perform the remap
+ remap < float , float , 2 , 1 > ( a , coordinate_array , target_array ) ;
+
+ now the three resultant values are in the target array.
+
+ And that's about it - vspline aims to provide all possible variants of b-splines, code to
+ create and evaluate them and to do so for arrays of coordinates. So if you dig deeper into
+ the code base, you'll find that you can stray off the default path, but there should rarely
+ be any need not to use the high-level object 'bspline' or the remap function, or it's relative,
+ which doesn't construct the spline internally but takes a bspline as a parameter. The
+ transformation-based remap function is an alternative if the coordinates at which the source
+ array is to be sampled are the result of a mathematical operation (the transformation function)
+ on the target coordinates. In this case you don't need the array of coordinates.
+
+ While one might argue that the remap routine I present shouldn't be lumped together with the
+ 'proper' b-spline code, I feel that only by tightly coupling it with the b-spline code I can
+ make it really fast. And only by processing several coordinates at once (by multithreading and
+ vectorization) the hardware can be exploited fully. I might even make remap a method of the
+ b-spline evaluator - yet another variant of operator(), processing whole arrays of coordinates
+ instead of merely aggregates of a handful.
+
+ \section design_sec Design
+
+ You can do everything vspline does with other software - there are several freely available
+ implementations of b-spline interpolation and remap routines. What I wanted to create was
+ an implementation which was as general as possible and at the same time as fast as possible,
+ and, on top of that, comprehensive.
+
+ These demands are not easy to satisfy at the same time, but I feel that my design comes
+ close. While generality is achieved by generic programming, speed needs exploitation of hardware
+ features, and merely relying on the compiler is not enough. The largest speedup I saw was
+ simply multithreading the code. This may seem like a trivial observation, but my design
+ is influenced by it: in order to efficiently multithread, the problem has to be partitioned
+ so that it can be processed by independent threads. You can see the partitioning both in
+ prefiltering and later in the remap routine, in fact, both even share code to do so. Currently
+ only multidimensional splines benefit from multithreading, but I dare say that huge 1D splines are
+ probably not *that* common - and partitioning them is trivial, if one uses a bit of overlap.
+ TODO: make no assumptions, just code the 1D partitioning
+
+ Another speedup method is data-parallel processing. This is often thought to be the domain of
+ GPUs, but modern CPUs also offer it in the form of vector units. I chose implementing data-parallel
+ processing in the CPU, as it offers tight integration with unvectorized CPU code. It's familiar
+ terrain, and the way from writing conventional CPU code to vector unit code is not too far,
+ when using tools like Vc, which abstract the hardware away.
+
+ Using both techniques together makes vspline fast. The target I was roughly aiming at was to
+ achieve frame rates of ca. 50 fps in float RGB and full HD, producing the images via remap from
+ a precalculated warp array. On my system, I have almost reached that goal - my remap times are
+ around 21 msec (for a cubic spline). The idea is to exploit the CPU fully, leaving the GPU, if
+ present, free to do something else, instead of letting the CPU idle while the GPU performs the
+ remapping. Keeping only the CPU busy also reduces overall traffic on the system. To really benefit
+ from my scheme, you have to have a reasonably modern CPU, though.
+
+ \section Literature
+
+ There is a large amount of literature on b-splines available online. Here's a pick:
+
+ http://bigwww.epfl.ch/thevenaz/interpolation/
+
+ http://soliton.ae.gatech.edu/people/jcraig/classes/ae4375/notes/b-splines-04.pdf
+
+ http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-basis.html
+
+ http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-ex-1.html
+*/
diff --git a/eval.h b/eval.h
new file mode 100644
index 0000000..a3bc39c
--- /dev/null
+++ b/eval.h
@@ -0,0 +1,1099 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/*! \file eval.h
+
+ \brief code to evaluate uniform b-splines
+
+ This body of code contains the central class evaluator and auxilliary classes
+ which are needed for it's smooth operation.
+
+ The evaluation is a reasonably straightforward process: A subset of the coefficient
+ array, containing coefficients 'near' the point of interest, is picked out, and
+ a weighted summation over this subset produces the result of the evaluation.
+ The complex bit is to have the right coefficients in the first place
+ (this is what prefiltering does), and to use the appropriate weights on
+ the coefficient window. For b-splines, there is an efficient method to
+ calculate the weights by means of a matrix multiplication, which is easily
+ extended to handle b-spline derivatives as well. Since this code lends itself
+ to a generic implementation, and it can be parametrized by the spline's order,
+ and since the method performs well, I use it here in preference to the code
+ which Thevenaz uses (which is, for the orders of splines it encompasses, the matrix
+ multiplication written out with a few optimizations, like omitting multiplications
+ with zero, and slightly more concise calculation of powers)
+*/
+
+#ifndef VSPLINE_EVAL_H
+#define VSPLINE_EVAL_H
+
+#include <thread>
+#include <math.h>
+#include <complex>
+#include <cmath>
+#include <iostream>
+#include <array>
+#include <assert.h>
+
+#include <vigra/multi_array.hxx>
+#include <vigra/multi_iterator.hxx>
+#include <vigra/multi_math.hxx>
+
+#include "basis.h"
+#include "mapping.h"
+#include "bspline.h"
+
+namespace vspline {
+
+using namespace std ;
+using namespace vigra ;
+using namespace vigra::multi_math;
+
+/// The routine 'calculate_weight_matrix' originates from vigra. I took the original
+/// routine BSplineBase<ORDER, T>::calculateWeightMatrix() from vigra and changed it
+/// in several ways:
+///
+/// - the spline degree is now a runtime parameter, not a template argument
+/// - the derivative degree is passed in as an additional parameter, directly
+/// yielding the appropriate weight matrix needed to calculate a b-spline's derivative
+/// with only a slight modification to the original code
+/// - the code uses my modified bspline basis function which takes the degree as a
+/// run time parameter instead of a template argument and works with integral
+/// operands and precalculated values, which makes it very fast, even for high
+/// spline degrees. bspline_basis() is in basis.h.
+
+template < class target_type >
+MultiArray < 2 , target_type > calculate_weight_matrix ( int degree , int derivative )
+{
+ const int order = degree + 1 ;
+
+ if ( derivative >= order ) // guard against impossible parameters
+ return MultiArray < 2 , target_type >() ;
+
+ // allocate space for the weight matrix
+ MultiArray < 2 , target_type > res = MultiArray < 2 , target_type > ( order , order - derivative ) ;
+
+ long double faculty = 1.0 ;
+
+ for ( int row = 0 ; row < order - derivative ; row++ )
+ {
+ if ( row > 1 )
+ faculty *= row ;
+
+ int x = degree / 2 ; // (note: integer division)
+
+ // we store to a MultiArray, which is row-major, so storing as we do
+ // places the results in memory in the precise order in which we want to
+ // use them in the weight calculation.
+ // note how we pass x to bspline_basis() as an integer. This way, we pick
+ // a very efficient version of the basis function which only evaluates at
+ // whole numbers. This basis function version does hardly any calculations
+ // but instead relies on precalculated values. see bspline_basis in prefilter.h
+
+ for ( int column = 0 ; column < order ; ++column , --x )
+ res ( column , row ) = bspline_basis<long double> ( x , degree , row + derivative ) / faculty;
+ }
+
+ return res;
+}
+
+/// while we deal with B-splines here, there is no need to limit the evaluator
+/// code only to B-spline basis functions. The logic is the same for any type of evaluation
+/// which functions like a separable convolution of an equilateral subarray of the coefficient
+/// array, and the only thing specific to b-splines is the weight generation.
+///
+/// So I coded the general case, which can use any weight generation function. Coding this
+/// introduces a certain degree of complexity, which I feel is justified for the flexibility
+/// gained. And it may turn out useful if we employ gaussians as basis functions to
+/// approximate high-order b-splines. The complexity is mainly due to the fact that, while
+/// we can write a simple (templated) function to generate weights (as above), we can't pass
+/// such a template as an object to a function. Instead we use an abstract base class for
+/// the weight functor and inherit from it for specific weight generation methods.
+///
+/// I made some investigations towards coding evaluation of splines with different orders
+/// along the different axes, but this introduced too much extra complexity for my taste and
+/// took me too far away from simply providing efficient code for b-splines, so I abandoned
+/// the attempts. Therefore the weight functors for a specific spline all have to have a common
+/// ORDER and generate ORDER weights. The only way to force lesser order weight functors into
+/// this scheme if it has to be done is to set some of the weights to zero. Weight functors
+/// of higher ORDER than the spline can't be accomodated, if that should be necessary, the ORDER
+/// of the entire spline has to be raised.
+///
+/// Note the use of 'delta' in the functions below. this is due to the fact that these functors
+/// are called with the fractional part of a real valued coordinate.
+///
+/// first we define a base class for (multi-)functors calculating weights.
+/// this base class can accomodate weight calculation with any weight generation
+/// function using the same signature. It is not specific to b-splines.
+/// We access the weight functors via a pointer to this base class in the code below.
+
+template < typename rc_type >
+struct weight_functor_base
+{
+ // we define two pure virtual overloads for operator(), one for unvectorized
+ // and one for vectorized operation. In case the scope of evaluation is extended
+ // to other types of values, we'll have to add the corresponding signatures here.
+
+ virtual void operator() ( rc_type* result , const rc_type& delta ) = 0 ;
+
+#ifdef USE_VC
+
+ typedef Vc::Vector < rc_type > rc_type_v ;
+
+ virtual void operator() ( rc_type_v* result , const rc_type_v& delta ) = 0 ;
+
+#endif
+} ;
+
+/// this functor calculates weights for a b-spline or it's derivatives.
+/// with d == 0, the weights are calculated for plain evaluation.
+/// Initially I implemented weight_matrix as a static member, hoping the code
+/// would perform better, but I could not detect significant benefits. Letting
+/// the constructor choose the derivative gives more flexibility and less type
+/// proliferation.
+
+template < typename target_type , // type for weights (may be a Vc::Vector)
+ typename rc_type > // single real value
+
+struct bspline_derivative_weights
+#ifdef USE_VC
+: public Vc::VectorAlignedBase
+#endif
+{
+ typedef typename MultiArray<2,rc_type>::iterator wm_iter ;
+
+ MultiArray<2,rc_type> weight_matrix ;
+ const int degree ;
+ const int derivative ;
+ const int columns ;
+ wm_iter wm_begin ;
+ wm_iter wm_end ;
+
+ bspline_derivative_weights ( int _degree , int _derivative = 0 )
+ : weight_matrix ( calculate_weight_matrix<rc_type> ( _degree , _derivative ) ) ,
+ degree ( _degree ) ,
+ derivative ( _derivative ) ,
+ columns ( _degree + 1 )
+ {
+ wm_begin = weight_matrix.begin() ;
+ wm_end = weight_matrix.end() ;
+ } ;
+
+ void operator() ( target_type* result , const target_type & delta )
+ {
+ register target_type power = delta ;
+ register wm_iter factor_it = wm_begin ;
+ register const wm_iter factor_end = wm_end ;
+
+ // the result is initialized with the first row of the 'weight matrix'.
+ // We save ourselves multiplying it with delta^0.
+
+ for ( int c = 0 ; c < columns ; c++ )
+ {
+ result[c] = *factor_it ;
+ ++factor_it ;
+ }
+
+ if ( degree )
+ {
+ for ( ; ; )
+ {
+ for ( int c = 0 ; c < columns ; c++ )
+ {
+ result[c] += power * *factor_it ;
+ ++factor_it ;
+ }
+ if ( factor_it == factor_end ) // avoid multiplication if exhausted, break now
+ break ;
+ power *= delta ; // otherwise produce next power(s) of delta(s)
+ }
+ }
+ }
+} ;
+
+/// we derive from the weight functor base class to obtain a (multi-) functor
+/// specifically for (derivatives of) a b-spline :
+
+template < typename rc_type >
+struct bspline_derivative_weight_functor
+: public weight_functor_base < rc_type >
+{
+ typedef weight_functor_base < rc_type > base_class ;
+
+ // set up the fully specialized functors to which operator() delegates:
+
+ bspline_derivative_weights < rc_type , rc_type > weights ;
+
+#ifdef USE_VC
+ using typename base_class::rc_type_v ;
+
+ bspline_derivative_weights < rc_type_v , rc_type > weights_v ;
+#endif
+
+ bspline_derivative_weight_functor ( int degree , int d = 0 )
+ : weights ( degree , d )
+#ifdef USE_VC
+ , weights_v ( degree , d )
+#endif
+ {
+ }
+
+ // handle the weight calculation by delegation to the functors set up at construction
+
+ virtual void operator() ( rc_type* result , const rc_type& delta )
+ {
+ weights ( result , delta ) ;
+ }
+
+#ifdef USE_VC
+ virtual void operator() ( rc_type_v* result , const rc_type_v& delta )
+ {
+ weights_v ( result , delta ) ;
+ }
+#endif
+} ;
+
+// not a very useful weight function, but it suits to prove the concept of plug-in
+// weight functions works as intended. Instead of calculating the weights, as in the functor
+// above, this one simply returns equal weights. The result is that, no matter what delta
+// is passed in, the weights are the same and average over the coefficients to which they
+// are applied.
+// Note here that one important property of the weights is that they constitute
+// a partition of unity. Both the (derivative) b-spline weights and this simple
+// weight functor share this property.
+// currently unused, but left in for now
+
+/*
+
+template < typename rc_type >
+struct average_weight_functor
+: public weight_functor_base < rc_type >
+{
+ typedef weight_functor_base < rc_type > base_class ;
+ using typename base_class::rc_type_v ;
+
+ const rc_type weight ;
+ const int order ;
+
+ average_weight_functor ( int degree )
+ : weight ( rc_type ( 1.0 ) / rc_type ( degree + 1 ) ) ,
+ order ( degree + 1 )
+ { } ;
+
+ virtual void operator() ( rc_type* result , const rc_type& delta )
+ {
+ for ( int e = 0 ; e < order ; e++ )
+ result[e] = weight ;
+ }
+
+ virtual void operator() ( rc_type_v* result , const rc_type_v& delta )
+ {
+ for ( int e = 0 ; e < order ; e++ )
+ result[e] = weight ;
+ }
+} ;
+
+// here's another example, using a gaussian to approximate the b-spline basis function
+
+template < typename rc_type >
+struct gaussian_weight_functor
+: public weight_functor_base < rc_type >
+{
+ typedef weight_functor_base < rc_type > base_class ;
+
+ const int degree ;
+ const int order ;
+
+ gaussian_weight_functor ( int _degree , int d = 0 )
+ : degree ( _degree ) ,
+ order ( _degree + 1 )
+ { } ;
+
+ virtual void operator() ( rc_type* result , const rc_type& delta )
+ {
+ rc_type x = - degree / 2 - delta ; // only odd splines for now
+ for ( int e = 0 ; e < order ; e++ , x += rc_type(1.0) )
+ result[e] = gaussian_bspline_basis_approximation<rc_type> ( x , degree ) ;
+ }
+
+#ifdef USE_VC
+
+ using typename base_class::rc_type_v ;
+ virtual void operator() ( rc_type_v* result , const rc_type_v& delta )
+ {
+ rc_type_v x = rc_type_v ( - degree / 2 ) - delta ; // only odd splines for now
+ for ( int e = 0 ; e < order ; e++ , x += rc_type(1.0) )
+ result[e] = gaussian_bspline_basis_approximation<rc_type_v> ( x , degree ) ;
+ }
+
+#endif
+} ;
+
+*/
+
+/*
+// for speed tests we duplicate above weight generation functor definitions
+// to see if calculating weights for all dimensions at once is faster
+
+template < typename rc_type , typename rc_type_v = rc_type >
+struct alt_weight_functor_base
+{
+ // we define two pure virtual overloads for operator(), one for unvectorized
+ // and one for vectorized operation. In case the scope of evaluation is extended
+ // to other types of values, we'll have to add the corresponding signatures here.
+
+ virtual void operator() ( rc_type* result , const rc_type& delta ) = 0 ;
+
+#ifdef USE_VC
+
+ virtual void operator() ( rc_type_v* result , const rc_type_v& delta ) = 0 ;
+
+#endif
+} ;
+
+/// we derive from the weight functor base class to obtain a (multi-) functor
+/// specifically for (derivatives of) a b-spline :
+
+template < typename rc_type , typename rc_type_v = rc_type >
+struct alt_bspline_derivative_weight_functor
+: public alt_weight_functor_base < rc_type , rc_type_v >
+{
+ typedef alt_weight_functor_base < rc_type , rc_type_v > base_class ;
+
+ // set up the fully specialized functors to which operator() delegates:
+
+ bspline_derivative_weights < rc_type , rc_type > weights ;
+
+#ifdef USE_VC
+
+ bspline_derivative_weights < rc_type_v , rc_type > weights_v ;
+
+#endif
+
+ alt_bspline_derivative_weight_functor ( int degree , int d = 0 )
+ : weights ( degree , d )
+#ifdef USE_VC
+ , weights_v ( degree , d )
+#endif
+ {
+ }
+
+ // handle the weight calculation by delegation to the functors set up at construction
+
+ virtual void operator() ( rc_type* result , const rc_type& delta )
+ {
+ weights ( result , delta ) ;
+ }
+
+#ifdef USE_VC
+ virtual void operator() ( rc_type_v* result , const rc_type_v& delta )
+ {
+ weights_v ( result , delta ) ;
+ }
+#endif
+} ;
+*/
+
+/// class evaluator encodes evaluation of a B-spline. This is coded so that it is
+/// agnostic of the spline's dimension, order, and data type. While the typedefs are many and
+/// look difficult, they are mainly used to use concise names in the actual calculation
+/// routines, which are by contrast simple.
+/// I have already hinted at the process used, but here it is again in a nutshell:
+/// The coordinate at which the spline is to be evaluated is split into it's integral part
+/// and a remaining fraction. The integral part defines the location where a window from the
+/// coefficient array is taken, and the fractional part defines the weights to use in calculating
+/// a weighted sum over this window. This weighted sum represents the result of the evaluation.
+/// Coordinate splitting (and mapping to the spline's defined range) is done with mapping
+/// objects, as defined above. The generation of the weights to be applied to the window
+/// of coefficients is performed by employing the weight functors above. What's left to do is
+/// to bring all the components together, which happens in class evaluator. The workhorse
+/// code in the subclasses _eval and _v_eval takes care of performing the necessary operations
+/// recursively ove the dimensions of the spline.
+
+// TODO: if evaluator objects are only ever used one per thread, the 'workspace' for the
+// weights might as well be made a member of class evaluator. The current implementation is
+// thread-safe, though, and would allow to use the same evaluator object from several threads.
+
+template < int dimension , ///< dimension of the spline
+ class value_type , ///< type of spline coefficients/result (pixels etc.)
+ class rc_type = float , ///< singular real coordinate, float or double
+ class ic_type = int > ///< singular integral coordinate, currently only int
+
+class evaluator
+#ifdef USE_VC
+: public Vc::VectorAlignedBase
+#endif
+{
+public:
+
+ enum { level = dimension - 1 } ;
+
+ /// value_type is the data type of the coefficients the evaluator processes,
+ /// and also the data type for the results it produces. These values may be
+ /// aggregates like pixels or TinyVectors. For certain operations these values
+ /// are taken apart into their components, using vigra's ExpandElement mechanism.
+ /// This mechanism can be used to introduce aggregate types that aren't already
+ /// known to vigra. ele_type is the type of an individual element of such an
+ /// aggregate; if value_type isn't an aggregate, it's the same as value_type.
+ /// If a value_type is used which isn't known to vigra's ExpandElement
+ /// mechanism already, the expansion has to be defined in order to make this
+ /// value_type work with this code.
+
+ enum { channels = ExpandElementResult < value_type > :: size } ;
+
+ typedef typename ExpandElementResult < value_type > :: type ele_type ;
+
+ /// array_type is used for the coefficient array
+
+ typedef MultiArrayView < dimension , value_type > array_type ;
+
+ /// type used for nD integral coordinates, array shapes
+
+ typedef typename array_type::difference_type shape_type ;
+
+ /// type for a multidimensional real coordinate
+
+ typedef TinyVector < rc_type , dimension > nd_rc_type ;
+
+ /// type for a 'split' coordinate, mainly used internally in the evaluator, but
+ /// also in 'warp arrays' used for remapping
+
+ typedef split_type < dimension , ic_type , rc_type > split_coordinate_type ;
+
+ /// the evaluator can handle raw coordinates, but it needs a mapping to do so.
+
+ typedef typename MultiArray < 1 , ic_type > :: iterator offset_iterator ;
+ typedef MultiArrayView < dimension + 1 , ele_type > component_view_type ;
+ typedef typename component_view_type::difference_type expanded_shape_type ;
+ typedef TinyVector < ele_type* , channels > component_base_type ;
+
+#ifdef USE_VC
+
+ // for vectorized operation, we need a few extra typedefs
+ // I use a _v suffix to indicate vectorized types and the prefixes
+ // mc_ for multichannel and nd_ for multidimensional
+
+ // TODO: while the use of SimdArrays is convenient, I have reason to believe
+ // it's significantly slower than using Vc::Vectors. So I'd like a mechanism
+ // to use Vc::Vectors if the size lends itself to it (float/int combination)
+ // and only use SimdArrays when necessary (double data, float coordinates)
+
+ // TODO: investigate Vc's simdize functionality; the explicit coding of the
+ // vectorized types below might be simplified by 'simdizing' singular types
+
+ /// a vector of the elementary type of value_type,
+ /// which is used for coefficients and results:
+
+ typedef Vc::Vector < ele_type > ele_v ;
+
+ /// element count of Simd data types, always the same as the number of elements in a Vc::Vector
+ /// of ele_type and used throughout. If elementary types of a size different from ele_type's
+ /// are used, they are vectorized by using SimdArrays.
+
+ enum { vsize = ele_v::Size } ;
+
+ /// compatible-sized SimdArray of vsize ic_type
+
+ typedef Vc::SimdArray < ic_type , vsize > ic_v ;
+
+ /// compatible-sized SimdArray of vsize rc_type
+
+ typedef Vc::SimdArray < rc_type , vsize > rc_v ;
+
+ /// multichannel value as SoA, for pixels etc.
+
+ typedef TinyVector < ele_v , channels > mc_ele_v ;
+
+ /// SoA for nD coordinates/components
+
+ typedef TinyVector < rc_v , dimension > nd_rc_v ;
+
+ /// SoA for nD shapes (or multidimensional indices)
+
+ typedef TinyVector < ic_v , dimension > nd_ic_v ;
+
+ /// SoA for multichannel indices (used for gather/scatter operations)
+
+ typedef TinyVector < ic_v , channels > mc_ic_v ;
+
+#else
+
+ enum { vsize = 1 } ;
+
+#endif // USE_VC
+
+ typedef nd_mapping < split_coordinate_type , dimension , vsize > nd_mapping_type ;
+
+ typedef weight_functor_base < ele_type > weight_functor_base_type ;
+
+ /// in the context of b-spline calculation, this is the weight generating
+ /// functor which will be used:
+
+ typedef bspline_derivative_weight_functor < ele_type > bspline_weight_functor_type ;
+
+// to try out gaussian weights, one might instead use
+// typedef gaussian_weight_functor < ele_type > bspline_weight_functor_type ;
+
+ /// we need one functor per dimension:
+
+ typedef TinyVector < weight_functor_base_type* , dimension > nd_weight_functor ;
+
+ // while in the context of B-splines the weight functors are, of course, functors which
+ // calculate the weights via the B-spline basis functions, the formulation we use here
+ // allows us to use any set of functors that satisfy the argument type. With this
+ // flexible approach, trying out other basis functions becomes simple: just write the
+ // functor and pass it in, it doesn't have to have anything to do with B-splines at all.
+ // But currently we limit the evaluator to use b-spline weights.
+
+private:
+
+ nd_weight_functor fweight ; ///< set of pointers to weight functors, one per dimension
+ const array_type& coefficients ; ///< b-spline coefficient array
+ const expanded_shape_type expanded_stride ; ///< strides in terms of expanded value_type
+ MultiArray < 1 , ic_type > offsets ; ///< offsets in terms of value_type
+ MultiArray < 1 , ic_type > component_offsets ; ///< offsets in terms of ele_type, for vc op
+ component_base_type component_base ;
+ component_view_type component_view ;
+ nd_mapping_type mmap ; ///< mapping of real coordinates to spline coordinates
+ bspline_weight_functor_type wfd0 ; ///< default weight functor: underived bspline
+ const int spline_degree ;
+ const int ORDER ;
+
+public:
+
+#ifdef USE_VC
+ nd_ic_v nd_interleave ; ///< gather/scatter indexes for interleaving nD
+ mc_ic_v mc_interleave ; ///< and mc vectors
+#endif
+
+ /// this constructor is the most flexible variant.
+
+ evaluator ( const array_type& _coefficients ,
+ nd_mapping_type _mmap ,
+ int _spline_degree ,
+ TinyVector < int , dimension > _derivative = TinyVector < int , dimension >(0) )
+ : coefficients ( _coefficients ) ,
+ spline_degree ( _spline_degree ) ,
+ ORDER ( _spline_degree + 1 ) ,
+ component_view ( _coefficients.expandElements ( 0 ) ) ,
+ expanded_stride ( _coefficients.expandElements ( 0 ).stride() ) ,
+ wfd0 ( _spline_degree , 0 ) ,
+ mmap ( _mmap ) // I enforce the passing in of a mapping, even though it
+ // may not be used at all. TODO: can I do better?
+ {
+ // initalize the weight functors. In this constructor, we use only bspline weight
+ // functors, even though the evaluator can operate with all weight functors
+ // filling in the right number of basis values given a delta. To make use of this
+ // flexibility, one would derive from this class or write another constructor.
+ // Note how we code so that the default case (plain evaluation with no derivatives)
+ // results in use of only one single weight functor.
+
+ for ( int d = 0 ; d < dimension ; d++ )
+ {
+ if ( _derivative[d] )
+ {
+ fweight[d] = new bspline_weight_functor_type ( _spline_degree , _derivative[d] ) ;
+ }
+ else
+ {
+ fweight[d] = &wfd0 ; // pick the default if derivative is 0
+ }
+ }
+
+ // calculate the number of offsets needed and create the array to hold them
+ // The evaluation forms a weighted sum of a window into the coeffcicent array.
+ // The sequence of offsets we calculate here is the set of pointer differences
+ // from the first element in that window to all elements in the window. It's
+ // another way of coding this window, where all index calculations have already
+ // been done beforehand rather than performing it during the traversal of the
+ // window by means of stride/shape arithmetic. Coding the window in this fashion
+ // also makes it easy to vectorize the code.
+
+ int noffsets = ORDER ;
+ for ( int exp = 1 ; exp < dimension ; exp++ )
+ noffsets *= ORDER ;
+
+ offsets = MultiArray < 1 , ptrdiff_t > ( noffsets ) ;
+ component_offsets = MultiArray < 1 , ptrdiff_t > ( noffsets ) ;
+
+ // we fill the offset array in a simple fashion: we do a traversal of the window
+ // and calculate the pointer difference for every element reached. We use the
+ // same loop to code the corresponding offsets to elementary values (ele_type)
+
+ auto sample = coefficients.subarray ( shape_type() , shape_type(ORDER) ) ;
+ auto base = sample.data() ;
+ auto target = offsets.begin() ;
+ auto component_target = component_offsets.begin() ;
+
+ for ( auto &e : sample )
+ {
+ *target = &e - base ;
+ *component_target = channels * *target ;
+ ++target ;
+ ++component_target ;
+ }
+
+ // set up a set of base adresses for the component channels. This is needed
+ // for the vectorization, as the vector units can only work on elementary types
+ // (ele_type) and not on aggregates, like pixels.
+
+ expanded_shape_type eshp ;
+ for ( int i = 0 ; i < channels ; i++ )
+ {
+ eshp[0] = i ;
+ component_base[i] = &(component_view[eshp]) ;
+ }
+
+#ifdef USE_VC
+
+ // fill the gather/scatter information for vectorized operation
+ // we only need to do this once on evaluator creation. For now this
+ // code is limited to use ints to scatter/gather. This limits the
+ // array size which can be processed, but in real applications this
+ // should rarely become a problem. Still a thing to keep in mind.
+ // TODO this should be factored out and be static
+
+ for ( int d = 0 ; d < dimension ; d++ )
+ {
+ nd_interleave[d] = ic_v::IndexesFromZero() ;
+ nd_interleave[d] *= ic_type ( dimension ) ;
+ nd_interleave[d] += ic_type ( d ) ;
+ }
+
+ for ( int ch = 0 ; ch < channels ; ch++ )
+ {
+ mc_interleave[ch] = ic_v::IndexesFromZero() ;
+ mc_interleave[ch] *= ic_type ( channels ) ;
+ mc_interleave[ch] += ic_type ( ch ) ;
+ }
+
+#endif
+
+ } ;
+
+
+ /// simplified constructor from a bspline object
+
+ evaluator ( const bspline < value_type , dimension > & bspl ,
+ TinyVector < int , dimension > _derivative = TinyVector < int , dimension >(0) )
+ : evaluator ( bspl.coeffs ,
+ nd_mapping_type ( bspl ) ,
+ bspl.spline_degree ,
+ _derivative )
+ {
+ if ( bspl.spline_degree > 1 && ! bspl.braced )
+ throw not_supported ( "for spline degree > 1: evaluation needs braced coefficients" ) ;
+ } ;
+
+ nd_mapping_type& get_mapping()
+ {
+ return mmap ;
+ }
+
+ int workspace_size()
+ {
+ // allocate enough dtype to contain bases for all dimensions
+ return ORDER * dimension ;
+ }
+
+ // TODO: docu here needs to be adapted, there is no more basis iterator, instead
+ // p_workspace is used
+
+ /// fill_multi_basis calculates the weights to be applied to a section of the coefficients from
+ /// the fractional parts of the split coordinates. What is calculated here is the evaluation
+ /// of the spline's basis function at dx, dx+1 , dx+2..., but doing it naively is computationally
+ /// expensive, as the evaluation of the spline's basis function at arbitrary values has to
+ /// look at the value, find out the right interval, and then calculate the value with the
+ /// appropriate function. But we always have to calculate the basis function for *all*
+ /// intervals anyway, and the method used here performs this tasks effectively using a
+ /// vector/matrix multiplication.
+ ///
+ /// If the spline is more than 1-dimensional, we need a set of weights for every dimension.
+ /// We use a TinyVector of basis_type for the purpose, and iterate over it recursively
+ /// in the evaluation process.
+ ///
+ /// Contrary to my initial implementation, I fill the 'workspace' in order ascending with
+ /// the axis, so now weights for axis 0 are first etc.. This results in a slighly funny-looking
+ /// initial call to _eval, but the confusion from reverse-filling the workspace was probably worse.
+
+ template < typename dtype ,
+ typename nd_rc_type >
+ void fill_multi_basis ( dtype * p_workspace ,
+ const nd_rc_type& c )
+ {
+ auto ci = c.cbegin() ;
+ for ( int axis = 0 ; axis < dimension ; ++ci , ++axis )
+ (*(fweight[axis])) ( p_workspace + axis * ORDER , *ci ) ;
+ }
+
+ /// _eval is the workhorse routine and implements the recursive arithmetic needed to
+ /// evaluate the spline. First the 'basis' for the current dimension is obtained
+ /// from the basis iterator. Once the basis has been obtained, it's values are
+ /// multiplied with the results of recursively calling _eval for the next
+ /// lower dimension and the products summed up to produce the return value.
+ /// The scheme of using a recursive evaluation has several benefits:
+ /// - it needs no explicit intermediate storage of partial sums (uses stack instead)
+ /// - it makes the process dimension-agnostic in an elegant way
+ ///
+ /// this _eval works with a base pointer and an iterator over offsets, just like the vectorized version.
+ /// note that this routine is used for operation on braced splines, with the sequence of offsets to be
+ /// visited fixed at the evaluator's construction. But in this non-vectorized routine, passing in a
+ /// different sequence of offsets for evaluation in an area where boundary conditions apply would be
+ /// feasible (akin to Thevenaz' indexing), even though it would require a lot of new logic, since
+ /// currently the bracing takes care of the boundary conditions.
+ ///
+ /// I'd write a plain function template and partially specialize it, but that's not allowed,
+ /// so I use a functor instead:
+
+ template < int level , class dtype >
+ struct _eval
+ {
+ dtype operator() ( const dtype* & pdata ,
+ offset_iterator & ofs ,
+ const ele_type * p_workspace ,
+ const int& ORDER
+ )
+ {
+ dtype result = dtype() ;
+ for ( int i = 0 ; i < ORDER ; i++ )
+ {
+ result += p_workspace[i]
+ * _eval < level - 1 , dtype >() ( pdata , ofs , p_workspace - ORDER , ORDER ) ;
+ }
+ return result ;
+ }
+ } ;
+
+ /// at level 0 the recursion ends, now we finally apply the weights for axis 0
+ /// to the window of coefficients. Note how ofs is passed in per reference. This looks
+ /// wrong, but it's necessary: When, in the course of the recursion, the level 0
+ /// routine is called again, it needs to access the next bunch of ORDER coefficients.
+ /// Just incrementing the reference saves us incrementing higher up.
+
+ template < class dtype >
+ struct _eval < 0 , dtype >
+ {
+ dtype operator() ( const dtype* & pdata ,
+ offset_iterator & ofs ,
+ const ele_type * p_workspace ,
+ const int& ORDER
+ )
+ {
+ dtype result = dtype() ;
+ for ( int i = 0 ; i < ORDER ; i++ )
+ {
+ result += pdata [ *ofs ] * p_workspace[i] ;
+ ++ofs ;
+ }
+ return result ;
+ }
+ } ;
+
+#ifdef USE_VC
+
+ /// vectorized version of _eval
+ /// to operate with vsize values synchronously, we need a bit more indexing than in the
+ /// non-vectorized version. the second parameter, origin, constitutes a gather operand
+ /// which, applied to the base adress, handles a set of windows to be processed in parallel.
+ /// if the gather operation is repeated with offsetted base addresses, the result vector is
+ /// built in the same way as the single result value in the unvectorized code above.
+ /// note that the vectorized routine can't function like this when it comes to
+ /// evaluating unbraced splines: it relies on the bracing and can't do without it, because
+ /// it works with a fixed sequence of offsets, whereas the evaluation of an unbraced spline
+ /// might use a different offset sequence for values affected by the boundary condition.
+ /// Nevertheless I have chosen this implementation, as the speed gain by vectorization is
+ /// so large that the extra memory needed for the bracing seems irrelevant. I have to concede,
+ /// though, that this rules out in-place spline generation on a packed array of knot point values -
+ /// either the knot point data come in with bracing space already present, or the operation has
+ /// to use a separate target array.
+
+ template < class dtype , int level >
+ struct _v_eval
+ {
+ dtype operator() ( const component_base_type& base , ///< base adresses of components
+ const ic_v& origin , ///< offsets to evaluation window origins
+ offset_iterator & ofs , ///< offsets to coefficients inside this window
+ const ele_v * p_workspace , ///< vectorized weights
+ const int& ORDER )
+ {
+ dtype sum = dtype() ; ///< to accumulate the result
+ dtype subsum ; ///< to pick up the result of the recursive call
+
+ for ( int i = 0 ; i < ORDER ; i++ )
+ {
+ subsum = _v_eval < dtype , level - 1 >() ( base , origin , ofs , p_workspace - ORDER , ORDER );
+ for ( int ch = 0 ; ch < channels ; ch++ )
+ {
+ sum[ch] += p_workspace[i] * subsum[ch] ;
+ }
+ }
+ return sum ;
+ }
+ } ;
+
+ /// the level 0 routine terminates the recursion
+
+ template < class dtype >
+ struct _v_eval < dtype , 0 >
+ {
+ dtype operator() ( const component_base_type& base , ///< base adresses of components
+ const ic_v& origin , ///< offsets to evaluation window origins
+ offset_iterator & ofs , ///< offsets to coefficients in this window
+ const ele_v * p_workspace , ///< vectorized weights
+ const int& ORDER )
+ {
+ dtype sum = dtype() ;
+
+ for ( int i = 0 ; i < ORDER ; i++ )
+ {
+ for ( int ch = 0 ; ch < channels ; ch++ )
+ {
+ sum[ch] += p_workspace[i] * ele_v ( base[ch] , origin + *ofs ) ;
+ }
+ ++ofs ;
+ }
+ return sum ;
+ }
+ } ;
+
+#endif // USE_VC
+
+ // next are the variants of operator(). there are quite a few, since I've coded for operation
+ // from different starting points and both for vectorized and nonvectorized operation.
+
+ /// variant of operator() which takes a shape and a multi_basis. This is the final delegate
+ /// calling the recursive _eval method. Note how we pass _eval the workspace, increasing the
+ /// pointer by level * ORDER. By doing so we adress the weights for the highest dimension,
+ /// which is processed at the highest level of the recursion. The recursive call to _eval
+ /// *decreases* p_workspace by ORDER, etc. until for processing along the 0 (or x) axis
+ /// the workspace pointer is equal again to what this here eval receives as p_workspace.
+
+ value_type eval ( const shape_type& s , // lower corner of the subarray
+ ele_type * p_workspace ) // precalculated multi_basis
+ {
+ const value_type * base = & coefficients [ s ] ;
+ auto ofs = offsets.begin() ; // offsets reflects the positions inside the subarray
+ return _eval<level,value_type>() ( base , ofs , p_workspace + level * ORDER , ORDER ) ;
+ }
+
+ /// this variant of eval takes a split coordinate:
+
+ value_type operator() ( const split_coordinate_type& sc , // presplit coordinate
+ ele_type * p_workspace ) // space for weights
+ {
+ fill_multi_basis ( p_workspace , sc.tune ) ;
+ return eval ( sc.select , p_workspace ) ;
+ }
+
+ /// this variant take a coordinate which hasn't been mapped yet, so it uses
+ /// the nd_mapping, mmap, and applies it. It's the most convenient form but
+ /// also the slowest, due to the mapping, which is quite expensive computationally.
+
+ value_type operator() ( const nd_rc_type& c , /// coordinate
+ ele_type * p_workspace ) /// space for weights
+ {
+ split_coordinate_type sc ;
+ mmap ( c , sc ) ; /// apply the mappings
+ fill_multi_basis ( p_workspace , sc.tune ) ;
+ return eval ( sc.select , p_workspace ) ;
+ }
+
+ /// variant to use if the caller doesn't provide a workspace
+ /// this is less efficient than the above version, so for mass evaluations, like in the
+ /// remap routine, it isn't used, but it's nice to have for ad-hoc use where one doesn't
+ /// want to look into the workings of the code.
+
+ value_type operator() ( const nd_rc_type& c ) // coordinate
+ {
+ ele_type p_workspace[workspace_size()] ; // provide space for weights
+ return operator() ( c , p_workspace ) ; // delegate to above routine
+ }
+
+ /* currently unused:
+ /// we also allow passing in of a single real value which is used to
+ /// construct a nd_rc_type. beware! might cause hard-to-track bugs
+
+ value_type operator() ( const rc_type& c )
+ {
+ return operator() ( nd_rc_type(c) ) ;
+ }
+ */
+
+#ifdef USE_VC
+
+ /// vectorized variants of the previous routines
+
+ mc_ele_v operator() ( nd_ic_v& s , // lower corners of the subarrays
+ ele_v * p_workspace ) // precalculated weights
+ {
+ // first we sum up the coordinates in all dimensions into origin values. this is analogous
+ // to forming origin = & coefficients [ ... ] - coefficients.data() in unvectorized code
+ // but since we can't do vectorized adress arithmetic (TODO can we?) ...
+ // note that we use both the strides and offsets appropriate for an expanded array,
+ // and component_base has pointers to the component type.
+
+ ic_v origin = s[0] * ic_type ( expanded_stride [ 1 ] ) ;
+ for ( int d = 1 ; d < dimension ; d++ )
+ origin += s[d] * ic_type ( expanded_stride [ d + 1 ] ) ;
+
+ // to iterate over the positions of all coefficients in the window over which the weighted sum
+ // is formed, we use this iterator:
+
+ auto ofs = component_offsets.begin() ;
+
+ // now we can call the recursive _eval routine
+
+ return _v_eval < mc_ele_v , level >() ( component_base , origin , ofs ,
+ p_workspace + level * ORDER , ORDER ) ;
+ }
+
+ /// here we take the approach to require the calling function to present pointers to
+ /// vsize input and vsize output values, stored contiguously, so that we can use
+ /// 'standard' gather/scatter operations here with precomputed indexes. Performing the
+ /// (de)interleaving here simplifies matters for the calling code if it has the data
+ /// in contiguous memory. But this is not always the case, for example when the
+ /// data are strided.
+
+ void operator() ( const split_coordinate_type* const psc , // pointer to vsize presplit coordinates
+ value_type* result , // pointer to vsize result values
+ ele_v * p_workspace ) // space for weight vectors
+ {
+ nd_ic_v select ;
+ nd_rc_v tune ;
+
+ // this is awkward, but if we get split coordinates interleaved like this,
+ // we have to manually deinterleave them unless we make potentially unsafe
+ // assumptions about the size of the components (if they are the same and packed,
+ // we might gather instead...)
+
+ for ( int vi = 0 ; vi < vsize ; vi++ )
+ {
+ for ( int d = 0 ; d < dimension ; d++ )
+ {
+ select[d][vi] = int ( psc[vi].select[d] ) ;
+ tune[d][vi] = rc_type ( psc[vi].tune[d] ) ;
+ }
+ }
+
+ // calculate the result
+
+ fill_multi_basis ( p_workspace , tune ) ;
+
+ mc_ele_v v_result = operator() ( select , p_workspace ) ;
+
+ // and deposit it in the memory the caller provides
+ for ( int ch = 0 ; ch < channels ; ch++ )
+ v_result[ch].scatter ( (ele_type*)result , mc_interleave[ch] ) ;
+ }
+
+ /// This variant of operator() works directly on vector data (of unsplit coordinates)
+ /// This burdens the calling code with (de)interleaving the data. But often the calling
+ /// code performs a traversal of a large body of data and is therefore in a better position
+ /// to perform the (de)interleaving e.g. by a gather/scatter operation.
+
+ void operator() ( const nd_rc_v & input , // number of dimensions * coordinate vectors
+ mc_ele_v & result , // number of channels * value vectors
+ ele_v * p_workspace ) // space for weight vectors
+ {
+ nd_ic_v select ;
+ nd_rc_v tune ;
+
+ // map the incoming coordinates to the spline's range
+
+ mmap ( input , select , tune ) ;
+
+ // calculate the weights
+
+ fill_multi_basis ( p_workspace , tune ) ;
+
+ // delegate
+
+ result = operator() ( select , p_workspace ) ;
+ }
+
+ /// This variant operates on unsplit coordinates. Here again we require the calling function
+ /// to pass pointers to vsize input and output values in contiguous memory. The data are
+ /// (de)interleved in this routine, the actual evaluation is delegated to the variant working
+ /// on vectorized data.
+
+ void operator() ( const nd_rc_type* const pmc , // pointer to vsize muli_coordinates
+ value_type* result , // pointer to vsize result values
+ ele_v * p_workspace ) // space for weight vectors
+ {
+ nd_rc_v input ;
+ mc_ele_v v_result ;
+
+ // gather the incoming (interleaved) coordinates
+ for ( int d = 0 ; d < dimension ; d++ )
+ input[d].gather ( (const rc_type* const)pmc , nd_interleave[d] ) ;
+
+ // call operator() for vectorized data
+ operator() ( input , v_result , p_workspace ) ;
+
+ // and deposit it in the memory the caller provides
+ for ( int ch = 0 ; ch < channels ; ch++ )
+ v_result[ch].scatter ( (ele_type*)result , mc_interleave[ch] ) ;
+ }
+
+ /// mixed form, where input is a vectorized coordinate
+ /// and output goes to interleaved memory
+
+ void operator() ( const nd_rc_v & input , // number of dimensions * coordinate vectors
+ value_type* result , // pointer to vsize result values
+ ele_v * p_workspace ) // space for weight vectors
+ {
+ mc_ele_v v_result ;
+
+ // call operator() for vectorized data
+
+ operator() ( input , v_result , p_workspace ) ;
+
+ // and deposit result in the memory the caller provides
+
+ for ( int ch = 0 ; ch < channels ; ch++ )
+ v_result[ch].scatter ( (ele_type*)result , mc_interleave[ch] ) ;
+ }
+
+#endif // USE_VC
+
+ ~evaluator()
+ {
+ // we don't want a memory leak!
+ for ( int d = 0 ; d < dimension ; d++ )
+ {
+ if ( fweight[d] != &wfd0 )
+ delete fweight[d] ;
+ }
+ }
+} ;
+
+} ; // end of namespace vspline
+
+#endif // VSPLINE_EVAL_H
diff --git a/example/eval.cc b/example/eval.cc
new file mode 100644
index 0000000..1577993
--- /dev/null
+++ b/example/eval.cc
@@ -0,0 +1,132 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/// eval.cc
+///
+/// takes a set of knot point values from cin, calculates a 1D b-spline
+/// over them, and evaluates it at coordinates taken from cin
+
+#include <vspline/vspline.h>
+#include <iomanip>
+
+using namespace std ;
+using namespace vigra ;
+using namespace vspline ;
+
+int main ( int argc , char * argv[] )
+{
+ // get the spline degree and boundary conditions from the console
+
+ cout << "enter spline degree: " ;
+ int spline_degree ;
+ cin >> spline_degree ;
+
+ int bci = -1 ;
+ bc_code bc ;
+
+ while ( bci < 1 || bci > 4 )
+ {
+ cout << "choose boundary condition" << endl ;
+ cout << "1) MIRROR" << endl ;
+ cout << "2) REFLECT" << endl ;
+ cout << "3) NATURAL" << endl ;
+ cout << "4) PERIODIC" << endl ;
+ cin >> bci ;
+ }
+
+ switch ( bci )
+ {
+ case 1 :
+ bc = MIRROR ;
+ break ;
+ case 2 :
+ bc = REFLECT ;
+ break ;
+ case 3 :
+ bc = NATURAL ;
+ break ;
+ case 4 :
+ bc = PERIODIC ;
+ break ;
+ }
+ // put the BC code into a TinyVector
+ TinyVector < bc_code , 1 > bcv ( bc ) ;
+
+ // obtain knot point values
+
+ double v ;
+ std::vector<double> dv ;
+ cout << "enter knot point values (end with EOF)" << endl ;
+ while ( cin >> v )
+ dv.push_back ( v ) ;
+
+ cin.clear() ;
+
+ // put the size into a TinyVector
+ TinyVector < int , 1 > shape ( dv.size() ) ;
+
+ // fix the type for the bspline object
+ typedef bspline < double , 1 > spline_type ;
+ spline_type bsp ( shape , spline_degree , bcv , EXPLICIT ) ;
+ cout << "created bspline object:" << endl << bsp << endl ;
+
+ // fill the data into the spline's 'core' area
+ for ( size_t i = 0 ; i < dv.size() ; i++ )
+ bsp.core[i] = dv[i] ;
+
+ // prefilter the data
+ bsp.prefilter() ;
+
+ cout << fixed << showpoint << setprecision(12) ;
+ cout << "spline coefficients (with frame)" << endl ;
+ for ( auto& coeff : bsp.container )
+ cout << " " << coeff << endl ;
+
+ // fix the type for the evaluator and create it
+ typedef evaluator < 1 , double , double , int > eval_type ;
+ eval_type ev ( bsp ) ;
+ auto map = ev.get_mapping() ;
+ int ic ;
+ double rc ;
+
+ cout << "enter coordinates to evaluate (end with EOF)" << endl ;
+ while ( ! cin.eof() )
+ {
+ // get a coordinate
+ cin >> v ;
+ // evaluate it
+ double res = ev ( v ) ;
+ // apply the mapping to the coordinate to output that as well
+ map ( v , ic , rc , 0 ) ;
+
+ cout << v << " -> ( " << ic << " , " << rc << " ) -> " << res << endl ;
+ }
+}
diff --git a/example/gradient.cc b/example/gradient.cc
new file mode 100644
index 0000000..2a787f3
--- /dev/null
+++ b/example/gradient.cc
@@ -0,0 +1,90 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/// gradient.cc
+///
+/// If we create a b-spline over an array containing, at each grid point,
+/// the sum of the grid point's coordinates, each 1D row, column, etc will
+/// hold a linear gradient with first derivative == 1. If we use NATURAL
+/// BCs, evaluating the spline with real coordinates anywhere inside the
+/// defined range should produce precisely the sum of the coordinates.
+/// This is a good test for both the precision of the evaluation and it's
+/// correct functioning, particularly with higher-D arrays.
+
+#include <vspline/vspline.h>
+
+using namespace std ;
+
+main ( int argc , char * argv[] )
+{
+ typedef vspline::bspline < double , 5 > spline_type ;
+ typedef typename spline_type::shape_type shape_type ;
+ typedef typename spline_type::view_type view_type ;
+ typedef typename spline_type::bcv_type bcv_type ;
+
+ shape_type core_shape = { 10 , 4 , 13 , 9 , 6 } ;
+
+ // note how with these small array dimensions, we can't use the EXPLICIT scheme:
+ // the horizon, 12, is wider than the smallest extent, and bracing (with the current
+ // bracing code) will produce erroneous results.
+
+ spline_type bspl ( core_shape , 3 , bcv_type ( vspline::NATURAL ) ) ;
+ view_type core = bspl.core ;
+
+ for ( int d = 0 ; d < bspl.dimension ; d++ )
+ {
+ for ( int c = 0 ; c < core_shape[d] ; c++ )
+ core.bindAt ( d , c ) += c ;
+ }
+
+ bspl.prefilter() ;
+
+ typedef vspline::evaluator < bspl.dimension , double , double > evaluator_type ;
+ typedef typename evaluator_type::nd_rc_type coordinate_type ;
+
+ evaluator_type ev ( bspl ) ;
+ double * ws = new double [ ev.workspace_size() ] ;
+
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ // std::mt19937 gen(12345); // fix starting value for reproducibility
+
+ coordinate_type c ;
+
+ for ( int times = 0 ; times < 10000 ; times++ )
+ {
+ for ( int d = 0 ; d < bspl.dimension ; d++ )
+ c[d] = ( core_shape[d] - 1 ) * std::generate_canonical<double, 20>(gen) ;
+ double delta = ev(c) - sum(c) ;
+ if ( delta > 2.0e-14 )
+ cout << c << " -> delta = " << delta << endl ;
+ }
+}
\ No newline at end of file
diff --git a/example/pano_extract.cc b/example/pano_extract.cc
new file mode 100644
index 0000000..4eb792e
--- /dev/null
+++ b/example/pano_extract.cc
@@ -0,0 +1,776 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform rational b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/// \file pano_extract.cc
+///
+/// \brief demonstration of transformation-based remap
+///
+/// This program extracts an rectilinear image
+/// panorama with a given size, horizontal field of view, yaw, pitch and roll
+/// from a (full) spherical
+
+#include <cmath>
+
+#include <vspline/vspline.h>
+
+#include <vigra/multi_math.hxx>
+
+using namespace vigra ;
+using namespace vigra::multi_math;
+using namespace std ;
+using namespace vspline ;
+
+#include <vigra/stdimage.hxx>
+#include <vigra/imageinfo.hxx>
+#include <vigra/impex.hxx>
+#include <vigra/impexalpha.hxx>
+
+#define PRINT_ELAPSED
+
+#ifdef PRINT_ELAPSED
+#include <ctime>
+#include <chrono>
+#endif
+
+#include <vigra/quaternion.hxx>
+
+/// concatenation of roll, pitch and yaw into a single quaternion
+/// we use a right-handed coordinate system: the positive x axis points towards us,
+/// the positive y axis to the right and the positive z axis up. the subjective
+/// point of view is taken to be at the origin, so the view directly ahead is
+/// along the negative x-axis.
+
+template < typename rc_type >
+Quaternion<rc_type> get_rotation_q ( rc_type yaw , rc_type pitch , rc_type roll )
+{
+ typedef Quaternion<rc_type> quaternion ;
+
+ // first calculate the component quaternions.
+ // for a right-handed system and clockwise rotations, we have this formula:
+ // q = cos ( theta / 2 ) + ( ux * i + uy * j + uz * k ) * sin ( theta / 2 )
+ // where theta is the angle of rotation and (ux, uy, uz) is the axis around
+ // which we want to rotate some 3D object.
+
+ // we take roll to be a clockwise rotation around the line of sight (-1, 0, 0)
+ // to achieve the clockwise rotation of the section taken out of the panorama,
+ // we have to rotate the target counterclockwise. So we get -sin ( -roll / 2.0 ),
+ // and the two negative signs cancel out.
+ quaternion q_roll ( cos ( roll / 2.0 ) , sin ( roll / 2.0 ) , 0.0 , 0.0 ) ;
+
+ // if we consider a target in the line of sight, we take pitch as moving it upwards,
+ // rotating clockwise around the positive y axis (0 , 1, 0)
+ quaternion q_pitch ( cos ( pitch / 2.0 ) , 0.0 , sin ( pitch / 2.0 ) , 0.0 ) ;
+
+ // if we consider a target in the line of sight, we take yaw as moving it to the right,
+ // which is counterclockwise around the z axis, or clockwise around the negative
+ // z axis (0, 0, -1)
+ quaternion q_yaw ( cos ( yaw / 2.0 ) , 0.0 , 0.0 , - sin ( yaw / 2.0 ) ) ;
+
+ // now produce the concatenated operation by multiplication of the components
+ quaternion total = q_yaw ;
+ total *= q_pitch ;
+ total *= q_roll ;
+
+ return total ;
+}
+
+/// apply the rotation codified in a quaternion to a 3D point
+
+template < typename rc_type >
+TinyVector < rc_type , 3 >
+rotate_q ( const TinyVector < rc_type , 3 > & v , Quaternion<rc_type> q )
+{
+ typedef Quaternion<rc_type> quaternion ;
+ TinyVector < rc_type , 3 > result ;
+ quaternion vq ( 0.0 , v[0] , v[1] , v[2] ) ;
+ quaternion qc = conj ( q ) ;
+ q *= vq ;
+ q *= qc ;
+ result[0] = q[1] ;
+ result[1] = q[2] ;
+ result[2] = q[3] ;
+ return result ;
+}
+
+/// tf_spherical_rectilinear transforms incoming (target) image coordinates to corresponding
+/// spherical coordinates into the sherical panoramic image. The main coding effort
+/// for the intended image transformation is in this functor. The strategy is this:
+/// with the transformation-based remap, we receive 2D coordinate into the target image.
+/// We try and precalculate everything we can from the constructor parameters and 'load'
+/// the functor with the precalculated values, leaving only the steps which depend on
+/// the incoming cordinates to the operator() of the functor. Instead of rotating
+/// every incoming coordinate wit yaw, pitch and roll, we only rotate three points:
+/// the top left, lower left and upper right of the target image. While we are using
+/// cartesian coordinates, we can now simply generate the transformed cartesian coordinates
+/// by following straight lines with dx, dy and dz derived from the three rotated points.
+/// Once we have the cartesian coordinates, we only have to transform them to spherical
+/// coordinates and do a bit of scaling and shifting to receive coordinates into the
+/// panoramic source image. Note that in this code we model pixels as small rectangular
+/// shapes. If an image's width is w, it has w pixels along the x axis. The 'position'
+/// of a pixel is identified with it's center. If the pixels are dx apart, The 'positions'
+/// of the pixels are dx/2, dx+dx/2, 2dx+dx/2 etc. dx is equal to the image width divided
+/// by w.
+
+template < class rc_type >
+class tf_spherical_rectilinear
+{
+ typedef Quaternion<rc_type> quaternion ;
+ typedef TinyVector<rc_type,3> p3d ;
+ typedef TinyVector < rc_type , 2 > coordinate_type ;
+
+ p3d s0 ; // position of sample(0,0) of target
+ p3d dh ; // step vector in target's horizontal
+ p3d dv ; // step vector in target's vertical
+ rc_type scale_x ; // scaling factors to transform theta/phi to pano pixel coordinates
+ rc_type scale_y ;
+
+public:
+
+ tf_spherical_rectilinear ( int w_pano , // width of the (spherical) panorama
+ int h_pano , // height of same
+ rc_type _hfov , // horizontal field of view of section (in degrees)
+ int w_out , // width of same
+ int h_out , // height of same
+ rc_type _yaw , // yaw of extracted section
+ rc_type _pitch , // pitch of same
+ rc_type _roll ) // roll of same
+ {
+ // convert incoming angles to radians
+ rc_type hfov = _hfov * rc_type ( M_PI ) / rc_type ( 180.0 ) ;
+ rc_type yaw = _yaw * rc_type ( M_PI ) / rc_type ( 180.0 ) ;
+ rc_type pitch = _pitch * rc_type ( M_PI ) / rc_type ( 180.0 ) ;
+ rc_type roll = _roll * rc_type ( M_PI ) / rc_type ( 180.0 ) ;
+
+ // calculate a quaternion representing the positioning of the target
+ // as a 3D rotation of the same from an initial position centered at the
+ // spherical image's center.
+ quaternion qr = get_rotation_q ( yaw , pitch , roll ) ;
+
+ // calculate inverse aspect ratio of target
+ rc_type inverse_aspect_ratio = rc_type ( h_out ) / rc_type ( w_out ) ;
+
+ // initially position target so that it's center touches the unit sphere at it's equator.
+ // Then get it's half width (cartesian distance of center to edge)
+ rc_type wh = tan ( hfov / rc_type ( 2.0 ) ) ;
+// cout << "hfov: " << hfov << " wh: " << wh << endl ;
+
+ // now calculate the position of the top left corner of the target (origin),
+ // and of the upper right and lower left corner
+ p3d origin = { rc_type ( -1.0 ) , - wh , wh * inverse_aspect_ratio } ;
+ p3d ur = { rc_type ( -1.0 ) , wh , wh * inverse_aspect_ratio } ;
+ p3d ll = { rc_type ( -1.0 ) , - wh , - wh * inverse_aspect_ratio } ;
+
+// cout << "origin: " << origin << " ur: " << ur << " ll: " << ll << endl ;
+ // perform the rotation of the target by applying the quaternion
+ origin = rotate_q ( origin , qr ) ;
+ ur = rotate_q ( ur , qr ) ;
+ ll = rotate_q ( ll , qr ) ;
+
+ // calculate the difference from a target pixel to it's horizontal/vertical neighbour
+ // TODO: can we lower the error here????
+
+ dh = ( ur - origin ) / rc_type ( w_out ) ;
+ dv = ( ll - origin ) / rc_type ( h_out ) ;
+
+ // the center of the target's (0,0) pixel is half a step from the origin
+ s0 = origin + rc_type ( 0.5 ) * dh + rc_type ( 0.5 ) * dv ;
+
+ // calculate the scaling factors
+ scale_x = w_pano / rc_type ( 2.0 * M_PI ) ;
+ scale_y = h_pano / rc_type ( M_PI ) ;
+ } ;
+
+ /// operator() of the transformation receives an incoming 2D coordinate into the
+ /// target image and produces a 2D coordinate into the source image (the panorama).
+ /// The intermediate 3D processing is encapsuled in the functor.
+
+ void operator() ( const coordinate_type & c_in ,
+ coordinate_type & c_out )
+ {
+ rc_type x = c_in[0] ; // incoming coordinates (into the target)
+ rc_type y = c_in[1] ;
+
+ p3d s = s0 + x * dh + y * dv ; // get the target pixel's 3D location
+
+ // calculate the corresponding spherical coordinate
+ rc_type norm = sqrt ( s[0] * s[0] + s[1] * s[1] + s[2] * s[2] ) ;
+
+ // mind the sign: we're going clockwise, atan2 ( y , x ) is going counterclockwise
+ rc_type theta = - atan2 ( s[1] , s[0] ) ;
+
+ rc_type phi = acos ( s[2] / norm ) ;
+
+ // scale/shift the resulting spherical coordinates to the size of the panoramic image
+ // and assign the result to the output coordinates. Why -0.5? If we were to 'land'
+ // at an angle of 0.0, this would be flush with the image's margin, .5 pixels to the
+ // 'left' of the first pixel's center.
+ c_out[0] = theta * scale_x - rc_type ( 0.5 ) ;
+ c_out[1] = phi * scale_y - rc_type ( 0.5 );
+ }
+
+#ifdef USE_VC
+
+ /// operator() for vectorized operation. This takes the Simd type as it's template argument,
+ /// so we don't have to make it explicit here as long as all operations we use are supported.
+ /// It's easier this way, since the Simd type is a SimdArray the size of which is determined
+ /// by the coordinate type (float or double) used in process_image.
+ ///
+ /// The code is slightly more involved than the scalar version above, also because it makes
+ /// the 3 components of the 3D variables explicit. Vc doesn't offer a vectorized acos function,
+ /// so we use the asin instead.
+ ///
+ /// The vectorized code produces a measurable speedup, most likely due to the vectorized
+ /// transcendental functions.
+
+ template < class rc_v >
+ void operator() ( const TinyVector < rc_v , 2 > & c_in ,
+ TinyVector < rc_v , 2 > & c_out )
+ {
+ rc_v x = c_in[0] ;
+ rc_v y = c_in[1] ;
+
+ rc_v sx ( s0[0] ) ;
+ rc_v sy ( s0[1] ) ;
+ rc_v sz ( s0[2] ) ;
+
+ sx += x * dh[0] + y * dv[0] ;
+ sy += x * dh[1] + y * dv[1] ;
+ sz += x * dh[2] + y * dv[2] ;
+
+ rc_v norm = sqrt ( sx * sx + sy * sy + sz * sz ) ;
+
+ rc_v theta = - atan2 ( sy , sx ) ;
+
+ // rc_v phi = acos ( sz / norm ) ; // oops... would be nice but Vc doesn't have acos!
+ rc_v phi ( rc_v ( M_PI / 2.0 ) ) ;
+ phi -= asin ( sz / norm ) ;
+
+ c_out[0] = theta * scale_x - rc_type ( 0.5 ) ;
+ c_out[1] = phi * scale_y - rc_type ( 0.5 ) ;
+ }
+
+#endif
+
+} ;
+
+// To present a program which is useful and 'correct' in photography terms,
+// we have to deal with gamma and colour space. We make the silent assumption
+// that incomning data with unsigned char and unsigned short pixels are in sRGB,
+// while other incoming types are linear RGB already.
+
+#include <vigra/colorconversions.hxx>
+
+/// functor to convert a UINT8 RGB pixel from sRGB to linear RGB
+/// this uses vigra's functor, which in turn uses a lookup table
+/// and is therefore fast.
+
+void degamma ( TinyVector<unsigned char,3> & v )
+{
+ static vigra::sRGB2RGBFunctor< unsigned char , unsigned char > fun ;
+ v = fun ( v ) ;
+}
+
+/// functor to convert a pixel from sRGB to linear RGB. This functor
+/// actually performs the calculation. This is used for other pixels.
+
+template < typename value_type , int _max = 255 >
+void to_l_rgb ( value_type& v )
+{
+ typedef typename vigra::ExpandElementResult < value_type > :: type ele_type ;
+ const ele_type max ( _max ) ;
+
+ v /= max ;
+
+ for ( auto & component : v )
+ {
+ component = ( component <= 0.04045 )
+ ? max * component / 12.92
+ : max * pow ( (component + 0.055) / 1.055 , 2.4 ) ;
+ }
+}
+
+#ifdef USE_VC
+
+/// functor to convert a vector of single values to linear RGB
+
+template < typename entry_type , int _max = 255 >
+void v_to_l_rgb ( Vc::Vector < entry_type > & v )
+{
+ const entry_type max ( _max ) ;
+
+ v *= entry_type ( 1.0 / max ) ;
+
+ auto mask = ( v <= entry_type ( 0.04045 ) ) ;
+ v ( mask ) *= entry_type ( max / 12.92 ) ;
+ v ( ! mask ) = entry_type ( max )
+ * exp ( entry_type ( 2.4 )
+ * log ( ( v + entry_type ( 0.055 ) ) * entry_type ( 1.0 / 1.055 ) )
+ ) ;
+
+// ouch... Vc does not provide pow(), so we use
+// pow(x,y) = exp^(y*lnx)
+
+// would be nice:
+// * pow ( ( v + entry_type ( 0.055 ) ) * entry_type ( 1.0 / 1.055 ) ,
+// entry_type ( 2.4 ) ) ;
+}
+
+#endif
+
+/// process_image is the workhorse routine bringing it all together.
+///
+/// reading the image is reasonably involved, since it tries to cover all sorts of
+/// image input:
+///
+/// - images with 8 bit, 16 bit and real-valued pixels
+/// - images with or without alpha channel
+///
+/// BC code SPHERICAL can not be processed with an implicit scheme, so we have to set
+/// up the b-spline to use an explicit scheme. The difference, from the user side, is
+/// marginal.
+///
+/// Once the spline is ready, the transformation functor is set up with the parameters
+/// passed into main. Finally, tf_remap is called to do the processing.
+///
+/// process_image also handles images with an alpha channel. The alpha channel is also
+/// mapped to the target image (if it's present), but for the alpha channel it only uses
+/// linear interpolation (a b-spline of degree 1).
+
+template < class rc_type >
+void process_image ( char * name ,
+ rc_type roll ,
+ rc_type pitch ,
+ rc_type yaw ,
+ int width ,
+ int height ,
+ rc_type hfov ,
+ int spline_degree )
+{
+ // first we have to read and preprocess the source data. The naive way of
+ // doing this would be to read the image into some buffer and then erect a
+ // b-spline over this buffer. But class b-spline is designed to offer a better
+ // way: we create the b-spline object *first*, then acquire the data straight
+ // into the b-spline object's 'core' area.
+
+ cout << fixed << showpoint ; // << setprecision(32) ;
+
+ vigra::ImageImportInfo imageInfo(name);
+ // print some information
+ std::cout << "Image information:\n";
+ std::cout << " file format: " << imageInfo.getFileType() << std::endl;
+ std::cout << " width: " << imageInfo.width() << std::endl;
+ std::cout << " height: " << imageInfo.height() << std::endl;
+ std::cout << " pixel type: " << imageInfo.getPixelType() << std::endl;
+ std::cout << " color image: ";
+ if (imageInfo.isColor()) std::cout << "yes (";
+ else std::cout << "no (";
+ std::cout << "number of channels: " << imageInfo.numBands() << ")\n";
+
+ int extra_bands = imageInfo.numExtraBands() ;
+
+ if ( extra_bands > 1 )
+ {
+ // are there really images with more than one extra channel?
+ throw not_supported ( "can only process at most one extra channel" ) ;
+ }
+
+ typedef vigra::RGBValue<rc_type,0,1,2> pixel_type;
+ typedef vigra::RGBValue<UInt16,0,1,2> pixel_type_ui16 ;
+ typedef vigra::MultiArray<2, pixel_type> array_type ;
+ typedef vigra::MultiArrayView<2, pixel_type> view_type ;
+ typedef typename view_type::difference_type shape_type ;
+
+ TinyVector < bc_code , 2 > bcv = { PERIODIC , SPHERICAL } ;
+
+ // find out the image's shape
+ shape_type core_shape = imageInfo.shape() ;
+ // create a suitable bspline object
+ bspline < pixel_type , 2 > bspl ( core_shape , spline_degree , bcv , EXPLICIT ) ;
+ // get the view to the core coefficient area (to put the image data there)
+ view_type core = bspl.core ;
+
+ cout << "created bspline object:" << endl << bspl << endl ;
+
+ // now we're being tightfisted with memory. We could just go ahead and create
+ // a b-spline object for the alpha channel no matter if it's actually present, but
+ // instead we only set up the empty shell of the storage needed and only fill it
+ // with life (memory) if we have to:
+
+ // potential storage for alpha channel plus brace, empty for now
+ vigra::MultiArray<2,float> alpha_channel ;
+ // view to alpha channel's core area, empty for now
+ vigra::MultiArrayView<2,float> alpha_view ;
+
+ if ( extra_bands )
+ {
+ // if there is an alpha channel, we want to load it into the core area of an array
+ // which we can use as a container for a degree-1 b-spline. This container array
+ // will be a bit larger than the core shape, due to the boundary conditions used.
+ // we have a convenience function yielding this shape:
+ shape_type alpha_shape = bspline<float,2>::container_size ( core_shape , 1 , bcv ) ;
+ // only if the alpha channel is actually present, we fill the array and the view
+ // above with life:
+ vigra::MultiArray<2,float> target ( alpha_shape ) ;
+ alpha_channel.swap ( target ) ; // swap data into alpha_channel
+ // get a view to it's core area
+ shape_type left_corner = bracer<view_type>::left_corner ( bcv , 1 ) ;
+ alpha_view = alpha_channel.subarray ( left_corner , left_corner + core_shape ) ;
+ }
+
+
+ cout << "reading panorama data... " << endl ;
+
+#ifdef PRINT_ELAPSED
+ std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
+#endif
+
+ auto input_pixel_type = imageInfo.getPixelType() ;
+ auto enum_pixel_type = imageInfo.pixelType() ;
+
+ TinyVector < rc_type , 3 > min_value ;
+ TinyVector < rc_type , 3 > max_value ;
+ int component_max ;
+
+ if ( enum_pixel_type == vigra::ImageImportInfo::UINT8 )
+ {
+ // for UINT8 pixels, we use can degamma with a lookup table, which is less
+ // procise but much faster, but we have to read to a char buffer first:
+
+ typedef vigra::RGBValue<unsigned char,0,1,2> uc_pixel ;
+
+ vigra::MultiArray < 2 , uc_pixel > buffer ( core_shape ) ;
+ typedef vigra::MultiArrayView < 2 , uc_pixel > buffer_view ;
+
+ if ( extra_bands )
+ vigra::importImageAlpha ( imageInfo , buffer , alpha_view ) ;
+ else
+ vigra::importImage ( imageInfo , buffer ) ;
+
+#ifdef PRINT_ELAPSED
+ std::chrono::system_clock::time_point end = std::chrono::system_clock::now();
+ cout << "reading took "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
+ << " ms" << endl ;
+#endif
+
+#ifdef PRINT_ELAPSED
+ start = std::chrono::system_clock::now();
+#endif
+
+ // vectorization doesn't work for unsigned char, but we can multithread:
+
+ divide_and_conquer < buffer_view > :: run ( buffer , degamma ) ;
+
+ // still we need to work on floating point data
+
+ auto it = core.begin() ;
+ for ( auto & pix : buffer )
+ {
+ *it = pix ;
+ ++it ;
+ }
+
+ component_max = 255 ;
+ }
+ else if ( enum_pixel_type == vigra::ImageImportInfo::UINT16 )
+ {
+ // for UINT16, we degamma in floating point. We do this in-place,
+ // and we can use 'divide_and_conquer'
+
+ if ( extra_bands )
+ vigra::importImageAlpha ( imageInfo , core , alpha_view ) ;
+ else
+ vigra::importImage ( imageInfo , core ) ;
+
+#ifdef PRINT_ELAPSED
+ std::chrono::system_clock::time_point end = std::chrono::system_clock::now();
+ cout << "reading took "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
+ << " ms" << endl ;
+#endif
+
+#ifdef PRINT_ELAPSED
+ start = std::chrono::system_clock::now();
+#endif
+
+ // perform conversion from sRGB to linear RGB.
+ // silently assumes input actually is sRGB...
+ // this conversion takes quite a lot of time!
+
+#ifdef USE_VC
+ divide_and_conquer < decltype(core) > :: run ( core , v_to_l_rgb<rc_type,65535> ) ;
+#else
+ divide_and_conquer < decltype(core) > :: run ( core , to_l_rgb<pixel_type,65535> ) ;
+#endif
+ component_max = 65535 ;
+ }
+ else
+ {
+ // whatever pixel type this is, just import, don't degamma
+
+ if ( extra_bands )
+ vigra::importImageAlpha ( imageInfo , core , alpha_view ) ;
+ else
+ vigra::importImage ( imageInfo , core ) ;
+
+#ifdef PRINT_ELAPSED
+ std::chrono::system_clock::time_point end = std::chrono::system_clock::now();
+ cout << "reading took "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
+ << " ms" << endl ;
+#endif
+
+#ifdef PRINT_ELAPSED
+ start = std::chrono::system_clock::now();
+#endif
+
+ }
+
+ // we determine minimum and maximum values for every channel to use saturation
+ // arithmetics further down
+ // TODO: this also looks at transparent pixels...
+
+ for ( int ch = 0 ; ch < 3 ; ch++ )
+ {
+ vigra::FindMinMax<rc_type> minmax; // functor to find range
+ vigra::inspectMultiArray ( core.bindElementChannel(ch) , minmax ) ;
+ min_value[ch] = minmax.min ;
+ max_value[ch] = minmax.max ;
+ cout << "incoming: ch " << ch << " minimum " << minmax.min << " maximum " << minmax.max << endl ;
+ }
+ rc_type rgb_min = min ( min_value ) ;
+ rc_type rgb_max = max ( max_value ) ;
+ cout << "RGB min: " << rgb_min << " RGB max: " << rgb_max << endl ;
+
+#ifdef PRINT_ELAPSED
+ std::chrono::system_clock::time_point end = std::chrono::system_clock::now();
+ cout << "converting to linear RGB took "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
+ << " ms" << endl ;
+#endif
+
+#ifdef PRINT_ELAPSED
+ start = std::chrono::system_clock::now();
+#endif
+
+ cout << "creating b-spline... " << std::flush ;
+
+ // perform prefiltering
+ bspl.prefilter() ;
+
+#ifdef PRINT_ELAPSED
+ end = std::chrono::system_clock::now();
+ cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
+ << " ms" << endl ;
+#endif
+
+ shape_type out_shape = shape_type ( width , height ) ;
+ array_type result ( out_shape ) ;
+ vigra::MultiArray<2,float> alpha_result ;
+
+ // now we set up the transformation functor
+
+ tf_spherical_rectilinear<rc_type> tf_se ( core_shape[0] , // width of the (spherical) panorama
+ core_shape[1] , // height of same
+ hfov , // horizontal field of view of section (in degrees)
+ width , // width of same
+ height , // height of same
+ yaw , // yaw of extracted section
+ pitch , // pitch of same
+ roll ) ; // roll of same
+
+#ifdef USE_VC
+
+ // this looks funny - passing in the same transform twice, both for scalar and for vector
+ // operation? but tf_se is a functor with overloaded operator(), and as the accepted types
+ // of std::function are encoded in vspline::transformation's two-argument constructor's
+ // signature, the correct overload is picked for each case. Note that using the single-argument
+ // constructor would also work here, but result in broadcasting the scalar routine to the vector
+ // data, resulting in slower operation. The one-argument form is only a crutch if vectorized
+ // code can't be had.
+
+ vspline::transformation < pixel_type , rc_type , 2 , 2 >
+ tf ( tf_se , tf_se ) ;
+
+#else
+
+ // ... or if Vc can't be used. In this case only the single-argument constructor
+ // can be used:
+
+ vspline::transformation < pixel_type , rc_type , 2 , 2 >
+ tf ( tf_se ) ;
+
+#endif
+
+ // this usually takes the least amount of time:
+
+ cout << "creating target image... " << std::flush ;
+
+#ifdef PRINT_ELAPSED
+ start = std::chrono::system_clock::now();
+#endif
+
+ for ( int times = 0 ; times < 1 ; times++ )
+ {
+ vspline::tf_remap < pixel_type , rc_type , 2 , 2 >
+ ( bspl , tf , result ) ;
+ }
+
+ if ( extra_bands )
+ {
+ // create a b-spline over the alpha channel, using the data in alpha_channel.
+ // this way we have the alpha channel data from the image in alpha_channel's core
+ // area, and the only thing left to do is the prefiltering, which amounts to
+ // bracing the data, since we're only using a degree 1 spline (linear interpolation)
+ // on the alpha channel.
+
+ bspline < float , 2 > bspl_alpha ( core_shape , 1 , bcv , BRACED , -1 , alpha_channel ) ;
+ bspl_alpha.prefilter() ;
+
+ // swap data into alpha_result (fill it with life, it was created empty)
+ vigra::MultiArray<2,float> target ( out_shape ) ;
+ alpha_result.swap ( target ) ;
+
+ // we need a slightly different transformation here, working on single floats
+ // instead of pixels of three RGB values
+
+#ifdef USE_VC
+ vspline::transformation < float , rc_type , 2 , 2 >
+ tf_alpha ( tf_se , tf_se ) ;
+#else
+ vspline::transformation < float , rc_type , 2 , 2 >
+ tf_alpha ( tf_se ) ;
+#endif
+
+ // now we do a transformation-based remap of the alpha channel
+ vspline::tf_remap < float , rc_type , 2 , 2 >
+ ( bspl_alpha , tf_alpha , alpha_result ) ;
+ }
+
+#ifdef PRINT_ELAPSED
+ end = std::chrono::system_clock::now();
+ cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
+ << " ms" << endl ;
+#endif
+
+ // b-splines may produce overshoots, especially if the input data have
+ // numerical flaws, like sharp discontinuities. so we cut off any values
+ // exceeding the original image's extremal values (saturation arithmetics)
+
+ for ( int ch = 0 ; ch < 3 ; ch++ )
+ {
+ vigra::FindMinMax<rc_type> minmax; // functor to find range
+ vigra::inspectMultiArray ( result.bindElementChannel(ch) , minmax ) ;
+ cout << "result: ch " << ch << " minimum " << minmax.min << " maximum " << minmax.max << endl ;
+ cout << "values outside incoming range (overshoot) will be clipped" << endl ;
+ for ( auto& pix : result.bindElementChannel(ch) )
+ {
+ if ( pix > max_value[ch] )
+ pix = max_value[ch] ;
+ if ( pix < min_value[ch] )
+ pix = min_value[ch] ;
+ }
+ }
+
+ // next we convert back from linear RGB to sRGB using vigra's functor for convenience.
+ // TODO: for now, this is neither multithreaded nor vectorized...
+
+ if ( enum_pixel_type == vigra::ImageImportInfo::UINT8
+ || enum_pixel_type == vigra::ImageImportInfo::UINT16 )
+ {
+ vigra::RGB2sRGBFunctor < rc_type , rc_type > to_gamma ( component_max ) ;
+ for ( auto & pix : result )
+ pix = to_gamma ( pix ) ;
+ }
+ // and export with a forced range mapping to avoid vigra's automatic mapping of the
+ // brightness values to 0...max.
+ // TODO .setForcedRangeMapping is not in vigra documentation
+
+ cout << "saving result to extract.tif" << endl ;
+
+ if ( extra_bands )
+ vigra::exportImageAlpha ( result ,
+ alpha_result ,
+ vigra::ImageExportInfo ( "extract.tif" )
+ .setPixelType(input_pixel_type)
+ .setCompression("DEFLATE")
+ .setForcedRangeMapping ( rgb_min , rgb_max , rgb_min , rgb_max ) );
+ else
+ vigra::exportImage ( result ,
+ vigra::ImageExportInfo ( "extract.tif" )
+ .setPixelType(input_pixel_type)
+ .setCompression("DEFLATE")
+ .setForcedRangeMapping ( rgb_min , rgb_max , rgb_min , rgb_max ) );
+}
+
+/// main only takes the parameters and passes them on to process_image.
+/// Admittedly the UI is a bit spartan.
+
+int main ( int argc , char * argv[] )
+{
+ if ( argc < 9 )
+ {
+ cout << std::string ( argv[0] ) << " - extract rectilinear section from spherical image" << endl ;
+ cout << "parameters: spherical image, roll, pitch, yaw," << endl ;
+ cout << "rectilinear image width, height, horizontal field of view, spline degree" << endl ;
+ cout << "all angles in degrees" << endl ;
+ cout << "example: pano_extract pano.tif 0.0 -90.0 0.0 2000 2000 60.0 3" << endl ;
+ cout << "creates a 60 degree wide nadir image from pano.tif" << endl ;
+ cout << "output is written to extract.tif" << endl ;
+ cout << "It's assumed images are sRGB if pixels are UCHAR8 or UCHAR16" << endl ;
+ cout << "or linear RGB if otherwise. Monochrome is not supported." << endl ;
+ cout << "alpha channel will be honoured if present." << endl ;
+ return 1 ;
+ }
+
+ cout << fixed << showpoint ;
+
+ float r = atof ( argv[2] ) ;
+ float p = atof ( argv[3] ) ;
+ float y = atof ( argv[4] ) ;
+ int w = atoi ( argv[5] ) ;
+ int h = atoi ( argv[6] ) ;
+ float hfov = atof ( argv[7] ) ;
+ int d = atoi ( argv[8] ) ;
+
+ cout << "image " << std::string(argv[1]) << endl ;
+ cout << "roll " << r << " pitch " << p << " yaw " << y << endl ;
+ cout << "output width: " << w << " height: " << h << endl ;
+ cout << "output hfov: " << hfov << endl ;
+ cout << "spline degree: " << d << endl ;
+
+ process_image<float> ( argv[1] , r , p , y , w , h , hfov , d ) ;
+
+ return 0 ;
+}
+
+
+
diff --git a/example/roundtrip.cc b/example/roundtrip.cc
new file mode 100644
index 0000000..e0e60a0
--- /dev/null
+++ b/example/roundtrip.cc
@@ -0,0 +1,405 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/// roundtrip.cc
+///
+/// load an image, create a b-spline from it, and restore the original data,
+/// both by normal evaluation and by convolution with the restoration kernel.
+/// all of this is done 100 times each with different boundary conditions,
+/// spline degrees and in float and double arothmetic, the processing times
+/// and differences between input and restored signal are printed to cout.
+///
+/// compile with:
+/// g++ -std=c++11 -o roundtrip -O3 -pthread -DUSE_VC=1 roundtrip.cc -lvigraimpex -lVc
+
+#include <vspline/vspline.h>
+
+#include <vigra/stdimage.hxx>
+#include <vigra/imageinfo.hxx>
+#include <vigra/impex.hxx>
+#include <vigra/accumulator.hxx>
+#include <vigra/multi_math.hxx>
+
+#define PRINT_ELAPSED
+
+#ifdef PRINT_ELAPSED
+#include <ctime>
+#include <chrono>
+#endif
+
+using namespace std ;
+using namespace vigra ;
+using namespace vigra::acc;
+using namespace vigra::multi_math;
+
+/// we use identity transformations on coordinates. Incoming coordinates will be
+/// translated straight to outgoing coordinates. If we use such a transformation
+/// we'd expect to recover the input.
+
+template < class rc_type > // float or double for coordinates
+void tf_identity ( const TinyVector < rc_type , 2 > & c_in ,
+ TinyVector < rc_type , 2 > & c_out )
+{
+ c_out = c_in ;
+}
+
+#ifdef USE_VC
+
+template < class rc_v >
+void vtf_identity ( const TinyVector < rc_v , 2 > & c_in ,
+ TinyVector < rc_v , 2 > & c_out )
+{
+ c_out = c_in ;
+}
+
+#endif
+
+template < class array_type , typename real_type >
+void check_diff ( const array_type v1 )
+{
+// array_type v1 = ( target - data ) ;
+ typedef vigra::MultiArray<2,real_type> error_array ;
+ error_array ea ( vigra::multi_math::squaredNorm ( v1 ) ) ;
+ AccumulatorChain<real_type,Select< Mean, Maximum> > ac ;
+ extractFeatures(ea.begin(), ea.end(), ac);
+ std::cout << "warped image diff Mean: " << sqrt(get<Mean>(ac)) << std::endl;
+ std::cout << "warped image diff Maximum: " << sqrt(get<Maximum>(ac)) << std::endl;
+}
+
+template < class view_type , typename real_type , typename rc_type >
+void roundtrip ( view_type & data ,
+ vspline::bc_code bc ,
+ int DEGREE ,
+ bool use_vc ,
+ int TIMES = 10 )
+{
+ typedef typename view_type::value_type pixel_type ;
+ typedef typename view_type::difference_type Shape;
+ typedef MultiArray < 2 , pixel_type > array_type ;
+ typedef int int_type ;
+
+#ifdef USE_VC
+
+ const int vsize = Vc::Vector < real_type > :: Size ;
+
+#else
+
+ const int vsize = 1 ;
+
+#endif
+
+ TinyVector < vspline::bc_code , 2 > bcv ( bc ) ;
+
+ int Nx = data.width() ;
+ int Ny = data.height() ;
+// cout << "Nx: " << Nx << " Ny: " << Ny << endl ;
+
+ vspline::bspline < pixel_type , 2 > bsp ( data.shape() , DEGREE , bcv ) ; // , vspline::EXPLICIT ) ;
+ bsp.core = data ;
+// cout << "created bspline object:" << endl << bsp << endl ;
+
+#ifdef PRINT_ELAPSED
+ std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
+#endif
+
+ for ( int times = 0 ; times < TIMES - 1 ; times++ )
+ bsp.prefilter ( use_vc ) ;
+
+ bsp.core = data ;
+ bsp.prefilter ( use_vc ) ;
+
+#ifdef PRINT_ELAPSED
+ std::chrono::system_clock::time_point end = std::chrono::system_clock::now();
+ cout << "avg 10 x prefilter:........................ "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() / TIMES
+ << " ms" << endl ;
+#endif
+
+ // get a view to the core coefficients (those which aren't part of the brace)
+ view_type cfview = bsp.core ;
+
+ // create an evaluator
+ typedef vspline::evaluator<2,pixel_type,rc_type,int_type> eval_type ;
+
+ // create the evaluator
+ eval_type ev ( bsp ) ;
+
+ // get the coordinate and split coordinate types from the evaluator
+ typedef typename eval_type::nd_rc_type coordinate_type ;
+ typedef typename eval_type::split_coordinate_type split_type ;
+
+ typedef vspline::nd_mapping < split_type , 2 , vsize > mmap ;
+
+ // obtain the mapping from the evaluator:
+ mmap map = ev.get_mapping() ;
+
+ typedef vigra::MultiArray<2, coordinate_type> coordinate_array ;
+
+ typedef vigra::MultiArray<2, split_type> warp_array ;
+ typedef vigra::MultiArrayView<2, split_type> warp_view ;
+
+ int Tx = Nx ;
+ int Ty = Ny ;
+
+ // now we create a warp array of coordinates at which the spline will be evaluated.
+ // We create two versions of the warp array: one storing ordinary real coordinates
+ // (fwarp) and the other storing pre-split coordinates. Also create a target array
+ // to contain the result.
+
+ coordinate_array fwarp ( Shape(Tx,Ty) ) ;
+ warp_array _warp ( Shape(Tx+5,Ty+4) ) ;
+ warp_view warp = _warp.subarray ( Shape(2,1) , Shape(-3,-3) ) ;
+ array_type target ( Shape(Tx,Ty) ) ;
+ rc_type dfx = 0.0 , dfy = 0.0 ;
+// if ( bcv[0] == vspline::REFLECT )
+// dfx = .5 ;
+// if ( bcv[1] == vspline::REFLECT )
+// dfy = .5 ;
+
+ for ( int times = 0 ; times < 1 ; times++ )
+ {
+ for ( int y = 0 ; y < Ty ; y++ )
+ {
+ rc_type fy = (rc_type)(y) + dfy ;
+ for ( int x = 0 ; x < Tx ; x++ )
+ {
+ rc_type fx = (rc_type)(x) + dfx ;
+ // store the 'ordinary' cordinate to fwarp[x,y]
+ fwarp [ Shape ( x , y ) ] = coordinate_type ( fx , fy ) ;
+ // and apply the mapping to (fx, fy), storing the result to warp[x,y]
+ map ( fx , warp [ Shape ( x , y ) ] , 0 ) ;
+ map ( fy , warp [ Shape ( x , y ) ] , 1 ) ;
+ }
+ }
+ }
+
+// coordinate_type c = { 1.0 , 1.0 } ;
+// pixel_type px = ev ( c ) ;
+// cout << c << " -> " << px << endl ;
+// coordinate_type c1 = { -1.0 , -1.0 } ;
+// pixel_type px1 = ev ( c1 ) ;
+// cout << c1 << " -> " << px1 << endl ;
+
+#ifdef PRINT_ELAPSED
+ start = std::chrono::system_clock::now();
+#endif
+
+ for ( int times = 0 ; times < TIMES ; times++ )
+ vspline::remap<pixel_type,split_type,2,2>
+ ( bsp , warp , target , use_vc ) ;
+
+
+#ifdef PRINT_ELAPSED
+ end = std::chrono::system_clock::now();
+ cout << "avg 10 x remap1 from pre-split coordinates: "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() / 10.0
+ << " ms" << endl ;
+#endif
+
+ check_diff<array_type,real_type> ( target - data ) ;
+
+#ifdef PRINT_ELAPSED
+ start = std::chrono::system_clock::now();
+#endif
+
+ for ( int times = 0 ; times < TIMES ; times++ )
+ vspline::remap<pixel_type,coordinate_type,2,2>
+ ( bsp , fwarp , target , use_vc ) ;
+
+
+#ifdef PRINT_ELAPSED
+ end = std::chrono::system_clock::now();
+ cout << "avg 10 x remap1 from unsplit coordinates:.. "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() / 10.0
+ << " ms" << endl ;
+#endif
+
+ check_diff<array_type,real_type> ( target - data ) ;
+
+#ifdef PRINT_ELAPSED
+ start = std::chrono::system_clock::now();
+#endif
+
+ for ( int times = 0 ; times < TIMES ; times++ )
+ vspline::remap<pixel_type,coordinate_type,2,2>
+ ( data , fwarp , target , bcv , DEGREE , use_vc ) ;
+
+
+#ifdef PRINT_ELAPSED
+ end = std::chrono::system_clock::now();
+ cout << "avg 10 x remap with internal spline:....... "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() / 10.0
+ << " ms" << endl ;
+#endif
+
+ check_diff<array_type,real_type> ( target - data ) ;
+
+#ifdef USE_VC
+
+ vspline::transformation < pixel_type , rc_type , 2 , 2 >
+ tf ( tf_identity<rc_type> ) ; // , vtf_identity<rc_v> ) ;
+
+#else
+
+ // using this call when USE_VC is defined will result in broadcasting
+ // of the single-element coordinate transform. The effect is the same,
+ // but the code is potentially slower.
+
+ vspline::transformation < pixel_type , rc_type , 2 , 2 >
+ tf ( tf_identity<rc_type> ) ;
+
+#endif
+
+#ifdef PRINT_ELAPSED
+ start = std::chrono::system_clock::now();
+#endif
+
+ for ( int times = 0 ; times < TIMES ; times++ )
+ vspline::tf_remap < pixel_type , rc_type , 2 , 2 >
+ ( data , tf , target , bcv , DEGREE , use_vc ) ;
+
+
+ // note:: with REFLECT this doesn't come out right, because of the .5 difference!
+
+#ifdef PRINT_ELAPSED
+ end = std::chrono::system_clock::now();
+ cout << "avg 10 x remap with functor & internal bspl "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() / 10.0
+ << " ms" << endl ;
+#endif
+
+#ifdef PRINT_ELAPSED
+ start = std::chrono::system_clock::now();
+#endif
+
+ for ( int times = 0 ; times < TIMES ; times++ )
+ vspline::tf_remap < pixel_type , rc_type , 2 , 2 >
+ ( bsp , tf , target , use_vc ) ;
+
+ // note:: with REFLECT this doesn't come out right, because of the .5 difference!
+
+#ifdef PRINT_ELAPSED
+ end = std::chrono::system_clock::now();
+ cout << "avg 10 x remap with functor & external bspl "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() / 10.0
+ << " ms" << endl ;
+#endif
+
+ check_diff < array_type , real_type > ( target - data ) ;
+
+ cout << "difference original data/restored data:" << endl ;
+ vspline::restore_from_braced<view_type> ( bsp.coeffs , bsp.coeffs , DEGREE ) ;
+ array_type diff ( cfview - data ) ;
+ check_diff<view_type,real_type> ( diff ) ;
+ cout << endl ;
+}
+
+template < class real_type , class rc_type >
+void process_image ( char * name )
+{
+ cout << fixed << showpoint ; // << setprecision(32) ;
+
+ // the import and info-displaying code is taken from vigra:
+
+ vigra::ImageImportInfo imageInfo(name);
+ // print some information
+ std::cout << "Image information:\n";
+ std::cout << " file format: " << imageInfo.getFileType() << std::endl;
+ std::cout << " width: " << imageInfo.width() << std::endl;
+ std::cout << " height: " << imageInfo.height() << std::endl;
+ std::cout << " pixel type: " << imageInfo.getPixelType() << std::endl;
+ std::cout << " color image: ";
+ if (imageInfo.isColor()) std::cout << "yes (";
+ else std::cout << "no (";
+ std::cout << "number of channels: " << imageInfo.numBands() << ")\n";
+
+ typedef vigra::RGBValue<real_type,0,1,2> pixel_type;
+ typedef vigra::MultiArray<2, pixel_type> array_type ;
+ typedef vigra::MultiArrayView<2, pixel_type> view_type ;
+
+// // to test that strided data are processed correctly, we load the image
+// // to an inner subarray of containArray
+//
+// array_type containArray(imageInfo.shape()+vigra::Shape2(3,5));
+// view_type imageArray = containArray.subarray(vigra::Shape2(1,2),vigra::Shape2(-2,-3)) ;
+
+ array_type containArray ( imageInfo.shape() );
+ view_type imageArray ( containArray ) ;
+
+ vigra::importImage(imageInfo, imageArray);
+
+ // test these bc codes:
+ vspline::bc_code bcs[] = { vspline::MIRROR , vspline::REFLECT , vspline::NATURAL , vspline::PERIODIC } ;
+
+ for ( int b = 0 ; b < 4 ; b++ )
+ {
+ vspline::bc_code bc = bcs[b] ;
+ for ( int spline_degree = 0 ; spline_degree < 8 ; spline_degree++ )
+ {
+ cout << "testing bc code " << vspline::bc_name[bc]
+ << " spline degree " << spline_degree << endl ;
+ roundtrip < view_type , real_type , rc_type > ( imageArray , bc , spline_degree , false ) ;
+
+ cout << "testing bc code " << vspline::bc_name[bc]
+ << " spline degree " << spline_degree << " using Vc" << endl ;
+ roundtrip < view_type , real_type , rc_type > ( imageArray , bc , spline_degree , true ) ;
+ }
+ }
+}
+
+int main ( int argc , char * argv[] )
+{
+ cout << fixed << showpoint ;
+
+// double coordinates work as well, but currently I am getting an annoying warning:
+
+// /usr/local/include/vigra/tinyvector.hxx: In static member function ‘static void vigra::detail
+// ::UnrollLoop<LEVEL>::assignScalar(T1*, T2) [with T1 = Vc_1::SimdArray<double, 8ul, Vc_1::Vect
+// or<double, Vc_1::VectorAbi::Avx>, 4ul>; T2 = Vc_1::SimdArray<double, 8ul, Vc_1::Vector<double
+// , Vc_1::VectorAbi::Avx>, 4ul>; int LEVEL = 2]’:
+// /usr/local/include/vigra/tinyvector.hxx:428:17: note: The ABI for passing parameters with 64-
+// byte alignment has changed in GCC 4.6
+// static void assignScalar(T1 * left, T2 right)
+
+// so I leave the test with double coordinates commented out for now:
+
+ cout << "testing float data, float coordinates" << endl ;
+ process_image<float,float> ( argv[1] ) ;
+
+ cout << endl << "testing double data, double coordinates" << endl ;
+ process_image<double,double> ( argv[1] ) ;
+
+ cout << "testing float data, double coordinates" << endl ;
+ process_image<float,double> ( argv[1] ) ;
+
+ cout << endl << "testing double data, float coordinates" << endl ;
+ process_image<double,float> ( argv[1] ) ;
+}
diff --git a/example/times.txt b/example/times.txt
new file mode 100644
index 0000000..52ffd3e
--- /dev/null
+++ b/example/times.txt
@@ -0,0 +1,5502 @@
+testing float data, float coordinates
+Image information:
+ file format: JPEG
+ width: 1920
+ height: 1079
+ pixel type: UINT8
+ color image: yes (number of channels: 3)
+testing bc code MIRROR spline degree 0
+avg 10 x prefilter:........................ 4.700000 ms
+avg 10 x remap1 from pre-split coordinates: 15.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 26.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 29.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 32.400000 ms
+avg 10 x remap with functor & external bspl 23.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 0 using Vc
+avg 10 x prefilter:........................ 5.200000 ms
+avg 10 x remap1 from pre-split coordinates: 6.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 6.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 14.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 15.600000 ms
+avg 10 x remap with functor & external bspl 6.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 1
+avg 10 x prefilter:........................ 4.500000 ms
+avg 10 x remap1 from pre-split coordinates: 28.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 34.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 41.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 43.400000 ms
+avg 10 x remap with functor & external bspl 37.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 1 using Vc
+avg 10 x prefilter:........................ 4.900000 ms
+avg 10 x remap1 from pre-split coordinates: 9.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 11.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 18.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 17.600000 ms
+avg 10 x remap with functor & external bspl 9.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 2
+avg 10 x prefilter:........................ 14.500000 ms
+avg 10 x remap1 from pre-split coordinates: 47.000000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap1 from unsplit coordinates:.. 54.000000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with internal spline:....... 75.700000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with functor & internal bspl 71.700000 ms
+avg 10 x remap with functor & external bspl 55.000000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000070
+
+testing bc code MIRROR spline degree 2 using Vc
+avg 10 x prefilter:........................ 10.400000 ms
+avg 10 x remap1 from pre-split coordinates: 14.100000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap1 from unsplit coordinates:.. 15.400000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with internal spline:....... 27.300000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with functor & internal bspl 28.400000 ms
+avg 10 x remap with functor & external bspl 14.500000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000070
+
+testing bc code MIRROR spline degree 3
+avg 10 x prefilter:........................ 14.500000 ms
+avg 10 x remap1 from pre-split coordinates: 73.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+avg 10 x remap1 from unsplit coordinates:.. 82.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+avg 10 x remap with internal spline:....... 99.900000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+avg 10 x remap with functor & internal bspl 99.000000 ms
+avg 10 x remap with functor & external bspl 86.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000117
+
+testing bc code MIRROR spline degree 3 using Vc
+avg 10 x prefilter:........................ 10.200000 ms
+avg 10 x remap1 from pre-split coordinates: 21.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+avg 10 x remap1 from unsplit coordinates:.. 23.100000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+avg 10 x remap with internal spline:....... 34.300000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+avg 10 x remap with functor & internal bspl 34.800000 ms
+avg 10 x remap with functor & external bspl 22.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000117
+
+testing bc code MIRROR spline degree 4
+avg 10 x prefilter:........................ 23.800000 ms
+avg 10 x remap1 from pre-split coordinates: 109.600000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap1 from unsplit coordinates:.. 116.100000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with internal spline:....... 145.000000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with functor & internal bspl 147.200000 ms
+avg 10 x remap with functor & external bspl 116.800000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+
+testing bc code MIRROR spline degree 4 using Vc
+avg 10 x prefilter:........................ 12.800000 ms
+avg 10 x remap1 from pre-split coordinates: 30.400000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap1 from unsplit coordinates:.. 32.600000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with internal spline:....... 45.500000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with functor & internal bspl 45.100000 ms
+avg 10 x remap with functor & external bspl 31.300000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+
+testing bc code MIRROR spline degree 5
+avg 10 x prefilter:........................ 24.600000 ms
+avg 10 x remap1 from pre-split coordinates: 152.300000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+avg 10 x remap1 from unsplit coordinates:.. 161.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+avg 10 x remap with internal spline:....... 182.300000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+avg 10 x remap with functor & internal bspl 185.000000 ms
+avg 10 x remap with functor & external bspl 158.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000101
+
+testing bc code MIRROR spline degree 5 using Vc
+avg 10 x prefilter:........................ 12.800000 ms
+avg 10 x remap1 from pre-split coordinates: 41.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+avg 10 x remap1 from unsplit coordinates:.. 40.900000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+avg 10 x remap with internal spline:....... 68.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+avg 10 x remap with functor & internal bspl 56.800000 ms
+avg 10 x remap with functor & external bspl 42.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000101
+
+testing bc code MIRROR spline degree 6
+avg 10 x prefilter:........................ 36.600000 ms
+avg 10 x remap1 from pre-split coordinates: 197.300000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+avg 10 x remap1 from unsplit coordinates:.. 207.400000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+avg 10 x remap with internal spline:....... 246.700000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+avg 10 x remap with functor & internal bspl 243.800000 ms
+avg 10 x remap with functor & external bspl 206.700000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000111
+
+testing bc code MIRROR spline degree 6 using Vc
+avg 10 x prefilter:........................ 12.900000 ms
+avg 10 x remap1 from pre-split coordinates: 58.200000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+avg 10 x remap1 from unsplit coordinates:.. 59.600000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+avg 10 x remap with internal spline:....... 69.700000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+avg 10 x remap with functor & internal bspl 73.100000 ms
+avg 10 x remap with functor & external bspl 55.500000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000111
+
+testing bc code MIRROR spline degree 7
+avg 10 x prefilter:........................ 36.800000 ms
+avg 10 x remap1 from pre-split coordinates: 252.800000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000123
+avg 10 x remap1 from unsplit coordinates:.. 266.200000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000123
+avg 10 x remap with internal spline:....... 298.600000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000123
+avg 10 x remap with functor & internal bspl 302.100000 ms
+avg 10 x remap with functor & external bspl 269.100000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000123
+difference original data/restored data:
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000112
+
+testing bc code MIRROR spline degree 7 using Vc
+avg 10 x prefilter:........................ 13.600000 ms
+avg 10 x remap1 from pre-split coordinates: 71.400000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000127
+avg 10 x remap1 from unsplit coordinates:.. 70.800000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000127
+avg 10 x remap with internal spline:....... 87.500000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000127
+avg 10 x remap with functor & internal bspl 87.700000 ms
+avg 10 x remap with functor & external bspl 70.300000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000127
+difference original data/restored data:
+warped image diff Mean: 0.000024
+warped image diff Maximum: 0.000142
+
+testing bc code MIRROR spline degree 8
+avg 10 x prefilter:........................ 48.300000 ms
+avg 10 x remap1 from pre-split coordinates: 325.000000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+avg 10 x remap1 from unsplit coordinates:.. 323.900000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+avg 10 x remap with internal spline:....... 378.000000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+avg 10 x remap with functor & internal bspl 375.200000 ms
+avg 10 x remap with functor & external bspl 332.900000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000135
+
+testing bc code MIRROR spline degree 8 using Vc
+avg 10 x prefilter:........................ 16.100000 ms
+avg 10 x remap1 from pre-split coordinates: 89.800000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+avg 10 x remap1 from unsplit coordinates:.. 90.300000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+avg 10 x remap with internal spline:....... 109.200000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+avg 10 x remap with functor & internal bspl 114.800000 ms
+avg 10 x remap with functor & external bspl 93.300000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000135
+
+testing bc code REFLECT spline degree 0
+avg 10 x prefilter:........................ 4.400000 ms
+avg 10 x remap1 from pre-split coordinates: 16.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 21.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 29.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 30.300000 ms
+avg 10 x remap with functor & external bspl 23.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 0 using Vc
+avg 10 x prefilter:........................ 4.800000 ms
+avg 10 x remap1 from pre-split coordinates: 6.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 7.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 15.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 13.800000 ms
+avg 10 x remap with functor & external bspl 7.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 1
+avg 10 x prefilter:........................ 4.500000 ms
+avg 10 x remap1 from pre-split coordinates: 28.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 37.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 42.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 44.400000 ms
+avg 10 x remap with functor & external bspl 37.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 1 using Vc
+avg 10 x prefilter:........................ 5.300000 ms
+avg 10 x remap1 from pre-split coordinates: 9.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 10.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 18.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 19.000000 ms
+avg 10 x remap with functor & external bspl 9.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 2
+avg 10 x prefilter:........................ 14.700000 ms
+avg 10 x remap1 from pre-split coordinates: 47.400000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+avg 10 x remap1 from unsplit coordinates:.. 58.100000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+avg 10 x remap with internal spline:....... 69.000000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+avg 10 x remap with functor & internal bspl 71.200000 ms
+avg 10 x remap with functor & external bspl 55.100000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000078
+
+testing bc code REFLECT spline degree 2 using Vc
+avg 10 x prefilter:........................ 10.100000 ms
+avg 10 x remap1 from pre-split coordinates: 14.500000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+avg 10 x remap1 from unsplit coordinates:.. 14.600000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+avg 10 x remap with internal spline:....... 27.500000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+avg 10 x remap with functor & internal bspl 27.300000 ms
+avg 10 x remap with functor & external bspl 14.500000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000078
+
+testing bc code REFLECT spline degree 3
+avg 10 x prefilter:........................ 15.300000 ms
+avg 10 x remap1 from pre-split coordinates: 73.300000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+avg 10 x remap1 from unsplit coordinates:.. 83.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+avg 10 x remap with internal spline:....... 97.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+avg 10 x remap with functor & internal bspl 104.200000 ms
+avg 10 x remap with functor & external bspl 82.900000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000135
+
+testing bc code REFLECT spline degree 3 using Vc
+avg 10 x prefilter:........................ 9.800000 ms
+avg 10 x remap1 from pre-split coordinates: 22.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+avg 10 x remap1 from unsplit coordinates:.. 21.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+avg 10 x remap with internal spline:....... 36.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+avg 10 x remap with functor & internal bspl 35.100000 ms
+avg 10 x remap with functor & external bspl 21.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000135
+
+testing bc code REFLECT spline degree 4
+avg 10 x prefilter:........................ 25.500000 ms
+avg 10 x remap1 from pre-split coordinates: 106.800000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+avg 10 x remap1 from unsplit coordinates:.. 116.400000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+avg 10 x remap with internal spline:....... 142.100000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+avg 10 x remap with functor & internal bspl 146.500000 ms
+avg 10 x remap with functor & external bspl 116.900000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000147
+
+testing bc code REFLECT spline degree 4 using Vc
+avg 10 x prefilter:........................ 11.600000 ms
+avg 10 x remap1 from pre-split coordinates: 32.100000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+avg 10 x remap1 from unsplit coordinates:.. 31.200000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+avg 10 x remap with internal spline:....... 45.600000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+avg 10 x remap with functor & internal bspl 45.800000 ms
+avg 10 x remap with functor & external bspl 31.600000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000147
+
+testing bc code REFLECT spline degree 5
+avg 10 x prefilter:........................ 25.100000 ms
+avg 10 x remap1 from pre-split coordinates: 151.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+avg 10 x remap1 from unsplit coordinates:.. 161.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+avg 10 x remap with internal spline:....... 183.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+avg 10 x remap with functor & internal bspl 185.800000 ms
+avg 10 x remap with functor & external bspl 163.100000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000129
+
+testing bc code REFLECT spline degree 5 using Vc
+avg 10 x prefilter:........................ 11.500000 ms
+avg 10 x remap1 from pre-split coordinates: 41.900000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+avg 10 x remap1 from unsplit coordinates:.. 47.900000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+avg 10 x remap with internal spline:....... 56.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+avg 10 x remap with functor & internal bspl 55.700000 ms
+avg 10 x remap with functor & external bspl 42.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000129
+
+testing bc code REFLECT spline degree 6
+avg 10 x prefilter:........................ 36.100000 ms
+avg 10 x remap1 from pre-split coordinates: 198.100000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+avg 10 x remap1 from unsplit coordinates:.. 206.300000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+avg 10 x remap with internal spline:....... 250.000000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+avg 10 x remap with functor & internal bspl 245.200000 ms
+avg 10 x remap with functor & external bspl 207.200000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000163
+
+testing bc code REFLECT spline degree 6 using Vc
+avg 10 x prefilter:........................ 13.700000 ms
+avg 10 x remap1 from pre-split coordinates: 58.900000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+avg 10 x remap1 from unsplit coordinates:.. 62.400000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+avg 10 x remap with internal spline:....... 73.900000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+avg 10 x remap with functor & internal bspl 71.000000 ms
+avg 10 x remap with functor & external bspl 55.000000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000163
+
+testing bc code REFLECT spline degree 7
+avg 10 x prefilter:........................ 36.400000 ms
+avg 10 x remap1 from pre-split coordinates: 254.800000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000154
+avg 10 x remap1 from unsplit coordinates:.. 267.700000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000154
+avg 10 x remap with internal spline:....... 299.200000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000154
+avg 10 x remap with functor & internal bspl 310.000000 ms
+avg 10 x remap with functor & external bspl 260.800000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000154
+difference original data/restored data:
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000165
+
+testing bc code REFLECT spline degree 7 using Vc
+avg 10 x prefilter:........................ 13.600000 ms
+avg 10 x remap1 from pre-split coordinates: 71.900000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000197
+avg 10 x remap1 from unsplit coordinates:.. 72.800000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000197
+avg 10 x remap with internal spline:....... 91.300000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000197
+avg 10 x remap with functor & internal bspl 86.000000 ms
+avg 10 x remap with functor & external bspl 72.900000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000197
+difference original data/restored data:
+warped image diff Mean: 0.000024
+warped image diff Maximum: 0.000201
+
+testing bc code REFLECT spline degree 8
+avg 10 x prefilter:........................ 48.800000 ms
+avg 10 x remap1 from pre-split coordinates: 316.200000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+avg 10 x remap1 from unsplit coordinates:.. 324.300000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+avg 10 x remap with internal spline:....... 381.200000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+avg 10 x remap with functor & internal bspl 378.700000 ms
+avg 10 x remap with functor & external bspl 323.900000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000179
+
+testing bc code REFLECT spline degree 8 using Vc
+avg 10 x prefilter:........................ 15.400000 ms
+avg 10 x remap1 from pre-split coordinates: 89.900000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+avg 10 x remap1 from unsplit coordinates:.. 92.000000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+avg 10 x remap with internal spline:....... 106.500000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+avg 10 x remap with functor & internal bspl 112.100000 ms
+avg 10 x remap with functor & external bspl 89.800000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000179
+
+testing bc code PERIODIC spline degree 0
+avg 10 x prefilter:........................ 4.800000 ms
+avg 10 x remap1 from pre-split coordinates: 15.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 21.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 29.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 31.600000 ms
+avg 10 x remap with functor & external bspl 23.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 0 using Vc
+avg 10 x prefilter:........................ 5.000000 ms
+avg 10 x remap1 from pre-split coordinates: 7.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 7.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 14.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 14.800000 ms
+avg 10 x remap with functor & external bspl 7.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 1
+avg 10 x prefilter:........................ 4.400000 ms
+avg 10 x remap1 from pre-split coordinates: 27.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 34.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 42.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 45.800000 ms
+avg 10 x remap with functor & external bspl 35.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 1 using Vc
+avg 10 x prefilter:........................ 5.000000 ms
+avg 10 x remap1 from pre-split coordinates: 9.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 10.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 17.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 17.700000 ms
+avg 10 x remap with functor & external bspl 9.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 2
+avg 10 x prefilter:........................ 15.300000 ms
+avg 10 x remap1 from pre-split coordinates: 52.000000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+avg 10 x remap1 from unsplit coordinates:.. 52.700000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+avg 10 x remap with internal spline:....... 71.100000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+avg 10 x remap with functor & internal bspl 71.800000 ms
+avg 10 x remap with functor & external bspl 55.400000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000076
+
+testing bc code PERIODIC spline degree 2 using Vc
+avg 10 x prefilter:........................ 10.300000 ms
+avg 10 x remap1 from pre-split coordinates: 13.900000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+avg 10 x remap1 from unsplit coordinates:.. 14.400000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+avg 10 x remap with internal spline:....... 28.100000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+avg 10 x remap with functor & internal bspl 27.600000 ms
+avg 10 x remap with functor & external bspl 16.400000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000076
+
+testing bc code PERIODIC spline degree 3
+avg 10 x prefilter:........................ 15.300000 ms
+avg 10 x remap1 from pre-split coordinates: 76.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+avg 10 x remap1 from unsplit coordinates:.. 79.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+avg 10 x remap with internal spline:....... 98.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+avg 10 x remap with functor & internal bspl 103.400000 ms
+avg 10 x remap with functor & external bspl 80.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000094
+
+testing bc code PERIODIC spline degree 3 using Vc
+avg 10 x prefilter:........................ 10.500000 ms
+avg 10 x remap1 from pre-split coordinates: 21.300000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+avg 10 x remap1 from unsplit coordinates:.. 22.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+avg 10 x remap with internal spline:....... 35.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+avg 10 x remap with functor & internal bspl 36.700000 ms
+avg 10 x remap with functor & external bspl 22.300000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000094
+
+testing bc code PERIODIC spline degree 4
+avg 10 x prefilter:........................ 24.700000 ms
+avg 10 x remap1 from pre-split coordinates: 108.600000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap1 from unsplit coordinates:.. 118.800000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with internal spline:....... 150.200000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with functor & internal bspl 143.000000 ms
+avg 10 x remap with functor & external bspl 116.200000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000143
+
+testing bc code PERIODIC spline degree 4 using Vc
+avg 10 x prefilter:........................ 11.700000 ms
+avg 10 x remap1 from pre-split coordinates: 32.000000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap1 from unsplit coordinates:.. 32.400000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with internal spline:....... 46.200000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with functor & internal bspl 46.200000 ms
+avg 10 x remap with functor & external bspl 32.500000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000143
+
+testing bc code PERIODIC spline degree 5
+avg 10 x prefilter:........................ 24.900000 ms
+avg 10 x remap1 from pre-split coordinates: 148.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+avg 10 x remap1 from unsplit coordinates:.. 162.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+avg 10 x remap with internal spline:....... 181.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+avg 10 x remap with functor & internal bspl 186.600000 ms
+avg 10 x remap with functor & external bspl 156.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000114
+
+testing bc code PERIODIC spline degree 5 using Vc
+avg 10 x prefilter:........................ 11.200000 ms
+avg 10 x remap1 from pre-split coordinates: 46.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+avg 10 x remap1 from unsplit coordinates:.. 45.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+avg 10 x remap with internal spline:....... 57.900000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+avg 10 x remap with functor & internal bspl 57.800000 ms
+avg 10 x remap with functor & external bspl 42.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000114
+
+testing bc code PERIODIC spline degree 6
+avg 10 x prefilter:........................ 35.800000 ms
+avg 10 x remap1 from pre-split coordinates: 202.600000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+avg 10 x remap1 from unsplit coordinates:.. 207.600000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+avg 10 x remap with internal spline:....... 252.400000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+avg 10 x remap with functor & internal bspl 245.100000 ms
+avg 10 x remap with functor & external bspl 206.900000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000132
+
+testing bc code PERIODIC spline degree 6 using Vc
+avg 10 x prefilter:........................ 17.100000 ms
+avg 10 x remap1 from pre-split coordinates: 56.000000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+avg 10 x remap1 from unsplit coordinates:.. 56.100000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+avg 10 x remap with internal spline:....... 70.200000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+avg 10 x remap with functor & internal bspl 71.100000 ms
+avg 10 x remap with functor & external bspl 55.700000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000132
+
+testing bc code PERIODIC spline degree 7
+avg 10 x prefilter:........................ 37.200000 ms
+avg 10 x remap1 from pre-split coordinates: 255.800000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000187
+avg 10 x remap1 from unsplit coordinates:.. 266.900000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000187
+avg 10 x remap with internal spline:....... 298.500000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000187
+avg 10 x remap with functor & internal bspl 305.500000 ms
+avg 10 x remap with functor & external bspl 260.200000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000187
+difference original data/restored data:
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000159
+
+testing bc code PERIODIC spline degree 7 using Vc
+avg 10 x prefilter:........................ 13.400000 ms
+avg 10 x remap1 from pre-split coordinates: 69.900000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000153
+avg 10 x remap1 from unsplit coordinates:.. 71.500000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000153
+avg 10 x remap with internal spline:....... 90.800000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000153
+avg 10 x remap with functor & internal bspl 87.400000 ms
+avg 10 x remap with functor & external bspl 76.000000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000153
+difference original data/restored data:
+warped image diff Mean: 0.000024
+warped image diff Maximum: 0.000160
+
+testing bc code PERIODIC spline degree 8
+avg 10 x prefilter:........................ 50.800000 ms
+avg 10 x remap1 from pre-split coordinates: 317.300000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+avg 10 x remap1 from unsplit coordinates:.. 326.500000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+avg 10 x remap with internal spline:....... 375.500000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+avg 10 x remap with functor & internal bspl 382.600000 ms
+avg 10 x remap with functor & external bspl 323.000000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000167
+
+testing bc code PERIODIC spline degree 8 using Vc
+avg 10 x prefilter:........................ 15.500000 ms
+avg 10 x remap1 from pre-split coordinates: 91.600000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+avg 10 x remap1 from unsplit coordinates:.. 89.900000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+avg 10 x remap with internal spline:....... 121.500000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+avg 10 x remap with functor & internal bspl 109.600000 ms
+avg 10 x remap with functor & external bspl 91.400000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000167
+
+testing bc code NATURAL spline degree 0
+avg 10 x prefilter:........................ 4.400000 ms
+avg 10 x remap1 from pre-split coordinates: 14.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 21.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 30.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 30.000000 ms
+avg 10 x remap with functor & external bspl 23.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 0 using Vc
+avg 10 x prefilter:........................ 5.000000 ms
+avg 10 x remap1 from pre-split coordinates: 7.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 7.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 16.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 14.500000 ms
+avg 10 x remap with functor & external bspl 6.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 1
+avg 10 x prefilter:........................ 4.400000 ms
+avg 10 x remap1 from pre-split coordinates: 28.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 36.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 41.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 43.700000 ms
+avg 10 x remap with functor & external bspl 34.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 1 using Vc
+avg 10 x prefilter:........................ 4.800000 ms
+avg 10 x remap1 from pre-split coordinates: 9.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 9.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 17.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 21.600000 ms
+avg 10 x remap with functor & external bspl 9.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 2
+avg 10 x prefilter:........................ 13.500000 ms
+avg 10 x remap1 from pre-split coordinates: 49.000000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap1 from unsplit coordinates:.. 54.200000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with internal spline:....... 70.200000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with functor & internal bspl 70.800000 ms
+avg 10 x remap with functor & external bspl 55.700000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000075
+
+testing bc code NATURAL spline degree 2 using Vc
+avg 10 x prefilter:........................ 10.300000 ms
+avg 10 x remap1 from pre-split coordinates: 13.900000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap1 from unsplit coordinates:.. 16.500000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with internal spline:....... 27.100000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with functor & internal bspl 28.200000 ms
+avg 10 x remap with functor & external bspl 13.900000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000075
+
+testing bc code NATURAL spline degree 3
+avg 10 x prefilter:........................ 16.500000 ms
+avg 10 x remap1 from pre-split coordinates: 74.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+avg 10 x remap1 from unsplit coordinates:.. 80.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+avg 10 x remap with internal spline:....... 101.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+avg 10 x remap with functor & internal bspl 98.600000 ms
+avg 10 x remap with functor & external bspl 81.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000144
+
+testing bc code NATURAL spline degree 3 using Vc
+avg 10 x prefilter:........................ 10.500000 ms
+avg 10 x remap1 from pre-split coordinates: 20.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+avg 10 x remap1 from unsplit coordinates:.. 21.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+avg 10 x remap with internal spline:....... 35.300000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+avg 10 x remap with functor & internal bspl 33.900000 ms
+avg 10 x remap with functor & external bspl 22.900000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000144
+
+testing bc code NATURAL spline degree 4
+avg 10 x prefilter:........................ 24.300000 ms
+avg 10 x remap1 from pre-split coordinates: 109.300000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+avg 10 x remap1 from unsplit coordinates:.. 115.000000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+avg 10 x remap with internal spline:....... 151.200000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+avg 10 x remap with functor & internal bspl 144.400000 ms
+avg 10 x remap with functor & external bspl 115.000000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000198
+
+testing bc code NATURAL spline degree 4 using Vc
+avg 10 x prefilter:........................ 12.400000 ms
+avg 10 x remap1 from pre-split coordinates: 30.800000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+avg 10 x remap1 from unsplit coordinates:.. 31.500000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+avg 10 x remap with internal spline:....... 47.500000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+avg 10 x remap with functor & internal bspl 48.600000 ms
+avg 10 x remap with functor & external bspl 31.500000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000198
+
+testing bc code NATURAL spline degree 5
+avg 10 x prefilter:........................ 25.200000 ms
+avg 10 x remap1 from pre-split coordinates: 155.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+avg 10 x remap1 from unsplit coordinates:.. 154.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+avg 10 x remap with internal spline:....... 180.900000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+avg 10 x remap with functor & internal bspl 185.700000 ms
+avg 10 x remap with functor & external bspl 166.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000180
+
+testing bc code NATURAL spline degree 5 using Vc
+avg 10 x prefilter:........................ 11.800000 ms
+avg 10 x remap1 from pre-split coordinates: 42.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+avg 10 x remap1 from unsplit coordinates:.. 42.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+avg 10 x remap with internal spline:....... 56.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+avg 10 x remap with functor & internal bspl 56.100000 ms
+avg 10 x remap with functor & external bspl 42.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000180
+
+testing bc code NATURAL spline degree 6
+avg 10 x prefilter:........................ 37.000000 ms
+avg 10 x remap1 from pre-split coordinates: 201.300000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+avg 10 x remap1 from unsplit coordinates:.. 208.200000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+avg 10 x remap with internal spline:....... 241.000000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+avg 10 x remap with functor & internal bspl 245.700000 ms
+avg 10 x remap with functor & external bspl 211.500000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000174
+
+testing bc code NATURAL spline degree 6 using Vc
+avg 10 x prefilter:........................ 13.800000 ms
+avg 10 x remap1 from pre-split coordinates: 56.300000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+avg 10 x remap1 from unsplit coordinates:.. 55.100000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+avg 10 x remap with internal spline:....... 69.300000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+avg 10 x remap with functor & internal bspl 73.100000 ms
+avg 10 x remap with functor & external bspl 56.300000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000174
+
+testing bc code NATURAL spline degree 7
+avg 10 x prefilter:........................ 36.400000 ms
+avg 10 x remap1 from pre-split coordinates: 254.600000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000214
+avg 10 x remap1 from unsplit coordinates:.. 264.300000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000214
+avg 10 x remap with internal spline:....... 298.800000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000214
+avg 10 x remap with functor & internal bspl 304.200000 ms
+avg 10 x remap with functor & external bspl 260.700000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000214
+difference original data/restored data:
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000214
+
+testing bc code NATURAL spline degree 7 using Vc
+avg 10 x prefilter:........................ 13.200000 ms
+avg 10 x remap1 from pre-split coordinates: 72.900000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000307
+avg 10 x remap1 from unsplit coordinates:.. 74.500000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000307
+avg 10 x remap with internal spline:....... 89.300000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000307
+avg 10 x remap with functor & internal bspl 92.000000 ms
+avg 10 x remap with functor & external bspl 71.700000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000307
+difference original data/restored data:
+warped image diff Mean: 0.000024
+warped image diff Maximum: 0.000307
+
+testing bc code NATURAL spline degree 8
+avg 10 x prefilter:........................ 47.400000 ms
+avg 10 x remap1 from pre-split coordinates: 317.000000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+avg 10 x remap1 from unsplit coordinates:.. 327.900000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+avg 10 x remap with internal spline:....... 373.800000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+avg 10 x remap with functor & internal bspl 385.700000 ms
+avg 10 x remap with functor & external bspl 322.200000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000258
+
+testing bc code NATURAL spline degree 8 using Vc
+avg 10 x prefilter:........................ 16.700000 ms
+avg 10 x remap1 from pre-split coordinates: 91.100000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+avg 10 x remap1 from unsplit coordinates:.. 87.800000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+avg 10 x remap with internal spline:....... 111.100000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+avg 10 x remap with functor & internal bspl 106.500000 ms
+avg 10 x remap with functor & external bspl 90.400000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000258
+
+
+testing double data, float coordinates
+Image information:
+ file format: JPEG
+ width: 1920
+ height: 1079
+ pixel type: UINT8
+ color image: yes (number of channels: 3)
+testing bc code MIRROR spline degree 0
+avg 10 x prefilter:........................ 8.400000 ms
+avg 10 x remap1 from pre-split coordinates: 13.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 18.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 32.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 35.600000 ms
+avg 10 x remap with functor & external bspl 20.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 0 using Vc
+avg 10 x prefilter:........................ 8.900000 ms
+avg 10 x remap1 from pre-split coordinates: 11.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 11.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 26.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 24.500000 ms
+avg 10 x remap with functor & external bspl 10.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 1
+avg 10 x prefilter:........................ 8.300000 ms
+avg 10 x remap1 from pre-split coordinates: 19.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 26.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 40.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 43.100000 ms
+avg 10 x remap with functor & external bspl 28.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 1 using Vc
+avg 10 x prefilter:........................ 9.700000 ms
+avg 10 x remap1 from pre-split coordinates: 14.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 15.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 29.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 30.200000 ms
+avg 10 x remap with functor & external bspl 14.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 2
+avg 10 x prefilter:........................ 17.700000 ms
+avg 10 x remap1 from pre-split coordinates: 29.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 37.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 60.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 62.400000 ms
+avg 10 x remap with functor & external bspl 38.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 2 using Vc
+avg 10 x prefilter:........................ 18.900000 ms
+avg 10 x remap1 from pre-split coordinates: 22.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 23.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 47.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 47.300000 ms
+avg 10 x remap with functor & external bspl 23.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 3
+avg 10 x prefilter:........................ 17.300000 ms
+avg 10 x remap1 from pre-split coordinates: 51.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 53.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 77.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 78.300000 ms
+avg 10 x remap with functor & external bspl 56.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 3 using Vc
+avg 10 x prefilter:........................ 18.300000 ms
+avg 10 x remap1 from pre-split coordinates: 32.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 32.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 56.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 58.600000 ms
+avg 10 x remap with functor & external bspl 32.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 4
+avg 10 x prefilter:........................ 18.700000 ms
+avg 10 x remap1 from pre-split coordinates: 68.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 79.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 99.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 101.400000 ms
+avg 10 x remap with functor & external bspl 76.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 4 using Vc
+avg 10 x prefilter:........................ 22.500000 ms
+avg 10 x remap1 from pre-split coordinates: 45.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 45.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 71.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 74.700000 ms
+avg 10 x remap with functor & external bspl 46.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 5
+avg 10 x prefilter:........................ 18.200000 ms
+avg 10 x remap1 from pre-split coordinates: 98.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 100.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 125.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 127.300000 ms
+avg 10 x remap with functor & external bspl 104.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 5 using Vc
+avg 10 x prefilter:........................ 21.500000 ms
+avg 10 x remap1 from pre-split coordinates: 60.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 61.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 95.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 89.100000 ms
+avg 10 x remap with functor & external bspl 61.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 6
+avg 10 x prefilter:........................ 21.700000 ms
+avg 10 x remap1 from pre-split coordinates: 125.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 130.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 157.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 167.100000 ms
+avg 10 x remap with functor & external bspl 133.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 6 using Vc
+avg 10 x prefilter:........................ 24.700000 ms
+avg 10 x remap1 from pre-split coordinates: 79.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 80.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 113.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 111.400000 ms
+avg 10 x remap with functor & external bspl 85.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 7
+avg 10 x prefilter:........................ 20.200000 ms
+avg 10 x remap1 from pre-split coordinates: 163.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 165.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 192.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 201.400000 ms
+avg 10 x remap with functor & external bspl 169.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 7 using Vc
+avg 10 x prefilter:........................ 24.500000 ms
+avg 10 x remap1 from pre-split coordinates: 101.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 102.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 130.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 136.500000 ms
+avg 10 x remap with functor & external bspl 100.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 8
+avg 10 x prefilter:........................ 26.000000 ms
+avg 10 x remap1 from pre-split coordinates: 202.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 207.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 239.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 240.300000 ms
+avg 10 x remap with functor & external bspl 210.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 8 using Vc
+avg 10 x prefilter:........................ 29.600000 ms
+avg 10 x remap1 from pre-split coordinates: 127.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 124.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 159.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 163.400000 ms
+avg 10 x remap with functor & external bspl 124.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 0
+avg 10 x prefilter:........................ 8.900000 ms
+avg 10 x remap1 from pre-split coordinates: 12.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 22.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 33.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 33.700000 ms
+avg 10 x remap with functor & external bspl 19.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 0 using Vc
+avg 10 x prefilter:........................ 9.600000 ms
+avg 10 x remap1 from pre-split coordinates: 11.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 11.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 26.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 24.200000 ms
+avg 10 x remap with functor & external bspl 12.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 1
+avg 10 x prefilter:........................ 8.400000 ms
+avg 10 x remap1 from pre-split coordinates: 19.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 26.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 40.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 42.300000 ms
+avg 10 x remap with functor & external bspl 27.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 1 using Vc
+avg 10 x prefilter:........................ 8.900000 ms
+avg 10 x remap1 from pre-split coordinates: 14.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 17.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 28.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 32.100000 ms
+avg 10 x remap with functor & external bspl 15.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 2
+avg 10 x prefilter:........................ 18.400000 ms
+avg 10 x remap1 from pre-split coordinates: 30.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 36.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 61.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 61.100000 ms
+avg 10 x remap with functor & external bspl 39.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 2 using Vc
+avg 10 x prefilter:........................ 18.800000 ms
+avg 10 x remap1 from pre-split coordinates: 22.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 21.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 47.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 47.400000 ms
+avg 10 x remap with functor & external bspl 23.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 3
+avg 10 x prefilter:........................ 17.000000 ms
+avg 10 x remap1 from pre-split coordinates: 47.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 55.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 78.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 78.600000 ms
+avg 10 x remap with functor & external bspl 55.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 3 using Vc
+avg 10 x prefilter:........................ 19.000000 ms
+avg 10 x remap1 from pre-split coordinates: 35.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 32.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 56.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 61.900000 ms
+avg 10 x remap with functor & external bspl 32.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 4
+avg 10 x prefilter:........................ 19.500000 ms
+avg 10 x remap1 from pre-split coordinates: 68.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 75.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 98.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 103.700000 ms
+avg 10 x remap with functor & external bspl 77.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 4 using Vc
+avg 10 x prefilter:........................ 23.500000 ms
+avg 10 x remap1 from pre-split coordinates: 46.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 45.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 74.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 72.100000 ms
+avg 10 x remap with functor & external bspl 45.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 5
+avg 10 x prefilter:........................ 19.800000 ms
+avg 10 x remap1 from pre-split coordinates: 95.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 103.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 124.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 131.600000 ms
+avg 10 x remap with functor & external bspl 103.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 5 using Vc
+avg 10 x prefilter:........................ 22.100000 ms
+avg 10 x remap1 from pre-split coordinates: 59.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 62.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 88.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 90.500000 ms
+avg 10 x remap with functor & external bspl 60.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 6
+avg 10 x prefilter:........................ 20.000000 ms
+avg 10 x remap1 from pre-split coordinates: 134.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 130.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 156.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 160.000000 ms
+avg 10 x remap with functor & external bspl 137.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 6 using Vc
+avg 10 x prefilter:........................ 23.600000 ms
+avg 10 x remap1 from pre-split coordinates: 84.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 79.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 109.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 109.800000 ms
+avg 10 x remap with functor & external bspl 79.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 7
+avg 10 x prefilter:........................ 22.000000 ms
+avg 10 x remap1 from pre-split coordinates: 162.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 174.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 190.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 195.800000 ms
+avg 10 x remap with functor & external bspl 170.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 7 using Vc
+avg 10 x prefilter:........................ 25.500000 ms
+avg 10 x remap1 from pre-split coordinates: 105.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 100.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 130.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 130.800000 ms
+avg 10 x remap with functor & external bspl 101.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 8
+avg 10 x prefilter:........................ 25.200000 ms
+avg 10 x remap1 from pre-split coordinates: 205.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 204.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 237.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 246.300000 ms
+avg 10 x remap with functor & external bspl 209.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 8 using Vc
+avg 10 x prefilter:........................ 28.100000 ms
+avg 10 x remap1 from pre-split coordinates: 124.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 125.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 166.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 158.900000 ms
+avg 10 x remap with functor & external bspl 123.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 0
+avg 10 x prefilter:........................ 8.300000 ms
+avg 10 x remap1 from pre-split coordinates: 12.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 19.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 30.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 34.300000 ms
+avg 10 x remap with functor & external bspl 19.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 0 using Vc
+avg 10 x prefilter:........................ 9.100000 ms
+avg 10 x remap1 from pre-split coordinates: 12.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 10.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 25.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 25.900000 ms
+avg 10 x remap with functor & external bspl 11.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 1
+avg 10 x prefilter:........................ 8.200000 ms
+avg 10 x remap1 from pre-split coordinates: 20.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 24.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 39.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 43.500000 ms
+avg 10 x remap with functor & external bspl 26.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 1 using Vc
+avg 10 x prefilter:........................ 8.900000 ms
+avg 10 x remap1 from pre-split coordinates: 14.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 16.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 28.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 30.000000 ms
+avg 10 x remap with functor & external bspl 15.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 2
+avg 10 x prefilter:........................ 18.200000 ms
+avg 10 x remap1 from pre-split coordinates: 30.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 35.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 62.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 60.900000 ms
+avg 10 x remap with functor & external bspl 38.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 2 using Vc
+avg 10 x prefilter:........................ 18.700000 ms
+avg 10 x remap1 from pre-split coordinates: 23.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 21.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 47.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 46.900000 ms
+avg 10 x remap with functor & external bspl 23.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 3
+avg 10 x prefilter:........................ 17.100000 ms
+avg 10 x remap1 from pre-split coordinates: 49.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 52.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 75.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 78.200000 ms
+avg 10 x remap with functor & external bspl 54.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 3 using Vc
+avg 10 x prefilter:........................ 18.500000 ms
+avg 10 x remap1 from pre-split coordinates: 32.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 33.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 57.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 57.800000 ms
+avg 10 x remap with functor & external bspl 33.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 4
+avg 10 x prefilter:........................ 20.500000 ms
+avg 10 x remap1 from pre-split coordinates: 68.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 79.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 98.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 100.700000 ms
+avg 10 x remap with functor & external bspl 75.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 4 using Vc
+avg 10 x prefilter:........................ 21.200000 ms
+avg 10 x remap1 from pre-split coordinates: 45.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 44.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 76.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 74.200000 ms
+avg 10 x remap with functor & external bspl 46.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 5
+avg 10 x prefilter:........................ 19.800000 ms
+avg 10 x remap1 from pre-split coordinates: 99.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 99.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 123.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 127.000000 ms
+avg 10 x remap with functor & external bspl 103.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 5 using Vc
+avg 10 x prefilter:........................ 22.500000 ms
+avg 10 x remap1 from pre-split coordinates: 60.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 61.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 91.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 89.400000 ms
+avg 10 x remap with functor & external bspl 60.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 6
+avg 10 x prefilter:........................ 21.900000 ms
+avg 10 x remap1 from pre-split coordinates: 125.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 131.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 156.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 165.500000 ms
+avg 10 x remap with functor & external bspl 132.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 6 using Vc
+avg 10 x prefilter:........................ 24.000000 ms
+avg 10 x remap1 from pre-split coordinates: 80.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 78.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 112.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 108.900000 ms
+avg 10 x remap with functor & external bspl 84.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 7
+avg 10 x prefilter:........................ 20.400000 ms
+avg 10 x remap1 from pre-split coordinates: 162.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 164.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 193.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 202.600000 ms
+avg 10 x remap with functor & external bspl 168.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 7 using Vc
+avg 10 x prefilter:........................ 25.300000 ms
+avg 10 x remap1 from pre-split coordinates: 100.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 102.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 131.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 136.400000 ms
+avg 10 x remap with functor & external bspl 101.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 8
+avg 10 x prefilter:........................ 24.600000 ms
+avg 10 x remap1 from pre-split coordinates: 201.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 214.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 241.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 239.200000 ms
+avg 10 x remap with functor & external bspl 211.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 8 using Vc
+avg 10 x prefilter:........................ 30.200000 ms
+avg 10 x remap1 from pre-split coordinates: 128.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 124.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 157.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 159.700000 ms
+avg 10 x remap with functor & external bspl 124.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 0
+avg 10 x prefilter:........................ 9.200000 ms
+avg 10 x remap1 from pre-split coordinates: 16.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 18.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 32.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 34.300000 ms
+avg 10 x remap with functor & external bspl 19.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 0 using Vc
+avg 10 x prefilter:........................ 9.700000 ms
+avg 10 x remap1 from pre-split coordinates: 11.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 11.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 25.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 24.900000 ms
+avg 10 x remap with functor & external bspl 12.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 1
+avg 10 x prefilter:........................ 8.200000 ms
+avg 10 x remap1 from pre-split coordinates: 19.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 24.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 39.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 40.300000 ms
+avg 10 x remap with functor & external bspl 26.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 1 using Vc
+avg 10 x prefilter:........................ 9.200000 ms
+avg 10 x remap1 from pre-split coordinates: 14.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 15.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 30.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 30.200000 ms
+avg 10 x remap with functor & external bspl 15.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 2
+avg 10 x prefilter:........................ 18.000000 ms
+avg 10 x remap1 from pre-split coordinates: 29.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 37.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 63.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 61.900000 ms
+avg 10 x remap with functor & external bspl 39.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 2 using Vc
+avg 10 x prefilter:........................ 19.200000 ms
+avg 10 x remap1 from pre-split coordinates: 23.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 22.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 47.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 47.700000 ms
+avg 10 x remap with functor & external bspl 23.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 3
+avg 10 x prefilter:........................ 17.300000 ms
+avg 10 x remap1 from pre-split coordinates: 47.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 53.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 77.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 78.800000 ms
+avg 10 x remap with functor & external bspl 54.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 3 using Vc
+avg 10 x prefilter:........................ 18.400000 ms
+avg 10 x remap1 from pre-split coordinates: 35.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 32.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 56.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 56.100000 ms
+avg 10 x remap with functor & external bspl 32.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 4
+avg 10 x prefilter:........................ 18.900000 ms
+avg 10 x remap1 from pre-split coordinates: 68.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 73.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 97.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 103.200000 ms
+avg 10 x remap with functor & external bspl 76.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 4 using Vc
+avg 10 x prefilter:........................ 26.300000 ms
+avg 10 x remap1 from pre-split coordinates: 44.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 46.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 71.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 72.400000 ms
+avg 10 x remap with functor & external bspl 45.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 5
+avg 10 x prefilter:........................ 18.900000 ms
+avg 10 x remap1 from pre-split coordinates: 94.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 101.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 123.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 130.900000 ms
+avg 10 x remap with functor & external bspl 102.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 5 using Vc
+avg 10 x prefilter:........................ 22.000000 ms
+avg 10 x remap1 from pre-split coordinates: 61.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 61.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 87.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 89.400000 ms
+avg 10 x remap with functor & external bspl 61.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 6
+avg 10 x prefilter:........................ 21.300000 ms
+avg 10 x remap1 from pre-split coordinates: 128.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 130.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 155.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 159.300000 ms
+avg 10 x remap with functor & external bspl 135.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 6 using Vc
+avg 10 x prefilter:........................ 25.300000 ms
+avg 10 x remap1 from pre-split coordinates: 85.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 78.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 110.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 109.800000 ms
+avg 10 x remap with functor & external bspl 78.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 7
+avg 10 x prefilter:........................ 20.800000 ms
+avg 10 x remap1 from pre-split coordinates: 163.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 171.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 190.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 195.400000 ms
+avg 10 x remap with functor & external bspl 171.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 7 using Vc
+avg 10 x prefilter:........................ 26.500000 ms
+avg 10 x remap1 from pre-split coordinates: 105.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 99.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 131.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 131.100000 ms
+avg 10 x remap with functor & external bspl 100.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 8
+avg 10 x prefilter:........................ 24.200000 ms
+avg 10 x remap1 from pre-split coordinates: 205.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 204.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 235.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 250.600000 ms
+avg 10 x remap with functor & external bspl 211.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 8 using Vc
+avg 10 x prefilter:........................ 28.000000 ms
+avg 10 x remap1 from pre-split coordinates: 127.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 128.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 162.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 158.100000 ms
+avg 10 x remap with functor & external bspl 124.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing float data, double coordinates
+Image information:
+ file format: JPEG
+ width: 1920
+ height: 1079
+ pixel type: UINT8
+ color image: yes (number of channels: 3)
+testing bc code MIRROR spline degree 0
+avg 10 x prefilter:........................ 4.500000 ms
+avg 10 x remap1 from pre-split coordinates: 15.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 21.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 30.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 33.700000 ms
+avg 10 x remap with functor & external bspl 25.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 0 using Vc
+avg 10 x prefilter:........................ 4.800000 ms
+avg 10 x remap1 from pre-split coordinates: 8.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 10.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 18.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 19.900000 ms
+avg 10 x remap with functor & external bspl 10.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 1
+avg 10 x prefilter:........................ 4.500000 ms
+avg 10 x remap1 from pre-split coordinates: 29.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 36.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 42.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 50.300000 ms
+avg 10 x remap with functor & external bspl 37.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 1 using Vc
+avg 10 x prefilter:........................ 5.200000 ms
+avg 10 x remap1 from pre-split coordinates: 9.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 12.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 21.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 19.400000 ms
+avg 10 x remap with functor & external bspl 11.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 2
+avg 10 x prefilter:........................ 14.800000 ms
+avg 10 x remap1 from pre-split coordinates: 47.300000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap1 from unsplit coordinates:.. 55.500000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with internal spline:....... 69.600000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with functor & internal bspl 74.000000 ms
+avg 10 x remap with functor & external bspl 58.600000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000070
+
+testing bc code MIRROR spline degree 2 using Vc
+avg 10 x prefilter:........................ 10.300000 ms
+avg 10 x remap1 from pre-split coordinates: 14.900000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap1 from unsplit coordinates:.. 18.600000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with internal spline:....... 32.900000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with functor & internal bspl 32.700000 ms
+avg 10 x remap with functor & external bspl 18.800000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000070
+
+testing bc code MIRROR spline degree 3
+avg 10 x prefilter:........................ 14.700000 ms
+avg 10 x remap1 from pre-split coordinates: 78.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+avg 10 x remap1 from unsplit coordinates:.. 82.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+avg 10 x remap with internal spline:....... 98.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+avg 10 x remap with functor & internal bspl 100.200000 ms
+avg 10 x remap with functor & external bspl 82.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000117
+
+testing bc code MIRROR spline degree 3 using Vc
+avg 10 x prefilter:........................ 10.700000 ms
+avg 10 x remap1 from pre-split coordinates: 21.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+avg 10 x remap1 from unsplit coordinates:.. 25.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+avg 10 x remap with internal spline:....... 39.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+avg 10 x remap with functor & internal bspl 37.300000 ms
+avg 10 x remap with functor & external bspl 24.300000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000137
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000117
+
+testing bc code MIRROR spline degree 4
+avg 10 x prefilter:........................ 25.400000 ms
+avg 10 x remap1 from pre-split coordinates: 114.200000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap1 from unsplit coordinates:.. 115.500000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with internal spline:....... 142.800000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with functor & internal bspl 145.600000 ms
+avg 10 x remap with functor & external bspl 121.100000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+
+testing bc code MIRROR spline degree 4 using Vc
+avg 10 x prefilter:........................ 11.500000 ms
+avg 10 x remap1 from pre-split coordinates: 31.700000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap1 from unsplit coordinates:.. 34.500000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with internal spline:....... 50.300000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with functor & internal bspl 54.400000 ms
+avg 10 x remap with functor & external bspl 34.700000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+
+testing bc code MIRROR spline degree 5
+avg 10 x prefilter:........................ 25.200000 ms
+avg 10 x remap1 from pre-split coordinates: 150.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+avg 10 x remap1 from unsplit coordinates:.. 156.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+avg 10 x remap with internal spline:....... 186.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+avg 10 x remap with functor & internal bspl 191.100000 ms
+avg 10 x remap with functor & external bspl 158.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000101
+
+testing bc code MIRROR spline degree 5 using Vc
+avg 10 x prefilter:........................ 11.600000 ms
+avg 10 x remap1 from pre-split coordinates: 41.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+avg 10 x remap1 from unsplit coordinates:.. 45.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+avg 10 x remap with internal spline:....... 58.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+avg 10 x remap with functor & internal bspl 59.100000 ms
+avg 10 x remap with functor & external bspl 47.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000102
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000101
+
+testing bc code MIRROR spline degree 6
+avg 10 x prefilter:........................ 36.700000 ms
+avg 10 x remap1 from pre-split coordinates: 205.300000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+avg 10 x remap1 from unsplit coordinates:.. 206.000000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+avg 10 x remap with internal spline:....... 243.400000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+avg 10 x remap with functor & internal bspl 256.000000 ms
+avg 10 x remap with functor & external bspl 208.100000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000111
+
+testing bc code MIRROR spline degree 6 using Vc
+avg 10 x prefilter:........................ 14.100000 ms
+avg 10 x remap1 from pre-split coordinates: 56.200000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+avg 10 x remap1 from unsplit coordinates:.. 59.800000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+avg 10 x remap with internal spline:....... 75.200000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+avg 10 x remap with functor & internal bspl 80.000000 ms
+avg 10 x remap with functor & external bspl 60.000000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000114
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000111
+
+testing bc code MIRROR spline degree 7
+avg 10 x prefilter:........................ 35.500000 ms
+avg 10 x remap1 from pre-split coordinates: 258.200000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000123
+avg 10 x remap1 from unsplit coordinates:.. 259.700000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000123
+avg 10 x remap with internal spline:....... 301.700000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000123
+avg 10 x remap with functor & internal bspl 302.900000 ms
+avg 10 x remap with functor & external bspl 265.600000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000123
+difference original data/restored data:
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000112
+
+testing bc code MIRROR spline degree 7 using Vc
+avg 10 x prefilter:........................ 14.100000 ms
+avg 10 x remap1 from pre-split coordinates: 74.600000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000127
+avg 10 x remap1 from unsplit coordinates:.. 74.800000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000127
+avg 10 x remap with internal spline:....... 90.500000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000127
+avg 10 x remap with functor & internal bspl 89.100000 ms
+avg 10 x remap with functor & external bspl 73.300000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000127
+difference original data/restored data:
+warped image diff Mean: 0.000024
+warped image diff Maximum: 0.000142
+
+testing bc code MIRROR spline degree 8
+avg 10 x prefilter:........................ 47.900000 ms
+avg 10 x remap1 from pre-split coordinates: 322.700000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+avg 10 x remap1 from unsplit coordinates:.. 323.700000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+avg 10 x remap with internal spline:....... 382.500000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+avg 10 x remap with functor & internal bspl 377.200000 ms
+avg 10 x remap with functor & external bspl 334.800000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000135
+
+testing bc code MIRROR spline degree 8 using Vc
+avg 10 x prefilter:........................ 15.500000 ms
+avg 10 x remap1 from pre-split coordinates: 88.700000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+avg 10 x remap1 from unsplit coordinates:.. 91.300000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+avg 10 x remap with internal spline:....... 112.000000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+avg 10 x remap with functor & internal bspl 109.300000 ms
+avg 10 x remap with functor & external bspl 93.600000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000139
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000135
+
+testing bc code REFLECT spline degree 0
+avg 10 x prefilter:........................ 4.500000 ms
+avg 10 x remap1 from pre-split coordinates: 15.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 22.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 30.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 32.700000 ms
+avg 10 x remap with functor & external bspl 31.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 0 using Vc
+avg 10 x prefilter:........................ 4.800000 ms
+avg 10 x remap1 from pre-split coordinates: 7.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 10.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 17.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 17.300000 ms
+avg 10 x remap with functor & external bspl 10.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 1
+avg 10 x prefilter:........................ 4.500000 ms
+avg 10 x remap1 from pre-split coordinates: 27.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 35.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 43.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 46.500000 ms
+avg 10 x remap with functor & external bspl 39.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 1 using Vc
+avg 10 x prefilter:........................ 5.000000 ms
+avg 10 x remap1 from pre-split coordinates: 9.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 13.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 20.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 21.100000 ms
+avg 10 x remap with functor & external bspl 12.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 2
+avg 10 x prefilter:........................ 14.100000 ms
+avg 10 x remap1 from pre-split coordinates: 47.800000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+avg 10 x remap1 from unsplit coordinates:.. 55.600000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+avg 10 x remap with internal spline:....... 69.800000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+avg 10 x remap with functor & internal bspl 74.700000 ms
+avg 10 x remap with functor & external bspl 63.300000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000078
+
+testing bc code REFLECT spline degree 2 using Vc
+avg 10 x prefilter:........................ 10.200000 ms
+avg 10 x remap1 from pre-split coordinates: 14.200000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+avg 10 x remap1 from unsplit coordinates:.. 18.100000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+avg 10 x remap with internal spline:....... 30.800000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+avg 10 x remap with functor & internal bspl 30.500000 ms
+avg 10 x remap with functor & external bspl 17.700000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000092
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000078
+
+testing bc code REFLECT spline degree 3
+avg 10 x prefilter:........................ 13.800000 ms
+avg 10 x remap1 from pre-split coordinates: 75.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+avg 10 x remap1 from unsplit coordinates:.. 80.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+avg 10 x remap with internal spline:....... 97.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+avg 10 x remap with functor & internal bspl 101.400000 ms
+avg 10 x remap with functor & external bspl 85.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000135
+
+testing bc code REFLECT spline degree 3 using Vc
+avg 10 x prefilter:........................ 10.300000 ms
+avg 10 x remap1 from pre-split coordinates: 22.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+avg 10 x remap1 from unsplit coordinates:.. 28.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+avg 10 x remap with internal spline:....... 37.300000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+avg 10 x remap with functor & internal bspl 38.000000 ms
+avg 10 x remap with functor & external bspl 25.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000144
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000135
+
+testing bc code REFLECT spline degree 4
+avg 10 x prefilter:........................ 24.300000 ms
+avg 10 x remap1 from pre-split coordinates: 108.500000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+avg 10 x remap1 from unsplit coordinates:.. 114.400000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+avg 10 x remap with internal spline:....... 142.900000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+avg 10 x remap with functor & internal bspl 146.200000 ms
+avg 10 x remap with functor & external bspl 125.500000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000147
+
+testing bc code REFLECT spline degree 4 using Vc
+avg 10 x prefilter:........................ 12.100000 ms
+avg 10 x remap1 from pre-split coordinates: 31.400000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+avg 10 x remap1 from unsplit coordinates:.. 35.100000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+avg 10 x remap with internal spline:....... 48.000000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+avg 10 x remap with functor & internal bspl 48.200000 ms
+avg 10 x remap with functor & external bspl 34.000000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000154
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000147
+
+testing bc code REFLECT spline degree 5
+avg 10 x prefilter:........................ 24.300000 ms
+avg 10 x remap1 from pre-split coordinates: 150.900000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+avg 10 x remap1 from unsplit coordinates:.. 158.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+avg 10 x remap with internal spline:....... 188.100000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+avg 10 x remap with functor & internal bspl 188.700000 ms
+avg 10 x remap with functor & external bspl 160.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000129
+
+testing bc code REFLECT spline degree 5 using Vc
+avg 10 x prefilter:........................ 11.000000 ms
+avg 10 x remap1 from pre-split coordinates: 44.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+avg 10 x remap1 from unsplit coordinates:.. 46.100000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+avg 10 x remap with internal spline:....... 59.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+avg 10 x remap with functor & internal bspl 59.000000 ms
+avg 10 x remap with functor & external bspl 48.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000130
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000129
+
+testing bc code REFLECT spline degree 6
+avg 10 x prefilter:........................ 36.600000 ms
+avg 10 x remap1 from pre-split coordinates: 197.800000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+avg 10 x remap1 from unsplit coordinates:.. 203.500000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+avg 10 x remap with internal spline:....... 246.500000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+avg 10 x remap with functor & internal bspl 247.500000 ms
+avg 10 x remap with functor & external bspl 207.400000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000163
+
+testing bc code REFLECT spline degree 6 using Vc
+avg 10 x prefilter:........................ 13.900000 ms
+avg 10 x remap1 from pre-split coordinates: 57.700000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+avg 10 x remap1 from unsplit coordinates:.. 58.600000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+avg 10 x remap with internal spline:....... 73.700000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+avg 10 x remap with functor & internal bspl 78.600000 ms
+avg 10 x remap with functor & external bspl 60.500000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000145
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000163
+
+testing bc code REFLECT spline degree 7
+avg 10 x prefilter:........................ 36.400000 ms
+avg 10 x remap1 from pre-split coordinates: 253.200000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000154
+avg 10 x remap1 from unsplit coordinates:.. 261.800000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000154
+avg 10 x remap with internal spline:....... 309.600000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000154
+avg 10 x remap with functor & internal bspl 303.000000 ms
+avg 10 x remap with functor & external bspl 274.600000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000154
+difference original data/restored data:
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000165
+
+testing bc code REFLECT spline degree 7 using Vc
+avg 10 x prefilter:........................ 13.000000 ms
+avg 10 x remap1 from pre-split coordinates: 74.100000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000197
+avg 10 x remap1 from unsplit coordinates:.. 76.400000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000197
+avg 10 x remap with internal spline:....... 93.800000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000197
+avg 10 x remap with functor & internal bspl 93.700000 ms
+avg 10 x remap with functor & external bspl 76.400000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000197
+difference original data/restored data:
+warped image diff Mean: 0.000024
+warped image diff Maximum: 0.000201
+
+testing bc code REFLECT spline degree 8
+avg 10 x prefilter:........................ 48.500000 ms
+avg 10 x remap1 from pre-split coordinates: 322.700000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+avg 10 x remap1 from unsplit coordinates:.. 326.800000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+avg 10 x remap with internal spline:....... 379.200000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+avg 10 x remap with functor & internal bspl 378.800000 ms
+avg 10 x remap with functor & external bspl 334.300000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000179
+
+testing bc code REFLECT spline degree 8 using Vc
+avg 10 x prefilter:........................ 14.600000 ms
+avg 10 x remap1 from pre-split coordinates: 90.100000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+avg 10 x remap1 from unsplit coordinates:.. 92.500000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+avg 10 x remap with internal spline:....... 110.800000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+avg 10 x remap with functor & internal bspl 109.600000 ms
+avg 10 x remap with functor & external bspl 98.600000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000184
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000179
+
+testing bc code PERIODIC spline degree 0
+avg 10 x prefilter:........................ 4.500000 ms
+avg 10 x remap1 from pre-split coordinates: 15.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 23.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 28.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 33.700000 ms
+avg 10 x remap with functor & external bspl 27.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 0 using Vc
+avg 10 x prefilter:........................ 5.000000 ms
+avg 10 x remap1 from pre-split coordinates: 8.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 10.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 20.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 18.700000 ms
+avg 10 x remap with functor & external bspl 10.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 1
+avg 10 x prefilter:........................ 4.600000 ms
+avg 10 x remap1 from pre-split coordinates: 27.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 36.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 41.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 45.600000 ms
+avg 10 x remap with functor & external bspl 40.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 1 using Vc
+avg 10 x prefilter:........................ 5.100000 ms
+avg 10 x remap1 from pre-split coordinates: 9.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 13.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 19.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 20.000000 ms
+avg 10 x remap with functor & external bspl 13.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 2
+avg 10 x prefilter:........................ 15.000000 ms
+avg 10 x remap1 from pre-split coordinates: 48.100000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+avg 10 x remap1 from unsplit coordinates:.. 56.400000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+avg 10 x remap with internal spline:....... 71.700000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+avg 10 x remap with functor & internal bspl 74.200000 ms
+avg 10 x remap with functor & external bspl 57.000000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000076
+
+testing bc code PERIODIC spline degree 2 using Vc
+avg 10 x prefilter:........................ 10.100000 ms
+avg 10 x remap1 from pre-split coordinates: 14.100000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+avg 10 x remap1 from unsplit coordinates:.. 20.400000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+avg 10 x remap with internal spline:....... 31.100000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+avg 10 x remap with functor & internal bspl 33.000000 ms
+avg 10 x remap with functor & external bspl 18.200000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000084
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000076
+
+testing bc code PERIODIC spline degree 3
+avg 10 x prefilter:........................ 15.200000 ms
+avg 10 x remap1 from pre-split coordinates: 73.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+avg 10 x remap1 from unsplit coordinates:.. 86.300000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+avg 10 x remap with internal spline:....... 97.900000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+avg 10 x remap with functor & internal bspl 105.800000 ms
+avg 10 x remap with functor & external bspl 82.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000094
+
+testing bc code PERIODIC spline degree 3 using Vc
+avg 10 x prefilter:........................ 9.800000 ms
+avg 10 x remap1 from pre-split coordinates: 22.100000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+avg 10 x remap1 from unsplit coordinates:.. 24.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+avg 10 x remap with internal spline:....... 37.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+avg 10 x remap with functor & internal bspl 38.900000 ms
+avg 10 x remap with functor & external bspl 24.300000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000098
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000094
+
+testing bc code PERIODIC spline degree 4
+avg 10 x prefilter:........................ 24.900000 ms
+avg 10 x remap1 from pre-split coordinates: 108.200000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap1 from unsplit coordinates:.. 117.100000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with internal spline:....... 146.900000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with functor & internal bspl 146.900000 ms
+avg 10 x remap with functor & external bspl 117.600000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000143
+
+testing bc code PERIODIC spline degree 4 using Vc
+avg 10 x prefilter:........................ 11.500000 ms
+avg 10 x remap1 from pre-split coordinates: 32.900000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap1 from unsplit coordinates:.. 36.000000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with internal spline:....... 49.800000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+avg 10 x remap with functor & internal bspl 48.900000 ms
+avg 10 x remap with functor & external bspl 38.900000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000145
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000143
+
+testing bc code PERIODIC spline degree 5
+avg 10 x prefilter:........................ 24.300000 ms
+avg 10 x remap1 from pre-split coordinates: 154.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+avg 10 x remap1 from unsplit coordinates:.. 155.400000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+avg 10 x remap with internal spline:....... 181.900000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+avg 10 x remap with functor & internal bspl 187.100000 ms
+avg 10 x remap with functor & external bspl 158.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000114
+
+testing bc code PERIODIC spline degree 5 using Vc
+avg 10 x prefilter:........................ 14.800000 ms
+avg 10 x remap1 from pre-split coordinates: 43.100000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+avg 10 x remap1 from unsplit coordinates:.. 45.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+avg 10 x remap with internal spline:....... 58.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+avg 10 x remap with functor & internal bspl 58.700000 ms
+avg 10 x remap with functor & external bspl 45.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000114
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000114
+
+testing bc code PERIODIC spline degree 6
+avg 10 x prefilter:........................ 37.200000 ms
+avg 10 x remap1 from pre-split coordinates: 199.200000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+avg 10 x remap1 from unsplit coordinates:.. 207.600000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+avg 10 x remap with internal spline:....... 244.600000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+avg 10 x remap with functor & internal bspl 246.200000 ms
+avg 10 x remap with functor & external bspl 208.800000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000132
+
+testing bc code PERIODIC spline degree 6 using Vc
+avg 10 x prefilter:........................ 15.900000 ms
+avg 10 x remap1 from pre-split coordinates: 55.700000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+avg 10 x remap1 from unsplit coordinates:.. 59.600000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+avg 10 x remap with internal spline:....... 76.200000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+avg 10 x remap with functor & internal bspl 75.000000 ms
+avg 10 x remap with functor & external bspl 60.100000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000131
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000132
+
+testing bc code PERIODIC spline degree 7
+avg 10 x prefilter:........................ 35.300000 ms
+avg 10 x remap1 from pre-split coordinates: 255.100000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000187
+avg 10 x remap1 from unsplit coordinates:.. 264.800000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000187
+avg 10 x remap with internal spline:....... 297.600000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000187
+avg 10 x remap with functor & internal bspl 307.100000 ms
+avg 10 x remap with functor & external bspl 262.100000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000187
+difference original data/restored data:
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000159
+
+testing bc code PERIODIC spline degree 7 using Vc
+avg 10 x prefilter:........................ 14.900000 ms
+avg 10 x remap1 from pre-split coordinates: 70.200000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000153
+avg 10 x remap1 from unsplit coordinates:.. 76.200000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000153
+avg 10 x remap with internal spline:....... 92.700000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000153
+avg 10 x remap with functor & internal bspl 96.400000 ms
+avg 10 x remap with functor & external bspl 74.600000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000153
+difference original data/restored data:
+warped image diff Mean: 0.000024
+warped image diff Maximum: 0.000160
+
+testing bc code PERIODIC spline degree 8
+avg 10 x prefilter:........................ 49.700000 ms
+avg 10 x remap1 from pre-split coordinates: 316.600000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+avg 10 x remap1 from unsplit coordinates:.. 331.600000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+avg 10 x remap with internal spline:....... 375.300000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+avg 10 x remap with functor & internal bspl 389.500000 ms
+avg 10 x remap with functor & external bspl 327.300000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000167
+
+testing bc code PERIODIC spline degree 8 using Vc
+avg 10 x prefilter:........................ 15.500000 ms
+avg 10 x remap1 from pre-split coordinates: 90.400000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+avg 10 x remap1 from unsplit coordinates:.. 94.900000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+avg 10 x remap with internal spline:....... 115.100000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+avg 10 x remap with functor & internal bspl 109.700000 ms
+avg 10 x remap with functor & external bspl 92.500000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000173
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000167
+
+testing bc code NATURAL spline degree 0
+avg 10 x prefilter:........................ 4.500000 ms
+avg 10 x remap1 from pre-split coordinates: 16.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 22.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 29.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 34.800000 ms
+avg 10 x remap with functor & external bspl 25.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 0 using Vc
+avg 10 x prefilter:........................ 4.900000 ms
+avg 10 x remap1 from pre-split coordinates: 8.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 10.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 18.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 20.100000 ms
+avg 10 x remap with functor & external bspl 10.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 1
+avg 10 x prefilter:........................ 4.500000 ms
+avg 10 x remap1 from pre-split coordinates: 29.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 34.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 41.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 44.000000 ms
+avg 10 x remap with functor & external bspl 42.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 1 using Vc
+avg 10 x prefilter:........................ 5.500000 ms
+avg 10 x remap1 from pre-split coordinates: 9.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 13.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 22.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 20.100000 ms
+avg 10 x remap with functor & external bspl 12.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 2
+avg 10 x prefilter:........................ 15.900000 ms
+avg 10 x remap1 from pre-split coordinates: 47.700000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap1 from unsplit coordinates:.. 54.100000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with internal spline:....... 70.200000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with functor & internal bspl 73.400000 ms
+avg 10 x remap with functor & external bspl 57.300000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000075
+
+testing bc code NATURAL spline degree 2 using Vc
+avg 10 x prefilter:........................ 9.900000 ms
+avg 10 x remap1 from pre-split coordinates: 15.900000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap1 from unsplit coordinates:.. 20.700000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with internal spline:....... 32.900000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+avg 10 x remap with functor & internal bspl 33.000000 ms
+avg 10 x remap with functor & external bspl 18.800000 ms
+warped image diff Mean: 0.000018
+warped image diff Maximum: 0.000082
+difference original data/restored data:
+warped image diff Mean: 0.000017
+warped image diff Maximum: 0.000075
+
+testing bc code NATURAL spline degree 3
+avg 10 x prefilter:........................ 14.800000 ms
+avg 10 x remap1 from pre-split coordinates: 77.900000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+avg 10 x remap1 from unsplit coordinates:.. 81.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+avg 10 x remap with internal spline:....... 97.500000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+avg 10 x remap with functor & internal bspl 99.600000 ms
+avg 10 x remap with functor & external bspl 82.100000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000144
+
+testing bc code NATURAL spline degree 3 using Vc
+avg 10 x prefilter:........................ 10.300000 ms
+avg 10 x remap1 from pre-split coordinates: 21.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+avg 10 x remap1 from unsplit coordinates:.. 25.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+avg 10 x remap with internal spline:....... 38.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+avg 10 x remap with functor & internal bspl 38.400000 ms
+avg 10 x remap with functor & external bspl 25.300000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000165
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000144
+
+testing bc code NATURAL spline degree 4
+avg 10 x prefilter:........................ 26.100000 ms
+avg 10 x remap1 from pre-split coordinates: 108.000000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+avg 10 x remap1 from unsplit coordinates:.. 120.600000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+avg 10 x remap with internal spline:....... 143.000000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+avg 10 x remap with functor & internal bspl 146.200000 ms
+avg 10 x remap with functor & external bspl 121.500000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000198
+
+testing bc code NATURAL spline degree 4 using Vc
+avg 10 x prefilter:........................ 11.400000 ms
+avg 10 x remap1 from pre-split coordinates: 33.500000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+avg 10 x remap1 from unsplit coordinates:.. 36.200000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+avg 10 x remap with internal spline:....... 50.100000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+avg 10 x remap with functor & internal bspl 50.500000 ms
+avg 10 x remap with functor & external bspl 37.400000 ms
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000171
+difference original data/restored data:
+warped image diff Mean: 0.000045
+warped image diff Maximum: 0.000198
+
+testing bc code NATURAL spline degree 5
+avg 10 x prefilter:........................ 24.800000 ms
+avg 10 x remap1 from pre-split coordinates: 149.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+avg 10 x remap1 from unsplit coordinates:.. 155.000000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+avg 10 x remap with internal spline:....... 183.200000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+avg 10 x remap with functor & internal bspl 191.400000 ms
+avg 10 x remap with functor & external bspl 158.100000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000180
+
+testing bc code NATURAL spline degree 5 using Vc
+avg 10 x prefilter:........................ 11.000000 ms
+avg 10 x remap1 from pre-split coordinates: 42.600000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+avg 10 x remap1 from unsplit coordinates:.. 45.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+avg 10 x remap with internal spline:....... 58.800000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+avg 10 x remap with functor & internal bspl 59.400000 ms
+avg 10 x remap with functor & external bspl 44.700000 ms
+warped image diff Mean: 0.000023
+warped image diff Maximum: 0.000194
+difference original data/restored data:
+warped image diff Mean: 0.000022
+warped image diff Maximum: 0.000180
+
+testing bc code NATURAL spline degree 6
+avg 10 x prefilter:........................ 36.200000 ms
+avg 10 x remap1 from pre-split coordinates: 203.900000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+avg 10 x remap1 from unsplit coordinates:.. 205.300000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+avg 10 x remap with internal spline:....... 242.000000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+avg 10 x remap with functor & internal bspl 252.000000 ms
+avg 10 x remap with functor & external bspl 208.500000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000174
+
+testing bc code NATURAL spline degree 6 using Vc
+avg 10 x prefilter:........................ 13.100000 ms
+avg 10 x remap1 from pre-split coordinates: 58.400000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+avg 10 x remap1 from unsplit coordinates:.. 59.600000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+avg 10 x remap with internal spline:....... 74.600000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+avg 10 x remap with functor & internal bspl 81.100000 ms
+avg 10 x remap with functor & external bspl 60.200000 ms
+warped image diff Mean: 0.000027
+warped image diff Maximum: 0.000184
+difference original data/restored data:
+warped image diff Mean: 0.000026
+warped image diff Maximum: 0.000174
+
+testing bc code NATURAL spline degree 7
+avg 10 x prefilter:........................ 36.800000 ms
+avg 10 x remap1 from pre-split coordinates: 263.100000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000214
+avg 10 x remap1 from unsplit coordinates:.. 259.700000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000214
+avg 10 x remap with internal spline:....... 308.400000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000214
+avg 10 x remap with functor & internal bspl 301.900000 ms
+avg 10 x remap with functor & external bspl 264.300000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000214
+difference original data/restored data:
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000214
+
+testing bc code NATURAL spline degree 7 using Vc
+avg 10 x prefilter:........................ 14.400000 ms
+avg 10 x remap1 from pre-split coordinates: 70.600000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000307
+avg 10 x remap1 from unsplit coordinates:.. 80.900000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000307
+avg 10 x remap with internal spline:....... 91.300000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000307
+avg 10 x remap with functor & internal bspl 89.800000 ms
+avg 10 x remap with functor & external bspl 73.600000 ms
+warped image diff Mean: 0.000025
+warped image diff Maximum: 0.000307
+difference original data/restored data:
+warped image diff Mean: 0.000024
+warped image diff Maximum: 0.000307
+
+testing bc code NATURAL spline degree 8
+avg 10 x prefilter:........................ 48.500000 ms
+avg 10 x remap1 from pre-split coordinates: 322.400000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+avg 10 x remap1 from unsplit coordinates:.. 328.000000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+avg 10 x remap with internal spline:....... 379.500000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+avg 10 x remap with functor & internal bspl 385.500000 ms
+avg 10 x remap with functor & external bspl 327.500000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000258
+
+testing bc code NATURAL spline degree 8 using Vc
+avg 10 x prefilter:........................ 20.700000 ms
+avg 10 x remap1 from pre-split coordinates: 88.400000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+avg 10 x remap1 from unsplit coordinates:.. 95.800000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+avg 10 x remap with internal spline:....... 112.300000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+avg 10 x remap with functor & internal bspl 110.800000 ms
+avg 10 x remap with functor & external bspl 96.100000 ms
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000243
+difference original data/restored data:
+warped image diff Mean: 0.000031
+warped image diff Maximum: 0.000258
+
+
+testing double data, double coordinates
+Image information:
+ file format: JPEG
+ width: 1920
+ height: 1079
+ pixel type: UINT8
+ color image: yes (number of channels: 3)
+testing bc code MIRROR spline degree 0
+avg 10 x prefilter:........................ 8.200000 ms
+avg 10 x remap1 from pre-split coordinates: 12.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 20.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 31.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 37.700000 ms
+avg 10 x remap with functor & external bspl 24.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 0 using Vc
+avg 10 x prefilter:........................ 9.900000 ms
+avg 10 x remap1 from pre-split coordinates: 12.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 11.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 26.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 26.100000 ms
+avg 10 x remap with functor & external bspl 11.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 1
+avg 10 x prefilter:........................ 8.800000 ms
+avg 10 x remap1 from pre-split coordinates: 19.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 26.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 39.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 41.900000 ms
+avg 10 x remap with functor & external bspl 29.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 1 using Vc
+avg 10 x prefilter:........................ 8.600000 ms
+avg 10 x remap1 from pre-split coordinates: 15.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 14.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 31.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 29.600000 ms
+avg 10 x remap with functor & external bspl 17.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 2
+avg 10 x prefilter:........................ 17.600000 ms
+avg 10 x remap1 from pre-split coordinates: 31.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 39.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 60.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 63.500000 ms
+avg 10 x remap with functor & external bspl 40.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 2 using Vc
+avg 10 x prefilter:........................ 19.600000 ms
+avg 10 x remap1 from pre-split coordinates: 21.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 22.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 46.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 47.900000 ms
+avg 10 x remap with functor & external bspl 22.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 3
+avg 10 x prefilter:........................ 17.300000 ms
+avg 10 x remap1 from pre-split coordinates: 46.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 54.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 77.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 78.900000 ms
+avg 10 x remap with functor & external bspl 58.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 3 using Vc
+avg 10 x prefilter:........................ 18.600000 ms
+avg 10 x remap1 from pre-split coordinates: 31.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 32.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 59.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 58.200000 ms
+avg 10 x remap with functor & external bspl 33.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 4
+avg 10 x prefilter:........................ 18.600000 ms
+avg 10 x remap1 from pre-split coordinates: 69.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 76.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 100.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 103.900000 ms
+avg 10 x remap with functor & external bspl 81.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 4 using Vc
+avg 10 x prefilter:........................ 21.900000 ms
+avg 10 x remap1 from pre-split coordinates: 44.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 46.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 75.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 72.500000 ms
+avg 10 x remap with functor & external bspl 46.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 5
+avg 10 x prefilter:........................ 17.800000 ms
+avg 10 x remap1 from pre-split coordinates: 93.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 101.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 125.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 128.400000 ms
+avg 10 x remap with functor & external bspl 108.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 5 using Vc
+avg 10 x prefilter:........................ 22.700000 ms
+avg 10 x remap1 from pre-split coordinates: 60.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 59.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 88.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 89.700000 ms
+avg 10 x remap with functor & external bspl 62.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 6
+avg 10 x prefilter:........................ 21.700000 ms
+avg 10 x remap1 from pre-split coordinates: 123.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 139.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 159.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 165.800000 ms
+avg 10 x remap with functor & external bspl 138.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 6 using Vc
+avg 10 x prefilter:........................ 24.000000 ms
+avg 10 x remap1 from pre-split coordinates: 79.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 85.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 109.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 110.400000 ms
+avg 10 x remap with functor & external bspl 80.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 7
+avg 10 x prefilter:........................ 20.000000 ms
+avg 10 x remap1 from pre-split coordinates: 164.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 167.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 200.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 196.400000 ms
+avg 10 x remap with functor & external bspl 176.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 7 using Vc
+avg 10 x prefilter:........................ 25.900000 ms
+avg 10 x remap1 from pre-split coordinates: 99.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 107.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 132.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 131.700000 ms
+avg 10 x remap with functor & external bspl 101.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 8
+avg 10 x prefilter:........................ 24.600000 ms
+avg 10 x remap1 from pre-split coordinates: 199.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 210.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 239.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 245.000000 ms
+avg 10 x remap with functor & external bspl 217.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code MIRROR spline degree 8 using Vc
+avg 10 x prefilter:........................ 27.900000 ms
+avg 10 x remap1 from pre-split coordinates: 124.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 123.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 160.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 163.000000 ms
+avg 10 x remap with functor & external bspl 125.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 0
+avg 10 x prefilter:........................ 8.200000 ms
+avg 10 x remap1 from pre-split coordinates: 12.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 19.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 32.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 36.000000 ms
+avg 10 x remap with functor & external bspl 24.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 0 using Vc
+avg 10 x prefilter:........................ 8.800000 ms
+avg 10 x remap1 from pre-split coordinates: 11.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 12.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 26.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 26.600000 ms
+avg 10 x remap with functor & external bspl 12.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 1
+avg 10 x prefilter:........................ 9.000000 ms
+avg 10 x remap1 from pre-split coordinates: 19.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 27.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 39.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 42.700000 ms
+avg 10 x remap with functor & external bspl 30.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 1 using Vc
+avg 10 x prefilter:........................ 9.400000 ms
+avg 10 x remap1 from pre-split coordinates: 14.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 15.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 29.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 32.000000 ms
+avg 10 x remap with functor & external bspl 16.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 2
+avg 10 x prefilter:........................ 16.900000 ms
+avg 10 x remap1 from pre-split coordinates: 30.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 38.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 60.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 65.200000 ms
+avg 10 x remap with functor & external bspl 40.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 2 using Vc
+avg 10 x prefilter:........................ 18.500000 ms
+avg 10 x remap1 from pre-split coordinates: 21.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 22.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 48.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 47.700000 ms
+avg 10 x remap with functor & external bspl 22.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 3
+avg 10 x prefilter:........................ 17.000000 ms
+avg 10 x remap1 from pre-split coordinates: 46.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 57.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 77.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 79.900000 ms
+avg 10 x remap with functor & external bspl 56.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 3 using Vc
+avg 10 x prefilter:........................ 18.800000 ms
+avg 10 x remap1 from pre-split coordinates: 31.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 33.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 58.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 57.100000 ms
+avg 10 x remap with functor & external bspl 34.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 4
+avg 10 x prefilter:........................ 18.600000 ms
+avg 10 x remap1 from pre-split coordinates: 68.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 75.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 104.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 104.000000 ms
+avg 10 x remap with functor & external bspl 78.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 4 using Vc
+avg 10 x prefilter:........................ 20.700000 ms
+avg 10 x remap1 from pre-split coordinates: 43.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 45.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 71.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 74.800000 ms
+avg 10 x remap with functor & external bspl 45.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 5
+avg 10 x prefilter:........................ 19.300000 ms
+avg 10 x remap1 from pre-split coordinates: 99.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 101.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 126.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 130.300000 ms
+avg 10 x remap with functor & external bspl 104.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 5 using Vc
+avg 10 x prefilter:........................ 24.100000 ms
+avg 10 x remap1 from pre-split coordinates: 59.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 61.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 93.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 89.500000 ms
+avg 10 x remap with functor & external bspl 60.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 6
+avg 10 x prefilter:........................ 20.800000 ms
+avg 10 x remap1 from pre-split coordinates: 124.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 132.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 159.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 168.200000 ms
+avg 10 x remap with functor & external bspl 136.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 6 using Vc
+avg 10 x prefilter:........................ 24.500000 ms
+avg 10 x remap1 from pre-split coordinates: 78.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 80.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 111.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 110.600000 ms
+avg 10 x remap with functor & external bspl 85.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 7
+avg 10 x prefilter:........................ 21.900000 ms
+avg 10 x remap1 from pre-split coordinates: 158.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 168.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 195.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 200.500000 ms
+avg 10 x remap with functor & external bspl 171.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 7 using Vc
+avg 10 x prefilter:........................ 26.100000 ms
+avg 10 x remap1 from pre-split coordinates: 98.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 105.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 128.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 135.300000 ms
+avg 10 x remap with functor & external bspl 100.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 8
+avg 10 x prefilter:........................ 24.200000 ms
+avg 10 x remap1 from pre-split coordinates: 198.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 209.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 247.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 242.200000 ms
+avg 10 x remap with functor & external bspl 213.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code REFLECT spline degree 8 using Vc
+avg 10 x prefilter:........................ 28.400000 ms
+avg 10 x remap1 from pre-split coordinates: 131.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 127.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 158.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 160.900000 ms
+avg 10 x remap with functor & external bspl 126.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 0
+avg 10 x prefilter:........................ 8.000000 ms
+avg 10 x remap1 from pre-split coordinates: 11.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 22.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 33.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 35.300000 ms
+avg 10 x remap with functor & external bspl 24.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 0 using Vc
+avg 10 x prefilter:........................ 8.600000 ms
+avg 10 x remap1 from pre-split coordinates: 11.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 11.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 24.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 25.200000 ms
+avg 10 x remap with functor & external bspl 10.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 1
+avg 10 x prefilter:........................ 8.700000 ms
+avg 10 x remap1 from pre-split coordinates: 18.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 26.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 38.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 42.400000 ms
+avg 10 x remap with functor & external bspl 29.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 1 using Vc
+avg 10 x prefilter:........................ 8.700000 ms
+avg 10 x remap1 from pre-split coordinates: 15.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 15.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 29.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 29.900000 ms
+avg 10 x remap with functor & external bspl 17.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 2
+avg 10 x prefilter:........................ 17.100000 ms
+avg 10 x remap1 from pre-split coordinates: 30.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 37.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 59.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 65.400000 ms
+avg 10 x remap with functor & external bspl 41.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 2 using Vc
+avg 10 x prefilter:........................ 19.200000 ms
+avg 10 x remap1 from pre-split coordinates: 20.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 23.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 46.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 48.200000 ms
+avg 10 x remap with functor & external bspl 23.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 3
+avg 10 x prefilter:........................ 17.100000 ms
+avg 10 x remap1 from pre-split coordinates: 46.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 53.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 79.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 76.300000 ms
+avg 10 x remap with functor & external bspl 54.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 3 using Vc
+avg 10 x prefilter:........................ 18.700000 ms
+avg 10 x remap1 from pre-split coordinates: 35.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 31.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 57.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 56.000000 ms
+avg 10 x remap with functor & external bspl 31.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 4
+avg 10 x prefilter:........................ 17.100000 ms
+avg 10 x remap1 from pre-split coordinates: 68.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 75.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 99.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 105.700000 ms
+avg 10 x remap with functor & external bspl 79.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 4 using Vc
+avg 10 x prefilter:........................ 21.400000 ms
+avg 10 x remap1 from pre-split coordinates: 48.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 47.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 73.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 74.200000 ms
+avg 10 x remap with functor & external bspl 46.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 5
+avg 10 x prefilter:........................ 18.200000 ms
+avg 10 x remap1 from pre-split coordinates: 94.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 104.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 126.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 131.600000 ms
+avg 10 x remap with functor & external bspl 103.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 5 using Vc
+avg 10 x prefilter:........................ 21.000000 ms
+avg 10 x remap1 from pre-split coordinates: 60.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 60.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 87.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 87.100000 ms
+avg 10 x remap with functor & external bspl 61.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 6
+avg 10 x prefilter:........................ 20.600000 ms
+avg 10 x remap1 from pre-split coordinates: 128.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 133.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 159.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 161.100000 ms
+avg 10 x remap with functor & external bspl 137.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 6 using Vc
+avg 10 x prefilter:........................ 25.800000 ms
+avg 10 x remap1 from pre-split coordinates: 82.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 78.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 110.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 111.800000 ms
+avg 10 x remap with functor & external bspl 79.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 7
+avg 10 x prefilter:........................ 20.100000 ms
+avg 10 x remap1 from pre-split coordinates: 160.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 174.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 193.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 195.700000 ms
+avg 10 x remap with functor & external bspl 172.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 7 using Vc
+avg 10 x prefilter:........................ 26.400000 ms
+avg 10 x remap1 from pre-split coordinates: 103.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 100.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 130.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 129.400000 ms
+avg 10 x remap with functor & external bspl 102.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 8
+avg 10 x prefilter:........................ 23.400000 ms
+avg 10 x remap1 from pre-split coordinates: 206.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 209.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 239.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 249.000000 ms
+avg 10 x remap with functor & external bspl 214.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code PERIODIC spline degree 8 using Vc
+avg 10 x prefilter:........................ 30.400000 ms
+avg 10 x remap1 from pre-split coordinates: 123.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 126.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 163.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 157.400000 ms
+avg 10 x remap with functor & external bspl 124.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 0
+avg 10 x prefilter:........................ 7.700000 ms
+avg 10 x remap1 from pre-split coordinates: 13.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 17.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 32.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 36.500000 ms
+avg 10 x remap with functor & external bspl 22.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 0 using Vc
+avg 10 x prefilter:........................ 8.500000 ms
+avg 10 x remap1 from pre-split coordinates: 12.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 11.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 26.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 24.000000 ms
+avg 10 x remap with functor & external bspl 10.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 1
+avg 10 x prefilter:........................ 8.300000 ms
+avg 10 x remap1 from pre-split coordinates: 19.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 25.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 39.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 46.100000 ms
+avg 10 x remap with functor & external bspl 28.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 1 using Vc
+avg 10 x prefilter:........................ 9.300000 ms
+avg 10 x remap1 from pre-split coordinates: 14.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 15.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 29.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 30.700000 ms
+avg 10 x remap with functor & external bspl 15.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 2
+avg 10 x prefilter:........................ 17.000000 ms
+avg 10 x remap1 from pre-split coordinates: 30.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 37.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 59.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 63.900000 ms
+avg 10 x remap with functor & external bspl 39.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 2 using Vc
+avg 10 x prefilter:........................ 18.300000 ms
+avg 10 x remap1 from pre-split coordinates: 21.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 22.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 46.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 48.000000 ms
+avg 10 x remap with functor & external bspl 23.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 3
+avg 10 x prefilter:........................ 16.600000 ms
+avg 10 x remap1 from pre-split coordinates: 52.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 53.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 76.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 78.500000 ms
+avg 10 x remap with functor & external bspl 56.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 3 using Vc
+avg 10 x prefilter:........................ 18.700000 ms
+avg 10 x remap1 from pre-split coordinates: 32.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 33.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 55.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 57.500000 ms
+avg 10 x remap with functor & external bspl 31.900000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 4
+avg 10 x prefilter:........................ 18.300000 ms
+avg 10 x remap1 from pre-split coordinates: 68.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 78.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 100.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 102.800000 ms
+avg 10 x remap with functor & external bspl 78.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 4 using Vc
+avg 10 x prefilter:........................ 21.800000 ms
+avg 10 x remap1 from pre-split coordinates: 44.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 44.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 74.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 73.100000 ms
+avg 10 x remap with functor & external bspl 45.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 5
+avg 10 x prefilter:........................ 18.400000 ms
+avg 10 x remap1 from pre-split coordinates: 99.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 100.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 124.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 127.200000 ms
+avg 10 x remap with functor & external bspl 105.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 5 using Vc
+avg 10 x prefilter:........................ 21.200000 ms
+avg 10 x remap1 from pre-split coordinates: 59.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 60.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 93.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 87.500000 ms
+avg 10 x remap with functor & external bspl 60.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 6
+avg 10 x prefilter:........................ 19.900000 ms
+avg 10 x remap1 from pre-split coordinates: 123.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 134.100000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 156.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 169.800000 ms
+avg 10 x remap with functor & external bspl 136.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 6 using Vc
+avg 10 x prefilter:........................ 26.000000 ms
+avg 10 x remap1 from pre-split coordinates: 76.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 79.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 110.800000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 110.800000 ms
+avg 10 x remap with functor & external bspl 83.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 7
+avg 10 x prefilter:........................ 20.200000 ms
+avg 10 x remap1 from pre-split coordinates: 157.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 167.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 195.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 202.100000 ms
+avg 10 x remap with functor & external bspl 169.600000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 7 using Vc
+avg 10 x prefilter:........................ 25.800000 ms
+avg 10 x remap1 from pre-split coordinates: 99.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 104.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 130.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 136.900000 ms
+avg 10 x remap with functor & external bspl 100.300000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 8
+avg 10 x prefilter:........................ 24.100000 ms
+avg 10 x remap1 from pre-split coordinates: 198.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 211.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 246.700000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 246.200000 ms
+avg 10 x remap with functor & external bspl 216.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
+testing bc code NATURAL spline degree 8 using Vc
+avg 10 x prefilter:........................ 30.100000 ms
+avg 10 x remap1 from pre-split coordinates: 129.500000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap1 from unsplit coordinates:.. 125.000000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with internal spline:....... 161.200000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+avg 10 x remap with functor & internal bspl 160.900000 ms
+avg 10 x remap with functor & external bspl 123.400000 ms
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+difference original data/restored data:
+warped image diff Mean: 0.000000
+warped image diff Maximum: 0.000000
+
diff --git a/mapping.h b/mapping.h
new file mode 100644
index 0000000..7819af2
--- /dev/null
+++ b/mapping.h
@@ -0,0 +1,1555 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/*! \file mapping.h
+
+ \brief code to handle the processing of incoming real coordinates
+
+ One might argue that incoming coordinates should be required to be inside
+ the defined range of the coefficients. While this may or may not be enforced
+ or checked with the code given here, I also provide 'mapping' routines which
+ correspond to some common boundary conditions used for a spline. With these
+ mappings, incoming coordinates are 'folded into' the defined range of the spline
+ by applying the relevant transformation, like mirroring or applying a modulo
+ operation for periodic BCs.
+
+ While the mappings are central to the evaluation process, there isn't usually a
+ need to directly handle them: If a bspline object is used to contain a coefficient
+ array and it's metadata, the appropriate mapping can be derived from it, and the
+ mapping doesn't have to be made explicit.
+
+ Note how we use the same enumeration from common.h to codify boundary
+ conditions and bracings, because often they correspond, and, more specifically,
+ those boundary condition codes used to create coefficient arrays (bspline objects)
+ with implicit prefiltering schemes have corresponding mappings.
+
+ for BC codes MIRROR, REFLECT and PERIODIC this means that incoming coordinates will
+ be mapped so that they 'land' at a coordinate inside the defined range. But note that
+ for NATURAL BCs, there is no coordinate inside the defined range that would produce
+ the extrapolated value used for natural boundary conditions, since this value is not
+ to be found anywhere inside the defined range - it's instead the result of a mathematical
+ operation. So the mapping in this case picks the value at the end of the defined range.
+
+ First we set up a few types to handle 'split coordinates'. A split coordinate consits
+ of an integral part and a fractional part. Typically, these coordinates are the result
+ of applying the modf operation to a real-valued coordinate. While commonly the 'splitting'
+ of the real-valued coordinates is performed during the spline evaluation, this can lead
+ to poorer performance, and if the splitting is done beforehand and the split coordinates
+ are reused, there is less arithmetic to perform - at the cost of higher memory use, since
+ split coordinates are bulkier.
+
+ The three classes we use in this context are all intended as n-dimensional objects.
+ The definition of 'split_type' as containing two n-dimensional components instead of
+ a TinyVector of 1D-split types is due to the fact that we need these separate components
+ in the evaluation: the first one determines the origin of the subarray of the braced spline
+ which will be processed into the result, and the second component constitutes a
+ 'nd_fraction', which is needed to calculate the evaluator's weights. So we go with
+ the struct of arrays approach instead an array of structs. A slight disadvantage here is
+ that 1D split coordinates are also formulated as manifolds (TinyVector < ... , 1 >).
+
+ Next we define 'mappings'. Coordinates at which the spline is to be evaluated come in
+ originally as real values. These real values have to be split (therefore the types
+ above), but we also may want to perform the same mirroring or other boundary conditions
+ on input coordinates, since we can only evaluate the spline within it's defined range.
+ This is what mappings do. They apply the boundary conditions and then split the coordinate.
+ Our approach to incoming coordinates outside the spline's defined range is this:
+ If the boundary conditions lend themselves to it, such coordinates are mapped onto
+ coordinates inside the defined range ('folded in', as I say). If the boundary conditions
+ don't work this way (like with 'natural' boundary conditions, where the extrapolated signal
+ is the result of an arithmetic operation), we do something arbitrary to the outlying
+ coordinate (like clamp it to the neares extremal value) in the conviction that a user
+ requesting such boundary conditions is aware of the problem.
+
+ vectorized operation with long ints is not yet available, so for now I'll have to limit
+ the vectorized code to 32bit ints. This will still suffice for most cases, but if the arrays
+ become really large it'll eventually break.
+
+ I hope that by the time this becomes an issue, 64bit int SIMD operation becomes available,
+ until then, if it has to be used, only the non-vectorized variant can be used for long ints,
+ or (slow) workaround code for vectors of long values has to be provided (which I did out of
+ curiosity: it works, but it makes the vectorized code slower than the scalar)
+ Anyway I'd advocate splitting very large coefficient arrays into tiles. Since the support
+ of the evaluation routine is only as large as the spline's order, duplicated strips of
+ coefficients in the margins would take up a comparatively small part of the tile and the
+ access to memory would be more localized, since access within the same tile would be the
+ likely case. I have some hopes for vigra's chunked arrays in this context.
+
+*/
+
+#ifndef VSPLINE_MAPPING_H
+#define VSPLINE_MAPPING_H
+
+#include <vigra/multi_iterator.hxx>
+#include <vigra/multi_math.hxx>
+
+#include "common.h"
+
+namespace vspline {
+
+using namespace std ;
+using namespace vigra ;
+using namespace vigra::multi_math;
+
+typedef int default_ic_type ;
+typedef float default_rc_type ;
+
+// type naming scheme:
+// nd_ : multidimensional
+// mc_ : multichannel
+// ic : integral coordinate/integral component
+// rc : real coordinate/real component
+// value : 'payload' type, like pixel
+// ele : elementary type of val
+// _v: vecorized type, single Vc:Vector or SimdArray, or structure thereof
+
+/// nd_ic_type is simply a TinyVector of integral numbers. These are the
+/// integral parts of the incoming real coordinates, which are used to determine the
+/// location of the window into the coefficient array which will be used to produce the
+/// evaluation result for the incoming coordinates.
+
+template < int N = 1 ,
+ typename IT = default_ic_type >
+struct nd_ic_type
+:public TinyVector < IT , N >
+{
+ typedef IT ic_type ;
+ typedef IT value_type ;
+ enum { dimension = N } ;
+} ;
+
+/// nd_rc_type is a TinyVector of real numbers in the range of [0.0 - 1.0],
+/// in the case of odd splines, or [-0.5 - 0.5] in the case of even splines. They constitute
+/// the fractional part of a real coordinate left over when the integral part is taken away.
+/// They are the 'delta' which is fed into the weight-generating functors producing the
+/// weights of the weighted summation of the coefficients in the window which is defined
+/// by the integral parts of the coordinates.
+
+template < int N = 1 ,
+ typename FT = default_rc_type >
+struct nd_rc_type
+:public TinyVector < FT , N >
+{
+ typedef FT rc_type ;
+ typedef FT value_type ;
+ enum { dimension = N } ;
+} ;
+
+/// struct split_type contains n-dimensional 'split coordinates', consisting of the
+/// integral and fracional part of the original real coordinates, separated so that
+/// they can be processed by the evaluation routine.
+
+template < int N = 1 ,
+ typename IT = default_ic_type ,
+ typename FT = default_rc_type >
+struct split_type
+{
+ typedef IT ic_type ;
+ typedef FT rc_type ;
+ typedef FT value_type ;
+ enum { dimension = N } ;
+ nd_ic_type<N,IT> select ; ///< named select because it selects the range of coefficients
+ nd_rc_type<N,FT> tune ; ///< named tune because it is the base for the weights
+} ;
+
+#ifdef USE_VC
+
+/// since Vc doesn't offer a vectorized modf function, we have to code it. We make the effort not
+/// to simply iterate over the vector's components but write 'proper' vector code to make this
+/// operation as efficient as possible.
+
+template <class real_v>
+real_v v_modf ( const real_v& source ,
+ real_v * const iptr )
+{
+ typedef typename real_v::mask_type mask_type ;
+ typedef typename real_v::EntryType single_type ;
+
+ mask_type negative = Vc::isnegative ( source ) ;
+ real_v help ( source ) ;
+
+ // we treat the case that any of the incoming vector components is negative separately
+ // to avoid the corresponding code altogether in the - hopefully - most common case
+ // where none of the incoming values are in fact negative.
+
+ if ( any_of ( negative ) )
+ {
+ help(negative) = -source ;
+ (*iptr) = Vc::floor ( help ) ;
+ help -= (*iptr) ;
+ (*iptr)(negative) = -(*iptr) ;
+ help(negative) = -help ;
+ return help ;
+ }
+ else
+ {
+ // for all positive components, the operation is trivial:
+ (*iptr) = Vc::floor ( source ) ;
+ return ( help - *iptr ) ;
+ }
+}
+
+/// for now, I'm taking the easy road to a vectorized fmod by using apply and a lambda expression
+/// TODO: handcoding this might increase performance
+
+template <class real_v>
+real_v v_fmod ( const real_v& lhs ,
+ const typename real_v::EntryType rhs )
+{
+ return lhs.apply ( [&rhs] ( typename real_v::EntryType lhs )
+ {
+ return fmod ( lhs , rhs ) ;
+ }
+ ) ;
+}
+
+#endif // USE_VC
+
+/// to handle the transformation of real-valued coordinates to the integer/real pairs
+/// processed by the solver, we use 'mapping' objects. These handle several tasks needed
+/// in this context:
+///
+/// - they apply boundary conditions, like mirroring
+/// - they split the resulting value into an integral and a real part
+/// - they take into account special requirements for odd and even splines
+///
+/// we derive all mappings from this common base class and later on access mappings via
+/// a pointer to the base class, which has a virtual operator().
+///
+/// It's slightly annoying that the folding of the coordinates into the defined range
+/// and the splitting into real and integral part should be lumped together in one operation,
+/// but decoupling the two would require more arithmetic, and the goal is to be as fast as
+/// possible, after all. For now, I'll consider those mappings which perform 'folding in'
+/// of variables as an added bonus; the same results can be achieved by performing a coordinate
+/// transformation routine on the coordinates first and then applying a minimal mapping
+/// like 'reject' or 'limit'.
+///
+/// TODO: perform runtime measurements to see if the 'lumping together' really has a
+/// measurable performance advantage. If not, all mappings performing arithmetic could
+/// be abandoned, which would be cleaner design-wise.
+///
+/// I have made another design decision concerning the size of the bracing. Initially I was
+/// using a right brace which was as small as possible, but this forced me to check incoming
+/// coordinates to odd splines for v == M-1, because these then needed special treatment -
+/// the split coordinate had to be set to M-2, 1.0 instead of M-1, 0.0 which would have
+/// produced an out-of-bounds access. Now, for odd splines, I use a right brace which is
+/// one slice larger, and accessing the spline at M-1, 0.0 is now safe, so the test can be omitted.
+/// I feel the sacrifice of one slice's worth of memory is worth the performance gain and increased
+/// code transparency.
+
+template < typename split_type , int vsize = 1 >
+class mapping
+{
+public:
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+public:
+
+ /// we define a virtual operator(), this is what each specific mapping
+ /// has to provide. Currently all operator() variants are pure virtual
+ /// so that they fail to compile if they aren't defined 'further down'.
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv ) = 0 ;
+
+ /// operator() for this parameter signature is delegated to the first one.
+ /// while the first one operates on single values, here we have a split_type
+ /// (which is n-D), and pick out the split parts for a specific dimension
+
+ void operator() ( real_t v , split_type& s , const int & dim )
+ {
+ operator() ( v , s.select[dim] , s.tune[dim] ) ;
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv ) = 0 ;
+
+#endif
+
+ /// produce the spline coordinates expessed as a floating point value.
+ /// this is to test the mappings and make sure they do what they're supposed to do.
+
+ real_t test ( real_t v )
+ {
+ int_t iv ;
+ real_t fv ;
+ this->operator() ( v , iv , fv ) ;
+ return real_t(iv) + real_t(fv) ;
+ }
+
+} ;
+
+/// the simplest and fastest mapping is the 'raw' mapping. Here, only the split is performed,
+/// come what may. The user is responsible for the suitability of incoming coordinates, it is
+/// silently assumed that 0.0 <= v <= M-1; if this is not true, the results may be false or
+/// the program may crash.
+
+template < typename split_type , int vsize = 1 >
+class odd_mapping_raw: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+public:
+
+ odd_mapping_raw ( int M )
+ { } ;
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ real_t fl_i ;
+ fv = modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ rc_v fl_i ;
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+template < typename split_type , int vsize = 1 >
+class even_mapping_raw: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+public:
+
+ even_mapping_raw ( int M )
+ { } ;
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ real_t fl_i ;
+ fv = modf ( v + real_t ( 0.5 ) , &fl_i ) - real_t ( 0.5 ) ;
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ rc_v fl_i ;
+ v += real_t ( 0.5 ) ;
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder in [-0.5 ... 0.5]
+ fv -= real_t ( 0.5 ) ;
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+// All remaining mappings in this file are safe insofar as out-of-bounds incoming coordinates
+// will either result in an exception or be handled in a way specific to the mapping.
+
+/// the next mapping mode is LIMIT. Here any out-of-bounds coordinates are set to the
+/// nearest valid value.
+
+template < typename split_type , int vsize = 1 >
+class odd_mapping_limit: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+ const real_t _ceiling ;
+
+public:
+
+ odd_mapping_limit ( int M )
+ : _ceiling ( M - 1 )
+ { } ;
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ if ( v < 0.0 )
+ {
+ iv = 0 ; // if v is below 0.0, we pass the value for v == 0.0
+ fv = 0.0 ;
+ return ; // in this case we're done prematurely
+ }
+ else if ( v > _ceiling )
+ {
+ iv = int_t ( _ceiling ) ;
+ fv = 0.0 ;
+ return ;
+ }
+ real_t fl_i ;
+ fv = modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ v ( v > _ceiling ) = _ceiling ;
+
+ v ( v < real_t ( 0.0 ) ) = real_t ( 0.0 ) ;
+
+ rc_v fl_i ;
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+template < typename split_type , int vsize = 1 >
+class even_mapping_limit: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+ const real_t _ceiling ;
+
+public:
+
+ even_mapping_limit ( int M )
+ : _ceiling ( M - 1 )
+ { } ;
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ if ( v > _ceiling )
+ v = real_t ( _ceiling ) ;
+
+ else if ( v < 0 )
+ v = real_t ( 0.0 ) ;
+
+ real_t fl_i ;
+ fv = modf ( v + real_t(0.5) , &fl_i ) - real_t(0.5) ;
+ iv = int_t ( fl_i ) ;
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ v ( v > _ceiling ) = _ceiling ;
+
+ v ( v < real_t ( 0.0 ) ) = real_t ( 0.0 ) ;
+
+ rc_v fl_i ;
+ v += real_t(0.5) ;
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder in [-0.5 ... 0.5]
+ fv -= real_t(0.5) ;
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+/// with mapping mode REJECT, out-of-bounds incoming coordinate values result in
+/// an exception (out_of_bounds). The calling code has to catch the exception if it
+/// wants to proceed. If the mapping is on vector types, some of the result may be
+/// valid. The out-of-bounds results can be recognized by the int part being set to
+/// -1, which is a value which can't be valid. Any out-of-bounds element triggers
+/// the exception.
+/// TODO: We might simply throw the exception and not bother with the -1 masking.
+/// The exception object might be made to contain the offending value.
+
+template < typename split_type , int vsize = 1 >
+class odd_mapping_reject: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+ const real_t _ceiling ;
+
+public:
+
+ odd_mapping_reject ( int M )
+ : _ceiling ( M - 1 )
+ { } ;
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ if ( v < 0.0 || v > _ceiling ) // reject out-of-bounds values
+ throw out_of_bounds() ;
+
+ // now we are sure that v is safely inside [ 0 : _ceiling ]
+ real_t fl_i ;
+ fv = modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ rc_v fl_i ;
+
+ auto too_large = ( v > real_t ( _ceiling ) ) ;
+ auto too_small = ( v < real_t ( 0.0 ) ) ;
+ auto mask = too_small | too_large ;
+
+ if ( any_of ( mask ) )
+ {
+ // v has some out-of-bounds values
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = fl_i ; // set integer part from float representing it
+ iv ( mask ) = int_t ( -1 ) ; // set iv to -1 at out-of-bounds values
+ throw out_of_bounds() ;
+ }
+
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+/// I use the same rejection criterion here as in odd splines, which is debatable:
+/// one might argue that coordinate values from -0.5 to 0.0 and M - 1.0 to M - 0.5
+/// can be processed meaningfully and hence should not be rejected.
+
+template < typename split_type , int vsize = 1 >
+class even_mapping_reject: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+ const real_t _ceiling ;
+
+public:
+
+ even_mapping_reject ( int M )
+ : _ceiling ( M - 1 )
+ { } ;
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ real_t fl_i ;
+ if ( v < 0.0 || v > _ceiling ) // test range
+ throw out_of_bounds() ;
+
+ // split v into integral and remainder in [-0.5 ... 0.5]
+ fv = modf ( v + real_t(0.5) , &fl_i ) - real_t(0.5) ;
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ rc_v fl_i ;
+
+ auto too_large = ( v > _ceiling ) ;
+ auto too_small = ( v < real_t ( 0.0 ) ) ;
+ auto mask = too_small | too_large ;
+
+ if ( any_of ( mask ) )
+ {
+ // v has some out-of-bounds values
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = fl_i ; // set integer part from float representing it
+ iv ( mask ) = int_t ( -1 ) ; // set iv to -1 at out-of-bounds values
+ throw out_of_bounds() ;
+ }
+ // now we are sure that v is safely inside [ 0 : _ceiling [
+ v += real_t(0.5) ;
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder in [-0.5 ... 0.5]
+ fv -= real_t(0.5) ;
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+/// the next mapping is for coefficients/data mirrored at the ends of the defined
+/// range. This is probably the most commonly used type and also the type which
+/// P. Thevenaz recommends. Like all mappings defined in this body of code, it comes
+/// in two variants: one for odd splines and one for even ones. The handling is
+/// different for both cases: in an odd spline, the delta is in [0:1], in even
+/// splines it's in [-0.5:+0.5].
+///
+/// This is mirroring 'on the bounds':
+///
+/// f ( -x ) == f ( x )
+///
+/// f ( (M-1) + x ) == f ( (M-1) - x )
+
+template < typename split_type , int vsize = 1 >
+class odd_mapping_mirror: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+ const real_t _ceiling ;
+ const real_t _ceilx2 ;
+
+public:
+
+ odd_mapping_mirror ( int M )
+ : _ceilx2 ( M + M - 2 ) ,
+ _ceiling ( M - 1 )
+ { } ;
+
+ // with odd splines we have to be careful not to access the coefficient matrix
+ // with iv == M - 1 and this results in extra safeguarding code.
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ real_t fl_i ;
+ if ( v < 0.0 ) // apply mirror left boundary condition
+ v = -v ;
+ if ( v > _ceiling )
+ {
+ v = fmod ( v , _ceilx2 ) ; // map to first period (which is 2M)
+ if ( v > _ceiling )
+ {
+ v = _ceilx2 - v ; // right border mirror
+ }
+ }
+ // now we are sure that v is safely inside [ 0 : _ceiling ]
+ fv = modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ // vectorized version of operator()
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ rc_v fl_i ;
+
+ v = abs ( v ) ; // left mirror, v is now >= 0
+
+ if ( any_of ( v > _ceiling ) )
+ {
+ v = v_fmod ( v , _ceilx2 ) ; // map to one full period
+ v -= _ceiling ; // center
+ v = abs ( v ) ; // map to half period
+ v = _ceiling - v ; // flip
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = fl_i ; // set integer part from float representing it
+ return ;
+ }
+ // here we are sure that v is safely inside [ 0 : _ceiling ]
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+/// even splines give us more 'room to menoevre' - we can safely access the
+/// coefficient matrix at either end of the defined range and even up to half the
+/// spline's unit spacing beyond the boundaries. We pay for this by needing a
+/// brace as large as for the next-larger odd spline. This fact - that the support
+/// for even splines is just as large as the one for the next higher odd spline -
+/// makes them less commonly used.
+
+template < typename split_type , int vsize = 1 >
+class even_mapping_mirror: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+ const real_t _ceiling ;
+ const real_t _ceilx2 ;
+
+public:
+
+ even_mapping_mirror ( int M )
+ : _ceiling ( M - 1 ) ,
+ _ceilx2 ( M + M - 2 )
+ { } ;
+
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ real_t fl_i ;
+ if ( v < 0.0 ) // apply mirror left boundary condition
+ v = -v ;
+ if ( v > _ceiling )
+ {
+ v = fmod ( v , _ceilx2 ) ; // apply right border mirror
+ if ( v > _ceiling )
+ {
+ v = _ceilx2 - v ; // no need to guard against too large v
+ }
+ }
+ // now v is <= _ceiling.
+ // split v into integral and remainder in [-0.5 ... 0.5]
+ fv = modf ( v + real_t(0.5) , &fl_i ) - real_t(0.5) ;
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ rc_v fl_i ;
+
+ v = abs ( v ) ; // left mirror
+ if ( any_of ( v > _ceiling ) )
+ {
+ v = v_fmod ( v , _ceilx2 ) ; // map to one full period
+ v -= _ceiling ; // center
+ v = abs ( v ) ; // map to half period
+ v = _ceiling - v ; // flip
+ }
+ // now we are sure that v is safely inside [ 0 : _ceiling ]
+ v += real_t(0.5) ;
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder in [-0.5 ... 0.5]
+ fv -= real_t(0.5) ;
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+/// Here the periodic mapping is presented so that the first coefficient coincides
+/// with the origin of the coordinate system. The bracing is supposed to have been done with
+/// the periodic bracer (see brace.h), which provides enough coefficients to the right to allow
+/// for one full period to be calculated without bounds checking on the coefficients.
+/// Yet we could also conceive of the coefficients to coincide with coordinates
+/// .5, 1.5, 2.5 ...
+/// Which fits nicely with an even spline, which has a wider defined range due to
+/// the smaller support. In that case we could possibly even save ourselves the last coefficient.
+/// The first period finishes one unit spacing beyond the location of the last knot
+/// point, so if the spline is constructed over N values, the mapping has to be constructed
+/// with parameter M = N + 1. The bracing applied with the periodic bracer makes sure that
+/// coordinates up to M can be processed.
+
+template < typename split_type , int vsize = 1 >
+class odd_mapping_periodic: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+ const real_t _ceiling ;
+
+public:
+
+ odd_mapping_periodic ( int M )
+ : _ceiling ( M - 1 ) { } ;
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ real_t fl_i ;
+ if ( v < 0.0 )
+ {
+ v = _ceiling + fmod ( v , _ceiling ) ; // force to period - 1 and shift to first period
+ }
+ else if ( v >= _ceiling ) // note the use of >= here, v == _ceiling => v = 0
+ {
+ v = fmod ( v , _ceiling ) ; // force to first period (this also results in v <= _ceiling)
+ }
+ fv = modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ rc_v fl_i ;
+ if ( any_of ( v < real_t(0) ) || any_of ( v >= _ceiling ) )
+ {
+ v = v_fmod ( v , _ceiling ) ; // apply modulo (this also results in v <= _ceiling)
+ v ( v < real_t(0) ) += _ceiling ; // shift values below zero up one period
+ }
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+template < typename split_type , int vsize = 1 >
+class even_mapping_periodic: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+ const real_t _ceiling ;
+
+public:
+
+ even_mapping_periodic ( int M )
+ : _ceiling ( M - 1 ) { } ;
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ real_t fl_i ;
+
+ if ( v < 0.0 )
+ {
+ v = _ceiling + fmod ( v , _ceiling ) ; // force to period - 1 and shift to first period
+ }
+ else if ( v >= ( _ceiling ) )
+ {
+ v = fmod ( v , _ceiling ) ; // force to first period
+ }
+ // split v into integral and remainder in [-0.5 ... 0.5]
+ fv = modf ( v + real_t(0.5) , &fl_i ) - real_t(0.5) ;
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ rc_v fl_i ;
+ if ( any_of ( v < real_t(0) ) || any_of ( v >= _ceiling ) )
+ {
+ v = v_fmod ( v , _ceiling ) ; // apply modulo
+ v ( v < real_t(0) ) += _ceiling ; // shift values below zero up one period
+ }
+ v += real_t(0.5) ;
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder in [-0.5 ... 0.5]
+ fv -= real_t(0.5) ;
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+/// now the mapping for natural boundary conditions. Note that I use a generalization
+/// here: classically natural boundary conditions mean that all derivatives of the spline
+/// above the first are zero. Since the DSP approach to b-splines does not consider
+/// derivatives at the ends of the spline, there must be a different method to obtain the
+/// same result. I use point symmetry at the ends of the spline, which has the desired
+/// effect without using the derivatives explicitly.
+/// here we cannot obtain coordinates to data inside the defined range which would yield us
+/// values for the extrapolated signal (at least not in the adjoining area), since the
+/// values in the extrapolated signal are defined by
+/// f(x) - f(0) == f(0) - f(-x); f(x+n-1) - f(n-1) == f(n-1) - f (n-1-x)
+/// so there is an arithmetic operation involved which we can't represent by indexing.
+/// In other words, we can't 'fold' in the coordinates from outside the defined range
+/// as we could with periodic or mirror bcs.
+/// nevertheless we want to perform the usual transformations to real indices which
+/// correspond to data *inside* the defined range. What we do outside the defined range
+/// is arbitrary, so here I repeat the value at the bounds ad infinitum.
+/// TODO: this is silly, since it's not really specific to natural BCs.
+/// Might call it 'clamp' instead.
+
+template < typename split_type , int vsize = 1 >
+class odd_mapping_constant: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+ real_t _ceiling ;
+
+public:
+
+ odd_mapping_constant ( int M )
+ : _ceiling ( M - 1 ) { } ;
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ real_t fl_i ;
+ if ( v < 0.0 )
+ {
+ iv = 0 ; // if v is below 0.0, we pass the value for v == 0.0
+ fv = 0.0 ;
+ return ; // in this case we're done prematurely
+ }
+ else if ( v > _ceiling )
+ {
+ iv = int_t ( _ceiling ) ;
+ fv = 0.0 ;
+ return ;
+ }
+ fv = modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ rc_v fl_i ;
+ v ( v < real_t ( 0 ) ) = real_t ( 0 ) ;
+ v ( v > _ceiling ) = _ceiling ;
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+template < typename split_type , int vsize = 1 >
+class even_mapping_constant: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+ real_t _ceiling ;
+
+public:
+
+ even_mapping_constant ( int M )
+ : _ceiling ( M - 1 ) { } ;
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ if ( v < 0.0 )
+ {
+ iv = 0 ; // if v is below 0.0, we pass the value for v == 0.0
+ fv = 0.0 ;
+ return ; // in this case we're done prematurely
+ }
+ else if ( v > _ceiling )
+ {
+ iv = int_t ( _ceiling ) ;
+ fv = 0.0 ;
+ return ;
+ }
+ // split v into integral and remainder in [-0.5 ... 0.5]
+ real_t fl_i ;
+ fv = modf ( v + real_t(0.5) , &fl_i ) - real_t(0.5) ;
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ rc_v fl_i ;
+ v ( v < real_t(0) ) = real_t(0) ;
+ v ( v > _ceiling ) = _ceiling ;
+ v += real_t(0.5) ;
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder in [-0.5 ... 0.5]
+ fv -= real_t(0.5) ;
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+/// Mapping for REFLECT boundary conditions for odd splines
+///
+/// This mapping will map coordinates to [ -0.5 - M-0.5 ] and needs a spline with
+/// a wider bracing. If the spline is created via a bspline object with REFLECT BCs,
+/// this will be done automatically. The widened range is due to the point of reflection,
+/// which is half a unit spacing 'further out' than for MIRROR BCs.
+/// In my initial implementation I was using a coordinate shift by 0.5 to put the
+/// origin of the unmapped coordinates to the point of reflection, but I have now changed
+/// the behaviour to use the same origin as all the other mappings, for reasons of
+/// consistency and easier automatic testing - this way, the restoration with grid
+/// coordinates, which is used in the roundtrip test, will produce the expected result.
+/// So, the left point of reflection as at -0.5, the right point of reflection at M - 0.5.
+
+template < typename split_type , int vsize = 1 >
+class odd_mapping_reflect: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+ const real_t _ceiling ;
+ const real_t _ceilx2 ;
+
+public:
+
+ odd_mapping_reflect ( int M )
+ : _ceiling ( M - 2 ) ,
+ _ceilx2 ( M + M - 4 )
+ { } ;
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ v += real_t ( 0.5 ) ; // delete this to change back to origin at reflection
+ real_t fl_i ;
+ if ( v < real_t ( 0.0 ) ) // apply reflect left boundary condition
+ v = -v ;
+ if ( v > _ceiling )
+ {
+ v = fmod ( v , _ceilx2 ) ; // apply right border reflect
+ if ( v > _ceiling )
+ {
+ v = _ceilx2 - v ;
+ }
+ }
+ // now we have v in [ 0 | _ceiling ]
+ // which corresponds to spline coordinates [ 0.5 , _ceiling + 0.5 ]
+ v += 0.5 ;
+ fv = modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ rc_v fl_i ;
+ v += real_t ( 0.5 ) ;
+ v = abs ( v ) ;
+ if ( any_of ( v > _ceiling ) )
+ {
+ v = v_fmod ( v , _ceilx2 ) ; // map to one full period
+ v ( v > _ceiling ) = _ceilx2 - v ;
+ }
+ v += real_t(0.5) ;
+ fv = v_modf ( v , &fl_i ) ; // split v into integral and remainder from [0...1[
+ iv = fl_i ; // set integer part from float representing it
+ }
+
+#endif
+} ;
+
+/// mapping for REFLECT boundary conditions for even splines
+/// this mapping will map coordinates to [ -0.5 - M-0.5 ].
+/// for the even case, we don't need the widened brace, since it has smaller support,
+/// but we have to guard against the special case that v == M - 0.5, since this might
+/// be mapped to M, -0.5 which is outside the defined range. In this special case
+/// we use M-1, 0.5 instead which is equivalent and inside the range.
+
+template < typename split_type , int vsize = 1 >
+class even_mapping_reflect: public mapping < split_type , vsize >
+{
+
+
+ typedef typename split_type::ic_type int_t ;
+ typedef typename split_type::rc_type real_t ;
+
+ const real_t _ceiling ;
+ const real_t _ceilx2 ;
+
+public:
+
+ even_mapping_reflect ( int M )
+ : _ceiling ( M ) ,
+ _ceilx2 ( M + M )
+ { } ;
+
+ virtual void operator() ( real_t v , int_t& iv , real_t& fv )
+ {
+ v += real_t ( 0.5 ) ;
+ real_t fl_i ;
+ if ( v < 0.0 ) // apply reflect left boundary condition
+ v = -v ;
+ if ( v > _ceiling )
+ {
+ v = fmod ( v , _ceilx2 ) ; // apply right border reflect
+ if ( v > _ceiling )
+ {
+ v = _ceilx2 - v ;
+ }
+ }
+ if ( v >= _ceiling ) // guard against special == case; defensively use >= instead of ==
+ {
+ iv = _ceiling - 1 ;
+ fv = real_t ( 0.5 ) ;
+ return ;
+ }
+ // now we have v in [ 0 | _ceiling ]
+ // which corresponds to spline coordinates [ -0.5 | _ceiling + 0.5 [
+ // split v into integral and remainder in [-0.5 ... 0.5]
+ fv = modf ( v , &fl_i ) - real_t(0.5) ;
+ iv = int_t ( fl_i ) ; // set integer part from float representing it
+ }
+
+#ifdef USE_VC
+
+ typedef Vc::SimdArray < int_t , vsize > ic_v ;
+ typedef Vc::SimdArray < real_t , vsize > rc_v ;
+
+ /// this is the operator() for vectorized operation
+
+ virtual void operator() ( rc_v v , ic_v & iv , rc_v & fv )
+ {
+ rc_v fl_i ;
+ v += real_t ( 0.5 ) ;
+ v = abs ( v ) ;
+ if ( any_of ( v > _ceiling ) )
+ {
+ v = v_fmod ( v , _ceilx2 ) ; // map to one full period
+ v ( v > _ceiling ) = _ceilx2 - v ;
+ }
+ fv = v_modf ( v , &fl_i ) - real_t(0.5) ;
+ iv = fl_i ; // set integer part from float representing it
+ auto mask = ( v >= _ceiling ) ; // guard against special == case; defensively use >= instead of ==
+ if ( any_of ( mask ) )
+ {
+ iv ( mask ) = _ceiling - 1 ;
+ fv ( mask ) = real_t ( 0.5 ) ;
+ }
+ }
+
+#endif
+} ;
+
+/// create a mapping for an odd-degree spline given a BC code, the spline degree,
+/// and the extent along the axis in question
+
+template < typename split_type , int vsize = 1 >
+mapping < split_type , vsize > * create_odd_mapping ( bc_code bc , int spline_degree , int M )
+{
+ switch ( bc )
+ {
+ case RAW :
+ {
+ return new odd_mapping_raw < split_type , vsize > ( M ) ;
+ break ;
+ }
+ case LIMIT :
+ {
+ return new odd_mapping_limit < split_type , vsize > ( M ) ;
+ break ;
+ }
+ case REJECT :
+ {
+ return new odd_mapping_reject < split_type , vsize > ( M ) ;
+ break ;
+ }
+ case CONSTANT :
+ case NATURAL :
+ {
+ return new odd_mapping_constant < split_type , vsize > ( M ) ;
+ break ;
+ }
+ case MIRROR :
+ {
+ return new odd_mapping_mirror < split_type , vsize > ( M ) ;
+ break ;
+ }
+ case REFLECT :
+ case SPHERICAL :
+ {
+ return new odd_mapping_reflect < split_type , vsize > ( M + 2 ) ;
+ break ;
+ }
+ case PERIODIC :
+ {
+ return new odd_mapping_periodic < split_type , vsize > ( M + 1 ) ;
+ break ;
+ }
+ default:
+ {
+ // TODO: throw exception instead?
+ cerr << "mapping for BC code " << bc_name[bc] << " is not supported" << endl ;
+ return new odd_mapping_reject < split_type , vsize > ( M ) ;
+ break ;
+ }
+ }
+}
+
+/// create a mapping for an even-degree spline given a BC code, the spline degree,
+/// and the extent along the axis in question
+
+template < typename split_type , int vsize = 1 >
+mapping < split_type , vsize > * create_even_mapping ( bc_code bc , int spline_degree , int M )
+{
+ switch ( bc )
+ {
+ case RAW :
+ {
+ return new even_mapping_raw < split_type , vsize > ( M ) ;
+ break ;
+ }
+ case LIMIT :
+ {
+ return new even_mapping_limit < split_type , vsize > ( M ) ;
+ break ;
+ }
+ case REJECT :
+ {
+ return new even_mapping_reject < split_type , vsize > ( M ) ;
+ break ;
+ }
+ case NATURAL :
+ case CONSTANT :
+ {
+ return new even_mapping_constant < split_type , vsize > ( M ) ;
+ break ;
+ }
+ case MIRROR :
+ {
+ return new even_mapping_mirror < split_type , vsize > ( M ) ;
+ break ;
+ }
+ case REFLECT :
+ case SPHERICAL :
+ {
+ return new even_mapping_reflect < split_type , vsize > ( M ) ;
+ break ;
+ }
+ case PERIODIC :
+ {
+ return new even_mapping_periodic < split_type , vsize > ( M + 1 ) ;
+ break ;
+ }
+ default:
+ {
+ // TODO: throw exception instead?
+ cerr << "mapping for BC code " << bc_name[bc] << " is not supported" << endl ;
+ return new even_mapping_reject < split_type , vsize > ( M ) ;
+ break ;
+ }
+ }
+}
+
+/// class nd_mapping handles a set of mappings, one per axis.
+/// It provides the same operations as the 1D mapping class; for 1D use the axis
+/// to which the mapping is applied is determined by an additional parameter,
+/// axis. Internally, the mappings are accessed via a pointer to the base class.
+/// The additional routines (without exis parameter) iterate over all axes.
+/// Since in this object we keep base class pointers to the actual mappings,
+/// we need additional code to assure proper deletion of the mapping objects,
+/// which are created by new. Hence the copy constructor and assignment operator.
+
+template < typename split_type , int dimension , int vsize = 1 >
+class nd_mapping
+{
+public:
+
+ typedef mapping < split_type , vsize > mapping_type ;
+
+ typedef typename mapping_type::int_t int_t ;
+ typedef typename mapping_type::real_t real_t ;
+ typedef TinyVector < bc_code , dimension > bcv_type ;
+ typedef TinyVector < MultiArrayIndex , dimension > shape_type ;
+ typedef TinyVector < real_t , dimension > nd_real_t ;
+ typedef TinyVector < int_t , dimension > nd_int_t ;
+
+#ifdef USE_VC
+
+ typedef typename mapping_type::rc_v real_v ;
+ typedef typename mapping_type::ic_v int_v ;
+ typedef TinyVector < real_v , dimension > nd_real_v ;
+ typedef TinyVector < int_v , dimension > nd_int_v ;
+
+#endif
+
+private:
+
+ TinyVector < mapping_type* , dimension > map ; // container for the mappings
+ bcv_type bcv ;
+ int spline_degree ;
+ shape_type shape ;
+
+public:
+
+ /// 'standard' constructor for a nd_mapping. This constructor takes the
+ /// values which a nd_mapping requires for it's operation directly, and the
+ /// other constructors delegate to it.
+
+ nd_mapping ( const bcv_type& _bcv ,
+ const int& _spline_degree ,
+ const shape_type& _shape )
+ : bcv ( _bcv ) ,
+ spline_degree ( _spline_degree ) ,
+ shape ( _shape )
+ {
+ for ( int d = 0 ; d < dimension ; d++ )
+ {
+ if ( spline_degree & 1 )
+ map[d] = create_odd_mapping < split_type , vsize > ( bcv[d] , spline_degree , shape[d] ) ;
+ else
+ map[d] = create_even_mapping < split_type , vsize > ( bcv[d] , spline_degree , shape[d] ) ;
+ }
+ }
+
+ /// convenience variant taking a single boundary condition code, which is used for all axes
+
+ nd_mapping ( const bc_code& bc ,
+ const int& _spline_degree ,
+ const shape_type& _shape )
+ : nd_mapping ( bcv_type ( bc ) , spline_degree , _shape )
+ {
+ } ;
+
+ /// convenience variant constructing a nd_mapping from a bspline object
+ /// A bspline object has all components needed to construct a nd_mapping:
+ /// It has a set of boundary condition codes (those the spline was constructed with),
+ /// the spline's degree and the core shape of it's coefficient array, giving us the limits
+ /// for the mapping. Creating the mapping like this keeps us from using mapping modes
+ /// RAW, LIMIT and REJECT, since these are only codes for mappings, not for boundary
+ /// conditions a spline can be constructed with. But all the BC codes used for spline
+ /// construction, like MIRROR, REFLECT, NATURAL and PERIODIC have corresponding
+ /// mappings.
+
+ template < class bspline >
+ nd_mapping ( const bspline & bspl )
+ : nd_mapping ( bspl.bcv ,
+ bspl.spline_degree ,
+ bspl.core_shape )
+ { } ;
+
+ /// since we have members which need delete, we need a copy constructor
+
+ nd_mapping ( const nd_mapping& other )
+ : nd_mapping ( other.bcv , other.spline_degree , other.shape )
+ { } ;
+
+ /// assignment operator, for the same reason
+
+ nd_mapping& operator= ( const nd_mapping& other )
+ {
+ bcv = other.bcv ;
+ shape = other.shape ;
+ spline_degree = other.spline_degree ;
+ for ( int d = 0 ; d < dimension ; d++ )
+ {
+ if ( map[d] )
+ delete map[d] ;
+ if ( spline_degree & 1 )
+ map[d] = create_odd_mapping < split_type , vsize > ( bcv[d] , spline_degree , shape[d] ) ;
+ else
+ map[d] = create_even_mapping < split_type , vsize > ( bcv[d] , spline_degree , shape[d] ) ;
+ }
+ }
+
+ /// the destructor deletes the mapping objects. If the nd_mapping was
+ /// default-constructed, these will be 0, so we have to safeguard against this case.
+ /// It would be nice if we could provide a default constructor, but there is no
+ /// sensible default shape or spline degree we could provide.
+
+ ~nd_mapping()
+ {
+ for ( int d = 0 ; d < dimension ; d++ )
+ {
+ if ( map[d] )
+ delete map[d] ;
+ }
+ }
+
+ /// apply the mapping along axis 'axis' to coordinate v, resulting in the setting
+ /// of the integral part iv and the fraczional part fv
+
+ void operator() ( real_t v , int_t& iv , real_t& fv , const int & axis )
+ {
+ ( * ( map [ axis ] ) ) ( v , iv , fv ) ;
+ }
+
+ /// same operation, but along all axes, taking a multi-coordinate and setting
+ /// the corresponding n-dimensional objects for the integral and fractional parts
+
+ void operator() ( nd_real_t v , nd_int_t& iv , nd_real_t& fv )
+ {
+ for ( int axis = 0 ; axis < dimension ; axis++ )
+ ( * ( map [ axis ] ) ) ( v[axis] , iv[axis] , fv[axis] ) ;
+ }
+
+ /// different signature, now we handle a split_type object as the operation's target
+
+ void operator() ( real_t v , split_type& s , const int & axis )
+ {
+ ( * ( map [ axis ] ) ) ( v , s.select[axis] , s.tune[axis] ) ;
+ }
+
+ /// the same in nD
+
+ void operator() ( nd_real_t v , split_type& s )
+ {
+ for ( int axis = 0 ; axis < dimension ; axis++ )
+ ( * ( map [ axis ] ) ) ( v[axis] , s.select[axis] , s.tune[axis] ) ;
+ }
+
+#ifdef USE_VC
+
+ /// finally, operation on Vc vectors of incoming coordinates. Again, first the
+ /// 1D version with the 'axis' parameter
+
+ void operator() ( Vc::Vector < real_t > v ,
+ Vc::SimdArray < int_t , Vc::Vector<real_t>::size() >& iv ,
+ Vc::Vector < real_t >& fv ,
+ const int & axis )
+ {
+ ( * ( map [ axis ] ) ) ( v , iv , fv ) ;
+ }
+
+ /// and the nD version, operating on vector aggregates
+
+ void operator() ( nd_real_v v ,
+ nd_int_v & iv ,
+ nd_real_v & fv )
+ {
+ for ( int axis = 0 ; axis < dimension ; axis++ )
+ ( * ( map [ axis ] ) ) ( v[axis] , iv[axis] , fv[axis] ) ;
+ }
+
+#endif // USE_VC
+} ;
+
+/*
+void mapping_test()
+{
+ float in[] = { -1.0 , -0.5 , 0.0 , 0.5 , 8.5 , 9.0 , 9.4999 , 9.5 , 9.501 , 10.0 , 10.5 , 11.0 , 11.5 , 12.0 } ;
+ const int innum = sizeof(in) / sizeof(float) ;
+
+ typedef split_type<> st ;
+
+ mapping<st,innum> * pmap[] = { new odd_mapping_mirror<st,innum>(10) ,
+ new even_mapping_mirror<st,innum>(10) ,
+ new odd_mapping_periodic<st,innum>(10) ,
+ new even_mapping_periodic<st,innum>(10) ,
+ new odd_mapping_reflect<st,innum>(12) ,
+ new even_mapping_reflect<st,innum>(10) ,
+ new odd_mapping_constant<st,innum>(10) ,
+ new even_mapping_constant<st,innum>(10) } ;
+ char * nmap[] = { "odd_mapping_mirror(10)" ,
+ "even_mapping_mirror(10)" ,
+ "odd_mapping_periodic(10)" ,
+ "even_mapping_periodic(10)" ,
+ "odd_mapping_reflect(12)" ,
+ "even_mapping_reflect(10)" ,
+ "odd_mapping_constant(10)" ,
+ "even_mapping_constant(10)" } ;
+ mapping<st,innum>::int_t oi ;
+ mapping<st,innum>::real_t of ;
+ mapping<st,innum>::ic_v v_oi ;
+ mapping<st,innum>::rc_v v_of ;
+ mapping<st,innum>::rc_v v_in ;
+
+ for ( int i = 0 ; i < sizeof ( pmap ) / sizeof ( mapping<st,innum>* ) ; i++ )
+ {
+ cout << "testing " << nmap[i] << endl ;
+ for ( int k = 0 ; k < sizeof ( in ) / sizeof ( float ) ; k++ )
+ {
+ (*(pmap[i])) ( in[k] , oi , of ) ;
+ cout << in[k] << " -> " << oi << ", " << of << endl ;
+ }
+ }
+ for ( int i = 0 ; i < sizeof ( pmap ) / sizeof ( mapping<st,innum>* ) ; i++ )
+ {
+ cout << "testing " << nmap[i] << endl ;
+// for ( int k = 0 ; k < sizeof ( in ) / sizeof ( float ) ; k++ )
+// {
+ v_in = mapping<st,innum>::rc_v ( in ) ;
+ (*(pmap[i])) ( v_in , v_oi , v_of ) ;
+ cout << v_in << " -> " << v_oi << ", " << v_of << endl ;
+// }
+ }
+}
+*/
+} ; // end of namespace vspline
+
+#endif // VSPLINE_MAPPING_H
diff --git a/poles.cc b/poles.cc
new file mode 100644
index 0000000..f91715b
--- /dev/null
+++ b/poles.cc
@@ -0,0 +1,660 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/*! \file poles.cc
+
+ \brief precalculated prefilter poles and basis function values
+
+ The contents of this file below the coments can be generated using prefilter_poles.cc
+ While the precalculated basis function values can be generated in long double
+ precision (with code in basis.h), the filter poles are calculated using
+ gsl and BLAS, which provide only double precision.
+*/
+
+long double K0[] = {
+ 1L , // basis(0)
+ } ;
+long double K1[] = {
+ 1L , // basis(0)
+ 0.5L , // basis(0.5)
+ } ;
+long double K2[] = {
+ 0.75L , // basis(0)
+ 0.5L , // basis(0.5)
+ 0.125L , // basis(1)
+ } ;
+double Poles_2[] = {
+-0.17157287525381015314 ,
+} ;
+long double K3[] = {
+ 0.66666666666666666668L , // basis(0)
+ 0.47916666666666666666L , // basis(0.5)
+ 0.16666666666666666667L , // basis(1)
+ 0.020833333333333333334L , // basis(1.5)
+ } ;
+double Poles_3[] = {
+-0.26794919243112280682 ,
+} ;
+long double K4[] = {
+ 0.59895833333333333332L , // basis(0)
+ 0.45833333333333333334L , // basis(0.5)
+ 0.19791666666666666667L , // basis(1)
+ 0.041666666666666666668L , // basis(1.5)
+ 0.0026041666666666666667L , // basis(2)
+ } ;
+double Poles_4[] = {
+-0.36134122590021944266 ,
+-0.013725429297339164503 ,
+} ;
+long double K5[] = {
+ 0.55000000000000000001L , // basis(0)
+ 0.4380208333333333333L , // basis(0.5)
+ 0.21666666666666666667L , // basis(1)
+ 0.061718750000000000001L , // basis(1.5)
+ 0.0083333333333333333337L , // basis(2)
+ 0.00026041666666666666668L , // basis(2.5)
+ } ;
+double Poles_5[] = {
+-0.43057534709997430378 ,
+-0.043096288203264443428 ,
+} ;
+long double K6[] = {
+ 0.51102430555555555553L , // basis(0)
+ 0.41944444444444444449L , // basis(0.5)
+ 0.22879774305555555554L , // basis(1)
+ 0.079166666666666666666L , // basis(1.5)
+ 0.015668402777777777778L , // basis(2)
+ 0.001388888888888888889L , // basis(2.5)
+ 2.170138888888888889e-05L , // basis(3)
+ } ;
+double Poles_6[] = {
+-0.48829458930304570075 ,
+-0.081679271076237264237 ,
+-0.0014141518083258114435 ,
+} ;
+long double K7[] = {
+ 0.47936507936507936512L , // basis(0)
+ 0.4025964161706349206L , // basis(0.5)
+ 0.23630952380952380952L , // basis(1)
+ 0.094024367559523809525L , // basis(1.5)
+ 0.023809523809523809525L , // basis(2)
+ 0.0033776661706349206347L , // basis(2.5)
+ 0.00019841269841269841271L , // basis(3)
+ 1.5500992063492063493e-06L , // basis(3.5)
+ } ;
+double Poles_7[] = {
+-0.5352804307964414976 ,
+-0.12255461519232610512 ,
+-0.0091486948096082820747 ,
+} ;
+long double K8[] = {
+ 0.45292096819196428568L , // basis(0)
+ 0.38737599206349206352L , // basis(0.5)
+ 0.24077768477182539682L , // basis(1)
+ 0.10647321428571428571L , // basis(1.5)
+ 0.032126968625992063494L , // basis(2)
+ 0.0061259920634920634923L , // basis(2.5)
+ 0.00063476562499999999998L , // basis(3)
+ 2.4801587301587301589e-05L , // basis(3.5)
+ 9.6881200396825396832e-08L , // basis(4)
+ } ;
+double Poles_8[] = {
+-0.57468690924881216109 ,
+-0.16303526929727354955 ,
+-0.023632294694844447475 ,
+-0.00015382131064169135559 ,
+} ;
+long double K9[] = {
+ 0.43041776895943562614L , // basis(0)
+ 0.3736024025676532187L , // basis(0.5)
+ 0.24314925044091710759L , // basis(1)
+ 0.1168385769744819224L , // basis(1.5)
+ 0.040255731922398589063L , // basis(2)
+ 0.0094531293058311287482L , // basis(2.5)
+ 0.0013833774250440917108L , // basis(3)
+ 0.00010588576974481922398L , // basis(3.5)
+ 2.7557319223985890654e-06L , // basis(4)
+ 5.3822889109347442683e-09L , // basis(4.5)
+ } ;
+double Poles_9[] = {
+-0.60799738916862233751 ,
+-0.20175052019315406482 ,
+-0.04322260854048156492 ,
+-0.0021213069031808251541 ,
+} ;
+long double K10[] = {
+ 0.41096264282441854056L , // basis(0)
+ 0.36109843474426807762L , // basis(0.5)
+ 0.24406615618885719797L , // basis(1)
+ 0.12543871252204585538L , // basis(1.5)
+ 0.047983348920442019401L , // basis(2)
+ 0.013183421516754850087L , // basis(2.5)
+ 0.0024532852307408785274L , // basis(3)
+ 0.00027915564373897707232L , // basis(3.5)
+ 1.5887978636188271604e-05L , // basis(4)
+ 2.7557319223985890654e-07L , // basis(4.5)
+ 2.6911444554673721342e-10L , // basis(5)
+ } ;
+double Poles_10[] = {
+-0.63655066396958059904 ,
+-0.23818279837754796624 ,
+-0.065727033228304657109 ,
+-0.0075281946755491966489 ,
+-1.6982762823274620556e-05 ,
+} ;
+long double K11[] = {
+ 0.39392556517556517558L , // basis(0)
+ 0.34970223188744306906L , // basis(0.5)
+ 0.24396028739778739779L , // basis(1)
+ 0.13256116543210659421L , // basis(1.5)
+ 0.055202020202020202023L , // basis(2)
+ 0.017163149607531321399L , // basis(2.5)
+ 0.0038238786676286676285L , // basis(3)
+ 0.00057128626126327135446L , // basis(3.5)
+ 5.1006092672759339424e-05L , // basis(4)
+ 2.1667994232691498315e-06L , // basis(4.5)
+ 2.5052108385441718776e-08L , // basis(5)
+ 1.2232474797578964246e-11L , // basis(5.5)
+ } ;
+double Poles_11[] = {
+-0.66126606890063921451 ,
+-0.27218034929481393913 ,
+-0.089759599793708844118 ,
+-0.016669627366234951449 ,
+-0.00051055753444649576434 ,
+} ;
+long double K12[] = {
+ 0.37884408454472999147L , // basis(0)
+ 0.33927295023649190319L , // basis(0.5)
+ 0.24313091801014469471L , // basis(1)
+ 0.13845146655042488376L , // basis(1.5)
+ 0.061867668009041325489L , // basis(2)
+ 0.021268582401394901395L , // basis(2.5)
+ 0.0054581869256967252307L , // basis(3)
+ 0.00099847474413446635658L , // basis(3.5)
+ 0.00012091392059187537162L , // basis(4)
+ 8.5239798781465448132e-06L , // basis(4.5)
+ 2.7086165069699140876e-07L , // basis(5)
+ 2.0876756987868098981e-09L , // basis(5.5)
+ 5.0968644989912351028e-13L , // basis(6)
+ } ;
+double Poles_12[] = {
+-0.68286488419809487915 ,
+-0.30378079328817336746 ,
+-0.11435052002714780894 ,
+-0.028836190198661435652 ,
+-0.0025161662172618224839 ,
+-1.883305645063344802e-06 ,
+} ;
+long double K13[] = {
+ 0.36537086948545281884L , // basis(0)
+ 0.32968987958591001189L , // basis(0.5)
+ 0.24178841798633465302L , // basis(1)
+ 0.14331501747174208366L , // basis(1.5)
+ 0.067974967258821425491L , // basis(2)
+ 0.0254044062949849888L , // basis(2.5)
+ 0.0073122366959172514726L , // basis(3)
+ 0.0015671731081656330546L , // basis(3.5)
+ 0.00023762984700484700481L , // basis(4)
+ 2.3492285420207986942e-05L , // basis(4.5)
+ 1.3133086049752716419e-06L , // basis(5)
+ 3.125375747123929632e-08L , // basis(5.5)
+ 1.6059043836821614601e-10L , // basis(6)
+ 1.960332499612013501e-14L , // basis(6.5)
+ } ;
+double Poles_13[] = {
+-0.701894251817016257 ,
+-0.33310723293052579841 ,
+-0.13890111319434489401 ,
+-0.043213866740361948915 ,
+-0.0067380314152448743045 ,
+-0.00012510011321441739246 ,
+} ;
+long double K14[] = {
+ 0.35323915669918929845L , // basis(0)
+ 0.32085024502063192543L , // basis(0.5)
+ 0.24008299041558734203L , // basis(1)
+ 0.14732180094624291054L , // basis(1.5)
+ 0.073541032564067060978L , // basis(2)
+ 0.029499800232377117299L , // basis(2.5)
+ 0.009341081854512256905L , // basis(3)
+ 0.002275919650051594496L , // basis(3.5)
+ 0.00041109051149372196722L , // basis(4)
+ 5.2046374591017448155e-05L , // basis(4.5)
+ 4.2229561084936041826e-06L , // basis(5)
+ 1.8776463468923786384e-07L , // basis(5.5)
+ 3.3486357751247422931e-09L , // basis(6)
+ 1.1470745597729724715e-11L , // basis(6.5)
+ 7.0011874986143339324e-16L , // basis(7)
+ } ;
+double Poles_14[] = {
+-0.71878378723766189751 ,
+-0.36031907191881451524 ,
+-0.16303351479903732679 ,
+-0.059089482194828991946 ,
+-0.013246756734847169382 ,
+-0.00086402404095337124838 ,
+-2.0913096775274000322e-07 ,
+} ;
+long double K15[] = {
+ 0.34224026135534072046L , // basis(0)
+ 0.31266660625176080971L , // basis(0.5)
+ 0.23812319491070731152L , // basis(1)
+ 0.15061194980399698684L , // basis(1.5)
+ 0.078595253866748575742L , // basis(2)
+ 0.033503802571649835525L , // basis(2.5)
+ 0.011502274487496875064L , // basis(3)
+ 0.003117493948498863913L , // basis(3.5)
+ 0.00064854900635323915745L , // basis(4)
+ 9.9440249438946462506e-05L , // basis(4.5)
+ 1.057200426826749578e-05L , // basis(5)
+ 7.0683979027987963181e-07L , // basis(5.5)
+ 2.5045990654456262921e-08L , // basis(6)
+ 3.348642542939324287e-10L , // basis(6.5)
+ 7.6471637318198164765e-13L , // basis(7)
+ 2.3337291662047779775e-17L , // basis(7.5)
+ } ;
+double Poles_15[] = {
+-0.73387257168597164192 ,
+-0.3855857342780184549 ,
+-0.18652010845105168602 ,
+-0.075907592047656735623 ,
+-0.021752065796541687759 ,
+-0.0028011514820764091618 ,
+-3.0935680451474410063e-05 ,
+} ;
+long double K16[] = {
+ 0.33220826914249586032L , // basis(0)
+ 0.30506442781494322298L , // basis(0.5)
+ 0.23598831687663609049L , // basis(1)
+ 0.15330093144015230863L , // basis(1.5)
+ 0.083172975045518980468L , // basis(2)
+ 0.03738103391018481751L , // basis(2.5)
+ 0.013757630909488189399L , // basis(3)
+ 0.0040808725321077028254L , // basis(3.5)
+ 0.00095448286788948239937L , // basis(4)
+ 0.00017072700505627712969L , // basis(4.5)
+ 2.2348950637818187112e-05L , // basis(5)
+ 2.0041660421228046889e-06L , // basis(5.5)
+ 1.1074718796168506873e-07L , // basis(6)
+ 3.131465753406890973e-09L , // basis(6.5)
+ 3.1393546448057462799e-11L , // basis(7)
+ 4.7794773323873852978e-14L , // basis(7.5)
+ 7.2929036443899311795e-19L , // basis(8)
+ } ;
+double Poles_16[] = {
+-0.74743238775188380885 ,
+-0.40907360475830745195 ,
+-0.2092287193405746315 ,
+-0.093254718980160661301 ,
+-0.031867706120390963676 ,
+-0.0062584067851372366872 ,
+-0.00030156536330664312833 ,
+-2.3232486364235544612e-08 ,
+} ;
+long double K17[] = {
+ 0.32300939415699870668L , // basis(0)
+ 0.29797995870819162778L , // basis(0.5)
+ 0.23373674923065111L , // basis(1)
+ 0.15548403615015999844L , // basis(1.5)
+ 0.087311640770182303119L , // basis(2)
+ 0.041108064309116914771L , // basis(2.5)
+ 0.016073921990964784645L , // basis(3)
+ 0.0051528238735766806875L , // basis(3.5)
+ 0.0013308125721335362832L , // basis(4)
+ 0.00027040492583018919549L , // basis(4.5)
+ 4.1821549694989869669e-05L , // basis(5)
+ 4.695715379871064023e-06L , // basis(5.5)
+ 3.5643941839232455477e-07L , // basis(6)
+ 1.6314974698479856617e-08L , // basis(6.5)
+ 3.6845271901099787809e-10L , // basis(7)
+ 2.7700195120810122023e-12L , // basis(7.5)
+ 2.8114572543455207634e-15L , // basis(8)
+ 2.144971660114685641e-20L , // basis(8.5)
+ } ;
+double Poles_17[] = {
+-0.75968322407197097501 ,
+-0.43093965318021570932 ,
+-0.23108984359938430919 ,
+-0.11082899331622909911 ,
+-0.043213911456682692347 ,
+-0.011258183689472329655 ,
+-0.0011859331251521279364 ,
+-7.6875625812547303262e-06 ,
+} ;
+long double K18[] = {
+ 0.31453440085864671822L , // basis(0)
+ 0.29135844665108330336L , // basis(0.5)
+ 0.2314117793664616011L , // basis(1)
+ 0.15724011346206745634L , // basis(1.5)
+ 0.091048500593391361557L , // basis(2)
+ 0.044670474960158529868L , // basis(2.5)
+ 0.018422928690498247476L , // basis(3)
+ 0.0063191164101958155308L , // basis(3.5)
+ 0.0017772776557432943289L , // basis(4)
+ 0.00040219803091097442175L , // basis(4.5)
+ 7.1383891069110100448e-05L , // basis(5)
+ 9.5907105586580192779e-06L , // basis(5.5)
+ 9.271047742986201033e-07L , // basis(6)
+ 5.9734083260063868359e-08L , // basis(6.5)
+ 2.2685078926749432357e-09L , // basis(7)
+ 4.0941846266406646114e-11L , // basis(7.5)
+ 2.308349801939754902e-13L , // basis(8)
+ 1.5619206968586226463e-16L , // basis(8.5)
+ 5.958254611429682336e-22L , // basis(9)
+ } ;
+double Poles_18[] = {
+-0.77080505126463716437 ,
+-0.45132873338515144823 ,
+-0.25207457469899424707 ,
+-0.12841283679297030296 ,
+-0.055462967138511676257 ,
+-0.017662377684794876992 ,
+-0.0030119307290000858941 ,
+-0.00010633735588702059982 ,
+-2.5812403962584360567e-09 ,
+} ;
+long double K19[] = {
+ 0.3066931017379824246L , // basis(0)
+ 0.28515265744763108603L , // basis(0.5)
+ 0.22904564568118377632L , // basis(1)
+ 0.1586346253388907509L , // basis(1.5)
+ 0.094419295116760105743L , // basis(2)
+ 0.048060545425350700269L , // basis(2.5)
+ 0.020781149371245016366L , // basis(3)
+ 0.0075653834126722674764L , // basis(3.5)
+ 0.0022918668891541334257L , // basis(4)
+ 0.00056895229089948501399L , // basis(4.5)
+ 0.00011341320068077591568L , // basis(5)
+ 1.7663033358559161242e-05L , // basis(5.5)
+ 2.0693993456206894214e-06L , // basis(6)
+ 1.7275247843548983816e-07L , // basis(6.5)
+ 9.4683295350905535817e-09L , // basis(7)
+ 2.987004917810922453e-10L , // basis(7.5)
+ 4.3098159994772440922e-12L , // basis(8)
+ 1.8223814805986014024e-14L , // basis(8.5)
+ 8.2206352466243297175e-18L , // basis(9)
+ 1.5679617398499164042e-23L , // basis(9.5)
+ } ;
+double Poles_19[] = {
+-0.78094644484628727987 ,
+-0.47037281947078746214 ,
+-0.27218037628176311449 ,
+-0.14585089375766777109 ,
+-0.068345906124943789361 ,
+-0.025265073344845085518 ,
+-0.0059366595910830613492 ,
+-0.00050841019468083302468 ,
+-1.9154786562122251559e-06 ,
+} ;
+long double K20[] = {
+ 0.29941029032001264032L , // basis(0)
+ 0.27932165599364228926L , // basis(0.5)
+ 0.22666242185748694763L , // basis(1)
+ 0.15972211762658876278L , // basis(1.5)
+ 0.0974575566598727568L , // basis(2)
+ 0.051275465138013302938L , // basis(2.5)
+ 0.023129338338060293147L , // basis(3)
+ 0.0088777091023436491261L , // basis(3.5)
+ 0.0028712400200206135652L , // basis(4)
+ 0.00077261996725682196449L , // basis(4.5)
+ 0.00017015073085024172881L , // basis(5)
+ 3.0008819646690530456e-05L , // basis(5.5)
+ 4.1167033003850903957e-06L , // basis(6)
+ 4.2192794922896485481e-07L , // basis(6.5)
+ 3.0493046656519177393e-08L , // basis(7)
+ 1.4241282646631125569e-09L , // basis(7.5)
+ 3.7354418501332067724e-11L , // basis(8)
+ 4.3098940955120870235e-13L , // basis(8.5)
+ 1.3667861257365780153e-15L , // basis(9)
+ 4.1103176233121648586e-19L , // basis(9.5)
+ 3.9199043496247910105e-25L , // basis(10)
+ } ;
+double Poles_20[] = {
+-0.79023111767977516351 ,
+-0.48819126033675236398 ,
+-0.29142160165551617146 ,
+-0.16303353479638585388 ,
+-0.081648115630934034459 ,
+-0.033849479552361630419 ,
+-0.0099730290200507193399 ,
+-0.0014683217571042010263 ,
+-3.7746573197331790075e-05 ,
+-2.8679944881725126467e-10 ,
+} ;
+long double K21[] = {
+ 0.29262268723143477922L , // basis(0)
+ 0.2738298047486301248L , // basis(0.5)
+ 0.22428009387883276411L , // basis(1)
+ 0.16054821266164454585L , // basis(1.5)
+ 0.10019429073492722872L , // basis(2)
+ 0.054315966627272970968L , // basis(2.5)
+ 0.025451983263662738633L , // basis(3)
+ 0.010243000848845290252L , // basis(3.5)
+ 0.0035111077726313273026L , // basis(4)
+ 0.0010143045932529873796L , // basis(4.5)
+ 0.000243612424661332394L , // basis(5)
+ 4.7797839244413500002e-05L , // basis(5.5)
+ 7.4865177795402407054e-06L , // basis(6)
+ 9.0756157943914249454e-07L , // basis(6.5)
+ 8.1587909794275973583e-08L , // basis(7)
+ 5.1150819066710363876e-09L , // basis(7.5)
+ 2.0383683775099098269e-10L , // basis(8)
+ 4.4482237420372396468e-12L , // basis(8.5)
+ 4.104700189226971567e-14L , // basis(9)
+ 9.7627580792412901893e-17L , // basis(9.5)
+ 1.9572941063391261232e-20L , // basis(10)
+ 9.3331055943447405012e-27L , // basis(10.5)
+ } ;
+double Poles_21[] = {
+-0.79876288565466957436 ,
+-0.50489153745536197171 ,
+-0.30982319641503575092 ,
+-0.17988466679726275443 ,
+-0.095200812461283090826 ,
+-0.043213918440668783183 ,
+-0.01504549998728420962 ,
+-0.0031720039638856827036 ,
+-0.00021990295763158517806 ,
+-4.7797646894259869337e-07 ,
+} ;
+long double K22[] = {
+ 0.28627661405538603955L , // basis(0)
+ 0.26864594027689889732L , // basis(0.5)
+ 0.22191207309687150606L , // basis(1)
+ 0.1611512144701082552L , // basis(1.5)
+ 0.10265788953426401334L , // basis(2)
+ 0.057185290104801063607L , // basis(2.5)
+ 0.027736783120003498267L , // basis(3)
+ 0.011649203759035082664L , // basis(3.5)
+ 0.0042065557982618627852L , // basis(4)
+ 0.0012943433274091186101L , // basis(4.5)
+ 0.00033552928198532912351L , // basis(5)
+ 7.2224788646371748003e-05L , // basis(5.5)
+ 1.2671383794748147439e-05L , // basis(6)
+ 1.7682350579090077749e-06L , // basis(6.5)
+ 1.8993891467043433632e-07L , // basis(7)
+ 1.5010206322471487409e-08L , // basis(7.5)
+ 8.1770577437810697864e-10L , // basis(8)
+ 2.7833247876855379198e-11L , // basis(8.5)
+ 5.0557094184087925374e-13L , // basis(9)
+ 3.7315643098318982977e-15L , // basis(9.5)
+ 6.6564259722400510509e-18L , // basis(10)
+ 8.8967913924505732872e-22L , // basis(10.5)
+ 2.1211603623510773867e-28L , // basis(11)
+ } ;
+double Poles_22[] = {
+-0.80662949916286152963 ,
+-0.52057023687190062677 ,
+-0.3274164733138280603 ,
+-0.19635282650762261869 ,
+-0.10887245188483440916 ,
+-0.053181604599218119944 ,
+-0.021035660929842874001 ,
+-0.0057066136460001649564 ,
+-0.00072254796507928529137 ,
+-1.3458154983225084633e-05 ,
+-3.186643260432269507e-11 ,
+} ;
+long double K23[] = {
+ 0.28032619854980754502L , // basis(0)
+ 0.26374269458034057742L , // basis(0.5)
+ 0.21956831005031718209L , // basis(1)
+ 0.16156340331433543452L , // basis(1.5)
+ 0.1048741828768824975L , // basis(2)
+ 0.059888404600676471811L , // basis(2.5)
+ 0.029974159449075470104L , // basis(3)
+ 0.013085403104047330802L , // basis(3.5)
+ 0.0049523097091663721335L , // basis(4)
+ 0.0016124087669444304968L , // basis(4.5)
+ 0.00044731411734139782554L , // basis(5)
+ 0.0001044647630135970384L , // basis(5.5)
+ 2.0225085344373592521e-05L , // basis(6)
+ 3.1828904692399063536e-06L , // basis(6.5)
+ 3.9679866129008683199e-07L , // basis(7)
+ 3.7855233852927286935e-08L , // basis(7.5)
+ 2.6346734890183937921e-09L , // basis(8)
+ 1.2488410498396141086e-10L , // basis(8.5)
+ 3.6338307165683742375e-12L , // basis(9)
+ 5.4959585554808751978e-14L , // basis(9.5)
+ 3.2448470402629826029e-16L , // basis(10)
+ 4.3411473752750814749e-19L , // basis(10.5)
+ 3.868170170630684038e-23L , // basis(11)
+ 4.6112181790240812755e-30L , // basis(11.5)
+ } ;
+double Poles_23[] = {
+-0.81390562354320794558 ,
+-0.53531408371104993726 ,
+-0.34423627688965990901 ,
+-0.21240466055269885404 ,
+-0.12256116098899572098 ,
+-0.063602480154273194346 ,
+-0.027811662038017159748 ,
+-0.0090795953352833073946 ,
+-0.0017112714467820973156 ,
+-9.5733943500721317005e-05 ,
+-1.1936918816067781773e-07 ,
+} ;
+long double K24[] = {
+ 0.27473197352118810147L , // basis(0)
+ 0.25909593388549224613L , // basis(0.5)
+ 0.21725612218406020861L , // basis(1)
+ 0.16181208211791016533L , // basis(1.5)
+ 0.10686656672959712099L , // basis(2)
+ 0.062431425854373209442L , // basis(2.5)
+ 0.032156816325798337903L , // basis(3)
+ 0.014541849599514216045L , // basis(3.5)
+ 0.0057429446266243922923L , // basis(4)
+ 0.0019676174028389475043L , // basis(4.5)
+ 0.00058004996270088237076L , // basis(5)
+ 0.00014563543156618789351L , // basis(5.5)
+ 3.0746018052888292381e-05L , // basis(6)
+ 5.3704036096147168721e-06L , // basis(6.5)
+ 7.6016977670631529332e-07L , // basis(7)
+ 8.4861949009616751487e-08L , // basis(7.5)
+ 7.2045281870976666726e-09L , // basis(8)
+ 4.4229185004672962615e-10L , // basis(8.5)
+ 1.8261499938887221924e-11L , // basis(9)
+ 4.5452628388307088642e-13L , // basis(9.5)
+ 5.7253638111923437033e-15L , // basis(10)
+ 2.70404290721556569e-17L , // basis(10.5)
+ 2.7132171099984410352e-20L , // basis(11)
+ 1.6117375710961183492e-24L , // basis(11.5)
+ 9.6067045396335026575e-32L , // basis(12)
+ } ;
+double Poles_24[] = {
+-0.82065517417952760226 ,
+-0.54920097364808984075 ,
+-0.3603190653178175995 ,
+-0.22802014939914075353 ,
+-0.13618849963046011919 ,
+-0.074351497302516889043 ,
+-0.035244126673212937406 ,
+-0.013246375325256078484 ,
+-0.0032976826232791502103 ,
+-0.00035807154412069458092 ,
+-4.8126755630580574097e-06 ,
+-3.5407088073360672255e-12 ,
+} ;
+const double* precomputed_poles[] = {
+ 0,
+ 0,
+ Poles_2,
+ Poles_3,
+ Poles_4,
+ Poles_5,
+ Poles_6,
+ Poles_7,
+ Poles_8,
+ Poles_9,
+ Poles_10,
+ Poles_11,
+ Poles_12,
+ Poles_13,
+ Poles_14,
+ Poles_15,
+ Poles_16,
+ Poles_17,
+ Poles_18,
+ Poles_19,
+ Poles_20,
+ Poles_21,
+ Poles_22,
+ Poles_23,
+ Poles_24,
+} ;
+const long double* precomputed_basis_function_values[] = {
+ K0,
+ K1,
+ K2,
+ K3,
+ K4,
+ K5,
+ K6,
+ K7,
+ K8,
+ K9,
+ K10,
+ K11,
+ K12,
+ K13,
+ K14,
+ K15,
+ K16,
+ K17,
+ K18,
+ K19,
+ K20,
+ K21,
+ K22,
+ K23,
+ K24,
+} ;
diff --git a/prefilter.h b/prefilter.h
new file mode 100644
index 0000000..738fe71
--- /dev/null
+++ b/prefilter.h
@@ -0,0 +1,1915 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/*! \file prefilter.h
+
+ \brief Code to create the coefficient array for a b-spline.
+
+ The coefficients can be generated in two ways (that I know of): the first
+ is by solving a set of equations which encode the constraints of the spline.
+ A good example of how this is done can be found in libeinspline. I term it
+ the 'linear algebra approach'. In this implementation, I have chosen what I
+ call the 'DSP approach'. In a nutshell, the DSP approach looks at the b-spline's
+ reconstruction by convolving the coefficients with a specific kernel. This
+ kernel acts as a low-pass filter. To counteract the effect of this filter and
+ obtain the input signal from the convolution of the coefficients, a high-pass
+ filter with the inverse transfer function to the low-pass is used. This high-pass
+ has infinite support, but can still be calculated precisely within the bounds of
+ the arithmetic precision the CPU offers, due to the properties it has.
+
+ I recommend [CIT2000] for a formal explanation. At the core of my prefiltering
+ routines there is code from Philippe Thevenaz' accompanying code to this paper,
+ with slight modifications translating it to C++ and making it generic.
+ The greater part of this file deals with 'generifying' the process and to
+ employing multithreading and the CPU's vector units to gain speed.
+
+ This code makes heavy use of vigra, which provides handling of multidimensional
+ arrays and efficient handling of aggreagte types - to only mention two of it's
+ many qualities. The vectorization is done with Vc, which allowed me to code
+ the horizontal vectorization I use in a generic fashion.
+
+ For now, this file offers two implementations in one: the unvectorized version,
+ available as solve_vigra(), and the vectorized version, solve_vc(). Note that
+ the vectorized code is more constrained in what numeric types it can process
+ (namely, only float, double and their aggregates).
+ Unit testing code for these two versions is in prefilter_test...
+
+ In another version of this code I used vigra's BSPlineBase class to obtain prefilter
+ poles. This required passing the spline degree/order as a template parameter. Doing it
+ like this allows to make the Poles static members of the solver, but at the cost of
+ type proliferation. Here I chose not to follow this path and pass the spline order as a
+ parameter to the spline's constructor, thus reducing the number of solver specializations
+ and allowing automated testing with loops over the degree. This variant is slightly slower.
+
+ In addition to the code following the 'implicit scheme' proposed by Thevenaz, I provide
+ code to use an 'explicit scheme' to obtain the b-spline coefficients. The implicit scheme
+ makes assumptions about the continuation of the signal outside of the window of data which
+ is acceessible: that the data continue mirrored, reflected, etc. - but it proceeds to
+ capture these assumptions in formulae deriving suitable initial causal/anticausal coefficients
+ from them. Usually this is done with a certain 'horizon' which takes into account the limited
+ arithmetic precision of the calculations and abbreviates the initial coefficient calculation
+ to a certain chosen degree of precision. The same effect can be achieved by simply embedding
+ the knot point data into a frame containing extrapolated knot point data. If the frame is
+ chosen so wide that margin effects don't 'disturb' the core data, we end up with an equally
+ (im)precise result with an explicit scheme. The width of the frame now takes the roll of the
+ horizon used in the implicit scheme and has the same effect. While the explicit scheme needs
+ more memory, it has several advantages:
+
+ - there is no need to code specific routines for initial coefficient generation
+ - nor any need to explicitly run this code
+ - the iteration over the input becomes more straightforward
+ - any extrapolation scheme can be used easily
+
+ A disadvantage, apart from the higher memory consumption, is that one cannot give a
+ 'precise' solution, which the implicit scheme can do for the cases it can handle. But what
+ is 'precise'? Certainly there is no precision beyond the arithmetic precision offered by
+ the underlying system. So if the horizon is chosen wide enough, the resulting coefficients
+ become the same with all schemes. They are interchangeable.
+
+ In an image-processing context, the extra memory needed would typically be a small
+ single-digit percentage - not really a bother. In my trials, I found the runtime differences
+ between the two approaches negligible and the simplification of the code so attractive that
+ I was tempted to choose the explicit scheme over the implicit. Yet since the code for the
+ implicit scheme is there already and some of it is even used in the explicit scheme I keep
+ both methods in the code base for now.
+*/
+
+#ifndef VSPLINE_PREFILTER_H
+#define VSPLINE_PREFILTER_H
+
+#include <thread>
+#include <math.h>
+#include <complex>
+#include <cmath>
+#include <iostream>
+#include <array>
+#include <assert.h>
+
+#include <vigra/multi_array.hxx>
+#include <vigra/multi_iterator.hxx>
+#include <vigra/multi_math.hxx>
+#include <vigra/navigator.hxx>
+#include <vigra/bordertreatment.hxx>
+#include <vigra/multi_convolution.hxx>
+
+#include "common.h"
+#include "basis.h"
+
+namespace vspline {
+
+using namespace std ;
+using namespace vigra ;
+using namespace vigra::multi_math;
+
+/// class solver performs the conversion of a 1D string of data to spline coefficients with
+/// the 'DSP aproach', which performs coefficient generation by means of an IIR filter.
+/// The DSP approach is extremely versatile and the code is elegant, so I abandoned even my
+/// optimized cubic spline code, which used linear algebra methods, in it's favour.
+/// The 1D solving is, later on, used repeatedly along the axes of multidimensional
+/// data, but the formulation of the solving process is unaware of the dimensionality
+/// of the data, it follows the 1D iterators it receives, no matter what strides these
+/// iterators use to pick out data along an axis.
+///
+/// With large data sets, and with higher dimensionality, processing separately along each
+/// axis consumes a lot of memory bandwidth. There are ways out of this dilemma by interleaving
+/// the code. Disregarding the calculation of initial causal and anticausal coefficients, the code
+/// to do this would perform the forward filtering step for all axes at the same time and then, later,
+/// the backward filtering step for all axes at the same time. This is possible, since the order
+/// of the filter steps is irrelevant, and the traversal of the data can be arranged so that
+/// values needed for context of the filter are always present (the filters are recursive and only
+/// 'look' one way). I have investigated these variants, but especially the need to calculate
+/// initial causal/anticausal coefficients, and the additional complications arising from
+/// vectorization, have kept me from choosing this path for the current body of code. With the
+/// inclusion of the explicit scheme for prefiltering, dimension-interleaved prefiltering becomes
+/// more feasible, and I anticipate revisiting it.
+///
+/// Here I am using a scheme where I make access to 1D subsets of the data very efficient (if necessary
+/// by buffering lines/stripes of data) and rely on the fact that such simple, fast access plays
+/// well with the compiler's optimizer and pipelining in the CPU. From the trials on my own system
+/// I conclude that this approach does not perform significantly worse than interleaving schemes
+/// and is much easier to formulate and understand. And with fast access to 1D subsets, higher order
+/// splines become less of an issue; the extra arithemtic to prefilter for, say, quintic splines is
+/// done very quickly, since no additional memory access is needed beyond a buffer's worth of data
+/// already present in core memory.
+///
+/// class solver needs two template arguments, one for the type of iterator over the incoming
+/// data, and one for the type of iterator to the resultant coefficients. These will usually be
+/// the same, but formulating the code with two separate types makes it more versatile.
+
+template < typename in_iter , // iterator over the knot point values
+ typename out_iter > // iterator over the coefficient array
+class solver
+{
+ // both iterators must define value_type and have the same value_type
+
+ typedef typename in_iter::value_type value_type ;
+
+ static_assert ( std::is_same < typename out_iter::value_type , value_type > :: value ,
+ "prefilter input and output iterator must have the same value_type" ) ;
+
+ // while the iterators may refer to aggregates (pixels etc.), we also need access
+ // to the fundamental type of the aggregates. Further down, in the vectorized code,
+ // a specialization for Vc::Vector objects is given, so that the element type of
+ // Vc::Vectors can be inferred as well.
+
+ typedef typename ExpandElementResult < value_type > :: type mattype ;
+
+// // both iterators should be random access iterators.
+// // currently not enforced, too lazy to code the traits for vector stripe iterators...
+// typedef typename std::iterator_traits < in_iter > :: iterator_category in_cat ;
+// static_assert ( std::is_same < in_cat , std::random_access_iterator_tag > :: value ,
+// "prefilter input iterator must be random access iterator" ) ;
+//
+// typedef typename std::iterator_traits < out_iter > :: iterator_category out_cat ;
+// static_assert ( std::is_same < out_cat , std::random_access_iterator_tag > :: value ,
+// "prefilter output iterator must be random access iterator" ) ;
+
+
+ /// typedef the fully qualified type for brevity
+ typedef solver<in_iter,out_iter> solver_type ;
+
+public:
+
+ ArrayVector<mattype> Pole ; ///< Poles of the IIR filter
+ ArrayVector<int> Horizon ; ///< as many 'Horizons' as Poles
+ mattype Lambda ; ///< (potentiated) overall gain.
+ const int NbPoles ; ///< Number of filter poles
+ const int M ; ///< length of the data
+ const int SplineDegree ; ///< degree of the spline
+ const int SplineOrder ; ///< order of the spline (== degree + 1)
+
+ /// the solving routine and initial coefficient finding routines are called via method pointers.
+ /// these pointers are typedefed for better legibility:
+
+ typedef int ( solver_type::*p_solve ) ( in_iter input , out_iter output ) ;
+ typedef value_type ( solver_type::*p_icc1 ) ( in_iter input , int k ) ;
+ typedef value_type ( solver_type::*p_icc2 ) ( out_iter input , int k ) ;
+ typedef value_type ( solver_type::*p_iacc ) ( out_iter input , int k ) ;
+
+
+ // these are the method pointers used:
+
+ p_solve _p_solve ; ///< pointer to the solve method
+ p_icc1 _p_icc1 ; ///< pointer to calculation of initial causal coefficient with different
+ p_icc2 _p_icc2 ; ///< and equal data types of input and output
+ p_iacc _p_iacc ; ///< pointer to calculation of initial anticausal coefficient
+
+ /// solve() takes two iterators, one to the input data and one to the output space.
+ /// The containers must have the same size. It's safe to use solve() in-place.
+
+ int solve ( in_iter input , out_iter output )
+ {
+ (this->*_p_solve) ( input , output ) ;
+ }
+
+ /// for in-place operation we use the same solver routine.
+ /// I checked: a handcoded in-place routine using only a single
+ /// iterator is not noticeably faster than using one with two separate iterators.
+
+ int solve ( out_iter data )
+ {
+ (this->*_p_solve) ( data , data ) ;
+ }
+
+// I now use code for te 'DSP approach' to calculating the spline coefficients, or,
+// to use DSP terminology, to perform the 'prefiltering'. I use adapted versions of
+// Thevenaz' code to calculate the initial causal and anticausal coefficients. The
+// code is changed just a little to work with an iterator instead of a C vector.
+
+private:
+
+/// the next section holds routines to calculate the initial causal/anticausal coefficient
+/// for the solver routine. Since I now use the DSP approach to coefficient generation, this
+/// is where the boundary conditions manifest in code, while the solve routines are the same
+/// for all cases.
+///
+/// The code for mirrored BCs is adapted from P. Thevenaz' code, the other routines are my
+/// own doing, with aid from a digest of spline formulae I received from P. Thevenaz and which
+/// were helpful to verify the code against a trusted source. Anyway, it's all unit tested now
+/// and runs just fine.
+///
+/// note how, in the routines to find the initial causal coefficient, there are two different
+/// cases: first the 'accelerated loop', which is used when the theoretically infinite sum of
+/// terms has reached sufficient precision, and the 'full loop', which implements the mathematically
+/// precise representation of the limes of the infinite sum towards an infinite number of terms,
+/// which happens to be calculable due to the fact that the absolute value of all poles is < 1 and
+///
+/// lim n a
+/// sum a * q ^ k = ---
+/// n->inf k=0 1-q
+///
+///
+/// first are mirror BCs. This is mirroring 'on bounds',
+/// f(-x) == f(x) and f(n-1 +x) == f(n-1 + x)
+///
+/// note how mirror BCs are equivalent to requiring the first derivative to be zero in the
+/// linear algebra approach. Obviously with mirrored data this has to be the case; the location
+/// where mirroring occurs is always an extremum. So this case covers 'FLAT' BCs as well
+///
+/// the initial causal coefficient routines are templated by iterator type, because depending
+/// on the circumstances, they may be used either on the input or the output iterator.
+
+template < class IT >
+value_type icc_mirror ( IT c , int k )
+{
+ mattype z = Pole[k] ;
+ mattype zn, z2n, iz;
+ value_type Sum ;
+ int n ;
+
+ if (Horizon[k] < M) {
+ /* accelerated loop */
+ zn = z;
+ Sum = c[0];
+ for (n = 1; n < Horizon[k]; n++) {
+ Sum += zn * c[n];
+ zn *= z;
+ }
+ }
+ else {
+ /* full loop */
+ zn = z;
+ iz = 1.0 / z;
+ z2n = pow(z, (double)(M - 1));
+ Sum = c[0] + z2n * c[M - 1];
+ z2n *= z2n * iz;
+ for (n = 1; n <= M - 2; n++) {
+ Sum += (zn + z2n) * c[n];
+ zn *= z;
+ z2n *= iz;
+ }
+ Sum /= mattype(1.0 - zn * zn);
+ }
+// cout << "icc_mirror: " << Sum << endl ;
+ return(Sum);
+}
+
+/// the initial anticausal coefficient routines are always called with the output iterator,
+/// so they needn't be templated like the icc routines.
+///
+/// I still haven't understood the 'magic' which allows to calculate the initial anticausal
+/// coefficient from just two results of the causal filter, but I assume it's some exploitation
+/// of the symmetry of the data. This code is adapted from P. Thevenaz'.
+
+value_type iacc_mirror ( out_iter c , int k )
+{
+ mattype z = Pole[k] ;
+
+ return( mattype( z / ( z * z - 1.0 ) ) * ( c [ M - 1 ] + z * c [ M - 2 ] ) );
+}
+
+/// next are 'antimirrored' BCs. This is the same as 'natural' BCs: the signal is
+/// extrapolated via point mirroring at the ends, resulting in point-symmetry at the ends,
+/// which is equivalent to the second derivative being zero, the constraint used in
+/// the linear algebra approach to calculate 'natural' BCs:
+///
+/// f(x) - f(0) == f(0) - f(-x); f(x+n-1) - f(n-1) == f(n-1) - f (n-1-x)
+
+template < class IT >
+value_type icc_natural ( IT c , int k )
+{
+ mattype z = Pole[k] ;
+ mattype zn, z2n, iz;
+ value_type Sum , c02 ;
+ int n ;
+
+ // f(x) - f(0) == f(0) - f(-x)
+ // f(-x) == 2 * f(0) - f(x)
+
+ if (Horizon[k] < M) {
+ c02 = c[0] + c[0] ;
+ zn = z;
+ Sum = c[0];
+ for (n = 1; n < Horizon[k]; n++) {
+ Sum += zn * ( c02 - c[n] ) ;
+ zn *= z;
+ }
+ return(Sum);
+ }
+ else {
+ zn = z;
+ iz = 1.0 / z;
+ z2n = pow(z, (double)(M - 1)); // z2n == z^M-1
+ Sum = mattype( ( 1.0 + z ) / ( 1.0 - z ) ) * ( c[0] - z2n * c[M - 1] );
+ z2n *= z2n * iz; // z2n == z^2M-3
+ for (n = 1; n <= M - 2; n++) {
+ Sum -= (zn - z2n) * c[n];
+ zn *= z;
+ z2n *= iz;
+ }
+ return(Sum / mattype(1.0 - zn * zn));
+ }
+}
+
+/// I still haven't understood the 'magic' which allows to calculate the initial anticausal
+/// coefficient from just two results of the causal filter, but I assume it's some exploitation
+/// of the symmetry of the data. This code is adapted from P. Thevenaz' formula.
+
+value_type iacc_natural ( out_iter c , int k )
+{
+ mattype z = Pole[k] ;
+
+ return - mattype( z / ( ( 1.0 - z ) * ( 1.0 - z ) ) ) * ( c [ M - 1 ] - z * c [ M - 2 ] ) ;
+}
+
+/// next are reflective BCs. This is mirroring 'between bounds':
+///
+/// f ( -1 - x ) == f ( x ) and f ( n + x ) == f ( n-1 - x )
+///
+/// I took Thevenaz' routine for mirrored data as a template and adapted it.
+/// 'reflective' BCs have some nice properties which make them more suited than mirror BCs in
+/// some situations:
+/// - the artificial discontinuity is 'pushed out' half a unit spacing
+/// - the extrapolated data are just as long as the source data
+/// - they play well with even splines
+
+template < class IT >
+value_type icc_reflect ( IT c , int k )
+{
+ mattype z = Pole[k] ;
+ mattype zn, z2n, iz;
+ value_type Sum ;
+ int n ;
+
+ if (Horizon[k] < M) {
+ zn = z;
+ Sum = c[0];
+ for (n = 0; n < Horizon[k]; n++) {
+ Sum += zn * c[n];
+ zn *= z;
+ }
+ return(Sum);
+ }
+ else {
+ zn = z;
+ iz = 1.0 / z;
+ z2n = pow(z, (double)(2 * M));
+ Sum = 0 ;
+ for (n = 0; n < M - 1 ; n++) {
+ Sum += (zn + z2n) * c[n];
+ zn *= z;
+ z2n *= iz;
+ }
+ Sum += (zn + z2n) * c[n];
+ return c[0] + Sum / mattype(1.0 - zn * zn) ;
+ }
+}
+
+/// I still haven't understood the 'magic' which allows to calculate the initial anticausal
+/// coefficient from just one result of the causal filter, but I assume it's some exploitation
+/// of the symmetry of the data. I have to thank P. Thevenaz for his formula which let me code:
+
+value_type iacc_reflect ( out_iter c , int k )
+{
+ mattype z = Pole[k] ;
+
+ return c[M - 1] / mattype( 1.0 - 1.0 / z ) ;
+}
+
+/// next is periodic BCs. so, f(x) = f(x+N)
+///
+/// Implementing this is more straightforward than implementing the various mirrored types.
+/// The mirrored types are, in fact, also periodic, but with a period twice as large, since they
+/// repeat only after the first reflection. So especially the code for the full loop is more complex
+/// for mirrored types. The down side here is the lack of symmetry to exploit, which made me code
+/// a loop for the initial anticausal coefficient as well.
+
+template < class IT >
+value_type icc_periodic ( IT c , int k )
+{
+ mattype z = Pole[k] ;
+ mattype zn ;
+ value_type Sum ;
+ int n ;
+
+ if (Horizon[k] < M)
+ {
+ zn = z ;
+ Sum = c[0] ;
+ for ( n = M - 1 ; n > ( M - Horizon[k] ) ; n-- )
+ {
+ Sum += zn * c[n];
+ zn *= z;
+ }
+ }
+ else
+ {
+ zn = z;
+ Sum = c[0];
+ for ( n = M - 1 ; n > 0 ; n-- )
+ {
+ Sum += zn * c[n];
+ zn *= z;
+ }
+ Sum /= mattype( 1.0 - zn ) ;
+ }
+ return Sum ;
+}
+
+value_type iacc_periodic ( out_iter c , int k )
+{
+ mattype z = Pole[k] ;
+ mattype zn ;
+ value_type Sum ;
+
+ if (Horizon[k] < M)
+ {
+ zn = z ;
+ Sum = c[M-1] * z ;
+ for ( int n = 0 ; n < Horizon[k] ; n++ )
+ {
+ zn *= z;
+ Sum += zn * c[n];
+ }
+ Sum = -Sum ;
+ }
+ else
+ {
+ zn = z;
+ Sum = c[M-1];
+ for ( int n = 0 ; n < M - 1 ; n++ )
+ {
+ Sum += zn * c[n];
+ zn *= z;
+ }
+ Sum = z * Sum / mattype( zn - 1.0 );
+ }
+ return Sum ;
+}
+
+template < class IT >
+value_type icc_identity ( IT c , int k )
+{
+ return c[0] ;
+}
+
+value_type iacc_identity ( out_iter c , int k )
+{
+ return c[M-1] ;
+}
+
+/// now we come to the solving, or prefiltering code itself.
+/// there are some variants - a bit of code bloat due to the explicit handling of a few
+/// distinct cases; since this is core code I have opted to suffer some code duplication
+/// in exchange for maximum efficiency.
+/// The code itself is adapted from P. Thevenaz' code.
+///
+/// the first solve routine is to be used for the first dimension.
+/// here Lambda, the overall gain, is applied to the elements of the input as they
+/// are processed, saving the separate loop to preapply the gain. Subsequent poles
+/// and further dimensions then use the next routine. The gain which is applied here
+/// may be a power of the 'orthodox' gain, to avoid having to reapply the 'orthodox'
+/// Lambda with every dimension which is processed. See the constructor.
+
+int solve_gain_inlined ( in_iter c , out_iter x )
+{
+ assert ( M > 1 ) ;
+
+ value_type X ;
+
+ // process first pole, applying overall gain in the process
+ // of consuming the input. This gain may be a power of the 'orthodox'
+ // Lambda from Thevenaz' code. This is done when the input is multidimensional,
+ // in which case it's wasteful to apply Lambda in each dimension. In this situation
+ // it makes more sense to apply pow(Lambda,dimensions) when solving along the
+ // first axis and apply no gain when solving along the other axes.
+ // Also note that the application of the gain is performed during the processing
+ // of the first (maybe the only) pole of the filter, instead of running a separate
+ // loop over the input to apply it before processing starts.
+
+ // note how the gain is applied to the initial causal coefficient. This is
+ // equivalent to first applying the gain to the input and then calculating
+ // the initial causal coefficient from the amplified input.
+
+ // note the seemingly strange = X clause in the asignment. By performing this
+ // assignment, we buffer the result of the current filter step to be used in the
+ // next iteration instead of fetching it again from memory. In my trials, this
+ // performed better, especially on SIMD data.
+
+ x[0] = X = Lambda * (this->*_p_icc1) (c, 0);
+
+ /* causal recursion */
+ // the gain is applied to each input value as it is consumed
+
+ for (int n = 1; n < M; n++)
+ {
+ x[n] = X = Lambda * c[n] + Pole[0] * X ;
+ }
+
+ // now the input is used up and won't be looked at any more; all subsequent
+ // processing operates on the output.
+
+ /* anticausal initialization */
+
+ x[M - 1] = X = (this->*_p_iacc)(x, 0);
+
+ /* anticausal recursion */
+ for (int n = M - 2; 0 <= n; n--)
+ {
+ x[n] = X = Pole[0] * ( X - x[n]);
+ }
+
+ // for the remaining poles, if any, don't apply the gain
+ // and process the result from applying the first pole
+
+ for (int k = 1; k < NbPoles; k++)
+ {
+ /* causal initialization */
+ x[0] = X = (this->*_p_icc2)(x, k);
+
+ /* causal recursion */
+ for (int n = 1; n < M; n++)
+ {
+ x[n] = X = x[n] + Pole[k] * X ;
+ }
+
+ /* anticausal initialization */
+ x[M - 1] = X = (this->*_p_iacc)(x, k);
+
+ /* anticausal recursion */
+ for (int n = M - 2; 0 <= n; n--)
+ {
+ x[n] = X = Pole[k] * ( X - x[n] );
+ }
+ }
+}
+
+/// solve routine without application of any gain, it is assumed that this has been
+/// done already during an initial run with the routine above, or in some other way.
+
+int solve_no_gain ( in_iter c , out_iter x )
+{
+ assert ( M > 1 ) ;
+
+ value_type X ;
+
+ // process first pole, consuming the input
+
+ /* causal initialization */
+ x[0] = X = (this->*_p_icc1)(c, 0);
+
+ /* causal recursion */
+ for ( int n = 1; n < M; n++)
+ {
+ x[n] = X = c[n] + Pole[0] * X ;
+ }
+
+ /* anticausal initialization */
+ x[M - 1] = X = (this->*_p_iacc)(x, 0);
+
+ /* anticausal recursion */
+ for ( int n = M - 2; 0 <= n; n--)
+ {
+ x[n] = X = Pole[0] * ( X - x[n]);
+ }
+
+ // for the remaining poles, if any, work on the result
+ // of processing the first pole
+
+ for ( int k = 1 ; k < NbPoles; k++)
+ {
+ /* causal initialization */
+ x[0] = X = (this->*_p_icc2)(x, k);
+
+ /* causal recursion */
+ for (int n = 1; n < M; n++)
+ {
+ x[n] = X = x[n] + Pole[k] * X ;
+ }
+
+ /* anticausal initialization */
+ x[M - 1] = X = (this->*_p_iacc)(x, k);
+
+ /* anticausal recursion */
+ for (int n = M - 2; 0 <= n; n--)
+ {
+ x[n] = X = Pole[k] * ( X - x[n] );
+ }
+ }
+}
+
+/// shortcircuit routine, copies input to output
+///
+/// this routine can also be used for splines of degree 0 and 1, for simplicity's sake
+
+int solve_identity ( in_iter c , out_iter x )
+{
+ if ( x == c )
+ return 0 ;
+ for ( int n = 0 ; n < M ; n++ )
+ x[n] = c[n] ;
+}
+
+/// The last bit of work left in the solver is the constructor.
+/// The number of input/output values is passed into the constructur, limiting the
+/// solver to operate on data precisely of this length. apply_gain isn't immediately
+/// obvious: it's not a mere flag, but contains the exponent which should be applied
+/// to the gain. If, for example, a 2D spline is built, one might pass in 2 here for
+/// the first dimension, and 0 for the second. This way, one set of multiplications is
+/// saved, at the cost of slightly reduced accuracy for large spline degrees. For high
+/// spline degrees and higher dimensions, it's advisable to not use this mechanism and
+/// pass in apply_gain = 1 for all dimensions.
+///
+/// Next is the boundary condition to use for the current axis. This is one of
+/// MIRROR, REFLECT, NATURAL, and PERIODIC. Note that different axes can use
+/// different boundary conditions.
+///
+/// The last parameter determines the spline order, which is one larger than the
+/// spline degree TODO: change code to always use degree
+
+public:
+
+solver ( int _M , ///< number of input/output elements (DataLength)
+ int apply_gain , ///< power of Lambda to apply while processing the first pole of the filter
+ bc_code bc , ///< boundary conditions for this solver
+ int spline_order ) ///< desired spline order (4 for cubic)
+: M ( _M ) ,
+ SplineOrder ( spline_order ) ,
+ SplineDegree ( spline_order - 1 ) ,
+ NbPoles ( ( spline_order - 1 ) / 2 )
+{
+ // TODO: make tolerance a parameter
+
+ double Tolerance = 0.0 ; // any type - no tolerance
+
+ if ( std::is_same < mattype , float > :: value == true )
+ Tolerance = .000000001 ;
+
+ else if ( std::is_same < mattype , double > :: value == true )
+ Tolerance = .000000000000001 ;
+
+ // fetch the precomputed filter poles:
+
+ assert ( SplineDegree >= 0 && SplineDegree < 25 ) ;
+
+ if ( SplineDegree < 2 )
+ {
+ // this is the easy way to deal with low degree splines:
+ // copy the input to the output.
+ _p_solve = & solver_type::solve_identity ;
+ return ;
+ }
+
+ for ( int i = 0 ; i < NbPoles ; i++ )
+ {
+ double pole = precomputed_poles [ SplineDegree ] [ i ] ;
+ Pole.push_back ( pole );
+ if ( Tolerance )
+ Horizon.push_back ( ceil ( log ( Tolerance ) / log ( fabs ( pole ) ) ) ) ;
+ else
+ Horizon.push_back ( M ) ;
+ }
+
+// for ( int i = 0 ; i < NbPoles ; i++ )
+// cout << "Pole " << i << ": " << Pole[i] << " Hor " << Horizon[i] << endl ;
+
+ /* compute the overall gain */
+
+ Lambda = 1.0 ; // if apply_gain is 0, Lambda won't be applied at all
+
+ if ( apply_gain ) // if apply_gain is set, it will be used as an *exponent* on Lambda
+ // as to apply a power of Lambda when processing the first dimension
+ {
+ for (int k = 0; k < NbPoles; k++)
+ Lambda = Lambda * (1.0 - Pole[k]) * (1.0 - 1.0 / Pole[k]);
+
+ Lambda = pow ( Lambda , apply_gain ) ;
+
+ _p_solve = & solver_type::solve_gain_inlined ; // multiply input with pow(Lambda,apply_gain)
+ }
+ else
+ {
+ _p_solve = & solver_type::solve_no_gain ; // the gain has already been applied
+ }
+
+// cout << "Lambda: " << Lambda << endl ;
+
+ // while the forward/backward IIR filter in the solve_... routines is the same for all
+ // boundary conditions, the calculation of the initial causal and anticausal coefficients
+ // depends on the boundary conditions and is handled by a call through a method pointer
+ // in the solve_... routines.
+
+ if ( bc == MIRROR )
+ {
+ _p_icc1 = & solver_type::icc_mirror<in_iter> ;
+ _p_icc2 = & solver_type::icc_mirror<out_iter> ;
+ _p_iacc = & solver_type::iacc_mirror ;
+ }
+ else if ( bc == NATURAL )
+ {
+ _p_icc1 = & solver_type::icc_natural<in_iter> ;
+ _p_icc2 = & solver_type::icc_natural<out_iter> ;
+ _p_iacc = & solver_type::iacc_natural ;
+ }
+ else if ( bc == PERIODIC )
+ {
+ _p_icc1 = & solver_type::icc_periodic<in_iter> ;
+ _p_icc2 = & solver_type::icc_periodic<out_iter> ;
+ _p_iacc = & solver_type::iacc_periodic ;
+ }
+ else if ( bc == REFLECT )
+ {
+ _p_icc1 = & solver_type::icc_reflect<in_iter> ;
+ _p_icc2 = & solver_type::icc_reflect<out_iter> ;
+ _p_iacc = & solver_type::iacc_reflect ;
+ }
+ else if ( bc == ZEROPAD || bc == IGNORE )
+ {
+ _p_icc1 = & solver_type::icc_identity<in_iter> ;
+ _p_icc2 = & solver_type::icc_identity<out_iter> ;
+ _p_iacc = & solver_type::iacc_identity ;
+ }
+ else if ( bc == IDENTITY )
+ {
+ _p_solve = & solver_type::solve_identity ;
+ }
+ else
+ cerr << "bc code " << bc << " not supported" << endl ;
+}
+
+} ; // end of class solver
+
+// previous implementation, not using divide_and_conquer:
+/*
+/// helper routine to run the solving process with several threads
+
+template < typename in_nav_type , typename out_nav_type , typename solver_type >
+int process ( in_nav_type ni , out_nav_type no , solver_type * s )
+{
+ while ( ni.hasMore() )
+ {
+ s->solve ( ni.begin() , no.begin() ) ;
+ ++ni ;
+ ++no ;
+ }
+}
+
+/// The code for multithreaded operation processes only one axis per call. This way we can easily
+/// mix the initial, potentially more complex call where the knot point data are filtered for
+/// the first time, and the subsequent in-place operations over the coefficient array along the
+/// remaining axes.
+/// Here we formulate the version of the 1D solve routine which doesn't work in-place.
+/// There is a certain amount of code bloat because we allow for different
+/// input and output types and don't restrict the code to perform the calculation in-place, though
+/// this works just as well. The code bloat consists in a few more types to accomodate the potentially
+/// different input and output types. d, the axis to process, is the axis we use when we 'pull in'
+/// the input from the knot point data to the coefficient array. This 'pulling in' has to happen
+/// before the subsequent axes are processed in-place in the coefficient array.
+/// Initailly I coded an in-place version of this routine but threw it out, since the
+/// performance gain of the explicit in-place version is if at all negligible, and the extra
+/// template parameters aren't too much to handle.
+///
+/// solve_vigra is the newer version of this routine, it internally constructs the solver,
+/// which makes the call easier to comprehend.
+
+template < typename input_array_type , ///< type of array with knot point data
+ typename output_array_type > ///< type of array for coefficients (may be the same)
+void solve_vigra ( input_array_type &input , ///< knot point data. the routine can also operate in-place
+ output_array_type &output , ///< where input == output.
+ bc_code bc , ///< boundary treatment for this solver
+ int degree , ///< degree of the spline
+ int d , ///< axis to process
+ int nslices = ncores ) ///< number of threads to use
+{
+ const int dim = input_array_type::actual_dimension ;
+ typedef typename input_array_type::difference_type diff_t ;
+ diff_t shape = input.shape() ;
+
+ // we use vigra::MultiArrayNavigators which provide precisely the functionality we need,
+ // which is to provide iterators to all 1D subarrays along a given axis. Again we have to
+ // account for the possible difference between the incoming data and the output.
+
+ typedef vigra::MultiArrayNavigator<typename input_array_type::traverser, dim> input_Navigator;
+ typedef vigra::MultiArrayNavigator<typename output_array_type::traverser, dim> output_Navigator;
+ typedef typename input_Navigator::iterator input_nav_iter ;
+ typedef typename output_Navigator::iterator output_nav_iter ;
+
+ int lambda_exponent = 1 ;
+
+// deactivating the code below may produce slightly more precise results
+
+ if ( pow ( degree , dim ) < 64 ) // heuristic. for high degrees, below optimization reduces precision too much
+ {
+ lambda_exponent = 0 ;
+ if ( d == 0 )
+ lambda_exponent = dim ;
+ }
+
+ int count = input.shape ( d ) ;
+
+ typedef solver < input_nav_iter , output_nav_iter > solver_type ;
+
+ solver_type s ( count , lambda_exponent , bc , degree + 1 ) ;
+ solver_type * sp = &s ;
+
+ diff_t shp = shape ; // start with 'complete' shape
+ shp[d] = 1 ; // bind current dimension to 1
+
+ // find the outermost dimension that can be split nslices ways, and it's extent
+ // This is the same process as in split_array_..., but since we work with shapes here
+ // to set up the range for the Navigators, we don't use these routines.
+
+ int maxd = -1 ;
+ int max_extent = -1 ;
+ for ( int md = dim - 1 ; md >= 0 ; md-- )
+ {
+ if ( shp[md] > max_extent && shp[md] >= nslices )
+ {
+ max_extent = shp[md] ;
+ maxd = md ;
+ break ;
+ }
+ }
+
+ if ( max_extent == -1 )
+ {
+ // repeat process with relaxed conditions
+ for ( int md = dim - 1 ; md >= 0 ; md-- )
+ {
+ if ( shp[md] > max_extent )
+ {
+ max_extent = shp[md] ;
+ maxd = md ;
+ break ;
+ }
+ }
+ }
+
+ nslices = min ( max_extent , nslices ) ;
+ if ( nslices <= 1 )
+ {
+ // process in this thread
+ input_Navigator nav_in(input.traverser_begin(), input.shape() , d);
+ output_Navigator nav_out(output.traverser_begin(), input.shape() , d);
+ process<input_Navigator,output_Navigator,solver_type > ( nav_in , nav_out , sp ) ;
+ }
+ else
+ {
+ thread * t[nslices] ;
+
+ for ( int s = 0 ; s < nslices ; s++ )
+ {
+ diff_t s0 ; // origin of the view
+ s0[maxd] = ( s * max_extent ) / nslices ; // set start position in largest dimension to cut position
+ diff_t s1 = shape ; // end of the view
+ s1[maxd] = ( (s+1) * max_extent ) / nslices ; // end position is the next cut position (one beyond)
+ input_Navigator nav_in(input.traverser_begin(), s0 , s1 , d);
+ output_Navigator nav_out(output.traverser_begin(), s0 , s1 , d);
+ t[s] = new thread ( process<input_Navigator,output_Navigator,solver_type > ,
+ nav_in , nav_out , sp ) ;
+ }
+ for ( int s = 0 ; s < nslices ; s++ )
+ {
+ t[s]->join() ;
+ delete t[s] ;
+ }
+ }
+}
+*/
+
+/// process() prefilters a chunk of data along one axis. The solver is repeatedly
+/// called for 1D subarrays collinear to the processing axis.
+
+template < typename input_array_type , ///< type of array with knot point data
+ typename output_array_type , ///< type of array for coefficients (may be the same)
+ typename solver_type > ///< type of solver to use
+int process ( input_array_type &input , ///< knot point data. the routine can also operate in-place
+ output_array_type &output , ///< where input == output.)
+ solver_type &solver , ///< solver to use
+ int axis ///< axis to process
+ )
+{
+ const int dim = input_array_type::actual_dimension ;
+
+ typedef vigra::MultiArrayNavigator<typename input_array_type::traverser, dim> input_Navigator;
+ typedef vigra::MultiArrayNavigator<typename output_array_type::traverser, dim> output_Navigator;
+
+ input_Navigator nav_in ( input.traverser_begin() , input.shape() , axis ) ;
+ output_Navigator nav_out ( output.traverser_begin() , output.shape() , axis ) ;
+
+ while ( nav_in.hasMore() )
+ {
+ solver.solve ( nav_in.begin() , nav_out.begin() ) ;
+ ++nav_in ;
+ ++nav_out ;
+ }
+}
+
+/// solve_vigra() prefilters an array along a specific axis. This routine splits the
+/// input and output array into equal-sized chunks and processes each chunk in a separate
+/// thread. This is done by using 'divide_and_conquer'.
+
+template < typename input_array_type , ///< type of array with knot point data
+ typename output_array_type > ///< type of array for coefficients (may be the same)
+void solve_vigra ( input_array_type &input , ///< knot point data. the routine can also operate in-place
+ output_array_type &output , ///< where input == output.
+ bc_code bc , ///< boundary treatment for this solver
+ int degree , ///< degree of the spline
+ int d , ///< axis to process
+ int nslices = ncores ) ///< number of threads to use
+{
+ const int dim = input_array_type::actual_dimension ;
+
+ typedef vigra::MultiArrayNavigator<typename input_array_type::traverser, dim> input_Navigator;
+ typedef vigra::MultiArrayNavigator<typename output_array_type::traverser, dim> output_Navigator;
+ typedef typename input_Navigator::iterator input_nav_iter ;
+ typedef typename output_Navigator::iterator output_nav_iter ;
+ typedef solver < input_nav_iter , output_nav_iter > solver_type ;
+
+ int lambda_exponent = 1 ;
+
+// deactivating the code below may produce slightly more precise results
+
+ if ( pow ( degree , dim ) < 64 ) // heuristic. for high degrees, below optimization reduces precision too much
+ {
+ lambda_exponent = 0 ;
+ if ( d == 0 )
+ lambda_exponent = dim ;
+ }
+
+ solver_type s ( input.shape(d) , lambda_exponent , bc , degree + 1 ) ;
+
+ using namespace std::placeholders ;
+
+ // we use bind to create a functor which we can pass to divide_and_conquer_2.
+ // divide_and_conquer_2 will split input and output into chunks and then apply
+ // the functor to pairs of chunks in separate threads.
+
+ auto chunk_func_2
+ = std::bind ( process < input_array_type , output_array_type , solver_type > ,
+ _1 , // placeholders to accept data chunks
+ _2 , // from divide_and_conquer
+ std::ref(s) , // solver to apply
+ d ) ; // axis to process
+
+ // divide_and_conquer_2 performs the array splitting and multithreading
+
+ divide_and_conquer_2 < input_array_type , output_array_type >
+ :: run ( input , // knot point data
+ output , // space for coefficients
+ chunk_func_2 , // functor from above to apply the solver
+ d , // forbid splitting along axis d
+ nslices ) ; // use nslices threads
+}
+
+/// This routine calls the 1D prefiltering routine for all axes in turn.
+
+template < typename input_array_type , // type of array with knot point data
+ typename output_array_type > // type of array for coefficients (may be the same)
+void solve_vigra ( input_array_type & input ,
+ output_array_type & output ,
+ TinyVector<bc_code,input_array_type::actual_dimension> bc ,
+ int degree ,
+ int nslices = ncores )
+{
+ // check if operation is in-place. I assume that the test performed here
+ // is sufficient to determine if the operation is in-place.
+
+ bool in_place = false ;
+
+ if ( (void*)(input.data()) == (void*)(output.data()) )
+ in_place = true ;
+
+ // if input == output, with degree <= 1 we needn't do anything at all.
+
+ if ( in_place && degree <= 1 )
+ return ;
+
+ // do a bit of compatibility checking
+
+ const int dim = input_array_type::actual_dimension ;
+
+ if ( output_array_type::actual_dimension != dim )
+ {
+ throw dimension_mismatch ( "input and output array must have the same dimension" ) ;
+ }
+
+ typedef typename input_array_type::difference_type diff_t ;
+ diff_t shape = input.shape() ;
+ if ( output.shape() != shape )
+ {
+ throw shape_mismatch ( "input and output array must have the same shape" ) ;
+ }
+
+ typedef typename input_array_type::value_type compound_type ;
+ typedef typename ExpandElementResult<compound_type>::type mattype ;
+
+ // even if degree <= 1, we'll only arrive here if input != output.
+ // So we still have to copy the input data to the output (solve_identity)
+
+ solve_vigra<input_array_type,output_array_type>
+ ( input , output , bc[0] , degree , 0 , nslices ) ;
+
+ // but if degree <= 1 we're done already, since copying the data again
+ // in dimensions 1... is futile
+
+ if ( degree > 1 )
+ {
+ for ( int d = 1 ; d < dim ; d++ )
+ solve_vigra<output_array_type,output_array_type>
+ ( output , output , bc[d] , degree , d , nslices ) ;
+ }
+}
+
+/// An interlude: restoration of the original knot point data from the spline coefficients.
+/// This is easily done by a simple convolution with the values of the basis function
+/// taken at discrete points inside the defined range.
+/// The function can take arbitrary spline degrees as template argument.
+/// there are two functions to restore the original data from a spline: the first one takes a
+/// braced spline and convolves it with BORDER_TREATMENT_AVOID. This way the explicit
+/// border treatment manifest in the brace is used, and splines with arbitrary border conditions
+/// can be verified.
+/// TODO: bit rough and ready - restoration from braced should really only produce the inner
+/// part, not the area covered by the brace.
+
+template < class array >
+void restore_from_braced ( array &spline , array& target , int SplineDegree )
+{
+ typedef typename array::value_type value_type ;
+ const int half_ext = SplineDegree / 2 ;
+ vigra::Kernel1D<value_type> spline_kernel ;
+ spline_kernel.initExplicitly(-half_ext, half_ext) ;
+ for ( int i = -half_ext ; i <= half_ext ; i++ )
+ spline_kernel[i] = bspline_basis<double> ( i , SplineDegree ) ;
+// cout << "using kernel" ;
+// for ( int i = -half_ext ; i <= half_ext ; i++ )
+// cout << " " << spline_kernel[i] ;
+// cout << endl ;
+ spline_kernel.setBorderTreatment ( BORDER_TREATMENT_AVOID ) ;
+ separableConvolveMultiArray(spline, target, spline_kernel);
+}
+
+/// the second function takes a border treatment mode from vigra's collection and works
+/// on an unbraced spline. Some border treatment modes implemented here aren't available
+/// from vigra or aren't applicable
+/// - vigra vspline
+/// - BORDER_TREATMENT_AVOID used with braced splines
+/// - BORDER_TREATMENT_CLIP not sure
+/// - BORDER_TREATMENT_REPEAT nearest is flat or REFLECT, same effect in cubic splines
+/// - BORDER_TREATMENT_REFLECT MIRROR
+/// - BORDER_TREATMENT_WRAP PERIODIC
+/// - BORDER_TREATMENT_ZEROPAD -
+
+template < class array >
+void restore_from_unbraced ( array &spline , array& target , BorderTreatmentMode btm , int SplineDegree )
+{
+ typedef typename array::value_type value_type ;
+ const int half_ext = SplineDegree / 2 ;
+ vigra::Kernel1D<value_type> spline_kernel ;
+ spline_kernel.initExplicitly(-half_ext, half_ext) ;
+ for ( int i = -half_ext ; i <= half_ext ; i++ )
+ spline_kernel[i] = bspline_basis<double> ( i , SplineDegree ) ;
+ spline_kernel.setBorderTreatment ( btm ) ;
+ separableConvolveMultiArray(spline, target, spline_kernel);
+}
+
+} ; // end of namespace vspline
+
+// the use of Vc has to be switched on with the flag USE_VC.
+// The ramainder of the code in this file provides vectorized prefiltering
+// using Vc.
+
+#ifdef USE_VC
+
+namespace vigra
+{
+ /// specializes ExpandElementResult for Vc::Vector so that
+ /// solver objects operating on Vc::Vectors can infer the type of the
+ /// elements in the Vector.
+
+template <class T>
+struct ExpandElementResult < Vc::Vector<T> >
+{
+ typedef T type;
+ enum { size = Vc::Vector<T>::Size };
+} ;
+
+} ;
+
+namespace vspline
+{
+/// Here's my shot at using SIMD code for prefiltering.
+/// The complicated bit is the aggregation, since we're not doing a point operation, but
+/// have to keep context. So the aggregation has to happen perpendicular to the processing
+/// axis in order to maintain the same context relationship as without aggregation.
+/// We need some acrobatics to interface the image data with the SIMD data types.
+/// The following class implements a proxy object, which can be made into a vector type
+/// (on read access) and will store a vector type into a vigra array (on write access).
+/// Since the iterators we pass into the solver make use of expressions which use
+/// these proxy objects, we also need to define some arithmetic operators to be able to
+/// use the same code that we use without vectorization.
+///
+/// I have coded for explicit aligned load/stores when accessing aligned memory, but on
+/// my system I haven't seen real improvements from this. Anyway, it won't do harm.
+/// On my system, I have measured significant speedups for float data, while prefiltering
+/// double data performed slightly worse than the unvectorized version. For spline degrees
+/// 0 and 1, where the code currently copies the input to the output, vectorized prefiltering
+/// is generally slower, most likely due to the buffering taking place. So if performance
+/// is a consideration, it's best to check execution times rather than assuming that vectorized
+/// perfiltering is always faster. There's roundtrip.cc in the examples to do just that.
+
+template < class single_type , bool do_gather , bool do_mask , bool aligned = false >
+class vector_proxy
+{
+ static_assert ( ! ( do_gather == false && do_mask == true ) ,
+ "if vector_proxy uses masked operations, indexes must be provided" ) ;
+
+ typedef Vc::Vector<single_type> vector_type ;
+ typedef typename vector_type::IndexType index_type ;
+ typedef typename vector_type::Mask mask_type ;
+
+ single_type* base ;
+ index_type indexes ;
+ mask_type mask ;
+
+public:
+
+ /// a set of constructors, reflecting the three modes of data transfer
+ /// from memory to SIMD register:
+ /// - plain load/store
+ /// - gather/scatter
+ /// - masked gather/scatter
+ /// TODO: seems there is a masked load/store after all...
+ /// note that there is no masked load/store, even though one would assume so
+ /// for symmetry reasons. If it's masked, it has to be a gather/scatter operation.
+ /// I had initially coded for masked load/stores as well (by supplying a default mask)
+ /// but I took that code out.
+
+ vector_proxy ( single_type* _base )
+ : base ( _base )
+ {} ;
+
+ vector_proxy ( single_type* _base ,
+ index_type _indexes )
+ : base ( _base ) ,
+ indexes ( _indexes )
+ {} ;
+
+ vector_proxy ( single_type* _base ,
+ index_type _indexes ,
+ mask_type _mask )
+ : base ( _base ) ,
+ indexes ( _indexes ) ,
+ mask ( _mask )
+ {} ;
+
+ /// construction of an SIMD vector from a proxy object performs the appropriate
+ /// memory access. the logical operations are performed on template arguments, so
+ /// they aren't executed at run-time but at compile-time, with the optimizer removing
+ /// all but the specifically needed code.
+
+ operator vector_type() const
+ {
+ if ( do_gather && do_mask )
+ return vector_type ( base , indexes , mask ) ;
+ else if ( do_gather )
+ return vector_type ( base , indexes ) ;
+ else if ( aligned )
+ return vector_type ( base , Vc::Aligned ) ;
+ else
+ return vector_type ( base ) ;
+ }
+
+ /// this operator= results in the data in rhs to be written to memory. this is where the proxy
+ /// character shows most clearly, since there is no vector 'stored' somewhere and the write
+ /// operation has to be mapped somehow to 'ordinary' memory. Since none of the members
+ /// are affected by the operation, it's a const function.
+
+ const vector_type& operator= ( const vector_type& rhs ) const
+ {
+ if ( do_gather && do_mask )
+ rhs.scatter ( base , indexes , mask ) ;
+ else if ( do_gather )
+ rhs.scatter ( base , indexes ) ;
+ else if ( aligned )
+ rhs.store ( base , Vc::Aligned ) ;
+ else
+ rhs.store ( base ) ;
+ return rhs ;
+ }
+
+ /// next we define those operators we need for the solver. the operators are
+ /// defined for combinations of vectors and proxy types, and there are operators
+ /// with scalars as well. Most of the operator functions are declared as friends
+ /// inside this class.
+ /// Since the code is the same for all operators, we use a macro:
+
+#define opfuncs(op) \
+ vector_type operator op##= ( const vector_type& rhs ) const \
+ { \
+ return ( *this = vector_type ( *this ) op rhs ) ; \
+ } \
+ friend vector_type operator op ( vector_type lhs, const vector_proxy& rhs) \
+ { \
+ return ( lhs op vector_type(rhs) ) ; \
+ } \
+ friend vector_type operator op ( const vector_proxy& lhs, vector_type rhs) \
+ { \
+ return ( lhs op##= rhs ) ; \
+ } \
+ friend vector_type operator op ( const vector_proxy& lhs, const vector_proxy& rhs) \
+ { \
+ return vector_type(lhs) op vector_type(rhs) ; \
+ } \
+ friend vector_type operator op ( const single_type& lhs, const vector_proxy& rhs) \
+ { \
+ return lhs op vector_type(rhs) ; \
+ } \
+ friend vector_type operator op ( const vector_proxy& lhs, const single_type& rhs) \
+ { \
+ return vector_type(lhs) op rhs ; \
+ }
+
+ // we only support the basic arithmetic operators, that's all we need in the solver:
+
+ opfuncs(*)
+ opfuncs(/)
+ opfuncs(+)
+ opfuncs(-)
+} ;
+
+/// class vector_iterator defines an iterator over a stripe of memory, producing
+/// vector proxy objects on access. These act as interface to obtain Vc::Vectors
+/// from the memory or write Vc::Vectors to the memory. Using this iterator, we can
+/// implement vectorized operation of the solver.
+
+template < class single_type , bool do_gather , bool do_mask , bool aligned = false >
+struct vector_iterator: public Vc::VectorAlignedBase
+{
+ static_assert ( ! ( do_gather == false && do_mask == true ) ,
+ "if vector_iterator uses masked operations, indexes must be provided" ) ;
+
+ typedef Vc::Vector<single_type> vector_type ;
+ typedef Vc::Memory<vector_type> buffer_type ;
+ typedef vector_type value_type ;
+ typedef typename vector_type::IndexType index_type ;
+ typedef typename vector_type::Mask mask_type ;
+
+ single_type * base ;
+ ptrdiff_t stride ;
+ index_type indexes ;
+ mask_type mask ;
+
+ // explicit default constructor
+
+ vector_iterator()
+ { } ;
+
+ // constructor initializing all members
+
+ vector_iterator ( single_type * _base ,
+ ptrdiff_t _stride ,
+ index_type _indexes ,
+ mask_type _mask )
+ : base ( _base ) ,
+ stride ( _stride ) ,
+ indexes ( _indexes ) ,
+ mask ( _mask )
+ { } ;
+
+ // watch out! there is no masked load/store, and internally, Vc uses the same data type
+ // for masks and indexes. A call to this constructor with a mask_type argument will compile,
+ // but it will interpret the mask as indexes and result in the data being gathered/scattered,
+ // which is a hard-to-track bug.
+ // P.S. there are masked load/stores, but not via the vector type's constructor.
+ // TODO: see if we can support these as well
+
+ vector_iterator ( single_type * _base ,
+ ptrdiff_t _stride ,
+ index_type _indexes )
+ : base ( _base ) ,
+ stride ( _stride ) ,
+ indexes ( _indexes )
+ { } ;
+
+ vector_iterator ( single_type * _base ,
+ ptrdiff_t _stride )
+ : base ( _base ) ,
+ stride ( _stride )
+ { } ;
+
+ bool operator== ( const vector_iterator& other )
+ {
+ return ( base == other.base )
+ && ( stride == other.stride )
+ && all_of ( indexes == other.indexes ) ;
+ }
+
+ vector_proxy<single_type, do_gather, do_mask, aligned> operator[] ( const size_t& i )
+ {
+ if ( do_mask && do_gather )
+ return vector_proxy<single_type, do_gather, do_mask, aligned> ( base + i * stride , indexes , mask ) ;
+ else if ( do_gather )
+ return vector_proxy<single_type, do_gather, do_mask, aligned> ( base + i * stride , indexes ) ;
+ else
+ return vector_proxy<single_type, do_gather, do_mask, aligned> ( base + i * stride ) ;
+ }
+
+ vector_type operator[] ( const size_t& i ) const
+ {
+ if ( do_mask && do_gather )
+ return vector_type ( base + i * stride , indexes , mask ) ;
+ else if ( do_gather )
+ return vector_type ( base + i * stride , indexes ) ;
+ else if ( aligned )
+ return vector_type ( base + i * stride , Vc::Aligned ) ;
+ else
+ return vector_type ( base + i * stride ) ;
+ }
+
+ vector_proxy<single_type, do_gather, do_mask, aligned> operator*() const
+ {
+ if ( do_mask && do_gather )
+ return vector_proxy<single_type, do_gather, do_mask, aligned> ( base , indexes , mask ) ;
+ else if ( do_gather )
+ return vector_proxy<single_type, do_gather, do_mask, aligned> ( base , indexes ) ;
+ else
+ return vector_proxy<single_type, do_gather, do_mask, aligned> ( base ) ;
+ }
+
+ /// only preincrement and predecrement are defined for now.
+ /// Note that this implementation changes the base pointer!
+
+ vector_proxy<single_type, do_gather, do_mask, aligned> operator++() // preincrement
+ {
+ base += stride ;
+ return *(*(this)) ;
+ }
+
+ vector_proxy<single_type, do_gather, do_mask, aligned> operator--() // predecrement
+ {
+ base -= stride ;
+ return *(*(this)) ;
+ }
+
+ /// copy out count vectors to a Vc::Memory object
+
+ void buffer ( buffer_type& target , int count )
+ {
+ single_type * save_base = base ;
+
+ for ( int i = 0 ; i < count ; i++ )
+ {
+ target.vector(i) = *(*this) ;
+ base += stride ;
+ }
+ base = save_base ;
+ }
+
+ /// store count vectors from a Vc::Memory object
+
+ void unbuffer ( buffer_type& source , int count )
+ {
+ single_type * save_base = base ;
+
+ for ( int i = 0 ; i < count ; i++ )
+ {
+ *(*this) = source.vector(i) ;
+ base += stride ;
+ }
+ base = save_base ;
+ }
+} ;
+
+/// class aggregator handles the reinterpretation of the incoming/outgoing data
+/// to SIMD-processable types. The data come in as a vigra MultiArrayView of either
+/// a fundamental type or a compound type, like RGB pixel or TinyVector. The array
+/// is expanded into a view to it's fundamental type and the data are grouped in
+/// SIMD vectors perpendicular to the processing axis.
+///
+/// class aggregator functions like an iterator over 'stripes' of data: stripe number v
+/// is accessed by get_stripe(), which returns an iterator over the SIMD vectors in
+/// that stripe. This iterator is passed to the solver. So it's quite similar to a Navigator.
+///
+/// Initially I coded for direct access to the array data, but then I switched to
+/// copying out the array data into a buffer and pass out an iterator to the buffer.
+/// So now, once processing is done, a call to flush() stores the data back to the
+/// array. For simple cases (degree 3) this is as fast, for higher degrees it is faster.
+/// And the code is more straightforward.
+///
+/// I had code running where I did not buffer if the data are contiguous, but
+/// something was wrong with it and it failed the unit tests. TODO investigate
+
+template < class base_view_type >
+class aggregator: public Vc::VectorAlignedBase
+{
+public:
+
+enum { actual_dimension = base_view_type::actual_dimension ,
+ expanded_dimension = base_view_type::actual_dimension + 1 } ;
+
+typedef typename base_view_type::value_type compound_type ;
+typedef typename base_view_type::difference_type shape_type ;
+typedef typename ExpandElementResult<compound_type>::type single_type ;
+typedef MultiArrayView < expanded_dimension , single_type > expanded_view_type ;
+typedef MultiArrayView < actual_dimension , single_type > slice_type ;
+
+typedef Vc::Vector<single_type> vector_type ;
+typedef typename vector_type::IndexType index_type ;
+typedef typename vector_type::Mask mask_type ;
+typedef Vc::Memory<index_type> gather_map_type ;
+typedef Vc::Memory<vector_type> buffer_type ;
+
+typedef vector_iterator < single_type , false , false > plain_vector_iterator ;
+
+// currently I always buffer to an aligned buffer and use aligned ops on it:
+
+typedef vector_iterator < single_type , false , false , true > aligned_plain_vector_iterator ;
+
+typedef vector_iterator < single_type , true , false > gathering_vector_iterator ;
+typedef vector_iterator < single_type , true , true > gathering_masked_vector_iterator ;
+
+bool consult_gather_map ;
+index_type default_indexes ;
+
+size_t count ;
+size_t full_vectors ;
+size_t total_vectors ;
+mask_type mask ;
+single_type * base ;
+ptrdiff_t stride ;
+size_t index_count ;
+size_t remainder ;
+
+gather_map_type gather_map ;
+buffer_type buffer ;
+
+aligned_plain_vector_iterator current_stripe_iterator ;
+
+// test an index_type for sequentiality
+static bool sequential ( const index_type & indexes )
+{
+ return Vc::all_of ( indexes - indexes[0] == index_type::IndexesFromZero() ) ;
+}
+
+aggregator ( base_view_type base_view , int axis )
+: gather_map(1) ,
+ buffer(1) ,
+ default_indexes ( index_type::IndexesFromZero() )
+{
+ // we grab the length of the processing axis here
+
+ count = base_view.shape ( axis ) ;
+
+ // next we create a view to the slice perpendicular to the processing axis.
+ // this is inexpensive, since the view doesn't hold any data. Note that the
+ // slice is taken from an element-expanded array view, since we want to form
+ // SIMD vectors from the data, and there aren't any vectors of, say, pixels.
+
+ expanded_view_type expanded_view = base_view.expandElements ( 0 ) ;
+ slice_type slice_view = expanded_view.bindAt ( axis + 1 , 0 ) ;
+
+ if ( slice_view.isUnstrided() )
+ consult_gather_map = false ; // we won't need a gather map in this case
+ else
+ consult_gather_map = true ;
+
+ // we analyze the slice and extract a few metrics:
+
+ assert ( slice_view.data() == &(slice_view[shape_type()]) ) ;
+ base = slice_view.data() ;
+
+ stride = expanded_view.stride()[axis+1] ;
+ index_count = slice_view.size() ;
+ full_vectors = slice_view.size() / vector_type::Size ;
+
+ if ( index_count == full_vectors * vector_type::Size )
+ {
+ total_vectors = full_vectors ;
+ remainder = 0 ;
+ }
+ else
+ {
+ // if the number of single_type data in the slice isn't a multiple of the
+ // SIMD vector size, we need masking for the last SIMD vector:
+ total_vectors = full_vectors + 1 ;
+ remainder = index_count - full_vectors * vector_type::Size ;
+ index_type help = index_type::IndexesFromZero() ;
+ mask = ( help < remainder ) ;
+ }
+
+ // looks a bit funny, but as we want the buffer to be a class member, we have
+ // to create it with (1) initially and then swap in the real buffer later.
+ buffer_type help_buffer ( count * vector_type::Size ) ;
+ buffer.swap ( help_buffer ) ;
+ current_stripe_iterator = aligned_plain_vector_iterator ( buffer , vector_type::Size ) ;
+
+ // if, above, we have determined that we should use and consult a gather map,
+ // we construct it now in the easiest way possible: instead of doing arithmetics
+ // with strides and shapes, we simply take the difference of the adress of each
+ // element in the slice from the adress of the first element:
+ if ( consult_gather_map )
+ {
+ // our gather map is a class member and has been initialized to some default
+ // value - now we fill it with real data.
+ gather_map_type gm ( index_count ) ;
+ gather_map.swap ( gm ) ;
+
+ auto sliter = slice_view.begin() ;
+
+ for ( size_t i = 0 ; i < index_count ; i++ )
+ {
+ gather_map [ i ] = &(*sliter) - base ;
+ ++sliter ;
+ }
+ }
+}
+
+aligned_plain_vector_iterator& get_stripe ( int v )
+{
+ if ( v < 0 || v >= total_vectors )
+ throw std::out_of_range ( "cannot access a stripe with this index" ) ;
+
+ index_type indexes ;
+
+ if ( v < full_vectors )
+ {
+ if ( consult_gather_map )
+ {
+ // cout << "1" ;
+ indexes = gather_map.vector(v) ;
+ // if ( indexes[0] + index_type::Size - 1 == indexes[index_type::Size-1] )
+ if ( sequential ( indexes ) )
+ {
+ // cout << "a - plain load" << endl ;
+ // all indexes are in sequence, so we can do a plain load here as well.
+ // at first this case looks like the else case two down, but here we might
+ // have jumps in the gather map (from 3D upwards), so we have to base by
+ // reading out the gather map.
+ plain_vector_iterator vi ( base + indexes[0] , stride ) ;
+ vi.buffer ( buffer , count ) ; // currently we always buffer
+ }
+ else
+ {
+ // cout << "b - gather indexed with " << indexes << endl ;
+ // we really need to gather
+ gathering_vector_iterator vi ( base, stride, indexes ) ;
+ vi.buffer ( buffer , count ) ;
+ }
+ }
+ else
+ {
+ // cout << "2 - plain load" << endl ;
+ // definitely no need to gather
+ plain_vector_iterator vi ( base + v * vector_type::Size , stride ) ;
+ vi.buffer ( buffer , count ) ; // currently we always buffer
+ }
+ }
+ else if ( v < total_vectors ) // one last incomplete vector, needs masked operation
+ {
+ if ( consult_gather_map )
+ {
+ indexes = gather_map.vector(v) ;
+ // cout << "3 - masked gather with " << indexes << ", " << mask << endl ;
+ gathering_masked_vector_iterator vi ( base, stride, indexes , mask ) ;
+ vi.buffer ( buffer , count ) ;
+ }
+ else
+ {
+ indexes = default_indexes ;
+ // cout << "4 - masked gather with default " << indexes << ", " << mask << endl ;
+ gathering_masked_vector_iterator vi ( base + v * vector_type::Size , stride, indexes , mask ) ;
+ vi.buffer ( buffer , count ) ;
+ }
+ }
+ return current_stripe_iterator ;
+}
+
+/// bit verbose, copies most of above routine. TODO factor sth. out.
+/// flush writes the buffer to the array. per default this writes to the aggregator's
+/// own buffer, but this can be overridden by passing in a pointer to the target buffer
+
+void flush ( int v , buffer_type * bp = 0 )
+{
+ if ( v < 0 || v >= total_vectors )
+ throw std::out_of_range ( "cannot access a stripe with this index" ) ;
+
+ index_type indexes ;
+
+ if ( bp == 0 )
+ bp = &buffer ;
+
+ if ( v < full_vectors )
+ {
+ if ( consult_gather_map )
+ {
+ // cout << "1" ;
+ indexes = gather_map.vector(v) ;
+ if ( indexes[0] + index_type::Size - 1 == indexes[index_type::Size-1] )
+ {
+ // cout << "a - plain store" << endl ;
+ // all indexes are in sequence, so we can do a plain load here as well.
+ // at first this case looks like the else case two down, but here we might
+ // have jumps in the gather map (from 3D upwards), so we have to base by
+ // reading out the gather map.
+ plain_vector_iterator vi ( base + indexes[0] , stride ) ;
+ vi.unbuffer ( *bp , count ) ;
+ }
+ else
+ {
+ // cout << "b - scatter indexed with " << indexes << endl ;
+ // we really need to gather
+ gathering_vector_iterator vi ( base, stride, indexes ) ;
+ vi.unbuffer ( *bp , count ) ;
+ }
+ }
+ else
+ {
+ // cout << "2 - plain store" << endl ;
+ // definitely no need to gather
+ plain_vector_iterator vi ( base + v * vector_type::Size , stride ) ;
+ vi.unbuffer ( *bp , count ) ;
+ }
+ }
+ else if ( v < total_vectors ) // one last incomplete vector, needs masked operation
+ {
+ if ( consult_gather_map )
+ {
+ indexes = gather_map.vector(v) ;
+ // cout << "3 - masked scatter with " << indexes << ", " << mask << endl ;
+ gathering_masked_vector_iterator vi ( base, stride, indexes , mask ) ;
+ vi.unbuffer ( *bp , count ) ;
+ }
+ else
+ {
+ indexes = default_indexes ;
+ // cout << "4 - masked scatter with default " << indexes << ", " << mask << endl ;
+ gathering_masked_vector_iterator vi ( base + v * vector_type::Size , stride, indexes , mask ) ;
+ vi.unbuffer ( *bp , count ) ;
+ }
+ }
+}
+
+} ;
+
+/// aggregating_filter handles the presentation of the data so that they can
+/// be processed vectorized by the solver.
+
+template < class base_view_type ,
+ class output_array_type >
+int aggregating_filter ( base_view_type &base_view ,
+ output_array_type &target ,
+ bc_code bc ,
+ int degree ,
+ int axis )
+{
+ typedef aggregator<base_view_type> aggregator_type ;
+ typedef aggregator<output_array_type> output_aggregator_type ;
+ typedef typename aggregator_type::aligned_plain_vector_iterator stripe_iterator ;
+ typedef typename output_aggregator_type::aligned_plain_vector_iterator output_stripe_iterator ;
+ typedef typename aggregator_type::single_type single_type ;
+
+ int lambda_exponent = 1 ;
+
+ // heuristic. for high degrees, below optimization would reduce precision too much
+ if ( pow ( degree , int(target.actual_dimension) ) < 64 ) // was: if ( degree < 7 )
+ {
+ lambda_exponent = 0 ;
+ if ( axis == 0 )
+ lambda_exponent = base_view_type::actual_dimension ;
+ }
+
+ aggregator_type a ( base_view , axis ) ;
+
+ if ( &base_view == &target )
+ {
+ solver < stripe_iterator , stripe_iterator > slv ( a.count , lambda_exponent , bc , degree + 1 ) ;
+
+ for ( int stripe = 0 ; stripe < a.total_vectors ; stripe++ )
+ {
+ stripe_iterator& s = a.get_stripe ( stripe ) ;
+ slv.solve ( s ) ;
+ a.flush ( stripe ) ;
+ }
+ }
+ else
+ {
+ output_aggregator_type target_aggregator ( target , axis ) ;
+ solver < stripe_iterator , output_stripe_iterator > slv ( a.count , lambda_exponent , bc , degree + 1 ) ;
+
+ for ( int stripe = 0 ; stripe < a.total_vectors ; stripe++ )
+ {
+ stripe_iterator& s = a.get_stripe ( stripe ) ;
+ slv.solve ( s ) ;
+// assert ( target_aggregator.buffer.vectorsCount() == a.buffer.vectorsCount() ) ;
+// assert ( target_aggregator.buffer.entriesCount() == a.buffer.entriesCount() ) ;
+ target_aggregator.flush ( stripe , &(a.buffer) ) ; // store to target instead of back to source
+ }
+ }
+ return 0 ;
+}
+
+// previous implementation, not using divide_and_conquer
+
+/*
+template < typename input_array_type ,
+ typename output_array_type > // type of array for coefficients (may be the same)
+void solve_vc ( input_array_type &input ,
+ output_array_type &output ,
+ bc_code bc , // boundary treatment for this solver
+ int degree ,
+ int d , // axis to process
+ int nslices = ncores ) // number of threads to use
+{
+ vector < input_array_type > iv ;
+ vector < output_array_type > ov ;
+
+ nslices = split_array_to_chunks<input_array_type> ( input , iv , nslices , d ) ;
+
+ thread * t[nslices] ;
+
+ if ( &input == &output )
+ {
+ for ( int s = 0 ; s < nslices ; s++ )
+ {
+ t[s] = new thread ( aggregating_filter<input_array_type,input_array_type> ,
+ std::ref(iv[s]) , std::ref(iv[s]) , bc , degree , d ) ;
+ }
+ }
+ else
+ {
+ split_array_to_chunks<output_array_type> ( output , ov , nslices , d ) ;
+ for ( int s = 0 ; s < nslices ; s++ )
+ {
+ t[s] = new thread ( aggregating_filter<input_array_type,output_array_type> ,
+ std::ref(iv[s]) , std::ref(ov[s]) , bc , degree , d ) ;
+ }
+ }
+
+ for ( int s = 0 ; s < nslices ; s++ )
+ {
+ t[s]->join() ;
+ delete t[s] ;
+ }
+}
+*/
+
+/// solve_vc is the routine to prefilter using vectorization with Vc.
+/// This is the single-axis version
+
+template < typename input_array_type ,
+ typename output_array_type > // type of array for coefficients (may be the same)
+void solve_vc ( input_array_type &input ,
+ output_array_type &output ,
+ bc_code bc , // boundary treatment for this solver
+ int degree ,
+ int d , // axis to process
+ int nslices = ncores ) // number of threads to use
+{
+ using namespace std::placeholders ;
+
+ // use bind to create a functor which can be passed to divide_and_conquer_2
+
+ auto chunk_func_2
+ = std::bind ( aggregating_filter < input_array_type , output_array_type > ,
+ _1 ,
+ _2 ,
+ bc ,
+ degree ,
+ d ) ;
+
+ // use divide_and_conquer_2 to split input and output into chunks and apply
+ // the functor above to each corresponding pair of chunks in a separate thread
+
+ divide_and_conquer_2 < input_array_type , output_array_type >
+ :: run ( input ,
+ output ,
+ chunk_func_2 ,
+ d ,
+ nslices ) ;
+}
+
+/// multi-axis version of solve_vc. This is what I use now per default, it performs
+/// the prefiltering with multiple threads and vectorization, which is the fastest
+/// way I could come up with.
+
+template < typename input_array_type , ///< type of array with knot point data
+ typename output_array_type > ///< type of array for coefficients (may be the same)
+void solve_vc ( input_array_type& input ,
+ output_array_type& output ,
+ TinyVector<bc_code,input_array_type::actual_dimension> bc ,
+ int degree ,
+ int nslices = ncores )
+{
+ // check if operation is in-place. I assume that the test performed here
+ // is sufficient to determine if the operation is in-place.
+
+ bool in_place = false ;
+
+ if ( (void*)(input.data()) == (void*)(output.data()) )
+ in_place = true ;
+
+ // if input == output, with degree <= 1 we needn't do anything at all.
+
+ if ( in_place && degree <= 1 )
+ return ;
+
+
+ // do a bit of compatibility checking
+
+ const int dim = input_array_type::actual_dimension ;
+
+ if ( output_array_type::actual_dimension != dim )
+ {
+ cerr << "solve_vigra: dimensions don't match" << endl ;
+ throw dimension_mismatch ( "input and output array must have the same dimension" ) ;
+ }
+
+ typedef typename input_array_type::difference_type diff_t ;
+ diff_t shape = input.shape() ;
+ if ( output.shape() != shape )
+ {
+ cerr << "solve_vigra: shapes don't match" << endl ;
+ throw dimension_mismatch ( "input and output array must have the same shape" ) ;
+ }
+
+ // even if degree <= 1, we'll only arrive here if input != output.
+ // So we still have to copy the input data to the output (solve_identity)
+
+ solve_vc<input_array_type,output_array_type>
+ ( input , output , bc[0] , degree , 0 , nslices ) ;
+
+ // but if degree <= 1 we're done already, since copying the data again
+ // in dimensions 1... is futile
+
+ if ( degree > 1 )
+ {
+ for ( int d = 1 ; d < dim ; d++ )
+ solve_vc<output_array_type,output_array_type>
+ ( output , output , bc[d] , degree , d , nslices ) ;
+ }
+}
+
+} ; // namespace vspline
+
+#endif // USE_VC
+
+#endif // VSPLINE_PREFILTER_H
diff --git a/prefilter_poles.cc b/prefilter_poles.cc
new file mode 100644
index 0000000..d30ec5b
--- /dev/null
+++ b/prefilter_poles.cc
@@ -0,0 +1,174 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform rational b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/*! \file prefilter_poles.cc
+
+ \brief calculates the poles of the b-spline prefilter using gsl and BLAS
+
+ this doesn't have to be done for installing vspline if poles.cc is
+ already present. Providing degrees up to 24 is just about what gsl
+ can handle, with such high degrees the evaluation becomes quite imprecise
+ as well, especialy for floats.
+
+ compile with:
+ g++ -std=c++11 prefilter_poles.cc -oprefilter_poles -lgsl -lblas
+ run
+ ./prefilter_poles > poles.cc
+
+ TODO: could do with some TLC...
+*/
+
+#include <iostream>
+#include <iomanip>
+
+#include <vigra/array_vector.hxx>
+#include <vigra/splines.hxx>
+#include <gsl/gsl_poly.h>
+#include <vspline/basis.h>
+
+using namespace std ;
+using namespace vigra ;
+
+// template < class real_type >
+// real_type bspline_basis ( real_type x , int degree , int derivative = 0 )
+// {
+// if ( degree == 0 )
+// {
+// if ( derivative == 0 )
+// return x < real_type(0.5) && real_type(-0.5) <= x ?
+// real_type(1.0)
+// : real_type(0.0);
+// else
+// return real_type(0.0);
+// }
+// if ( derivative == 0 )
+// {
+// real_type n12 = real_type((degree + 1.0) / 2.0);
+// return ( ( n12 + x )
+// * bspline_basis ( x + real_type(0.5) , degree - 1 , 0 )
+// + ( n12 - x )
+// * bspline_basis ( x - real_type(0.5) , degree - 1 , 0 )
+// )
+// / degree;
+// }
+// else
+// {
+// --derivative;
+// return bspline_basis ( x + real_type(0.5) , degree - 1 , derivative )
+// - bspline_basis ( x - real_type(0.5) , degree - 1 , derivative ) ;
+// }
+// }
+
+template < class T >
+ArrayVector<double>
+calculatePrefilterCoefficients(int DEGREE)
+{
+ ArrayVector<double> res;
+ const int r = DEGREE / 2;
+ double a[2*r+1] ;
+ double z[4*r+2] ;
+ cout << "long double K" << DEGREE << "[] = {" << endl ;
+ // we calculate the basis function values at 0.5 intervals
+ int imax = 2 * r ;
+ if ( DEGREE & 1 )
+ imax++ ;
+ for(int i = 0; i <= imax ; ++i)
+ {
+ long double half_i = i / (long double) 2.0 ;
+ long double v = vspline::gen_bspline_basis<long double> ( half_i , DEGREE , 0 ) ;
+ cout << " " << v << "L , // basis(" << half_i << ")" << endl ;
+ if ( ! ( i & 1 ) )
+ {
+ // for even i, we put the value in a[] as well - only even i
+ // correspond to the value of the basis function at integral values
+ // which we need for the poles
+ int ih = i / 2 ;
+ a [ r - ih ] = a [ r + ih ] = v ;
+ }
+ }
+ cout << " } ; " << endl ;
+
+ if(DEGREE > 1)
+ {
+ ArrayVector<double> roots;
+
+ // we set up the environment gsl needs to find the roots
+ gsl_poly_complex_workspace * w
+ = gsl_poly_complex_workspace_alloc (2*r+1);
+ // now we call gsl's root finder
+ gsl_poly_complex_solve (a, 2*r+1, w, z);
+ // and release it's workspace
+ gsl_poly_complex_workspace_free (w);
+
+ // we only look at the real parts of the values, which are stored
+ // interleaved real/imag. And we take them back to front, even though
+ // it doesn't matter to Thevenaz' code which end we start with - but conventionally
+ // Pole[0] is the root with the largest absolute, so I stick with that.
+ for(int i = 2 * r - 2 ; i >= 0; i-=2)
+ if(VIGRA_CSTD::fabs(z[i]) < 1.0)
+ res.push_back(z[i]);
+ }
+ return res;
+}
+
+// TODO ugly mishmash of prints and calculations...
+
+void print_poles ( int degree )
+{
+ ArrayVector<double> res = calculatePrefilterCoefficients<double> ( degree ) ;
+ if ( degree > 1 )
+ {
+ cout << "double Poles_" << degree << "[] = {" << endl ;
+ for ( auto r : res )
+ cout << r << " ," << endl ;
+ cout << "} ;" << endl ;
+ }
+}
+
+int main ( int argc , char * argv[] )
+{
+ cout << setprecision(20) ;
+
+ for ( int degree = 0 ; degree < 25 ; degree++ )
+ print_poles(degree) ;
+
+ cout << noshowpos ;
+ cout << "const double* precomputed_poles[] = {" << endl ;
+ cout << " 0, " << endl ;
+ cout << " 0, " << endl ;
+ for ( int i = 2 ; i < 25 ; i++ )
+ cout << " Poles_" << i << ", " << endl ;
+ cout << "} ;" << endl ;
+ cout << "const long double* precomputed_basis_function_values[] = {" << endl ;
+ for ( int i = 0 ; i < 25 ; i++ )
+ cout << " K" << i << ", " << endl ;
+ cout << "} ;" << endl ;
+}
diff --git a/remap.h b/remap.h
new file mode 100644
index 0000000..61e0622
--- /dev/null
+++ b/remap.h
@@ -0,0 +1,883 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/// \file remap.h
+///
+/// \brief set of generic remap functions
+///
+/// My foremost reason to have efficient B-spline processing is the formulation of a generic
+/// remap function. This is a function which takes an array of real-valued coordinates and
+/// an interpolator over a source array. Now each of the real-valued coordinates is fed into
+/// the interpolator in turn, yielding a value, which is placed in the output array at the same
+/// place the coordinate occupies in the coordinate array. To put it concisely, if we have
+///
+/// - c, the coordinate array (or 'warp' array)
+/// - a, the source array
+/// - i, the interpolator over a
+/// - j, the coordinates in c
+/// - and t, the target
+///
+/// remap defines the operation
+///
+/// t[j] = i(a,c[j]) for all j
+///
+/// st_remap is the single_threaded implementation; remap itself partitions it's work
+/// and creates several threads, each running one instance of st_remap.
+///
+/// all remap routines take several template arguments:
+///
+/// - value_type: like, float, double, complex<>, pixels, TinyVectors etc.
+/// - coordinate_type: TinyVector of float or double for coordinates, or split type
+/// - dim_in: number of dimensions of input array (and of 'warp' coordinates)
+/// - dim_out: number of dimensions of output array
+///
+/// You can see from the two dimension parameters that remaps to other-dimensional
+/// objects are supported. This makes it possible to, for example, remap from a
+/// volume to a 2D image, using a 2D warp array containing 3D coordinates.
+///
+/// In these routines, we can switch the use of vectorization on or off. When using
+/// vectorization, this is done in a straightforward fashion by aggregating the input.
+/// If the source/target array lend themselves to it, we can pass it's memory directly
+/// to the vectorized eval code. To keep the complexity down, if the memory is not
+/// suited, I 'manually' aggregate to a small buffer and pass the buffer to and fro.
+///
+/// There is also a second set of remap functions in this file, which don't take a
+/// 'warp' array but a functor instead. This functor receives, for every location in the
+/// output, the corresponding (discrete) coordinates, and returns (real) coordinates pointing
+/// into the input array. This variant exists, because a common situation in image transformations
+/// is that the geometric transformation from one image to another is defined by precisely
+/// such a functor, and the creation of the warp array only makes sense if the transformation
+/// is performed several times. With 'cheap' transformations the difference isn't large,
+/// but if the transformation is very complex, reusing it can save a lot of processing.
+/// What I have in mind here is image transformations as used in panorama stitching,
+/// lens correction, etc.
+///
+/// In the 'example' folder, there is a program called pano_extract.cc, which demonstrates
+/// the use of a transformation-based remap.
+
+#ifndef VSPLINE_REMAP_H
+#define VSPLINE_REMAP_H
+
+#include "eval.h"
+
+namespace vspline {
+
+using namespace std ;
+using namespace vigra ;
+using namespace vigra::multi_math;
+
+template < class value_type , // like, float, double, complex<>, pixels, TinyVectors etc.
+ class coordinate_type , // usually TinyVector of real for coordinates
+ int dim_in , // number of dimensions of input array (and of 'warp' coordinates)
+ int dim_out > // number of dimensions of output array
+int st_remap ( const bspline < value_type , dim_in > & bspl ,
+ MultiArrayView < dim_out , coordinate_type > warp ,
+ MultiArrayView < dim_out , value_type > output ,
+ bool use_vc = true
+ )
+{
+ typedef bspline < value_type , dim_in > spline_type ;
+ typedef typename coordinate_type::value_type rc_type ;
+ typedef evaluator < dim_in , value_type , rc_type , int > evaluator_type ;
+ typedef typename evaluator_type::ele_type ele_type ;
+
+ // do a bit of compatibility checking
+
+ if ( output.shape() != warp.shape() )
+ {
+ throw shape_mismatch ( "the shapes of the warp array and the output array do not match" ) ;
+ }
+
+ evaluator_type ev ( bspl ) ;
+
+ ele_type * p_workspace = new ele_type [ ev.workspace_size() ] ;
+
+#ifdef USE_VC
+
+ typedef typename evaluator_type::ele_v ele_v ;
+ const int vsize = evaluator_type::vsize ;
+
+ ele_v * p_workspace_v = new ele_v [ ev.workspace_size() ] ;
+
+#endif
+
+ // TODO: we blindly assume that we can coiterate in scan order here:
+
+ auto source_it = warp.begin() ;
+ auto target_it = output.begin() ;
+
+ // since coding for all variations of strided and unstrided memory in warp and output
+ // is a bit much, we only differentiate between the ideal case where all memory is unstrided
+ // and all other cases, where we use buffering of vsize elements. In both cases, we pass
+ // consecutive memory to the evaluator, which can deinterleave consistently.
+ // TODO: this pattern of coiteration/cotraversal with suitable iteration methods is
+ // reoccuring and might be worth factoring out, also because these traversals are
+ // often needed in collateral code, like in apply or transform functions.
+
+ int leftover = warp.elementCount() ;
+
+#ifdef USE_VC
+
+ if ( use_vc )
+ {
+ int aggregates = warp.elementCount() / vsize ; // number of full vectors
+ leftover = warp.elementCount() - aggregates * vsize ; // any leftover single values
+ coordinate_type * source = warp.data() ; // acces to memory
+ value_type * destination = output.data() ;
+
+ if ( warp.isUnstrided() )
+ {
+ // best case: warp array has consecutive memory
+ if ( output.isUnstrided() )
+ {
+ // best case: output array has consecutive memory, no need to buffer
+ for ( int a = 0 ; a < aggregates ; a++ , source += vsize , destination += vsize )
+ {
+ ev ( source , destination , p_workspace_v ) ;
+ }
+ target_it += aggregates * vsize ;
+ }
+ else
+ {
+ // we fall back to buffering and storing individual result values
+ // TODO while this is a straightforward implementation, it should be more efficient
+ // to (de)interleave here and call the fully vectorized evaluation code.
+ value_type target_buffer [ vsize ] ;
+ for ( int a = 0 ; a < aggregates ; a++ , source += vsize )
+ {
+ ev ( source , target_buffer , p_workspace_v ) ;
+ for ( int e = 0 ; e < vsize ; e++ )
+ {
+ *target_it = target_buffer[e] ;
+ ++target_it ;
+ }
+ }
+ }
+ source_it += aggregates * vsize ;
+ }
+ else
+ {
+ // we fall back to loading and buffering individual warp values
+ coordinate_type source_buffer [ vsize ] ;
+ if ( output.isUnstrided() )
+ {
+ // best case: output array has consecutive memory
+ for ( int a = 0 ; a < aggregates ; a++ , destination += vsize )
+ {
+ for ( int e = 0 ; e < vsize ; e++ )
+ {
+ source_buffer[e] = *source_it ;
+ ++source_it ;
+ }
+ ev ( source_buffer , destination , p_workspace_v ) ;
+ }
+ target_it += aggregates * vsize ;
+ }
+ else
+ {
+ // we also fall back to buffering and storing individual result values
+ value_type target_buffer [ vsize ] ;
+ for ( int a = 0 ; a < aggregates ; a++ )
+ {
+ for ( int e = 0 ; e < vsize ; e++ )
+ {
+ source_buffer[e] = *source_it ;
+ ++source_it ;
+ }
+ ev ( source_buffer , target_buffer , p_workspace_v ) ;
+ for ( int e = 0 ; e < vsize ; e++ )
+ {
+ *target_it = target_buffer[e] ;
+ ++target_it ;
+ }
+ }
+ }
+ }
+ delete[] p_workspace_v ;
+ }
+
+#endif // USE_VC
+
+ // process leftovers, if any - if vc isn't used, this loop does all the processing
+ while ( leftover-- )
+ {
+ // process leftovers with single-value evaluation
+ *target_it = ev ( *source_it , p_workspace ) ;
+ ++source_it ;
+ ++target_it ;
+ }
+
+ delete[] p_workspace ;
+ return 0 ;
+}
+
+/// this remap variant performs the remap with several threads. The routine above is single-threaded,
+/// but all individual evaluations are independent of each other. So making it multi-threaded
+/// is trivial: just split the warp array and target array n-ways and call the single-threaded
+/// version on these subarrays. We have utility code to perform the splitting of the arrays
+/// and processing the chunks in separate threads, namely divide_and_conquer_2.
+
+template < class value_type , // like, float, double, complex<>, pixels, TinyVectors etc.
+ class coordinate_type , // usually float for coordinates
+ int dim_in , // number of dimensions of input array (and of 'warp' coordinates)
+ int dim_out > // number of dimensions of output array
+int remap ( const bspline < value_type , dim_in > & bspl ,
+ MultiArrayView < dim_out , coordinate_type > warp ,
+ MultiArrayView < dim_out , value_type > output ,
+ bool use_vc = true ,
+ int nthreads = ncores
+ )
+{
+ using namespace std::placeholders ;
+
+ // we use bind to bind the first and last argument of the single-threaded
+ // remap function, leaving two free arguments which will accept the two arrays
+ // of data to coiterate over (being the chunks of warp and output which are created
+ // by divide_and_conquer_2)
+
+ auto chunk_func_2
+ = std::bind ( st_remap < value_type , coordinate_type , dim_in , dim_out > , // single-threaded remap
+ std::ref(bspl) , // bspline passed in above
+ _1 , // placeholders to accept data chunks
+ _2 , // from divide_and_conquer
+ use_vc ) ; // use_vc flag as passed in above
+
+ divide_and_conquer_2 < decltype ( warp ) , // run divide_and_conquer_2
+ decltype ( output ) >
+ :: run ( warp , // on the warp array
+ output , // and the output array
+ chunk_func_2 , // applying the functor we've made with bind
+ -1 , // allowing array splitting along any axis
+ nthreads ) ; // using nthreads threads
+} ;
+
+/// This is a variant of remap, which directly takes an array of values and remaps it,
+/// internally creating a b-spline of given order just for the purpose. This is used for
+/// one-shot remaps where the spline isn't reused.
+
+template < int dimension >
+using bcv_type = TinyVector < bc_code , dimension > ;
+
+template < class value_type , // like, float, double, complex<>, pixels, TinyVectors etc.
+ class coordinate_type , // usually TinyVector of float for coordinates
+ int dim_in , // number of dimensions of input array (and of 'warp' coordinates)
+ int dim_out > // number of dimensions of output array
+
+int remap ( MultiArrayView < dim_in , value_type > input ,
+ MultiArrayView < dim_out , coordinate_type > warp ,
+ MultiArrayView < dim_out , value_type > output ,
+ bcv_type < dim_in > bcv = bcv_type < dim_in > ( MIRROR ) ,
+ int degree = 3 ,
+ bool use_vc = true ,
+ int nthreads = ncores
+ )
+{
+ // check shape compatibility
+
+ if ( output.shape() != warp.shape() )
+ {
+ throw shape_mismatch ( "the shapes of the warp array and the output array do not match" ) ;
+ }
+
+ // create the bspline object
+ bspline < value_type , dim_in > bsp ( input.shape() , degree , bcv ) ;
+ // copy in the data
+ bsp.core = input ;
+ // prefilter
+ bsp.prefilter ( use_vc , nthreads ) ;
+
+ // and call the remap variant taking a bspline object,
+ // passing in the spline, the coordinate array and the target array
+
+ remap < value_type , coordinate_type , dim_in , dim_out > ( bsp , warp , output , use_vc , nthreads ) ;
+
+ return 0 ;
+}
+
+// Next we have some collateral code to get ready for the transformation-based remap
+
+/// type alias for a coordinate transformation functor, to (hopefully) make the signature
+/// more legible. In words: 'transform' is a standard function transforming an n-dimensional
+/// incoming coordinate to an m-dimensional outgoing coordinate. This function takes both
+/// coordinates as references.
+
+template < class rc_type , // elementary type for coordinates, usually float
+ int dim_in , // number of dimensions of input array (and of 'warp' coordinates)
+ int dim_out > // number of dimensions of output array
+using transform
+= std::function < void ( const TinyVector < rc_type , dim_out > & ,
+ TinyVector < rc_type , dim_in > & ) > ;
+
+#ifdef USE_VC
+
+/// We use this type alias, since the type is rather unwieldy
+/// what we need to define is a SimdArray of rc_type, which has just as
+/// many elements as a Vc::Vector of value_type's elementary type:
+
+template < class rc_type , class value_type >
+using rc_v
+= Vc::SimdArray < rc_type ,
+ Vc::Vector < typename ExpandElementResult < value_type >::type > ::Size > ;
+
+/// This type alias defines a vectorized coordinate transformation.
+/// it is the equivalent of the unvectorized type above, taking TinyVectors
+/// of the appropriate SimdArray objects instead of singular values.
+
+template < class rc_v , // SimdArray of vsize real coordinates
+ int dim_in , // number of dimensions of input array (and of 'warp' coordinates)
+ int dim_out > // number of dimensions of output array
+using vtransform
+= std::function < void
+ ( const TinyVector < rc_v , dim_out > & ,
+ TinyVector < rc_v , dim_in > & ) > ;
+
+#endif
+
+/// class transformation is a (multi-) functor, handling coordinate transformations
+/// this class simplifies pulling in transformations by automatically providing a brodcasting
+/// method to apply a single-value transformation to every element of an incoming SimdArray,
+/// if there is nor vectorized code at hand. This is less efficient, but often just enough,
+/// especially if the transformation is simple.
+///
+/// class transformation is constructed either with a single coordinate transformation
+/// functor, or, if USE_VC is defined, optionally with a vectorized coordinate transformation
+/// functor as second constructor argument. The functors have to satisfy the two type aliases
+/// transform and vtransform (see above). For illustration, consider this:
+///
+/// template < class rc_type >
+/// void tf_identity ( const TinyVector < rc_type , 2 > & c_in ,
+/// TinyVector < rc_type , 2 > & c_out )
+/// {
+/// c_out = c_in ;
+/// }
+///
+/// template < class rc_v >
+/// void vtf_identity ( const TinyVector < rc_v , 2 > & c_in ,
+/// TinyVector < rc_v , 2 > & c_out )
+/// {
+/// c_out = c_in ;
+/// }
+///
+/// vspline::transformation < pixel_type , rc_type , 2 , 2 >
+/// tf ( tf_identity<rc_type> , vtf_identity<rc_v> ) ;
+
+// TODO: it's not really necessary to pass in value_type as a template arg, it's only
+// used to determine the correct vsize.
+// TODO: did I get muddled with dim_in and dim_out here? doublecheck!
+// TODO: naming with 'in' and 'out'creates confusion. use other naming scheme
+
+template < class value_type , /// coefficient/result type
+ class rc_type , /// elementary coordinate type (float, double)
+ int dim_in , /// dimension of incoming coordinates (== dim. of target array)
+ int dim_out > /// dimension of outgoing coordinates (== dim. of coefficient array)
+class transformation
+{
+ typedef transform < rc_type , dim_in , dim_out > transform_type ;
+
+ transform_type tf ; /// functor for single-value coordinate transformation
+
+ typedef TinyVector < rc_type , dim_in > rc_in_type ;
+ typedef TinyVector < rc_type , dim_out > rc_out_type ;
+
+#ifdef USE_VC
+
+ typedef rc_v < rc_type , value_type > rc_v_type ;
+ const int vsize = rc_v_type::Size ;
+
+ typedef TinyVector < rc_v_type , dim_in > rc_v_in_type ;
+ typedef TinyVector < rc_v_type , dim_out > rc_v_out_type ;
+
+ typedef vtransform < rc_v_type , dim_in , dim_out > vtransform_type ;
+
+ vtransform_type vtf ; /// functor for vectorized operation
+
+ /// broadcast calls the single-element transform repeatedly to emulate vectorized operation
+
+ void broadcast ( const rc_v_in_type & c_in , rc_v_out_type & c_out )
+ {
+ rc_in_type cs_in ;
+ rc_out_type cs_out ;
+
+ for ( int e = 0 ; e < vsize ; e++ )
+ {
+ for ( int d = 0 ; d < dim_out ; d++ )
+ cs_in[d] = c_in[d][e] ;
+ tf ( cs_in , cs_out ) ;
+ for ( int d = 0 ; d < dim_in ; d++ )
+ c_out[d][e] = cs_out[d] ;
+ }
+ }
+
+public:
+
+ /// this constructor takes tranformation functors for both single-value and
+ /// vectorized coordinate transforms
+
+ transformation ( transform_type _tf , vtransform_type _vtf )
+ : tf ( _tf ) ,
+ vtf ( _vtf )
+ { } ;
+
+ /// this constructor takes only the single-value transformation functor.
+ /// broadcast (see above) is used to emulate vectorized transformation
+ /// If the performance penalty for not using a vectorized transformation is irrelevant,
+ /// or to try out a transformation, this mode of operation is just as good.
+
+ transformation ( transform_type _tf )
+ : tf ( _tf )
+ {
+ } ;
+
+#else
+
+public:
+
+ /// if USE_VC is not defined, we only have this single constructor:
+
+ transformation ( transform_type _tf )
+ : tf ( _tf )
+ { } ;
+
+#endif
+
+public :
+
+ /// class transformation's operator() delegates to the functor passed in at construction
+
+ void operator() ( const rc_in_type& c_in , rc_out_type& c_out )
+ {
+ tf ( c_in , c_out ) ;
+ }
+
+#ifdef USE_VC
+
+ /// if USE_VC is defined, we overload operator() for vecorized arguments
+
+ void operator() ( const rc_v_in_type& c_in , rc_v_out_type& c_out )
+ {
+ if ( vtf )
+ vtf ( c_in , c_out ) ;
+ else
+ broadcast ( c_in , c_out ) ;
+ }
+
+#endif
+} ;
+
+/// struct coordinate_iterator_v provides the vectorized equivalent of vigra's
+/// MultiCoordinateIterator. The iterator itself is not very elaborate, it only
+/// supports dereferencing and preincrement, but for the intended purpose that's
+/// enough. The iterator is cyclical, so the number of times it's used has to be
+/// controlled by the calling code.
+/// dereferencing this object will yield a vectorized coordinate which is guaranteed
+/// to deinterleave to vsize valid ordinary coordinates, but it is only guaranteed that
+/// these ordinary coordinates are unique if several preconditions are met:
+/// - the shape (end - start) must have more than vsize entries
+/// - the iteration doesn't proceed beyond the number of full vectors
+/// The vectorized coordinate delivered at the 'wrap-around-point', so one iteration
+/// after the last full set has ben taken, will be partly wrapped around to the start
+/// of the iteration. This makes sure the contained ordinary coordinates are valid.
+/// but accessing data through it will again touch elements which have already been
+/// seen. As long as the indices aren't used for an in-place operation, recalculating
+/// a few values does no harm.
+// TODO this is utility code and might be placed into another header, probably next to
+// the routine producing successive gather indices for vectorized array traversal
+
+template < class _rc_v , int _dimension >
+struct coordinate_iterator_v
+{
+ enum { dimension = _dimension } ;
+ typedef vigra::TinyVector < int , dimension > shape_type ;
+
+ typedef _rc_v rc_v ;
+ enum { vsize = rc_v::Size } ;
+ typedef vigra::TinyVector < rc_v , dimension > nd_rc_v ; // vectorized n-dimensional coordinate
+
+ shape_type start ;
+ shape_type end ;
+ shape_type shape ;
+ nd_rc_v c ;
+
+public:
+
+ coordinate_iterator_v ( shape_type _start , shape_type _end )
+ : start ( _start ) ,
+ end ( _end ) ,
+ shape ( _end - _start )
+ {
+ vigra::MultiCoordinateIterator<dimension> mci_start ( shape ) ;
+ auto mci_end = mci_start.getEndIterator() ;
+ auto mci = mci_start ;
+
+ for ( int i = 0 ; i < vsize ; i++ )
+ {
+ shape_type index = *mci + start ;
+ ++mci ;
+ if ( mci >= mci_end )
+ mci = mci_start ;
+ for ( int d = 0 ; d < dimension ; d++ )
+ c[d][i] = index[d] ;
+ }
+ } ;
+
+ const nd_rc_v& operator*()
+ {
+ return c ;
+ }
+
+ coordinate_iterator_v operator++()
+ {
+ c[0] += vsize ; // increase level 0 values
+ auto mask = ( c[0] >= end[0] ) ; // mask overflow
+ while ( any_of ( mask ) )
+ {
+ // we need to do more only if c[0] overflows
+ c[0] ( mask ) -= shape[0] ; // fold back to range
+ for ( int d = 1 ; d < dimension ; d++ )
+ {
+ c[d] ( mask ) ++ ; // increase next-higher dimension's index
+ mask = ( c[d] >= end[d] ) ; // check for range overflow
+ // resultant mask is either empty or the same as before
+ if ( none_of ( mask ) ) // if no overflow, we're done
+ break ;
+ c[d] ( mask ) = start[d] ; // set back to lower bound
+ // with the next loop iteration, the next dimension's index is increased
+ }
+ // having increased c[0] by vsize can require several iterations
+ // if shape[0] < vsize, so we need to mask, test, etc. again until
+ // all of c[0] are back in range
+ mask = ( c[0] >= end[0] ) ; // mask overflow
+ }
+ // otherwise, increasing input[0] landed inside the range,
+ return *this ;
+ } ;
+} ;
+
+/// this function provides a generalized geometric transformation
+/// of a source image
+///
+/// In image transformations, often the source location at which interpolation
+/// of the source image is required is defined by a transformation of the location
+/// of the target coordinate. There are (at least) two approaches to handle this.
+/// The first is what is implemented in the previous implementation of remap: we
+/// calculate a 'warp' array full of transformed coordinates and process it en bloc.
+/// Alternatively, with the coordinate transformation at hand, we can write a remap
+/// variant which performs the coordinate transformation to each target coordinate
+/// as it goes along, saving the construction of the warp array.
+///
+/// The transformation is passed in by using a 'transformation' object, which is a
+/// wrapper around the actual transformation routine(s).
+
+template < class value_type , // like, float, double, complex<>, pixels, TinyVectors etc.
+ class rc_type , // float or double, for coordinates
+ int dim_in , // number of dimensions of input array (and of 'warp' coordinates)
+ int dim_out > // number of dimensions of output array
+int st_tf_remap ( const bspline < value_type , dim_in > & bspl ,
+ transformation < value_type , rc_type , dim_in , dim_out > tf ,
+ MultiArrayView < dim_out , value_type > output ,
+ typename MultiArrayView < dim_out , value_type > :: difference_type offset ,
+ bool use_vc = true
+ )
+{
+ typedef bspline < value_type , dim_in > spline_type ;
+ typedef evaluator < dim_in , value_type , rc_type , int > evaluator_type ;
+ typedef typename evaluator_type::ele_type ele_type ;
+ evaluator_type ev ( bspl ) ;
+ ele_type * p_workspace = new ele_type [ ev.workspace_size() ] ;
+
+#ifdef USE_VC
+
+ typedef typename evaluator_type::ele_v ele_v ;
+ typedef typename evaluator_type::rc_v rc_v ;
+ typedef typename evaluator_type::mc_ele_v mc_ele_v ;
+ const int vsize = evaluator_type::vsize ;
+
+ ele_v * p_workspace_v = new ele_v [ ev.workspace_size() ] ;
+
+ TinyVector < rc_v , dim_out > c_in ; // incoming coordinates (into output, which has dim_out dims)
+ TinyVector < rc_v , dim_in > c_out ; // transformed coordinates (into input, which has dim_in dims)
+ mc_ele_v result ; // result, as struct of vectors
+
+#endif
+
+ typedef typename CoupledIteratorType < dim_out , value_type > :: type Iterator ;
+ Iterator target_it = createCoupledIterator ( output ) ;
+ int leftover = output.elementCount() ;
+
+#ifdef USE_VC
+
+ if ( use_vc )
+ {
+ int aggregates = output.elementCount() / vsize ; // number of full vectors
+ leftover = output.elementCount() - aggregates * vsize ; // any leftover single values
+ value_type * destination = output.data() ;
+ value_type target_buffer [ vsize ] ;
+ // use utility class coordinate_iterator_v to produce vectorized coordinates:
+ coordinate_iterator_v < rc_v , dim_out > civ ( offset , offset + output.shape() ) ;
+
+ for ( int a = 0 ; a < aggregates ; a++ )
+ {
+ tf ( *civ , c_out ) ; // perform the coordinate transformation
+ ++civ ;
+ if ( output.isUnstrided() )
+ {
+ // finally, here we evaluate the spline
+ ev ( c_out , destination , p_workspace_v ) ;
+ destination += vsize ;
+ }
+ else
+ {
+ // alternative evaluation if we can't write straight to destination
+ ev ( c_out , target_buffer , p_workspace_v ) ;
+ for ( int e = 0 ; e < vsize ; e++ )
+ {
+ target_it.get<1>() = target_buffer[e] ;
+ ++target_it ;
+ }
+ }
+ }
+ delete[] p_workspace_v ;
+ if ( output.isUnstrided() )
+ target_it += aggregates * vsize ;
+ }
+
+#endif // USE_VC
+
+ TinyVector < rc_type , dim_out > cs_in ;
+ TinyVector < rc_type , dim_in > cs_out ;
+
+ // process leftovers, if any - if vc isn't used, this loop does all the processing
+ while ( leftover-- )
+ {
+ // process leftovers with single-value evaluation of transformed coordinate
+ cs_in = target_it.get<0>() + offset ;
+ tf ( cs_in , cs_out ) ;
+ target_it.get<1>() = ev ( cs_out , p_workspace ) ;
+ ++target_it ;
+ }
+
+ delete[] p_workspace ;
+ return 0 ;
+}
+
+/// multithreaded tf_remap routine, splitting the target array to chunks.
+/// This is just as for the warp-array-based remap. The target array is split into
+/// chunks, which are in turn processed in a thread each by the single-threaded routine.
+/// Note the parameter 'offset'. Since we operate on the coordinates of the target array,
+/// we have to keep track of the coordinate the origin of each chunk had in the unsplit
+/// target. This is what 'offest' does, it's effectively added to the coordinates in the
+/// chunks when they are transformed.
+
+template < class value_type , // like, float, double, complex<>, pixels, TinyVectors etc.
+ class rc_type , // float or double for coordinates
+ int dim_in , // number of dimensions of input array (and of 'warp' coordinates)
+ int dim_out > // number of dimensions of output array
+int tf_remap ( const bspline < value_type , dim_in > & bspl ,
+ transformation < value_type , rc_type , dim_in , dim_out > tf ,
+ MultiArrayView < dim_out , value_type > output ,
+ bool use_vc = true ,
+ int nthreads = ncores
+ )
+{
+ using namespace std::placeholders ;
+
+ // we use bind to bind the first two and last argument of the single-threaded
+ // tf_remap function, leaving one free argument which will accept the array
+ // of data to iterate over (being the a chunk of 'output' which is created
+ // by divide_and_conquer)
+
+ auto chunk_func
+ = std::bind ( st_tf_remap < value_type , rc_type , dim_in , dim_out > , // single-threaded tf_remap
+ std::ref(bspl) , // bspline passed in above
+ tf , // transformation passed in above
+ _1 , // placeholders to accept data chunk
+ _2 , // and offset from divide_and_conquer
+ use_vc ) ; // use_vc flag as passed in above
+
+ divide_and_conquer < decltype ( output ) > // run divide_and_conquer
+ :: run ( output , // on the output array
+ chunk_func , // applying the functor we've made with bind
+ -1 , // allowing array splitting along any axis
+ nthreads ) ; // using nthreads threads
+}
+
+/// this highest-level transform-based remap takes an input array and creates
+/// a b-spline over it internally. Then it calles the previous routine, which
+/// takes a bspline as it's first parameter. Like with warp-array-based remap,
+/// this is for on-the-fly remapping where the b-spline won't be reused.
+
+template < class value_type , // like, float, double, complex<>, pixels, TinyVectors etc.
+ class rc_type , // usually float for coordinates
+ int dim_in , // number of dimensions of input array (and of 'warp' coordinates)
+ int dim_out > // number of dimensions of output array
+int tf_remap ( MultiArrayView < dim_in , value_type > input ,
+ transformation < value_type , rc_type , dim_in , dim_out > tf ,
+ MultiArrayView < dim_out , value_type > output ,
+ bcv_type<dim_in> bcv = bcv_type<dim_in> ( MIRROR ) ,
+ int degree = 3 ,
+ bool use_vc = true ,
+ int nthreads = ncores
+ )
+{
+ // create the bspline object
+ bspline < value_type , dim_in > bsp ( input.shape() , degree , bcv ) ;
+ // copy in the data
+ bsp.core = input ;
+ // prefilter
+ bsp.prefilter ( use_vc , nthreads ) ;
+
+ tf_remap<value_type,rc_type,dim_in,dim_out>
+ ( bsp , tf , output , use_vc , nthreads ) ;
+}
+
+/// this function creates a warp array instead of evaluating a spline. The warp array can
+/// then be used as input to a warp-array-based remap. This way, if the transformation
+/// is computationally expensive and it's resulting transformed coordinates can be reused
+/// several times, the coordinate transformation can be separated from the remap procedure.
+/// This is the single-threaded routine; further below we will use divide_and_conquer
+/// to apply this routine to several chunks of data in parallel.
+
+template < class value_type , // like, float, double, complex<>, pixels, TinyVectors etc.
+ class rc_type , // float or double, for coordinates
+ int dim_in , // number of dimensions of input array (and of 'warp' coordinates)
+ int dim_out > // number of dimensions of output array
+int st_make_warp_array ( transformation < value_type , rc_type , dim_in , dim_out > tf ,
+ MultiArrayView < dim_out ,
+ TinyVector < rc_type , dim_in > > & warp_array ,
+ typename MultiArrayView < dim_out , value_type > :: difference_type offset ,
+ bool use_vc = true
+ )
+{
+ /// incoming coordinates (into output, which has dim_out dims)
+ /// generated by vigra's CoupledIterator or, in the vectorized code,
+ /// by coordinate_iterator_v
+
+ typedef TinyVector < rc_type , dim_out > nd_rc_in ;
+
+ /// transformed coordinates (into input, which has dim_in dims)
+ /// populating the resulting warp array
+
+ typedef TinyVector < rc_type , dim_in > nd_rc_out ;
+
+ nd_rc_in cs_in ;
+ nd_rc_out cs_out ;
+
+ auto target_it = createCoupledIterator ( warp_array ) ;
+ int leftover = warp_array.elementCount() ;
+
+#ifdef USE_VC
+
+ typedef typename ExpandElementResult<value_type>::type ele_type ;
+ typedef Vc::Vector < ele_type > ele_v ;
+ const int vsize = ele_v::Size ;
+ typedef Vc::SimdArray < rc_type , vsize > rc_v ;
+
+ /// vecorized incoming coordinates (into output, which has dim_out dims)
+ typedef TinyVector < rc_v , dim_out > nd_rc_in_v ;
+
+ /// vectorized transformed coordinates (into input, which has dim_in dims)
+ typedef TinyVector < rc_v , dim_in > nd_rc_out_v ;
+
+ if ( use_vc )
+ {
+ int aggregates = warp_array.elementCount() / vsize ; // number of full vectors
+ leftover = warp_array.elementCount() - aggregates * vsize ; // any leftover single values
+ nd_rc_out_v target_buffer ; // vectorized warp coordinates
+
+ // use utility class coordinate_iterator_v to produce vectorized target coordinates:
+ coordinate_iterator_v < rc_v , dim_out > civ ( offset , offset + warp_array.shape() ) ;
+
+ for ( int a = 0 ; a < aggregates ; a++ )
+ {
+ tf ( *civ , target_buffer ) ; // perform the coordinate transformation
+ ++civ ;
+ // interleave target_buffer into warp array (TODO: maybe use scatter?)
+ for ( int e = 0 ; e < vsize ; e++ )
+ {
+ for ( int d = 0 ; d < dim_in ; d++ )
+ target_it.get<1>()[d] = target_buffer[d][e] ;
+ ++target_it ;
+ }
+ }
+ }
+
+#endif // USE_VC
+
+ // process leftovers, if any - if vc isn't used, this loop does all the processing
+ while ( leftover-- )
+ {
+ // process leftovers with single-value evaluation of transformed coordinate
+ cs_in = target_it.get<0>() + offset ;
+ tf ( cs_in , cs_out ) ;
+ target_it.get<1>() = cs_out ;
+ ++target_it ;
+ }
+
+ return 0 ;
+}
+
+/// multithreaded code to make a warp array using a coordinate transformation.
+
+template < class value_type , // like, float, double, complex<>, pixels, TinyVectors etc.
+ class rc_type , // float or double for coordinates
+ int dim_in , // number of dimensions of input array (and of 'warp' coordinates)
+ int dim_out > // number of dimensions of output array
+int make_warp_array ( transformation < value_type , rc_type , dim_in , dim_out > tf ,
+ MultiArrayView < dim_out ,
+ TinyVector < rc_type , dim_in > > warp_array ,
+ bool use_vc = true ,
+ int nthreads = ncores
+ )
+{
+ using namespace std::placeholders ;
+
+ // we use bind to bind the first two and last arguments of the single-threaded
+ // function, leaving one free argument which will accept the array
+ // of data to iterate over (being the a chunk of 'output' which is created
+ // by divide_and_conquer)
+
+ auto chunk_func
+ = std::bind ( st_make_warp_array < value_type , rc_type , dim_in , dim_out > , // single-threaded make_warp_array
+ tf , // transformation passed in above
+ _1 , // placeholders to accept data chunk
+ _2 , // and offset from divide_and_conquer
+ use_vc ) ; // use_vc flag as passed in above
+
+ divide_and_conquer < decltype ( warp_array ) > // run divide_and_conquer
+ :: run ( warp_array , // on the output array
+ chunk_func , // applying the functor we've made with bind
+ -1 , // allowing array splitting along any axis
+ nthreads ) ; // using nthreads threads
+}
+
+} ; // end of namespace vspline
+
+#endif // VSPLINE_REMAP_H
diff --git a/vspline.doxy b/vspline.doxy
new file mode 100644
index 0000000..5f4edf5
--- /dev/null
+++ b/vspline.doxy
@@ -0,0 +1,2303 @@
+# Doxyfile 1.8.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "vspline"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = 16
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Generic C++ Code for Uniform B-Splines"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = YES
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = . example
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED = USE_VC
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = YES
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/vspline.h b/vspline.h
new file mode 100644
index 0000000..60c3541
--- /dev/null
+++ b/vspline.h
@@ -0,0 +1,38 @@
+/************************************************************************/
+/* */
+/* vspline - a set of generic tools for creation and evaluation */
+/* of uniform b-splines */
+/* */
+/* Copyright 2015, 2016 by Kay F. Jahnke */
+/* */
+/* Permission is hereby granted, free of charge, to any person */
+/* obtaining a copy of this software and associated documentation */
+/* files (the "Software"), to deal in the Software without */
+/* restriction, including without limitation the rights to use, */
+/* copy, modify, merge, publish, distribute, sublicense, and/or */
+/* sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following */
+/* conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the */
+/* Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
+/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
+/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
+/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
+/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
+/* OTHER DEALINGS IN THE SOFTWARE. */
+/* */
+/************************************************************************/
+
+/*! \file vspline.h
+
+ \brief includes all headers from vspline (most of them indirectly)
+
+*/
+
+#include "remap.h"
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/vspline.git
More information about the debian-science-commits
mailing list