vspline 1.1.0
Generic C++11 Code for Uniform B-Splines
basis.h
Go to the documentation of this file.
1/************************************************************************/
2/* */
3/* vspline - a set of generic tools for creation and evaluation */
4/* of uniform b-splines */
5/* */
6/* Copyright 2015 - 2023 by Kay F. Jahnke */
7/* */
8/* The git repository for this software is at */
9/* */
10/* https://bitbucket.org/kfj/vspline */
11/* */
12/* Please direct questions, bug reports, and contributions to */
13/* */
14/* kfjahnke+vspline@gmail.com */
15/* */
16/* Permission is hereby granted, free of charge, to any person */
17/* obtaining a copy of this software and associated documentation */
18/* files (the "Software"), to deal in the Software without */
19/* restriction, including without limitation the rights to use, */
20/* copy, modify, merge, publish, distribute, sublicense, and/or */
21/* sell copies of the Software, and to permit persons to whom the */
22/* Software is furnished to do so, subject to the following */
23/* conditions: */
24/* */
25/* The above copyright notice and this permission notice shall be */
26/* included in all copies or substantial portions of the */
27/* Software. */
28/* */
29/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
30/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
31/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
32/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
33/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
34/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
35/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
36/* OTHER DEALINGS IN THE SOFTWARE. */
37/* */
38/************************************************************************/
39
40/*! \file basis.h
41
42 \brief Code to calculate the value of the B-spline basis function
43 and it's derivatives.
44
45 This file begins with some collateral code used to 'split' coordinates
46 into an integral part and a small real remainder. This split is used
47 in b-spline evaluation and fits thematically with the remainder of the
48 file, which deals with the basis function.
49
50 vspline only uses the B-spline basis function values at multiples
51 of 0.5. With these values it can construct it's evaluators which in turn
52 are capable of evaluating the spline at real coordinates. Since these
53 values aren't 'too many' but take some effort to calculate precisely,
54 they are provided as precalculated constants in poles.h, which also holds
55 the prefilter poles.
56
57 The basis function values at half unit steps are used for evaluation
58 via a 'weight matrix'. This is a matrix of numbers which can yield the
59 value of a b-spline by employing a simple fixed-time matrix/vector
60 multiplication (which also vectorizes well). In a similar way, a
61 'basis_functor' can be set up from these values which can provide
62 the value of the basis function for arbitrary real arguments.
63
64 for a discussion of the b-spline basis function, have a look at
65 http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-basis.html
66*/
67
68#ifndef VSPLINE_BASIS_H
69#define VSPLINE_BASIS_H
70
71#include "common.h"
72
73// poles.h has precomputed basis function values sampled at n * 1/2.
74// These values were calculated to very high precision in a separate
75// program (see bootstrap.cc) using GNU GMP, GSL and BLAS.
76
77#include "poles.h"
78
79namespace vspline {
80
81/// coordinates are split into an integral part and a remainder. this is
82/// used for weight generation, and also for calculating the basis function
83/// value. The split is done differently for odd and even splines.
84///
85/// Note how the initial call to std::floor produces a real type, which
86/// is used to subtract from 'v', yielding the remainder in 'fv'. Only after
87/// having used this real representation of the integral part, it is cast
88/// to an integral type by assigning it to 'iv'. This is the most efficient
89/// route, better than producing an integral-typed integral part directly
90/// and subtracting that from 'v', which would require another conversion.
91/// Technically, one might consider this 'split' as a remainder division by 1.
92
93template < typename ic_t , typename rc_t >
94void odd_split ( rc_t v , ic_t& iv , rc_t& fv )
95{
96 rc_t fl_i = floor ( v ) ;
97 fv = v - fl_i ;
98 vspline::assign ( iv , fl_i ) ;
99}
100
101// roll out the split function for vigra::TinyVectors
102
103template < typename ic_t , typename rc_t , int N >
104void odd_split ( vigra::TinyVector < rc_t , N > v ,
105 vigra::TinyVector < ic_t , N > & iv ,
106 vigra::TinyVector < rc_t , N > & fv )
107{
108 for ( int d = 0 ; d < N ; d++ )
109 odd_split ( v[d] , iv[d] , fv[d] ) ;
110}
111
112/// for even splines, the integral part is obtained by rounding. when the
113/// result of rounding is subtracted from the original coordinate, a value
114/// between -0.5 and 0.5 is obtained which is used for weight generation.
115
116// TODO: there is an issue here: the lower limit for an even spline
117// is -0.5, which should be rounded *towards* zero, but std::round rounds
118// away from zero. The same applies to the upper limit, which should
119// also be rounded towards zero, not away from it. Currently I am working
120// around the issue by increasing the spline's headroom by 1 for even splines,
121// but I'd like to be able to use rounding towards zero. It might be argued,
122// though, that potentially producing out-of-range access by values which
123// are only just outside the range is cutting it a bit fine and the extra
124// headroom for even splines makes the code more robust, so accepting the
125// extra headroom would be just as acceptable as the widened right brace for
126// some splines which saves checking the incoming coordinate against
127// the upper limit. The code increasing the headroom is in bspline.h,
128// in bspline's constructor just befor the call to setup_metrics.
129
130template < typename ic_t , typename rc_t >
131void even_split ( rc_t v , ic_t& iv , rc_t& fv )
132{
133 rc_t fl_i = round ( v ) ;
134 fv = v - fl_i ;
135 vspline::assign ( iv , fl_i ) ;
136}
137
138template < typename ic_t , typename rc_t , int N >
139void even_split ( vigra::TinyVector < rc_t , N > v ,
140 vigra::TinyVector < ic_t , N > & iv ,
141 vigra::TinyVector < rc_t , N > & fv )
142{
143 for ( int d = 0 ; d < N ; d++ )
144 even_split ( v[d] , iv[d] , fv[d] ) ;
145}
146
147/// bspline_basis_2 yields the value of the b-spline basis function
148/// at multiples of 1/2, for which vspline has precomputed values.
149/// Instead of passing real values x which are multiples of 1/2, this
150/// routine takes the doubled argument, so instead of calling it with
151/// x = 0.5, you call it with x2 = 1.
152///
153/// this is a helper routine to offer convenient access to vspline's
154/// precomputed basis function values, and it also handles the special
155/// case of degree 0, x = -1/2, where the b-spline basis function
156/// is not symmetric and yields 1.
157///
158/// Inside vspline, this routine is only used by calculate_weight_matrix.
159/// User code should rarely need it, but I hold on to it as a separate entity.
160/// The code also handles derivatives. This is done by recursion, which is
161/// potentially very slow (for high derivatives), so this routine should
162/// only be used for 'moderate' derivative values. For fast access to the
163/// basis function's derivatives, use of vspline::basis_functor is
164/// recommended instead.
165
166template < class real_type >
167real_type bspline_basis_2 ( int x2 , int degree , int derivative = 0 )
168{
169 real_type result ;
170
171 if ( derivative == 0 )
172 {
173 if ( degree == 0 )
174 {
175 if ( x2 == -1 || x2 == 0 )
176 result = real_type ( 1 ) ;
177 else
178 result = real_type ( 0 ) ;
179 }
180 else if ( abs ( x2 ) > degree )
181 {
182 result = real_type ( 0 ) ;
183 }
184 else
185 {
186 // here we pick the precomputed value:
187
188 const vspline::xlf_type * pk
190
191 result = real_type ( pk [ abs ( x2 ) ] ) ;
192 }
193 return result ;
194 }
195 else
196 {
197 // recurse. this will only produce recursion until 'derivative' becomes
198 // zero, at which point precomputed values are picked.
199
200 --derivative;
201 return bspline_basis_2<real_type> ( x2 + 1 , degree - 1 , derivative )
202 - bspline_basis_2<real_type> ( x2 - 1 , degree - 1 , derivative ) ;
203 }
204}
205
206/// a 'weight matrix' is used to calculate a set of weights for a given remainder
207/// part of a real coordinate. The 'weight matrix' is multipled with a vector
208/// containing the power series of the given remainder, yielding a set of weights
209/// to apply to a window of b-spline coefficients.
210///
211/// The routine 'calculate_weight_matrix' originated from vigra, but I rewrote it
212/// to avoid calculating values of derivatives of the basis function by using
213/// recursion. The content of the 'weight matrix' is now calculated directly
214/// with a forward iteration starting from precomputed basis function values,
215/// the derivatives needed are formed by repeatedly differencing these values.
216/// The recursive formulation in vigra makes sense, since the degree of the spline
217/// is a template argument in vigra and the recursive formulation can be evaluated
218/// at compile-time, allowing for certain optimizations. But in vspline, spline
219/// degree is a run-time variable, and vspline offers calculation of splines of
220/// degrees up to (currently) 45, which aren't feasibly calculable by recursion,
221/// with a recursion calling itself twice for every step: this would take a very
222/// long time or exceed the system's capacity. Analyzing the recursive implementation,
223/// it can be seen that it produces a great many redundant calculations, which soon
224/// exceed reasonable limits. With vigra's maximal spline degree the load is just
225/// about manageable, but beyond that (degree 19 or so at the time of this writing)
226/// it's clearly no-go.
227///
228/// The forward iteration is reasonably fast, even for high spline degrees,
229/// while my previous implementation did slow down very noticeably from, say,
230/// degree 20, making it unusable for high spline degrees. I really only noticed
231/// the problem after raising the maximal degree vspline can handle, following the
232/// rewrite of bootstrap.cc using arbitrary-precision maths.
233///
234/// A weight matrix is also used by 'basis_functor', in a similar way to how
235/// it's used for weight generation.
236
237template < class target_type >
238void calculate_weight_matrix ( vigra::MultiArray < 2 , target_type > & res )
239{
240 int order = res.shape ( 0 ) ;
241 int degree = order - 1 ;
242 int derivative = order - res.shape(1) ;
243
244 // guard against impossible parameters
245
246 if ( derivative >= order )
247 return ;
248
249 vspline::xlf_type faculty = 1 ; // why xlf_type? because integral types overflow
250
251 // we do the calculations for each row of the weight matrix in the same type
252 // as the precomputed basis function values, only casting down to 'target_type'
253 // once the row is ready
254
255 vspline::xlf_type der_line [ degree + 1 ] ;
256
257 for ( int row = 0 ; row < order - derivative ; row++ )
258 {
259 if ( row > 1 )
260 faculty *= row ;
261
262 // obtain pointers to beginning of row and past it's end
263
264 vspline::xlf_type * p_first = der_line ;
265 vspline::xlf_type * p_end = der_line + degree + 1 ;
266
267 // now we want to pick basis function values. The first row to go into
268 // the weight matrix gets basis function values for 'degree', the next
269 // one values for degree-1, but differenced once, the next one values
270 // for degree-2, differenced twice etc.
271 // Picking the values is done so that for odd degrees, basis function
272 // values for whole x are picked, for even spline degrees, values
273 // 1/2 + n, n E N are picked. Why so? When looking at the recursion
274 // used in bspline_basis_2, you can see that each step of the recursion
275 // forms the difference between a value 'to the left' and a value 'to
276 // the right' of the current position (x2-1 and x2+1). If you follow
277 // this pattern, you can see that, depending on the degree, the recursion
278 // will run down to either all even or all odd x2, so this is what we
279 // pick, since the other values will not participate in the result
280 // at all.
281 // Note how, to pick the basis function values, we use bspline_basis_2,
282 // which 'knows' how to get the right precomputed values. Contrary to my
283 // previous implementation, it is *not* used to calculate the derivatives,
284 // it is only used as a convenient way to pick the precomputed values.
285
286 int m = degree - derivative - row ;
287
288 if ( m == 0 )
289 {
290 der_line[0] = 1 ;
291 ++p_first ;
292 }
293 else if ( degree & 1 )
294 {
295 for ( int x2 = - m + 1 ; x2 <= m - 1 ; x2 += 2 )
296 {
297 *(p_first++) = bspline_basis_2<vspline::xlf_type> ( x2 , m ) ;
298 }
299 }
300 else
301 {
302 for ( int x2 = - m ; x2 <= m ; x2 += 2 )
303 {
304 *(p_first++) = bspline_basis_2<vspline::xlf_type> ( x2 , m ) ;
305 }
306 }
307
308 // fill the remainder of the line with zeroes
309
310 vspline::xlf_type * p = p_first ;
311 while ( p < p_end )
312 *(p++) = 0 ;
313
314 // now we have the initial basis function values. We need to differentiate
315 // the sequence, possibly several times. We have the initial values
316 // flush with the line's left bound, so we perform the differentiation
317 // back to front, and after the last differentiation the line is full.
318 // Note how this process might be abbreviated further by exploiting
319 // symmetry relations (most rows are symmetric or antisymmetric).
320 // I refrain from doing so (for now) and I suspect that this may even be
321 // preferable in respect to error propagation (TODO: check).
322
323 for ( int d = m ; d < degree ; d++ )
324 {
325 // deposit first difference after the last basis function value
326
327 vspline::xlf_type * put = p_first ;
328
329 // and form the difference to the value before it.
330
331 vspline::xlf_type * pick = put - 1 ;
332
333 // now form differences back to front
334
335 while ( pick >= der_line )
336 {
337 *put = *pick - *put ;
338 --put ;
339 --pick ;
340 }
341
342 // since we have nothing left 'to the left', where another zero
343 // would be, we simply invert the sign (*put = 0 - *put).
344
345 *put = - *put ;
346
347 // The next iteration has to start one place further to the right,
348 // since there is now one more value in the line
349
350 p_first++ ;
351 }
352
353 // the row is ready and can now be assigned to the corresponding row of
354 // the weight matrix after applying the final division by 'faculty' and,
355 // possibly, downcasting to 'target_type'.
356
357 // we store to a MultiArray, which is row-major, so storing as we do
358 // places the results in memory in the precise order in which we want to
359 // use them later on in the weight calculation.
360
361 for ( int k = 0 ; k < degree + 1 ; k++ )
362
363 res ( k , row ) = der_line[k] / faculty ;
364 }
365}
366
367/// basis_functor is an object producing the b-spline basis function value
368/// for given arguments, or optionally a derivative of the basis function.
369/// While basis_functor can produce single basis function values for single
370/// arguments, it can also produce a set of basis function values for a
371/// given 'delta'. This set is a unit-spaced sampling of the basis function
372/// sampled at n + delta for all n E N. Such samplings are used to evaluate
373/// b-splines; they constitute the set of weights which have to be applied
374/// to a set of b-spline coefficients to form the weighted sum which is the
375/// spline's value at a given position.
376
377/// The calculation is done by using a 'weight matrix'. The columns of the
378/// weight matrix contain the coefficients for the partial polynomials defining
379/// the basis function for the corresponding interval. In 'general' evaluation,
380/// all partial polynomials are evaluated. To obtain single basis function
381/// values, we pick out a single column only. By evaluating the partial
382/// polynomial for this slot, we obtain a single basis function value.
383///
384/// This functor provides the value(s) in constant time and there is no
385/// recursion. Setting up the functor costs a bit of time (for calculating
386/// the 'weight matrix'), evaluating it merely evaluates the partial
387/// polynomial(s) which is quick by comparison. So this is the way to go if
388/// basis function values are needed - especially if there is a need for
389/// several values of a given basis function. I refrain from giving a 'one-shot'
390/// function using a basis_functor - this is easily achieved by coding
391///
392/// b = basis_functor ( degree , derivative ) ( x ) ;
393///
394/// ... which does the trick, but 'wastes' the weight matrix.
395///
396/// The weight matrix and all variables use 'math_type' which defaults to
397/// xlf_type, vspline's most exact type. By instantiating with a lesser type,
398/// The computation can be done more quickly, but less precisely.
399
400template < typename math_type = vspline::xlf_type >
402{
403 vigra::MultiArray < 2 , math_type > weight_matrix ;
404 int degree ;
405
406 basis_functor ( int _degree , int _derivative = 0 )
407 : weight_matrix ( _degree + 1 , _degree + 1 - _derivative ) ,
408 degree ( _degree )
409 {
411 } ;
412
413// basis_functor & operator= ( const basis_functor & other )
414// {
415// weight_matrix = other.weight_matrix ;
416// degree = other.degree ;
417// return *this ;
418// }
419//
420// basis_functor ( const basis_functor & other )
421// {
422// weight_matrix = other.weight_matrix ;
423// degree = other.degree ;
424// }
425
426 /// operator() taking a column index and a remainder. If these values
427 /// are known already, the only thing left to do is the evaluation of
428 /// the partial polynomial. Note that this overload is not safe for
429 /// arbitrary x, it's assumed that calling code makes sure no invalid
430 /// arguments are passed - as in the overload below.
431
432 // TODO might generalize to allow vectorized operation
433
434 math_type operator() ( int x , math_type delta ) const
435 {
436 math_type result = weight_matrix ( x , 0 ) ;
437 math_type power = 1 ;
438
439 // remaining rows, if any, refine result
440
441 for ( int row = 1 ; row < weight_matrix.shape(1) ; row++ )
442 {
443 power *= delta ;
444 result += power * weight_matrix ( x , row ) ;
445 }
446
447 return result ;
448 }
449
450 /// operator() taking an arbitrary argument. This is the overload which
451 /// will likely be called from user code. The argument is clamped and
452 /// split, the split value is fed to the previous overload.
453 /// This routine provides a single result for a single argument and
454 /// is used if the basis function itself needs to be evaluated, which
455 /// doesn't happen much inside vspline. Access to sets of basis function
456 /// values used as weights in b-spline evaluation is coded below.
457
458 math_type operator() ( math_type rx ) const
459 {
460 int x ;
461 math_type delta ;
462
463 // we split the argument into an integer and a small real remainder
464
465 if ( degree & 1 )
466 vspline::odd_split ( rx , x , delta ) ;
467 else
468 {
469 if ( degree == 0 )
470 {
471 if ( rx >= -.5 && rx < 0.5 )
472 return 1 ;
473 return 0 ;
474 }
475 vspline::even_split ( rx , x , delta ) ;
476 }
477
478 x = degree / 2 - x ;
479
480 if ( x < 0 || x >= weight_matrix.shape(0) )
481 {
482 return 0 ;
483 }
484
485 return operator() ( x , delta ) ;
486 }
487
488 /// operator() overload to produce a set of weights for a given
489 /// delta in [-.5,.5] or [0,1]. This set of weights is needed if
490 /// a b-spline has to be evaluated at some coordinate k + delta,
491 /// where k is a whole number. For this evaluation, a set of
492 /// coefficients has to be multiplied with a set of weights, and
493 /// the products summed up. So this routine provides the set of
494 /// weights. It deposits weights for the given delta at the location
495 /// 'result' points to. target_type and delta_type may be fundamentals
496 /// or simdized types.
497 /// note that, if 'delta' is zero, 'power' will also be zero, and
498 /// therefore everything after the initialization of the result with
499 /// the first row of the weight matrix is futile. One might consider
500 /// testing for this special case, but this would cost extra cycles.
501 /// Instead, below, there is an overload which does not take a 'delta'.
502 /// Why so? The special case of delta == zero occurs certainly when
503 /// discrete coordinates are used, but rarely in 'normal' operation,
504 /// when the whole point of using a spline is to evaluate at real
505 /// coordinates with some delta.
506
507 template < class target_type , class delta_type >
508 void operator() ( target_type* result , const delta_type & delta ) const
509 {
510 target_type power ( delta ) ;
511 auto factor_it = weight_matrix.begin() ;
512 auto end = weight_matrix.end() ;
513
514 // the result is initialized with the first row of the 'weight matrix'.
515 // We save ourselves multiplying it with delta^0.
516
517 for ( int c = 0 ; c <= degree ; c++ )
518 {
519 result[c] = *factor_it ;
520 ++factor_it ;
521 }
522
523 if ( degree )
524 {
525 for ( ; ; )
526 {
527 for ( int c = 0 ; c <= degree ; c++ )
528 {
529 // KFJ 2019-02-12 tentative use of fma
530#ifdef USE_FMA
531 target_type factor ( *factor_it ) ;
532 target_type rr = result[c] ;
533 result[c] = fma ( power , factor , rr ) ;
534#else
535 result[c] += power * *factor_it ;
536#endif
537 ++factor_it ;
538 }
539 if ( factor_it == end )
540 {
541 // avoid next multiplication if exhausted, break now
542 break ;
543 }
544 // otherwise produce next power(s) of delta(s)
545 power *= target_type ( delta ) ;
546 }
547 }
548 }
549
550 // a different way of calling the overload of operator() above;
551 // easier to handle instatiation from the python module.
552
553 template < class target_type , class delta_type >
554 void weights ( target_type* result , const delta_type & delta ) const
555 {
556 operator() ( result , delta ) ;
557 }
558
559 /// overload without delta, implies (all) delta(s) == 0.
560 /// this is used for evaluation with discrete coordinates
561
562 template < class target_type >
563 void operator() ( target_type* result ) const
564 {
565 auto factor_it = weight_matrix.begin() ;
566
567 // the result is initialized with the first row of the 'weight matrix'.
568 // We save ourselves multiplying it with delta^0.
569
570 for ( int c = 0 ; c <= degree ; c++ )
571 {
572 result[c] = *factor_it ;
573 ++factor_it ;
574 }
575 }
576
577} ;
578
579/// this function deposits the reconstruction kernel in the array
580/// 'kernel'. This kernel can be used to convolve a
581/// set of coefficients, to obtain the original signal. This is a
582/// convenience function which merely picks the right values from
583/// the precomputed values in precomputed_basis_function_values.
584/// if 'odd' is passed false, the result is an even kernel. This
585/// kernel can't be used for reconstruction (.5 phase shift), but
586/// it's handy to get values half a unit step from the knot points.
587
588template < class target_type >
589void get_kernel ( const int & degree ,
590 vigra::MultiArrayView < 1 , target_type > & kernel ,
591 const bool & odd = true )
592{
593 assert ( degree >= 0 && degree <= vspline_constants::max_degree ) ;
594 if ( odd )
595 {
596 int headroom = degree / 2 ;
597 int ksize = headroom * 2 + 1 ;
598 assert ( kernel.size() == ksize ) ;
599
600 // pick the precomputed basis function values for the kernel.
601 // Note how the values in precomputed_basis_function_values
602 // (see poles.h) are provided at half-unit steps, hence the
603 // index acrobatics.
604
605 for ( int k = - headroom ; k <= headroom ; k++ )
606 {
607 int pick = 2 * std::abs ( k ) ;
608 kernel [ k + headroom ]
611 [ pick ] ;
612 }
613 }
614 else // produce an even kernel
615 {
616 int headroom = ( degree + 1 ) / 2 ;
617 int ksize = headroom * 2 ;
618 assert ( kernel.size() == ksize ) ;
619
620 for ( int k = 0 ; k < headroom ; k++ )
621 {
622 int pick = 2 * k + 1 ;
623 kernel [ headroom - k - 1 ]
624 = kernel [ headroom + k ]
627 [ pick ] ;
628 }
629 }
630}
631
632/// Implementation of the Cox-de Boor recursion formula to calculate
633/// the value of the bspline basis function for arbitrary real x.
634/// This code was taken from vigra but modified to take the spline degree
635/// as a parameter. Since this routine uses recursion, it's usefulness
636/// is limited to smaller degrees.
637///
638/// This routine operates in real and calculates the basis function value
639/// for arbitrary real x, but it suffers from cumulating errors, especially
640/// when the recursion is deep, so the results are not uniformly precise.
641///
642/// This code is expensive for higher spline orders because the routine
643/// calls itself twice recursively, so the performance is 2^N with the
644/// spline's degree. Luckily there are ways around using this routine at all
645/// - whenever we need the b-spline basis function value in vspline, it is at
646/// multiples of 1/2, and poles.h has precomputed values for all spline
647/// degrees covered by vspline. The value of the basis function itself
648/// can be obtained by using a vspline::basis_functor, which performs in
649/// fixed time and is set up quickly.
650///
651/// I leave this code in here for reference purposes - it's good to have
652/// another route to the basis function values, see self_test.cc.
653
654template < class real_type >
655real_type cdb_bspline_basis ( real_type x , int degree , int derivative = 0 )
656{
657 if ( degree == 0 )
658 {
659 if ( derivative == 0 )
660 return ( x < real_type(0.5) && real_type(-0.5) <= x )
661 ? real_type(1.0)
662 : real_type(0.0) ;
663 else
664 return real_type(0.0);
665 }
666 if ( derivative == 0 )
667 {
668 real_type n12 = real_type((degree + 1.0) / 2.0);
669 return ( ( n12 + x )
670 * cdb_bspline_basis<real_type> ( x + real_type(0.5) , degree - 1 , 0 )
671 + ( n12 - x )
672 * cdb_bspline_basis<real_type> ( x - real_type(0.5) , degree - 1 , 0 )
673 )
674 / degree;
675 }
676 else
677 {
678 --derivative;
679 return cdb_bspline_basis<real_type> ( x + real_type(0.5) , degree - 1 , derivative )
680 - cdb_bspline_basis<real_type> ( x - real_type(0.5) , degree - 1 , derivative ) ;
681 }
682}
683
684/// Gaussian approximation to B-spline basis function. This routine
685/// approximates the basis function of degree spline_degree for real x.
686/// I checked for all degrees up to 45. The partition of unity quality of the
687/// resulting reconstruction filter is okay for larger degrees, the cumulated
688/// error over the covered interval is quite low. Still, as the basis function
689/// is never actually evaluated in vspline (whenever it's needed, it is needed
690/// at n * 1/2 and we have precomputed values for that) there is not much point
691/// in having this function around. I leave the code in for now.
692
693template < typename real_type >
694real_type gaussian_bspline_basis_approximation ( real_type x , int degree )
695{
696 // heuristic: for slightly better fit use use
697 // real_type sigma = 0.021310018257 + ( degree + 1 ) / 12.0 ;
698 real_type sigma = ( degree + 1 ) / 12.0 ;
699 return real_type(1.0)
700 / sqrt ( real_type(2.0 * M_PI) * sigma )
701 * exp ( - ( x * x ) / ( real_type(2.0) * sigma ) ) ;
702}
703
704} ; // end of namespace vspline
705
706#endif // #define VSPLINE_BASIS_H
vigra::MultiArray< 2, pixel_type > target_type
Definition: ca_correct.cc:115
definitions common to all files in this project, utility code
const xlf_type *const precomputed_basis_function_values[]
Definition: poles.h:1950
Definition: basis.h:79
void calculate_weight_matrix(vigra::MultiArray< 2, target_type > &res)
a 'weight matrix' is used to calculate a set of weights for a given remainder part of a real coordina...
Definition: basis.h:238
void odd_split(rc_t v, ic_t &iv, rc_t &fv)
coordinates are split into an integral part and a remainder. this is used for weight generation,...
Definition: basis.h:94
real_type bspline_basis_2(int x2, int degree, int derivative=0)
bspline_basis_2 yields the value of the b-spline basis function at multiples of 1/2,...
Definition: basis.h:167
void get_kernel(const int &degree, vigra::MultiArrayView< 1, target_type > &kernel, const bool &odd=true)
this function deposits the reconstruction kernel in the array 'kernel'. This kernel can be used to co...
Definition: basis.h:589
void assign(T &t, const U &u)
Definition: vector.h:473
long double xlf_type
Definition: common.h:102
real_type gaussian_bspline_basis_approximation(real_type x, int degree)
Gaussian approximation to B-spline basis function. This routine approximates the basis function of de...
Definition: basis.h:694
void even_split(rc_t v, ic_t &iv, rc_t &fv)
for even splines, the integral part is obtained by rounding. when the result of rounding is subtracte...
Definition: basis.h:131
real_type cdb_bspline_basis(real_type x, int degree, int derivative=0)
Implementation of the Cox-de Boor recursion formula to calculate the value of the bspline basis funct...
Definition: basis.h:655
precalculated prefilter poles and basis function values
basis_functor is an object producing the b-spline basis function value for given arguments,...
Definition: basis.h:402
math_type operator()(int x, math_type delta) const
operator() taking a column index and a remainder. If these values are known already,...
Definition: basis.h:434
basis_functor(int _degree, int _derivative=0)
Definition: basis.h:406
void weights(target_type *result, const delta_type &delta) const
Definition: basis.h:554
vigra::MultiArray< 2, math_type > weight_matrix
Definition: basis.h:403