aimsalgo  5.0.5
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  carto::RawConverter<double, ChannelType>().convert(intensity, outValue);
232 
233  }
234  else
235  {
236 
237  outValue = outBackground;
238 
239  }
240 
241 }
242 
243 
244 template < class T >
245 void
248  int t, bool verbose ) const
249 {
250 
251  if ( ( &inVolume != _lastvolume || t != _lasttime )
252  && this->getOrder() > 1 )
253  {
254 
255  if ( verbose )
256  {
257 
258  std::cout << "[ updating spline coefficients : " << std::flush;
259 
260  }
261 
262  std::vector<int> dims = inVolume.getSize();
263  int inSizeX = dims[0];
264  int inSizeY = dims[1];
265  int inSizeZ = dims[2];
266 
268  carto::VolumeRef< double > > converter;
269  _splineCoefficients = carto::VolumeRef<double>( inSizeX, inSizeY,
270  inSizeZ );
271  _splineCoefficients->header().setProperty( "voxel_size",
272  inVolume.getVoxelSize() );
273  if( t > 0 )
274  {
275  carto::VolumeRef<ChannelType> tmpvol( inSizeX, inSizeY, inSizeZ );
276  ChannelType *p = &tmpvol( 0 );
277  const ChannelType *q;
278  int x, y, z;
279  for ( z = 0; z < inSizeZ; z++ )
280  for ( y = 0; y < inSizeY; y++ )
281  {
282  q = &inVolume.at( 0, y, z, t );
283  for ( x = 0; x < inSizeX; ++x, ++p, ++q )
284  *p = *q;
285  }
286  converter.convert( tmpvol, _splineCoefficients );
287  }
288  else
289  {
290  // converter needs a VolumeRefm inVolume is a Volume
292  inref.reset( const_cast<carto::Volume<ChannelType> *>( &inVolume ) );
293  converter.convert( inref, _splineCoefficients );
294  inref.release();
295  }
296 
297  if ( inSizeX > 1 )
298  {
299 
300  std::vector< double > data( inSizeX );
301  int x, y, z;
302  for ( z = 0; z < inSizeZ; z++ )
303  {
304 
305  for ( y = 0; y < inSizeY; y++ )
306  {
307 
308  for ( x = 0; x < inSizeX; x++ )
309  {
310 
311  data[ x ] = ( double )_splineCoefficients( x, y, z );
312 
313  }
314  iirConvolveMirror( data );
315  for ( x = 0; x < inSizeX; x++ )
316  {
317 
318  _splineCoefficients( x, y, z ) = data[ x ];
319 
320  }
321 
322  }
323 
324  }
325 
326  }
327 
328  if ( inSizeY > 1 )
329  {
330 
331  std::vector< double > data( inSizeY );
332  int x, y, z;
333  for ( z = 0; z < inSizeZ; z++ )
334  {
335 
336  for ( x = 0; x < inSizeX; x++ )
337  {
338 
339  for ( y = 0; y < inSizeY; y++ )
340  {
341 
342  data[ y ] = ( double )_splineCoefficients( x, y, z );
343 
344  }
345  iirConvolveMirror( data );
346  for ( y = 0; y < inSizeY; y++ )
347  {
348 
349  _splineCoefficients( x, y, z ) = data[ y ];
350 
351  }
352 
353  }
354 
355  }
356 
357  }
358 
359  if ( inSizeZ > 1 )
360  {
361 
362  std::vector< double > data( inSizeZ );
363  int x, y, z;
364  for ( y = 0; y < inSizeY; y++ )
365  {
366 
367  for ( x = 0; x < inSizeX; x++ )
368  {
369 
370  for ( z = 0; z < inSizeZ; z++ )
371  {
372 
373  data[ z ] = ( double )_splineCoefficients( x, y, z );
374 
375  }
376  iirConvolveMirror( data );
377  for ( z = 0; z < inSizeZ; z++ )
378  {
379 
380  _splineCoefficients( x, y, z ) = data[ z ];
381 
382  }
383 
384  }
385 
386  }
387 
388  }
389 
390  _lastvolume = &inVolume;
391  _lasttime = t;
392 
393  if ( verbose )
394  {
395 
396  std::cout << "done ]" << std::flush;
397 
398  }
399 
400  }
401 
402 }
403 
404 
405 template < class T >
406 void
407 SplineResampler< T >::iirConvolveMirror( std::vector< double >& data ) const
408 {
409 
410  double tolerance = log10( EPSILON );
411 
412  std::vector< double >::iterator d = data.begin(), de = data.end();
413  while ( d != de )
414  {
415 
416  // Note: Relatively to this article:
417  // - Unser et al. IEEE Transactions on Signal Processing 1993
418  // _gain = c0 * PROD(poles)
419  *d *= _gain;
420  ++ d;
421 
422  }
423 
424  int size = ( int )data.size();
425  if ( size == 1 )
426  {
427 
428  return;
429 
430  }
431 
432  int size2 = 2 * ( size - 1 );
433 
434  std::vector< double >::const_reverse_iterator p = _poles.rbegin(),
435  pe = _poles.rend();
436  double* dataBegin = &data[ 0 ];
437  double* dataEnd = dataBegin + data.size();
438  double* ptrD1;
439  double* ptrD2;
440 
441  int j, k;
442  double x0;
443  while ( p != pe )
444  {
445 
446  j = ( int )ceil( tolerance / log10( std::fabs( *p ) ) );
447  k = j - size2 * ( j / size2 );
448  j -= k;
449  if ( k < size )
450  {
451 
452  ptrD1 = dataBegin + k;
453  x0 = *ptrD1;
454 
455  }
456  else
457  {
458 
459  k = size2 - k;
460  ptrD1 = dataBegin + k;
461  x0 = *ptrD1;
462  while ( ++ptrD1 < dataEnd )
463  {
464 
465  x0 = *p * x0 + *ptrD1;
466 
467  }
468  -- ptrD1;
469 
470  }
471  while ( --ptrD1 >= dataBegin )
472  {
473 
474  x0 = *p * x0 + *ptrD1;
475 
476  }
477  while ( j > 0 )
478  {
479 
480  ++ ptrD1;
481  while ( ++ptrD1 < dataEnd )
482  {
483 
484  x0 = *p * x0 + *ptrD1;
485 
486  }
487  -- ptrD1;
488  while ( --ptrD1 >= dataBegin )
489  {
490 
491  x0 = *p * x0 + *ptrD1;
492 
493  }
494  j -= size2;
495 
496  }
497  ptrD2 = ptrD1 ++;
498  *ptrD1++ = x0;
499  x0 = *( ptrD2++ + size );
500  while ( ptrD1 < dataEnd )
501  {
502 
503  *ptrD1++ += *ptrD2++ * *p;
504 
505  }
506  *ptrD2 = ( 2.0 * *ptrD2 - x0 ) / ( 1.0 - *p * *p );
507  -- ptrD1;
508  while ( --ptrD2 >= dataBegin )
509  {
510 
511  *ptrD2 += *ptrD1-- * *p;
512 
513  }
514 
515  ++ p ;
516 
517  }
518 
519 }
520 
521 
522 template < class T >
523 int SplineResampler< T >::getFold( int i, int size ) const
524 {
525  return aims::mirrorCoeff(i, size);
526 }
527 
528 
529 #undef EPSILON
530 
531 
532 template <typename T>
536  inverse_transform_to_vox,
537  const ChannelType& outBackground,
538  carto::Volume< ChannelType > & outVolume,
539  bool verbose) const
540 {
541  Point3df outResolution( outVolume.getVoxelSize() );
542 
543  int outSizeX = outVolume.getSizeX();
544  int outSizeY = outVolume.getSizeY();
545  int outSizeZ = outVolume.getSizeZ();
546  int outSizeT = outVolume.getSizeT();
547  if( outSizeT > inVolume.getSizeT() )
548  outSizeT = inVolume.getSizeT();
549 
550  ChannelType* o;
551 
552  aims::Progression progress(0, static_cast<size_t>(outSizeX)
553  * outSizeY * outSizeZ * outSizeT);
554  Point3df outLoc;
555  int x, y, z, t;
556  for ( t = 0; t < outSizeT; t++ )
557  {
558  updateParametersChannel( inVolume, t, verbose );
559  outLoc = Point3df( 0.0, 0.0, 0.0 );
560 
561  for ( z = 0; z < outSizeZ; z++ )
562  {
563 
564  for ( y = 0; y < outSizeY; y++ )
565  {
566  o = &outVolume.at( 0, y, z, t );
567 
568  for ( x = 0; x < outSizeX; x++ )
569  {
570 
572  inVolume, inverse_transform_to_vox, outBackground,
573  outLoc, *o, t );
574  ++ o;
575  ++progress;
576  outLoc[0] += outResolution[0];
577 
578  }
579  outLoc[1] += outResolution[1];
580  outLoc[0] = 0.0;
581 
582  if(verbose) {
583  progress.print();
584  }
585  }
586  outLoc[2] += outResolution[2];
587  outLoc[1] = 0.0;
588 
589  }
590 
591  }
592 }
593 
594 // Single-channel version: just forwards calls to the spline resampler
595 template<typename T>
597 {
598 
599  static void doResample( const SplineResampler<T>* spline_resampler,
600  const carto::Volume< T > &input_data,
601  const soma::Transformation3d &inverse_transform,
602  const T &background,
603  const Point3df &output_location,
604  T &output_value,
605  int timestep )
606  {
607  spline_resampler->doResampleChannel(input_data,
608  inverse_transform,
609  background,
610  output_location,
611  output_value,
612  timestep);
613  }
614 
615  static void resample_inv_to_vox( const SplineResampler<T>* spline_resampler,
616  const carto::Volume< T >& input_data,
618  inverse_transform_to_vox,
619  const T& background,
620  carto::Volume< T > & output_data,
621  bool verbose = false )
622  {
623  // Call the base class version, which just calls doResample for every voxel
624  spline_resampler->Resampler<T>::resample_inv_to_vox(
625  input_data, inverse_transform_to_vox,
626  background, output_data, verbose);
627  }
628 
629  static void updateParameters( const SplineResampler<T>* spline_resampler,
630  const carto::Volume< T >& inVolume,
631  int t, bool verbose )
632  {
633  spline_resampler->updateParametersChannel(inVolume, t, verbose);
634  }
635 
636 };
637 
638 
639 // Multi-channel version: iterate over all channels
640 template<typename T>
642 {
644 
645  static void doResample( const SplineResampler<T>* spline_resampler,
646  const carto::Volume< T > &input_data,
647  const soma::Transformation3d &inverse_transform,
648  const T &background,
649  const Point3df &output_location,
650  T &output_value,
651  int timestep )
652  {
653  ChannelSelector< carto::VolumeRef<T>, carto::VolumeRef<ChannelType> >
654  selector;
655  carto::VolumeRef<ChannelType> input_channel;
656 
657  for (unsigned int channel = 0;
658  channel < carto::DataTypeTraits<T>::channelcount;
659  channel++)
660  {
661  // build a temporary VolumeRef
662  carto::VolumeRef<T> inref;
663  inref.reset( const_cast<carto::Volume<T> *>( &input_data ) );
664  input_channel = selector.select( inref, channel );
665  inref.reset();
666 
667  spline_resampler->updateParametersChannel(input_channel,
668  timestep, carto::verbose);
669  return spline_resampler->doResampleChannel(input_channel,
670  inverse_transform,
671  background[channel],
672  output_location,
673  output_value[channel],
674  timestep);
675  }
676  }
677 
678  static void resample_inv_to_vox( const SplineResampler<T>* spline_resampler,
679  const carto::Volume< T >& input_data,
681  inverse_transform_to_vox,
682  const T& background,
683  carto::Volume< T > & output_data,
684  bool verbose = false )
685  {
686  ChannelSelector< carto::VolumeRef<T>, carto::VolumeRef<ChannelType> >
687  selector;
688 
689  int dimX = output_data.getSizeX();
690  int dimY = output_data.getSizeY();
691  int dimZ = output_data.getSizeZ();
692  int dimT = output_data.getSizeT();
693  std::vector<float> vs = output_data.getVoxelSize();
694 
695  for (unsigned int channel = 0;
696  channel < carto::DataTypeTraits<T>::channelcount;
697  channel++)
698  {
699  carto::VolumeRef<ChannelType> input_channel;
700  carto::VolumeRef<ChannelType> output_channel( dimX, dimY, dimZ, dimT );
701 
702  output_channel.header().setProperty( "voxel_size", vs );
703 
704  output_channel.copyHeaderFrom( output_data.header() );
705 
706  // build a temporary VolumeRef
707  carto::VolumeRef<T> inref;
708  inref.reset( const_cast<carto::Volume<T> *>( &input_data ) );
709  /* We split the data and process resampling on each component */
710  input_channel = selector.select( inref, channel );
711  inref.release();
712 
713  spline_resampler->resample_channel_inv_to_vox(
714  *input_channel, inverse_transform_to_vox, background[ channel ],
715  *output_channel, verbose );
716 
717  // build a temporary VolumeRef
718  carto::VolumeRef<T> outref;
719  outref.reset( &output_data );
720  selector.set( outref, channel, output_channel );
721  outref.release();
722  }
723 
724  }
725 
726  static void updateParameters( const SplineResampler<T>* /*spline_resampler*/,
727  const carto::Volume< T >& /*inVolume*/,
728  int /*t*/, bool /*verbose*/ )
729  {
730  // do nothing, updateParametersChannel is called as needed by the methods
731  // above
732  }
733 
734 };
735 
736 
737 template < class T >
738 void
740  int t, bool verbose ) const
741 {
744 
745  Switch::updateParameters(this,
746  inVolume, t, verbose);
747 }
748 
749 template < class T >
752  const soma::Transformation3d& inverse_transform_to_vox,
753  const T& background,
754  carto::Volume< T > & output_data,
755  bool verbose ) const
756 {
759 
760  Switch::resample_inv_to_vox(this,
761  input_data, inverse_transform_to_vox,
762  background, output_data, verbose);
763 }
764 
765 
766 template < class T >
768 doResample( const carto::Volume< T > &input_data,
769  const soma::Transformation3d &inverse_transform,
770  const T &background,
771  const Point3df &output_location,
772  T &output_value,
773  int timestep ) const
774 {
777 
778  Switch::doResample(this, input_data, inverse_transform, background,
779  output_location, output_value, timestep);
780 }
781 
782 } // namespace aims
783 
784 #endif // !defined( AIMS_RESAMPLING_SPLINERESAMPLER_D_H )
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 > getSplineCoef(const carto::Volume< T > &inVolume, int t=0, bool verbose=false)
Computes spline coefficients corresponding to an input volume.
int getSizeZ() const
const T & at(long x, long y=0, long z=0, long t=0) const
carto::VolumeRef< double > _splineCoefficients
virtual void doResampleChannel(const carto::Volume< ChannelType > &inVolume, const soma::Transformation3d &transform3d, const ChannelType &outBackground, const Point3df &outLocation, ChannelType &outValue, int t) const
virtual void print(const bool force=false)
T * release()
carto::DataTypeTraits< T >::ChannelType ChannelType
int mirrorCoeff(int i, int size)
This method returns a mirror index when needed.
Point3dd transform(double x, double y, double z) const
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)
virtual void updateParametersChannel(const carto::Volume< ChannelType > &inVolume, int t, bool verbose) const
std::vector< double > _poles
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.
std::vector< int > getSize() const
virtual void convert(const INP &in, OUTP &out) const
int getSizeX() const
static void updateParameters(const SplineResampler< T > *, const carto::Volume< T > &, int, bool)
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)
void iirConvolveMirror(std::vector< double > &data) const
int verbose
int getFold(int i, int size) const
This method returns a mirror index when needed.
const PropertySet & header() const
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 setProperty(const std::string &, const T &)
void updateParameters(const carto::Volume< T > &inVolume, int t, bool verbose) const CARTO_OVERRIDE
Update the cache of spline coefficients if needed.
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)
void reset()
Clear the cache.
Resampling of data from a volume, applying a transformation.
Definition: resampler.h:75
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)
virtual int getOrder() const =0
Spline order (1 to 7)
#define EPSILON
virtual double getBSplineWeight(int i, double x) const =0
Returns .
static void updateParameters(const SplineResampler< T > *spline_resampler, const carto::Volume< T > &inVolume, int t, bool verbose)
const carto::Volume< ChannelType > * _lastvolume
virtual void copyHeaderFrom(const PropertySet &other)
std::vector< float > getVoxelSize() const
int getSizeY() const
const PropertySet & header() const
int getSizeT() const
void reset(T *p=NULL)
B-Spline-based resampling.