aimsalgo 6.0.0
Neuroimaging image processing
splineresampler_d.h
Go to the documentation of this file.
1/* This software and supporting documentation are distributed by
2 * Institut Federatif de Recherche 49
3 * CEA/NeuroSpin, Batiment 145,
4 * 91191 Gif-sur-Yvette cedex
5 * France
6 *
7 * This software is governed by the CeCILL-B license under
8 * French law and abiding by the rules of distribution of free software.
9 * You can use, modify and/or redistribute the software under the
10 * terms of the CeCILL-B license as circulated by CEA, CNRS
11 * and INRIA at the following URL "http://www.cecill.info".
12 *
13 * As a counterpart to the access to the source code and rights to copy,
14 * modify and redistribute granted by the license, users are provided only
15 * with a limited warranty and the software's author, the holder of the
16 * economic rights, and the successive licensors have only limited
17 * liability.
18 *
19 * In this respect, the user's attention is drawn to the risks associated
20 * with loading, using, modifying and/or developing or reproducing the
21 * software by the user in light of its specific status of free software,
22 * that may mean that it is complicated to manipulate, and that also
23 * therefore means that it is reserved for developers and experienced
24 * professionals having in-depth computer knowledge. Users are therefore
25 * encouraged to load and test the software's suitability as regards their
26 * requirements in conditions enabling the security of their systems and/or
27 * data to be ensured and, more generally, to use and operate it in the
28 * same conditions as regards security.
29 *
30 * The fact that you are presently reading this means that you have had
31 * knowledge of the CeCILL-B license and that you accept its terms.
32 */
33
34
35#ifndef AIMS_RESAMPLING_SPLINERESAMPLER_D_H
36#define AIMS_RESAMPLING_SPLINERESAMPLER_D_H
37
39
40#include <cassert>
41#include <cmath>
42#include <vector>
43#include <iostream>
44
45#include <cartobase/type/converter.h>
46#include <aims/utility/channel.h>
47#include <aims/utility/converter_volume.h>
48
49#define EPSILON 1.192092896e-7
50
51
52namespace aims
53{
54
55template < class T >
60
61
62template < class T >
64{
65
66 _lasttime = -1;
67 _lastvolume = 0;
68
69}
70
71template < class T >
73 const carto::Volume< T >& inVolume,
74 int t,
75 bool verbose )
76{
77 updateParameters( inVolume, t, verbose);
79
80}
81
82template < class T >
83void
85 const carto::Volume< ChannelType >& inVolume,
86 const soma::Transformation3d& invTransform3d,
87 const ChannelType& outBackground,
88 const Point3df& outLocation,
89 ChannelType& outValue, int t ) const
90{
91 assert(t == _lasttime);
92 (void)( t ); // compilation warning...
93
94 int order = this->getOrder();
95 int half = order / 2;
96 int width = order + 1;
97
98 double *s = &_splineCoefficients( 0, 0, 0 );
99 double *pi, *pj;
100
101 Point3df normalizedInLocation;
102 normalizedInLocation = invTransform3d.transform( outLocation );
103
104 float xf = round(normalizedInLocation[0]);
105 float yf = round(normalizedInLocation[1]);
106 float zf = round(normalizedInLocation[2]);
107
108 int dimx = inVolume.getSizeX();
109 int dimy = inVolume.getSizeY();
110 int dimz = inVolume.getSizeZ();
111
112 // The test is done using floating-point so that NaN values are excluded (the
113 // background value is returned if the transformation yields NaN)
114 if ( ( xf >= 0 ) && ( xf < dimx ) &&
115 ( yf >= 0 ) && ( yf < dimy ) &&
116 ( zf >= 0 ) && ( zf < dimz ) )
118 int x = static_cast<int>(xf);
119 int y = static_cast<int>(yf);
120 int z = static_cast<int>(zf);
121
122 std::vector< double > weightX( width ), weightY( width ), weightZ( width);
123 std::vector< int > foldX( width ), foldY( width ), foldZ( width );
124
125 double intensity, qi, qj;
126 int i, j, k;
127
128 if (dimz == 1 )
129 {
130
131 weightZ[ 0 ] = 1.0;
132 foldZ[ 0 ] = 0;
133
134 }
135 else
136 {
137
138 if ( order % 2 )
139 {
140
141 z = static_cast<int>(floor(normalizedInLocation[2]));
142 z -= half;
143
144 }
145 else
146 {
147
148 z -= half;
150 }
151 for ( k = 0; k < width; k++ )
152 {
153
154 weightZ[ k ] = getBSplineWeight( z + k,
155 normalizedInLocation[2] );
156 foldZ[ k ] = getFold( z + k, dimz ) *
157 ( &_splineCoefficients( 0, 0, 1 )
158 - &_splineCoefficients( 0 ) );
159
160 }
161
162 }
163
164 if ( order % 2 )
165 {
166
167 y = static_cast<int>(floor(normalizedInLocation[1]));
168 y -= half;
170 }
171 else
172 {
173
174 y -= half;
175
176 }
177 for ( j = 0; j < width; j++ )
178 {
179
180 weightY[ j ] = getBSplineWeight( y + j, normalizedInLocation[1] );
181 foldY[ j ] = getFold( y +j, dimy )
183
184 }
185
186 if ( order % 2 )
187 {
188
189 x = static_cast<int>(floor(normalizedInLocation[0]));
190 x -= half;
191
192 }
193 else
194 {
195
196 x -= half;
197
198 }
199 for ( i = 0; i < width; i++ )
200 {
201
202 weightX[ i ] = getBSplineWeight( x + i, normalizedInLocation[0] );
203 foldX[ i ] = getFold( x + i, dimx );
204
205 }
206
207 intensity = 0.0;
208 for ( k = 0; k < ( ( dimz == 1 ) ? 1 : width ); k++ )
209 {
210
211 pj = s + foldZ[ k ];
212 qj = 0.0;
213 for ( j = 0; j < width; j++ )
214 {
215
216 pi = pj + foldY[ j ];
217 qi = 0.0;
218 for ( i = 0; i < width; i++ )
219 {
220
221 qi += weightX[ i ] * ( double )*( pi + foldX[ i ] );
222
223 }
224 qj += weightY[ j ] * qi;
225
226 }
227 intensity += weightZ[ k ] * qj;
228
229 }
230
231 // Clip the values to the output datatype: such out-of-range should be
232 // rare, but can occur due to overshoot with spline order >= 2.
233 //
234 // Depending on the output ChannelType:
235 // - floating-point: do not clip, infinity may be used
236 // - integer: do clip to min-max range to avoid over/underflow
238 && intensity < std::numeric_limits<ChannelType>::lowest())
239 outValue = std::numeric_limits<ChannelType>::lowest();
243 else
244 carto::RawConverter<double, ChannelType>().convert(intensity,
245 outValue);
246
247 }
248 else
249 {
250
251 outValue = outBackground;
252
253 }
254
255}
256
257
258template < class T >
259void
262 int t, bool verbose ) const
263{
264
265 if ( ( &inVolume != _lastvolume || t != _lasttime )
266 && this->getOrder() > 1 )
267 {
268
269 if ( verbose )
270 {
271
272 std::cout << "[ updating spline coefficients : " << std::flush;
273
274 }
275
276 std::vector<int> dims = inVolume.getSize();
277 int inSizeX = dims[0];
278 int inSizeY = dims[1];
279 int inSizeZ = dims[2];
280
282 carto::VolumeRef< double > > converter;
284 inSizeZ );
285 _splineCoefficients->header().setProperty( "voxel_size",
286 inVolume.getVoxelSize() );
287 if( t > 0 )
288 {
289 carto::VolumeRef<ChannelType> tmpvol( inSizeX, inSizeY, inSizeZ );
290 ChannelType *p = &tmpvol( 0 );
291 const ChannelType *q;
292 int x, y, z;
293 for ( z = 0; z < inSizeZ; z++ )
294 for ( y = 0; y < inSizeY; y++ )
295 {
296 q = &inVolume.at( 0, y, z, t );
297 for ( x = 0; x < inSizeX; ++x, ++p, ++q )
298 *p = *q;
299 }
300 converter.convert( tmpvol, _splineCoefficients );
301 }
302 else
303 {
304 // converter needs a VolumeRefm inVolume is a Volume
306 inref.reset( const_cast<carto::Volume<ChannelType> *>( &inVolume ) );
307 converter.convert( inref, _splineCoefficients );
308 inref.release();
309 }
310
311 if ( inSizeX > 1 )
312 {
313
314 std::vector< double > data( inSizeX );
315 int x, y, z;
316 for ( z = 0; z < inSizeZ; z++ )
317 {
318
319 for ( y = 0; y < inSizeY; y++ )
320 {
321
322 for ( x = 0; x < inSizeX; x++ )
323 {
324
325 data[ x ] = ( double )_splineCoefficients( x, y, z );
326
327 }
328 iirConvolveMirror( data );
329 for ( x = 0; x < inSizeX; x++ )
330 {
331
332 _splineCoefficients( x, y, z ) = data[ x ];
333
334 }
335
336 }
337
338 }
339
340 }
341
342 if ( inSizeY > 1 )
343 {
344
345 std::vector< double > data( inSizeY );
346 int x, y, z;
347 for ( z = 0; z < inSizeZ; z++ )
348 {
349
350 for ( x = 0; x < inSizeX; x++ )
351 {
352
353 for ( y = 0; y < inSizeY; y++ )
354 {
355
356 data[ y ] = ( double )_splineCoefficients( x, y, z );
357
358 }
359 iirConvolveMirror( data );
360 for ( y = 0; y < inSizeY; y++ )
361 {
362
363 _splineCoefficients( x, y, z ) = data[ y ];
364
365 }
366
367 }
368
369 }
370
371 }
372
373 if ( inSizeZ > 1 )
374 {
375
376 std::vector< double > data( inSizeZ );
377 int x, y, z;
378 for ( y = 0; y < inSizeY; y++ )
379 {
380
381 for ( x = 0; x < inSizeX; x++ )
382 {
383
384 for ( z = 0; z < inSizeZ; z++ )
385 {
386
387 data[ z ] = ( double )_splineCoefficients( x, y, z );
388
389 }
390 iirConvolveMirror( data );
391 for ( z = 0; z < inSizeZ; z++ )
392 {
393
394 _splineCoefficients( x, y, z ) = data[ z ];
395
396 }
397
398 }
399
400 }
401
402 }
403
404 _lastvolume = &inVolume;
405 _lasttime = t;
406
407 if ( verbose )
408 {
409
410 std::cout << "done ]" << std::flush;
411
412 }
413
414 }
415
416}
417
418
419template < class T >
420void
421SplineResampler< T >::iirConvolveMirror( std::vector< double >& data ) const
422{
423
424 double tolerance = log10( EPSILON );
425
426 std::vector< double >::iterator d = data.begin(), de = data.end();
427 while ( d != de )
428 {
429
430 // Note: Relatively to this article:
431 // - Unser et al. IEEE Transactions on Signal Processing 1993
432 // _gain = c0 * PROD(poles)
433 *d *= _gain;
434 ++ d;
435
436 }
437
438 int size = ( int )data.size();
439 if ( size == 1 )
440 {
441
442 return;
443
444 }
445
446 int size2 = 2 * ( size - 1 );
447
448 std::vector< double >::const_reverse_iterator p = _poles.rbegin(),
449 pe = _poles.rend();
450 double* dataBegin = &data[ 0 ];
451 double* dataEnd = dataBegin + data.size();
452 double* ptrD1;
453 double* ptrD2;
454
455 int j, k;
456 double x0;
457 while ( p != pe )
458 {
459
460 j = ( int )ceil( tolerance / log10( std::fabs( *p ) ) );
461 k = j - size2 * ( j / size2 );
462 j -= k;
463 if ( k < size )
464 {
465
466 ptrD1 = dataBegin + k;
467 x0 = *ptrD1;
468
469 }
470 else
471 {
472
473 k = size2 - k;
474 ptrD1 = dataBegin + k;
475 x0 = *ptrD1;
476 while ( ++ptrD1 < dataEnd )
477 {
478
479 x0 = *p * x0 + *ptrD1;
480
481 }
482 -- ptrD1;
483
484 }
485 while ( --ptrD1 >= dataBegin )
486 {
487
488 x0 = *p * x0 + *ptrD1;
489
490 }
491 while ( j > 0 )
492 {
493
494 ++ ptrD1;
495 while ( ++ptrD1 < dataEnd )
496 {
497
498 x0 = *p * x0 + *ptrD1;
499
500 }
501 -- ptrD1;
502 while ( --ptrD1 >= dataBegin )
503 {
504
505 x0 = *p * x0 + *ptrD1;
506
507 }
508 j -= size2;
509
510 }
511 ptrD2 = ptrD1 ++;
512 *ptrD1++ = x0;
513 x0 = *( ptrD2++ + size );
514 while ( ptrD1 < dataEnd )
515 {
516
517 *ptrD1++ += *ptrD2++ * *p;
518
519 }
520 *ptrD2 = ( 2.0 * *ptrD2 - x0 ) / ( 1.0 - *p * *p );
521 -- ptrD1;
522 while ( --ptrD2 >= dataBegin )
523 {
524
525 *ptrD2 += *ptrD1-- * *p;
526
527 }
528
529 ++ p ;
530
531 }
532
533}
534
535
536template < class T >
537int SplineResampler< T >::getFold( int i, int size ) const
538{
539 return aims::mirrorCoeff(i, size);
540}
541
542
543#undef EPSILON
544
545
546template <typename T>
550 inverse_transform_to_vox,
551 const ChannelType& outBackground,
553 bool verbose) const
554{
555 Point3df outResolution( outVolume.getVoxelSize() );
556
557 int outSizeX = outVolume.getSizeX();
558 int outSizeY = outVolume.getSizeY();
559 int outSizeZ = outVolume.getSizeZ();
560 int outSizeT = outVolume.getSizeT();
561 if( outSizeT > inVolume.getSizeT() )
562 outSizeT = inVolume.getSizeT();
563
564 ChannelType* o;
565
566 aims::Progression progress(0, static_cast<size_t>(outSizeX)
567 * outSizeY * outSizeZ * outSizeT);
568 Point3df outLoc;
569 int x, y, z, t;
570 for ( t = 0; t < outSizeT; t++ )
571 {
572 updateParametersChannel( inVolume, t, verbose );
573 outLoc = Point3df( 0.0, 0.0, 0.0 );
574
575 for ( z = 0; z < outSizeZ; z++ )
576 {
577
578 for ( y = 0; y < outSizeY; y++ )
579 {
580 o = &outVolume.at( 0, y, z, t );
581
582 for ( x = 0; x < outSizeX; x++ )
583 {
584
586 inVolume, inverse_transform_to_vox, outBackground,
587 outLoc, *o, t );
588 ++ o;
589 ++progress;
590 outLoc[0] += outResolution[0];
591
592 }
593 outLoc[1] += outResolution[1];
594 outLoc[0] = 0.0;
595
596 if(verbose) {
597 progress.print();
598 }
599 }
600 outLoc[2] += outResolution[2];
601 outLoc[1] = 0.0;
602
603 }
604
605 }
606}
607
608// Single-channel version: just forwards calls to the spline resampler
609template<typename T>
611{
612
613 static void doResample( const SplineResampler<T>* spline_resampler,
614 const carto::Volume< T > &input_data,
615 const soma::Transformation3d &inverse_transform,
616 const T &background,
617 const Point3df &output_location,
618 T &output_value,
619 int timestep )
620 {
621 spline_resampler->doResampleChannel(input_data,
622 inverse_transform,
623 background,
624 output_location,
625 output_value,
626 timestep);
627 }
628
629 static void resample_inv_to_vox( const SplineResampler<T>* spline_resampler,
630 const carto::Volume< T >& input_data,
632 inverse_transform_to_vox,
633 const T& background,
634 carto::Volume< T > & output_data,
635 bool verbose = false )
636 {
637 // Call the base class version, which just calls doResample for every voxel
638 spline_resampler->Resampler<T>::resample_inv_to_vox(
639 input_data, inverse_transform_to_vox,
640 background, output_data, verbose);
641 }
642
643 static void updateParameters( const SplineResampler<T>* spline_resampler,
644 const carto::Volume< T >& inVolume,
645 int t, bool verbose )
646 {
647 spline_resampler->updateParametersChannel(inVolume, t, verbose);
648 }
649
650};
651
652
653// Multi-channel version: iterate over all channels
654template<typename T>
656{
658
659 static void doResample( const SplineResampler<T>* spline_resampler,
660 const carto::Volume< T > &input_data,
661 const soma::Transformation3d &inverse_transform,
662 const T &background,
663 const Point3df &output_location,
664 T &output_value,
665 int timestep )
666 {
667 ChannelSelector< carto::VolumeRef<T>, carto::VolumeRef<ChannelType> >
668 selector;
669 carto::VolumeRef<ChannelType> input_channel;
670
671 for (unsigned int channel = 0;
672 channel < carto::DataTypeTraits<T>::channelcount;
673 channel++)
674 {
675 // build a temporary VolumeRef
677 inref.reset( const_cast<carto::Volume<T> *>( &input_data ) );
678 input_channel = selector.select( inref, channel );
679 inref.reset();
680
681 spline_resampler->updateParametersChannel(input_channel,
682 timestep, carto::verbose);
683 return spline_resampler->doResampleChannel(input_channel,
684 inverse_transform,
685 background[channel],
686 output_location,
687 output_value[channel],
688 timestep);
689 }
690 }
691
692 static void resample_inv_to_vox( const SplineResampler<T>* spline_resampler,
693 const carto::Volume< T >& input_data,
695 inverse_transform_to_vox,
696 const T& background,
697 carto::Volume< T > & output_data,
698 bool verbose = false )
699 {
700 ChannelSelector< carto::VolumeRef<T>, carto::VolumeRef<ChannelType> >
701 selector;
702
703 int dimX = output_data.getSizeX();
704 int dimY = output_data.getSizeY();
705 int dimZ = output_data.getSizeZ();
706 int dimT = output_data.getSizeT();
707 std::vector<float> vs = output_data.getVoxelSize();
708
709 for (unsigned int channel = 0;
710 channel < carto::DataTypeTraits<T>::channelcount;
711 channel++)
712 {
713 carto::VolumeRef<ChannelType> input_channel;
714 carto::VolumeRef<ChannelType> output_channel( dimX, dimY, dimZ, dimT );
715
716 output_channel.header().setProperty( "voxel_size", vs );
717
718 output_channel.copyHeaderFrom( output_data.header() );
719
720 // build a temporary VolumeRef
722 inref.reset( const_cast<carto::Volume<T> *>( &input_data ) );
723 /* We split the data and process resampling on each component */
724 input_channel = selector.select( inref, channel );
725 inref.release();
726
727 spline_resampler->resample_channel_inv_to_vox(
728 *input_channel, inverse_transform_to_vox, background[ channel ],
729 *output_channel, verbose );
730
731 // build a temporary VolumeRef
732 carto::VolumeRef<T> outref;
733 outref.reset( &output_data );
734 selector.set( outref, channel, output_channel );
735 outref.release();
736 }
737
738 }
739
740 static void updateParameters( const SplineResampler<T>* /*spline_resampler*/,
741 const carto::Volume< T >& /*inVolume*/,
742 int /*t*/, bool /*verbose*/ )
743 {
744 // do nothing, updateParametersChannel is called as needed by the methods
745 // above
746 }
747
748};
749
750
751template < class T >
752void
754 int t, bool verbose ) const
755{
758
759 Switch::updateParameters(this,
760 inVolume, t, verbose);
761}
762
763template < class T >
765resample_inv_to_vox( const carto::Volume< T >& input_data,
766 const soma::Transformation3d& inverse_transform_to_vox,
767 const T& background,
768 carto::Volume< T > & output_data,
769 bool verbose ) const
770{
773
774 Switch::resample_inv_to_vox(this,
775 input_data, inverse_transform_to_vox,
776 background, output_data, verbose);
777}
778
779
780template < class T >
782doResample( const carto::Volume< T > &input_data,
783 const soma::Transformation3d &inverse_transform,
784 const T &background,
785 const Point3df &output_location,
786 T &output_value,
787 int timestep ) const
788{
791
792 Switch::doResample(this, input_data, inverse_transform, background,
793 output_location, output_value, timestep);
794}
795
796} // namespace aims
797
798#endif // !defined( AIMS_RESAMPLING_SPLINERESAMPLER_D_H )
virtual void print(const bool force=false)
B-Spline-based resampling.
std::vector< double > _poles
const carto::Volume< ChannelType > * _lastvolume
void doResample(const carto::Volume< T > &inVolume, const soma::Transformation3d &transform3d, const T &outBackground, const Point3df &outLocation, T &outValue, int t) const CARTO_OVERRIDE
Resample a volume at a single location.
void reset()
Clear the cache.
carto::VolumeRef< double > getSplineCoef(const carto::Volume< T > &inVolume, int t=0, bool verbose=false)
Computes spline coefficients corresponding to an input volume.
void resample_inv_to_vox(const carto::Volume< T > &input_data, const soma::Transformation3d &inverse_transform_to_vox, const T &background, carto::Volume< T > &output_data, bool verbose=false) const CARTO_OVERRIDE
Resample a volume into an existing output volume.
virtual int getOrder() const=0
int getFold(int i, int size) const
virtual void updateParametersChannel(const carto::Volume< ChannelType > &inVolume, int t, bool verbose) const
virtual void doResampleChannel(const carto::Volume< ChannelType > &inVolume, const soma::Transformation3d &transform3d, const ChannelType &outBackground, const Point3df &outLocation, ChannelType &outValue, int t) const
friend struct MultiChannelResamplerSwitch
carto::DataTypeTraits< T >::ChannelType ChannelType
void resample_channel_inv_to_vox(const carto::Volume< ChannelType > &inVolume, const soma::Transformation3d &inverse_transform_to_vox, const ChannelType &outBackground, carto::Volume< ChannelType > &outVolume, bool verbose) const
carto::VolumeRef< double > _splineCoefficients
void iirConvolveMirror(std::vector< double > &data) const
void updateParameters(const carto::Volume< T > &inVolume, int t, bool verbose) const CARTO_OVERRIDE
Update the cache of spline coefficients if needed.
virtual void convert(const INP &in, OUTP &out) const
const PropertySet & header() const
void setProperty(const std::string &, const T &)
int getSizeY() const
std::vector< float > getVoxelSize() const
int getSizeX() const
std::vector< int > getSize() const
int getSizeZ() const
int getSizeT() const
virtual void copyHeaderFrom(const PropertySet &other)
const PropertySet & header() const
const T & at(long x, long y=0, long z=0, long t=0) const
void reset(T *p=NULL)
T * release()
Point3dd transform(double x, double y, double z) const
ProgressInfo< double, double > Progression
int mirrorCoeff(int i, int size)
This method returns a mirror index when needed.
int verbose
#define EPSILON
static void updateParameters(const SplineResampler< T > *spline_resampler, const carto::Volume< T > &inVolume, int t, bool verbose)
static void doResample(const SplineResampler< T > *spline_resampler, const carto::Volume< T > &input_data, const soma::Transformation3d &inverse_transform, const T &background, const Point3df &output_location, T &output_value, int timestep)
static void resample_inv_to_vox(const SplineResampler< T > *spline_resampler, const carto::Volume< T > &input_data, const soma::Transformation3d &inverse_transform_to_vox, const T &background, carto::Volume< T > &output_data, bool verbose=false)
carto::DataTypeTraits< T >::ChannelType ChannelType
static void updateParameters(const SplineResampler< T > *, const carto::Volume< T > &, int, bool)
static void resample_inv_to_vox(const SplineResampler< T > *spline_resampler, const carto::Volume< T > &input_data, const soma::Transformation3d &inverse_transform_to_vox, const T &background, carto::Volume< T > &output_data, bool verbose=false)
static void doResample(const SplineResampler< T > *spline_resampler, const carto::Volume< T > &input_data, const soma::Transformation3d &inverse_transform, const T &background, const Point3df &output_location, T &output_value, int timestep)
static const bool is_multichannel
static const bool has_infinity
static _Tp max()
AimsVector< float, 3 > Point3df