vspline 1.1.0
Generic C++11 Code for Uniform B-Splines
scope_test.cc
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/* Permission is hereby granted, free of charge, to any person */
9/* obtaining a copy of this software and associated documentation */
10/* files (the "Software"), to deal in the Software without */
11/* restriction, including without limitation the rights to use, */
12/* copy, modify, merge, publish, distribute, sublicense, and/or */
13/* sell copies of the Software, and to permit persons to whom the */
14/* Software is furnished to do so, subject to the following */
15/* conditions: */
16/* */
17/* The above copyright notice and this permission notice shall be */
18/* included in all copies or substantial portions of the */
19/* Software. */
20/* */
21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
23/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
24/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
25/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
26/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
27/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
28/* OTHER DEALINGS IN THE SOFTWARE. */
29/* */
30/************************************************************************/
31
32/*! \file scope_test.cc
33
34 \brief tries to fathom vspline's scope
35
36 vspline is very flexible when it comes to types and other
37 compile-time issues. It's not really feasible to instantiate
38 every single possible combination of compile-time parameters,
39 but this program is an attempt to test a few relevant ones,
40 specifically for vspline's 'higher' API, namely classes
41 bspline and evaluator, and the functions in transform.h.
42
43 We test data in 1, 2 and 3 dimensions. For individual values,
44 we use plain fundamentals and TinyVectors of two and three
45 fundamentals, with the fundamental types float, double and
46 long double. Wherever possible, we perform the tests
47 with several vectorization widths, to make sure that the
48 wielding code performs as expected.
49
50 For every such combination, we perform this test sequence:
51 - produce random data and random coordinates
52 - create a b-spline with given degree and boundary conditions
53 from the data
54 - create a 'safe evaluator' for the spline
55 - use the evaluator to bulk-evaluate the random coordinates
56 - and to evaluate them one-by-one; compare the result
57 - restore the original data from the coefficients using
58 an index-based transform and also using vspline::restore;
59 compare the results.
60
61 On my system, this is just about what the compiler can handle,
62 and the compilation takes a long time. The test can be limited
63 to smaller parameter sets, a good place to narrow the scope is
64 by picking less rc_type/ele_type combinations in doit().
65
66 This test passes if
67 - the code compiles at all
68 - the program runs until completion
69 - the errors are near the resolution you'd expect from
70 the types involved, so you'd expect output like
71
72 ...
73 vsz +5 chn +2 trg_dim +3 cf_dim +3
74 float crd, float value
75 d Mean: +0.000000763713326440d Max: +0.000007158763497749
76 d Mean: +0.000000757349428671d Max: +0.000007168434323489
77 double crd, double value
78 d Mean: +0.000000000000009688d Max: +0.000000000000899253
79 d Mean: +0.000000000000009681d Max: +0.000000000000899253
80 long double crd, long double value
81 d Mean: +0.000000000000000001d Max: +0.000000000000000006
82 d Mean: +0.000000000000000001d Max: +0.000000000000000006
83 ...
84
85 This test does not focus on different spline degrees or
86 boundary conditions, it's purpose is to test compile-time
87 parametrization. But it can easily be modified to use
88 different spline degrees and boundary conditions by passing
89 the relevant parameters to test(); I have limited the
90 choice to a few 'typical' combinations. Another aspect which
91 is not tested is variation of the extents of the arrays of
92 data involved. Other tests, like 'restore_test.cc' and
93 'roundtrip.cc' focus more on these run-time parameters.
94
95 Testing varying vectorization widths is for completeness'
96 sake, normally using the defaults should give good performance.
97*/
98
99#include <vigra/multi_array.hxx>
100#include <vigra/accumulator.hxx>
101#include <vigra/multi_math.hxx>
102#include <iostream>
103#include <typeinfo>
104#include <random>
105
106#include <vspline/vspline.h>
107
108bool verbose = true ; // false ;
109
110// 'condense' aggregate types (TinyVectors etc.) into a single value
111
112template < typename T >
113double condense ( const T & t , std::true_type )
114{
115 return std::abs ( t ) ;
116}
117
118template < typename T >
119double condense ( const T & t , std::false_type )
120{
121 return sqrt ( sum ( t * t ) ) / t.size() ;
122}
123
124template < typename T >
125double condense ( const std::complex<T> & t , std::false_type )
126{
127 return std::abs ( t ) ;
128}
129
130template < class T >
131using is_singular = typename
132 std::conditional
133 < std::is_fundamental < T > :: value ,
134 std::true_type ,
135 std::false_type
136 > :: type ;
137
138template < typename T >
139double condense ( const T & t )
140{
141 return condense ( t , is_singular<T>() ) ;
142}
143
144// compare two arrays and calculate the mean and maximum difference
145// here we also take the 'value_extent' - the largest coefficient
146// value we have used - to put the error in relation, so that the high
147// coefficient values we use for integral coefficients don't produce
148// results which look wronger than they are ;)
149
150template < unsigned int dim , typename T >
151double check_diff ( vigra::MultiArrayView < dim , T > & reference ,
152 vigra::MultiArrayView < dim , T > & candidate ,
153 double value_extent
154 )
155{
156 using namespace vigra::multi_math ;
157 using namespace vigra::acc;
158
159 assert ( reference.shape() == candidate.shape() ) ;
160
161 vigra::MultiArray < 1 , double >
162 error_array ( vigra::Shape1(reference.size() ) ) ;
163
164 for ( int i = 0 ; i < reference.size() ; i++ )
165 {
166 auto help = candidate[i] - reference[i] ;
167 error_array [ i ] = condense ( help ) ;
168 }
169
170 AccumulatorChain < double , Select < Mean, Maximum > > ac ;
171 extractFeatures ( error_array.begin() , error_array.end() , ac ) ;
172 double mean = get<Mean>(ac) ;
173 double max = get<Maximum>(ac) ;
174 if ( verbose )
175 {
176 std::cout << "rel. error Mean: " << mean / value_extent
177 << " rel. error Max: " << max / value_extent << std::endl;
178 }
179 return max ;
180}
181
182using namespace vspline ;
183
184#define ast(x) std::integral_constant<int,x>
185
186// with this test routine, the idea is to test all of vspline's
187// higher functions with all possible compile time parameters.
188
189template < unsigned int cf_dim , // dimension of spline/coefficient array
190 unsigned int trg_dim , // dimension of target (result) array
191 int chn , // number of channels in spline's data type
192 int vsz , // vectorization width
193 typename rc_type , // elementary type of a real coordinate
194 typename ele_type , // elementary type of coefficients
195 typename math_ele_type > // elementary type used for arithmetics
196void test ( int spline_degree = 3 ,
198{
199 // TODO: when operating on integral values, vectorized and unvectorized
200 // operation sometimes produces results differing by at most 1, hence
201 // this expression for 'tolerance'. I'm not sure why this happens, it
202 // looks like different results of rounding/truncation, should track
203 // this down make sure the logic isn't flawed somewhere
204
205 double tolerance = std::is_integral < ele_type > :: value
206 ? 1.0 : 0.000001 ;
207
208 // for integral coefficients, we use high knot point values to
209 // provide sufficient dynamic range. the exact values are chosen
210 // in a slightly ad-hoc manner, but they should demonstrate that
211 // more bits can provide better results.
212
213 double value_extent = std::is_integral < ele_type > :: value
214 ? sizeof ( ele_type ) == 2
215 ? 1000.0
216 : sizeof ( ele_type ) == 4
217 ? 1000000.0
218 : sizeof ( ele_type ) == 8
219 ? 1000000000000.0
220 : 100.0
221 : 1.0 ;
222
223 typedef typename vigra::MultiArrayShape<cf_dim>::type cf_shape_t ;
224 typedef typename vigra::MultiArrayShape<trg_dim>::type trg_shape_t ;
225
226 typedef typename std::conditional
227 < chn == 1 ,
228 ele_type ,
229 vigra::TinyVector < ele_type , chn >
230 > :: type dtype ;
231
232 typedef typename std::conditional
233 < cf_dim == 1 ,
234 rc_type ,
235 vigra::TinyVector < rc_type , cf_dim >
236 > :: type crd_type ;
237
238 // allocate storage for original data
239
240 cf_shape_t cf_shape { 99 } ;
241
242 vigra::MultiArray < cf_dim , dtype >
243 original ( cf_shape ) ;
244
245 vigra::MultiArray < cf_dim , dtype >
246 restored ( cf_shape ) ;
247
248 // and storage for coordinates and results
249
250 trg_shape_t trg_shape { 101 } ;
251
252 vigra::MultiArray < trg_dim , crd_type >
253 coordinates ( trg_shape ) ;
254
255 vigra::MultiArray < 1 , rc_type >
256 crd_1d { 101 } ;
257
258 vigra::MultiArray < trg_dim , dtype >
259 target ( trg_shape ) ;
260
261 vigra::MultiArray < trg_dim , double >
262 d_target ( trg_shape ) ;
263
264 cf_shape_t gcf_shape { 101 } ;
265 vigra::MultiArray < cf_dim , dtype >
266 ge_target ( gcf_shape ) ;
267
268 vigra::MultiArray < cf_dim , dtype >
269 ge_target_2 ( gcf_shape ) ;
270
271 // produce random original data. We produce some very high values
272 // here, since we want to use them also for testing processing of
273 // integral data, where we want to exhaust the dynamic range of the
274 // integral type, since we can't have postcomma digits. The results
275 // for processing floats will therefore have errors in the order of
276 // magnitude of 1, rather than the usual errors around 1e-5 for
277 // float data, when the test data are small numbers in [0,1]
278
279 std::random_device rd ;
280 std::mt19937 gen ( rd() ) ;
281 std::uniform_real_distribution<> dis ( - value_extent , value_extent ) ;
282 auto data_ele_view = original.expandElements ( 0 ) ;
283 for ( auto & e : data_ele_view )
284 e = dis ( gen ) ;
285
286 // produce random coordinates. Note that many of these coordinates
287 // will be out-of-range. Thy are folded into the range by the
288 // 'safe evaluator', so this feature is also tested.
289 // we deliberately overshoot the defined range by more than the first
290 // reflection to be sure the extrapolation works as intended.
291
292 std::uniform_real_distribution<> crd_dis ( -300.0 , 300.0 ) ;
293 auto crd_ele_view = coordinates.expandElements ( 0 ) ;
294 for ( auto & e : crd_ele_view )
295 e = crd_dis ( gen ) ;
296
297 // produce a set of 'safe' 1D coordinates to test grid eval
298 // grid eval can't handle out-of-range coordinates, we have to
299 // stay within the spline's safe range.
300
301 std::uniform_real_distribution<> crd1_dis ( 98.0 ) ;
302 for ( auto & e : crd_1d )
303 e = crd1_dis ( gen ) ;
304
306 for ( int d = 0 ; d < cf_dim ; d++ )
307 grid [ d ] = crd_1d ;
308
309 // create a b-spline over the data, prefilter it and create
310 // an evaluator. note how we pass rc_type to make_safe_evaluator.
311 // we test with the same boundary conditions along all axes.
312
313 vigra::TinyVector < vspline::bc_code , cf_dim > bcv ( bc ) ;
314
316 spline_type bspl ( cf_shape ,
317 spline_degree ,
318 bcv ) ;
319
320 // we instantiate prefilter with the given vsz to make sure that
321 // we can indeed pick arbitrary vectorization widths.
322
323 bspl.template prefilter < dtype , math_ele_type , vsz >
324 ( original ) ;
325
326 // we instantiate ev with the given vsz to make sure that
327 // we can indeed pick arbitrary vectorization widths
328
329 enum { ev_vsz = vsz } ;
330
332 < spline_type , rc_type , ev_vsz , math_ele_type >
333 ( bspl ) ;
334
335 // run an array-based transform with the coordinates
336
337 vspline::transform ( ev , coordinates , target ) ;
338
339 // check the result against single-value evaluation.
340 // here we expect to see precisely equal results.
341 // note that in this test, we can't know what the
342 // correct result should be, we only make sure that
343 // vectorized evaluation and unvectorized evaluation
344 // produce near identical results.
345
346 auto it = target.begin() ;
347
348 for ( auto const & e : coordinates )
349 {
350 // TODO when working on int coefficients, I don't get total equality
351 assert ( condense ( *it - ev ( e ) ) <= tolerance ) ;
352 ++it ;
353 }
354
355 // create a safe evaluator with specified target type 'double'
356 // and process the coordinates in 'coordinates' with it. Check
357 // single-eval results against the result of 'transform'.
358
360 < spline_type , rc_type , ev_vsz ,
361 math_ele_type , double >
362 ( bspl ) ;
363
364 vspline::transform ( evd , coordinates , d_target ) ;
365
366 auto itd = d_target.begin() ;
367
368 for ( auto const & e : coordinates )
369 {
370 auto cnd = condense ( *itd - evd ( e ) ) ;
371 if ( cnd > tolerance )
372 std::cout << "**** consensed: " << cnd
373 << " gt. tolerance " << tolerance << std::endl ;
374 assert ( cnd <= tolerance ) ;
375 ++itd ;
376 }
377
378 // create an evaluator over the spline to pass it to grid eval.
379 // here we use a 'raw' evaluator, not the type make_safe_evaluator
380 // or make_evaluator would produce.
381
382 auto gev = vspline::evaluator
383 < crd_type , dtype , ev_vsz , -1 , math_ele_type >
384 ( bspl ) ;
385
386 // call grid-based transform, first with 'ev', which is
387 // of vspline::grok_type, next with 'gev', which is of type
388 // vspline::evaluator and uses different (and faster) code.
389
390 vspline::transform ( grid , ev , ge_target ) ;
391 vspline::transform ( grid , gev , ge_target_2 ) ;
392
393 // Now we 'manually' iterate over the coordinates in the grid
394 // and compare the result to the content of ge_target to make sure
395 // grid eval has worked correctly.
396
397 crd_type c ;
398 auto & cc = reinterpret_cast
399 < vigra::TinyVector < rc_type , cf_dim > & > ( c ) ;
400
401 vigra::MultiCoordinateIterator < cf_dim > mci ( gcf_shape ) ;
402 auto itg2 = ge_target_2.begin() ;
403
404 for ( auto & ref : ge_target )
405 {
406 // fill in the coordinate
407
408 for ( int d = 0 ; d < cf_dim ; d++ )
409 cc [ d ] = grid [ d ] [ (*mci) [ d ] ] ;
410
411 // evaluate at the coordinate and compare to grid eval's output.
412 // we expect equality of results here, since the arithmetic
413 // is near identical for both ways of generating the result
414 // TODO fails with -Ofast or -ffast-math, hence less strict
415
416// assert ( ref == gev ( c ) ) ;
417// assert ( ref == *itg2 ) ;
418
419 assert ( condense ( ref - gev(c) ) <= tolerance ) ;
420 assert ( condense ( ref - *itg2 ) <= tolerance ) ;
421
422 ++ mci ;
423 ++itg2 ;
424 }
425
426 // restore original data, first by using an index-based
427 // transform, then by calling 'restore' (using convolution)
428 // directly on the spline, producing the restored data in
429 // the spline's core.
430 // Note how when working on integral data, the index-based
431 // transform produced wildly wrong results due to the fact
432 // that the evaluation was done entirely in the integral type.
433 // The new evaluation code can, if math_ele_type is real,
434 // produce reasonable results which only suffer from
435 // quantization errors, which was only possible with
436 // restoration by convolution in the last version.
437
438 vspline::transform ( ev , restored ) ;
439
440 // we instantiate restore with the given vsz to make sure that
441 // we can indeed pick arbitrary vectorization widths.
442
443 vspline::restore < cf_dim , dtype , math_ele_type , vsz > ( bspl ) ;
444
445 // compare the data obtained from the two restoration methods.
446 // due to the slightly different arithmetic involved, we expect
447 // similar, but nor necessarily identical results.
448
449 check_diff ( original , restored , value_extent ) ;
450 check_diff ( original , bspl.core , value_extent ) ;
451}
452
453template < typename vsz_t ,
454 typename chn_t ,
455 typename trg_dim_t ,
456 typename cf_dim_t ,
457 typename ... other_types >
458void doit()
459{
460 enum { vsz = vsz_t::value } ;
461 enum { chn = chn_t::value } ;
462 enum { trg_dim = trg_dim_t::value } ;
463 enum { cf_dim = cf_dim_t::value } ;
464
465 std::cout << "vsz " << vsz
466 << " chn " << chn
467 << " trg_dim " << trg_dim
468 << " cf_dim " << cf_dim << std::endl ;
469
470 // we do a few exemplary runs - if we tried to exhaust all
471 // possible combinations of data types, spline degrees and
472 // boundary conditions, this would take forever, and we already
473 // have code in restore_test exploring that way. Here we're
474 // more interested in making sure that 1D and nD data and
475 // real and integral types are processed as expected and that
476 // all 'high-level' capabilities of vspline are invoked.
477
478 // a very 'standard' scenario: cubic b-spline, all in float:
479
480 std::cout << "float crd, float value " << std::endl ;
481
483 ( 3 , vspline::MIRROR ) ;
484
485 // testing an integral-valued spline
486
487 std::cout << "float crd, short value " << std::endl ;
488
490 ( 2 , vspline::MIRROR ) ;
491
492 std::cout << "float crd, int value " << std::endl ;
493
495 ( 4 , vspline::PERIODIC ) ;
496
497 // we can use long-valued coefficients, but evaluation for these
498 // types won't be vectorized with Vc; vspline will use it's own
499 // fallback type simd_tv
500
501 std::cout << "double crd, long value " << std::endl ;
502
504 ( 3 , vspline::PERIODIC ) ;
505
506 // all data in double. This will be vectorized, so it's quite
507 // fast, and yet very precise.
508
509 std::cout << "double crd, double value " << std::endl ;
510
512 ( 5 , vspline::REFLECT ) ;
513
514 // finally we test with all data in long double. This won't
515 // be vectorized at all, but it's extremely precise.
516
517 std::cout << "long double crd, long double value " << std::endl ;
518
519 test < cf_dim , trg_dim , chn , vsz ,
520 long double , long double , long double >
521 ( 3 , vspline::NATURAL ) ;
522}
523
524// when choosing vsize, the vectorization width, we don't go through
525// all values from 1 to 32, but only pick a few representative ones.
526// most of the time, vsize won't be picked 'manually' - and vectorization
527// widths which aren't a multiple of the hardware vector size are
528// quite futile, but here the point is to see if the code still
529// performs correctly even with 'odd' vsize values.
530// TODO: it doesn't - 3 fails below
531
532template < typename ... other_types >
533void set_vsz ( int vsz )
534{
535 switch ( vsz )
536 {
537 case 1:
538 doit < ast(1) , other_types ... >() ;
539 break ;
540 case 3:
541 doit < ast(3) , other_types ... >() ;
542 break ;
543 case 4:
544 doit < ast(4) , other_types ... >() ;
545 break ;
546 case 8:
547 doit < ast(8) , other_types ... >() ;
548 break ;
549 case 16:
550 doit < ast(16) , other_types ... >() ;
551 break ;
552 default:
553 break ;
554 }
555}
556
557template < typename ... other_types >
558void set_chn ( int chn , int vsz )
559{
560 switch ( chn )
561 {
562 case 1:
563 set_vsz < ast(1) , other_types ... > ( vsz ) ;
564 break ;
565 case 2:
566 set_vsz < ast(2) , other_types ... > ( vsz ) ;
567 break ;
568 case 3:
569 set_vsz < ast(3) , other_types ... > ( vsz ) ;
570 break ;
571 default:
572 break ;
573 }
574}
575
576template < typename ... other_types >
577void set_trg_dim ( int trg_dim , int chn , int vsz )
578{
579 switch ( trg_dim )
580 {
581 case 1:
582 set_chn < ast(1) , other_types ... > ( chn , vsz ) ;
583 break ;
584 case 2:
585 set_chn < ast(2) , other_types ... > ( chn , vsz ) ;
586 break ;
587 default:
588 break ;
589 }
590}
591
592template < typename ... other_types >
593void set_cf_dim ( int cf_dim , int trg_dim , int chn , int vsz )
594{
595 switch ( cf_dim )
596 {
597 case 1:
598 set_trg_dim < ast(1) , other_types ... > ( trg_dim , chn , vsz ) ;
599 break ;
600 case 2:
601 set_trg_dim < ast(2) , other_types ... > ( trg_dim , chn , vsz ) ;
602 break ;
603 default:
604 break ;
605 }
606}
607
608#define CF_DIM_MAX 2
609#define TRG_DIM_MAX 2
610#define CHN_MAX 2
611
612int main ( int argc , char * argv[] )
613{
614 std::cout << std::fixed << std::showpos << std::showpoint
615 << std::setprecision(18);
616 std::cerr << std::fixed << std::showpos << std::showpoint
617 << std::setprecision(18);
618
619 for ( int cf_dim = 1 ; cf_dim <= CF_DIM_MAX ; cf_dim++ )
620 {
621 for ( int trg_dim = 1 ; trg_dim <= TRG_DIM_MAX ; trg_dim++ )
622 {
623 for ( int chn = 1 ; chn <= CHN_MAX ; chn++ )
624 {
625 set_cf_dim ( cf_dim , trg_dim , chn , 1 ) ;
626 set_cf_dim ( cf_dim , trg_dim , chn , 3 ) ;
627 set_cf_dim ( cf_dim , trg_dim , chn , 8 ) ;
628 set_cf_dim ( cf_dim , trg_dim , chn , 16 ) ;
629 }
630 }
631 }
632
633 std::cout << "terminating" << std::endl ;
634}
635
vspline::bspline< pixel_type, 2 > spline_type
Definition: ca_correct.cc:111
class evaluator encodes evaluation of a spline-like object. This is a generalization of b-spline eval...
Definition: eval.h:1718
double dtype
Definition: eval.cc:93
double rc_type
Definition: eval.cc:94
typename std::conditional< std::is_fundamental< T > ::value, std::true_type, std::false_type > ::type is_singular
Definition: grind.cc:88
Definition: basis.h:79
void transform(const unary_functor_type &functor, const vigra::MultiArrayView< dimension, typename unary_functor_type::in_type > &input, vigra::MultiArrayView< dimension, typename unary_functor_type::out_type > &output, int njobs=vspline::default_njobs, vspline::atomic< bool > *p_cancel=0)
implementation of two-array transform using wielding::coupled_wield.
Definition: transform.h:211
vspline::grok_type< bspl_coordinate_type< spline_type, rc_type >, result_type, _vsize > make_safe_evaluator(const spline_type &bspl, vigra::TinyVector< int, spline_type::dimension > dspec=vigra::TinyVector< int, spline_type::dimension >(0), int shift=0)
make_safe_evaluator is a factory function, producing a functor which provides safe access to an evalu...
Definition: eval.h:2390
bc_code
This enumeration is used for codes connected to boundary conditions. There are two aspects to boundar...
Definition: common.h:71
@ NATURAL
Definition: common.h:75
@ REFLECT
Definition: common.h:74
@ PERIODIC
Definition: common.h:73
@ MIRROR
Definition: common.h:72
vigra::TinyVector< vigra::MultiArrayView< 1, rc_ele_type >, dimension > grid_spec
Definition: transform.h:701
int main(int argc, char *argv[])
Definition: scope_test.cc:612
void set_cf_dim(int cf_dim, int trg_dim, int chn, int vsz)
Definition: scope_test.cc:593
void set_trg_dim(int trg_dim, int chn, int vsz)
Definition: scope_test.cc:577
#define TRG_DIM_MAX
Definition: scope_test.cc:609
void set_vsz(int vsz)
Definition: scope_test.cc:533
#define ast(x)
Definition: scope_test.cc:184
void set_chn(int chn, int vsz)
Definition: scope_test.cc:558
#define CF_DIM_MAX
Definition: scope_test.cc:608
void test(int spline_degree=3, vspline::bc_code bc=vspline::MIRROR)
Definition: scope_test.cc:196
void doit()
Definition: scope_test.cc:458
double check_diff(vigra::MultiArrayView< dim, T > &reference, vigra::MultiArrayView< dim, T > &candidate, double value_extent)
Definition: scope_test.cc:151
bool verbose
Definition: scope_test.cc:108
double condense(const T &t, std::true_type)
Definition: scope_test.cc:113
#define CHN_MAX
Definition: scope_test.cc:610
class bspline now builds on class bspline_base, adding coefficient storage, while bspline_base provid...
Definition: bspline.h:499
view_type core
Definition: bspline.h:566
includes all headers from vspline (most of them indirectly)