aimsalgo  5.1.2
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 
46 #include <aims/utility/channel.h>
48 
49 #define EPSILON 1.192092896e-7
50 
51 
52 namespace aims
53 {
54 
55 template < class T >
57  : Resampler<T>(), _lastvolume( 0 ), _lasttime( -1 )
58 {
59 }
60 
61 
62 template < class T >
64 {
65 
66  _lasttime = -1;
67  _lastvolume = 0;
68 
69 }
70 
71 template < class T >
73  const carto::Volume< T >& inVolume,
74  int t,
75  bool verbose )
76 {
77  updateParameters( inVolume, t, verbose);
78  return _splineCoefficients;
79 
80 }
81 
82 template < class T >
83 void
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 ) )
117  {
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;
149 
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;
169 
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 )
182  * ( &_splineCoefficients( 0, 1 ) - &_splineCoefficients( 0 ) );
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
241  && intensity > std::numeric_limits<ChannelType>::max())
243  else
245  outValue);
246 
247  }
248  else
249  {
250 
251  outValue = outBackground;
252 
253  }
254 
255 }
256 
257 
258 template < class T >
259 void
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;
283  _splineCoefficients = carto::VolumeRef<double>( inSizeX, inSizeY,
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 
419 template < class T >
420 void
421 SplineResampler< 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 
536 template < class T >
537 int SplineResampler< T >::getFold( int i, int size ) const
538 {
539  return aims::mirrorCoeff(i, size);
540 }
541 
542 
543 #undef EPSILON
544 
545 
546 template <typename T>
550  inverse_transform_to_vox,
551  const ChannelType& outBackground,
552  carto::Volume< ChannelType > & outVolume,
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 
585  doResampleChannel(
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
609 template<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
654 template<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
676  carto::VolumeRef<T> inref;
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
721  carto::VolumeRef<T> inref;
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 
751 template < class T >
752 void
754  int t, bool verbose ) const
755 {
758 
759  Switch::updateParameters(this,
760  inVolume, t, verbose);
761 }
762 
763 template < class T >
765 resample_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 
780 template < class T >
782 doResample( 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)
Resampling of data from a volume, applying a transformation.
Definition: resampler.h:76
B-Spline-based resampling.
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.
int getFold(int i, int size) const
This method returns a mirror index when needed.
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
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
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 &)
void convert(const INP &in, OUTP &out) const
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
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 _Tp max()