BSplineFreeFormTransformationSV.h
1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2013-2015 Imperial College London
5  * Copyright 2013-2015 Andreas Schuh
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #ifndef MIRTK_BSplineFreeFormTransformationSV_H
21 #define MIRTK_BSplineFreeFormTransformationSV_H
22 
23 #include "mirtk/BSplineFreeFormTransformation3D.h"
24 
25 #include "mirtk/Math.h"
26 #include "mirtk/ImageFunction.h"
27 #include "mirtk/VoxelFunction.h"
28 #include "mirtk/FFDIntegrationMethod.h"
29 
30 
31 namespace mirtk {
32 
33 
34 /**
35  * Free-form transformation parameterized by a stationary velocity field.
36  *
37  * This class implements a free-form transformation which is represented
38  * by a stationary velocity field (2D/3D). The displacement field is obtained
39  * by exponentiating the velocity field, resulting in a diffeomorphic
40  * transformation. A stationary velocity field can be further approximated
41  * from a given displacement field using the group logarithm of diffeomorphisms.
42  */
44 {
45  mirtkTransformationMacro(BSplineFreeFormTransformationSV);
46 
47  // ---------------------------------------------------------------------------
48  // Attributes
49 
50  /// Cross-sectional upper integration limit, i.e., time difference between
51  /// target and source if both volumes have the same temporal origin.
52  /// Otherwise, the difference between the temporal origins as given by the
53  /// t and t0 parameters of LocalTransform et al. is used instead.
54  ///
55  /// \note Can be set to zero for the generation of a deformation movie using
56  /// the transformation tool based on ImageTransformation by varying
57  /// the -Tt parameter from zero to one and keeping -St fixed at one.
58  /// Use the doftool to set the --cross-sectional-time-interval to zero.
59  mirtkPublicAttributeMacro(double, T);
60 
61  /// Temporal unit used to determine actual number of integration steps.
62  /// If zero, the number of steps is independent of the temporal interval.
63  mirtkPublicAttributeMacro(double, TimeUnit);
64 
65  /// Number of integration steps per _TimeUnit
66  mirtkPublicAttributeMacro(int, NumberOfSteps);
67 
68  /// Maximum absolute value of scaled velocity when using the
69  /// scaling-and-squaring for computing the displacement field.
70  /// This introduces an adapative choice of the number of integration steps
71  /// needed. At a minimum, NumberOfSteps are taken, but if needed, more
72  /// steps added. If this attribute is set to zero, however, the number
73  /// of integration steps always corresponds to the specified NumberOfSteps.
74  mirtkPublicAttributeMacro(double, MaxScaledVelocity);
75 
76  /// Integration method used to obtain displacement from velocities
77  mirtkPublicAttributeMacro(FFDIntegrationMethod, IntegrationMethod);
78 
79  /// Whether to evaluate BCH formulat for parametric gradient calculation
80  /// on dense image lattice of non-parametric gradient using linear interpolation
81  mirtkPublicAttributeMacro(bool, UseDenseBCHGrid);
82 
83  /// Use Lie derivative definition of Lie bracket based on vector field
84  /// Jacobian matrices instead of difference of vector field composition
85  mirtkPublicAttributeMacro(bool, LieDerivative);
86 
87  /// Number of BCH terms used by ParametricGradient. When set to an invalid
88  /// value, i.e., no. of terms < 2, the gradient is computed using the specified
89  /// integration method instead.
90  mirtkPublicAttributeMacro(int, NumberOfBCHTerms);
91 
92  // ---------------------------------------------------------------------------
93  // Construction/Destruction
94 
95 protected:
96 
97  /// Initialize extrapolation function
99 
100 public:
101 
102  /// Default constructor
104 
105  /// Construct free-form transformation for given image domain and lattice spacing
107  double = -1, double = -1, double = -1);
108 
109  /// Construct free-form transformation for given target image and lattice spacing
111  double, double, double);
112 
113  /// Construct free-form transformation from existing 3D+t deformation field
114  explicit BSplineFreeFormTransformationSV(const GenericImage<double> &, bool = false);
115 
116  /// Copy constructor
118 
119  /// Destructor
121 
122  // Import other Initialize overloads
124 
125  /// Initialize free-form transformation constructed by default constructor
126  virtual void Initialize(const ImageAttributes &);
127 
128  /// Initialize free-form transformation which approximates a given transformation
129  virtual void Initialize(const ImageAttributes &, double, double, double,
130  const Transformation *);
131 
132  /// Subdivide FFD lattice
133  virtual void Subdivide(bool = true, bool = true, bool = true, bool = true);
134 
135  // ---------------------------------------------------------------------------
136  // Approximation/Interpolation
137 
139 
140  /// Approximate displacements: This function takes a set of points and a set
141  /// of displacements and finds !new! parameters such that the resulting
142  /// transformation approximates the displacements as good as possible.
143  virtual void ApproximateDOFs(const double *, const double *, const double *, const double *,
144  const double *, const double *, const double *, int);
145 
146  /// Finds gradient of approximation error: This function takes a set of points
147  /// and a set of errors. It finds a gradient w.r.t. the transformation parameters
148  /// which minimizes the L2 norm of the approximation error and adds it to the
149  /// input gradient with the given weight.
150  virtual void ApproximateDOFsGradient(const double *, const double *, const double *, const double *,
151  const double *, const double *, const double *, int,
152  double *, double = 1.0) const;
153 
154  /// Get image domain on which this free-form transformation should be defined
155  /// in order to reduce the error when the given transformation is approximated
156  /// and applied to resample an image on the specified image grid.
158  const Transformation *);
159 
160  /// Approximate another transformation and return approximation error
161  virtual double ApproximateAsNew(const ImageAttributes &, const Transformation *, int = 1, double = .0);
162 
163  /// Approximate displacements: This function takes a 3D displacement field
164  /// and finds a stationary velocity field which, when exponentiated, approximates
165  /// these displacements by computing the group logarithm of diffeomorphisms.
166  /// The displacements are replaced by the residual displacements of the newly
167  /// approximated transformation. Returns the approximation error of the resulting FFD.
168  virtual double ApproximateAsNew(GenericImage<double> &, int = 1, double = .0);
169 
170  /// Approximate displacements: This function takes a 3D displacement field
171  /// and finds a stationary velocity field which, when exponentiated, approximates
172  /// these displacements by computing the group logarithm of diffeomorphisms.
173  /// The displacements are replaced by the residual displacements of the newly
174  /// approximated transformation. Returns the approximation error of the resulting FFD.
175  virtual double ApproximateAsNew(GenericImage<double> &, bool, int = 3, int = 8);
176 
177  /// Interpolates displacements: This function takes a set of displacements defined at
178  /// the control points and finds a stationary velocity field such that the
179  /// resulting transformation interpolates these displacements.
180  virtual void Interpolate(const double *, const double *, const double *);
181 
182  /// Interpolates velocities: This function takes a set of velocites defined at
183  /// the control points and interpolates these velocities.
184  virtual void InterpolateVelocities(const double *, const double *, const double *);
185 
186  /// Approximate velocities: This function takes a velocity field and initializes
187  /// the cubic B-spline coefficients of this transformation such that the spline
188  /// function approximates the given velocity field. Returns the approximation error
189  /// of the velocity spline function.
191 
192  /// Combine transformations: This function takes a transformation and finds
193  /// a stationary velocity field which, when exponentiated, approximates the
194  /// composition of this and the given transformation.
195  virtual void CombineWith(const Transformation *);
196 
197  /// Combine transformations: This function takes a transformation and finds
198  /// a stationary velocity field which, when exponentiated, approximates the
199  /// composition of this and the given transformation.
200  virtual void CombineWith(const BSplineFreeFormTransformationSV *);
201 
202  /// Invert this transformation
203  virtual void Invert();
204 
205  // ---------------------------------------------------------------------------
206  // Parameters (non-DoFs)
207 
209 
210  /// Set named (non-DoF) parameter from value as string
211  virtual bool Set(const char *, const char *);
212 
213  /// Get (non-DoF) parameters as key/value as string map
214  virtual ParameterList Parameter() const;
215 
216  // ---------------------------------------------------------------------------
217  // Evaluation (for use FreeFormTransformationRungeKutta integration)
218 
222 
223  /// Evaluates the FFD at a point in lattice coordinates
224  void Evaluate(double &, double &, double &, double) const;
225 
226  /// Calculates the Jacobian of the FFD at a point in lattice coordinates
227  /// and converts the resulting Jacobian to derivatives w.r.t world coordinates
228  void EvaluateJacobianWorld(Matrix &, double, double, double, double) const;
229 
230  /// Calculates the Jacobian of the 2D transformation w.r.t. the transformation parameters
232  double, double) const;
233 
234  /// Calculates the Jacobian of the 3D transformation w.r.t. the transformation parameters
236  double, double, double) const;
237 
238  /// Calculates the Jacobian of the transformation w.r.t. the transformation parameters
240  double, double, double, double) const;
241 
242  // ---------------------------------------------------------------------------
243  // Point transformation
244 
245 public:
246 
248 
249 protected:
250 
251  /// Get number of integration steps for a given temporal integration interval
252  int NumberOfStepsForIntervalLength(double) const;
253 
254  /// Set number of integration steps for a given temporal integration interval
255  void NumberOfStepsForIntervalLength(double, int) const;
256 
257  /// Get length of steps for a given temporal integration interval
258  double StepLengthForIntervalLength(double) const;
259 
260 public:
261 
262  /// Scale velocities
263  ///
264  /// Alternatively, change integration inveral _T.
265  void ScaleVelocities(double);
266 
267  /// Upper integration limit used given the temporal origin of both target
268  /// and source images. If both images have the same temporal origin, the value
269  /// of the member variable @c T is returned. Note that @c T may be set to zero
270  /// in order to force no displacement between images located at the same point
271  /// in time to be zero. This is especially useful when animating the
272  /// deformation between two images with successively increasing upper
273  /// integration limit. Otherwise, if the temporal origin of the two images
274  /// differs, the signed difference between these is returned, i.e., t - t0,
275  /// which corresponds to a forward integration of the target point (x, y, z)
276  /// to the time point of the source image.
277  double UpperIntegrationLimit(double t, double t0) const;
278 
279  /// Transforms a single point using a Runge-Kutta integration
280  ///
281  /// \param[in] T Upper integration limit, i.e., length of time interval.
282  void IntegrateVelocities(double &, double &, double &, double T = 1.0) const;
283 
284  /// Compute displacement field using the scaling and squaring (SS) method
285  ///
286  /// \attention If an input/output displacement field is provided, the resulting
287  /// transformation is the composition exp(v) o d.
288  ///
289  /// \attention The scaling and squaring cannot be used to obtain the displacements
290  /// generated by a 3D velocity field on a 2D slice only. It always
291  /// must be applied to the entire domain of the velocity field.
292  ///
293  /// \param[in,out] d Displacement field generated by stationary velocity field.
294  /// \param[in] T Upper integration limit, i.e., length of time interval.
295  /// \param[in] wc Pre-computed world coordinates of image voxels.
296  template <class VoxelType>
298  double T = 1.0, const WorldCoordsImage *wc = NULL) const;
299 
300  /// Compute displacement field and derivatives using the scaling and squaring (SS) method
301  ///
302  /// \attention If an input/output displacement field is provided, the resulting
303  /// transformation is the composition exp(v) o d.
304  ///
305  /// \attention The scaling and squaring cannot be used to obtain the displacements
306  /// generated by a 3D velocity field on a 2D slice only. It always
307  /// must be applied to the entire domain of the velocity field.
308  ///
309  /// \param[in] attr Attributes of output images.
310  /// \param[in,out] d Displacement field generated by stationary velocity field.
311  /// \param[out] dx Partial derivatives of corresponding transformation w.r.t. x.
312  /// \param[out] dj Determinant of Jacobian w.r.t. x, i.e., det(jac(\p dx)).
313  /// \param[out] lj Log of determinant of Jacobian w.r.t. x, i.e., log(det(jac(\p dx))).
314  /// \param[in] T Upper integration limit, i.e., length of time interval.
315  /// \param[in] wc Pre-computed world coordinates of image voxels.
316  template <class VoxelType>
317  void ScalingAndSquaring(const ImageAttributes &attr,
320  GenericImage<VoxelType> *dj = NULL,
321  GenericImage<VoxelType> *lj = NULL,
322  double T = 1.0, const WorldCoordsImage *wc = NULL) const;
323 
324  /// Whether the caching of the transformation displacements is required
325  /// (or preferred) by this transformation. For some transformations such as
326  /// those parameterized by velocities, caching of the displacements for
327  /// each target voxel results in better performance or is needed for example
328  /// for the scaling and squaring method.
329  virtual bool RequiresCachingOfDisplacements() const;
330 
331  /// Transforms a single point using the local transformation component only
332  virtual void LocalTransform(double &, double &, double &, double = 0, double = NaN) const;
333 
334  /// Transforms a single point using the inverse of the local transformation only
335  virtual bool LocalInverse(double &, double &, double &, double = 0, double = NaN) const;
336 
337  /// Get stationary velocity field
338  void Velocity(GenericImage<float> &) const;
339 
340  /// Get stationary velocity field
341  void Velocity(GenericImage<double> &) const;
342 
343  /// Calculates the displacement vectors for a whole image domain
344  ///
345  /// \attention The displacements are computed at the positions after applying the
346  /// current displacements at each voxel. These displacements are then
347  /// added to the current displacements. Therefore, set the input
348  /// displacements to zero if only interested in the displacements of
349  /// this transformation at the voxel positions.
350  virtual void Displacement(GenericImage<double> &, double, double = NaN,
351  const WorldCoordsImage * = NULL) const;
352 
353  /// Calculates the displacement vectors for a whole image domain
354  ///
355  /// \attention The displacements are computed at the positions after applying the
356  /// current displacements at each voxel. These displacements are then
357  /// added to the current displacements. Therefore, set the input
358  /// displacements to zero if only interested in the displacements of
359  /// this transformation at the voxel positions.
360  virtual void Displacement(GenericImage<float> &, double, double = NaN,
361  const WorldCoordsImage * = NULL) const;
362 
363  /// Calculates the inverse displacement vectors for a whole image domain
364  ///
365  /// \attention The displacements are computed at the positions after applying the
366  /// current displacements at each voxel. These displacements are then
367  /// added to the current displacements. Therefore, set the input
368  /// displacements to zero if only interested in the displacements of
369  /// this transformation at the voxel positions.
370  ///
371  /// \returns Always zero.
372  virtual int InverseDisplacement(GenericImage<double> &, double, double = NaN,
373  const WorldCoordsImage * = NULL) const;
374 
375  /// Calculates the inverse displacement vectors for a whole image domain
376  ///
377  /// \attention The displacements are computed at the positions after applying the
378  /// current displacements at each voxel. These displacements are then
379  /// added to the current displacements. Therefore, set the input
380  /// displacements to zero if only interested in the displacements of
381  /// this transformation at the voxel positions.
382  ///
383  /// \returns Always zero.
384  virtual int InverseDisplacement(GenericImage<float> &, double, double = NaN,
385  const WorldCoordsImage * = NULL) const;
386 
387  // ---------------------------------------------------------------------------
388  // Derivatives
389 
392 
393  /// Calculates the Jacobian of the local transformation w.r.t world coordinates
394  virtual void LocalJacobian(Matrix &, double, double, double, double = 0, double = NaN) const;
395 
396  /// Calculates the Hessian for each component of the local transformation w.r.t world coordinates
397  virtual void LocalHessian(Matrix [3], double, double, double, double = 0, double = NaN) const;
398 
399  /// Calculates the Jacobian of the transformation w.r.t a control point
400  virtual void JacobianDOFs(Matrix &, int, double, double, double, double = 0, double = NaN) const;
401 
402  /// Calculates the Jacobian of the transformation w.r.t a transformation parameters
403  ///
404  /// \attention This overriden base class function returns one column of the 3x3 Jacobian
405  /// matrix, i.e., only the partial derivatives of the transformation
406  /// w.r.t. the specified transformation parameter (not control point).
407  /// This is in accordance to the Transformation::JacobianDOFs
408  /// definition, which is violated, however, by most other FFDs.
409  virtual void JacobianDOFs(double [3], int, double, double, double, double = 0, double = NaN) const;
410 
411  /// Calculates the Jacobian of the transformation w.r.t the transformation parameters
412  virtual void JacobianDOFs(TransformationJacobian &, double, double, double, double = 0, double = NaN) const;
413 
414  /// Calculates derivatives of the Jacobian determinant at world point w.r.t. DoFs of a control point
415  ///
416  /// \param[out] dJ Partial derivatives of Jacobian determinant at (x, y, z) w.r.t. DoFs of control point.
417  /// \param[in] cp Index of control point w.r.t. whose DoFs the derivatives are computed.
418  /// \param[in] x World coordinate along x axis at which to evaluate derivatives.
419  /// \param[in] y World coordinate along y axis at which to evaluate derivatives.
420  /// \param[in] z World coordinate along z axis at which to evaluate derivatives.
421  /// \param[in] adj Adjugate of Jacobian matrix evaluated at (x, y, z).
422  virtual void JacobianDetDerivative(double dJ[3], const Matrix &adj,
423  int cp, double x, double y, double z, double t = 0, double t0 = NaN,
424  bool wrt_world = true, bool use_spacing = true) const;
425 
426 protected:
427 
428  /// Evaluates the BCH formula s.t. u = log(exp(tau * v) o exp(eta * w))
429  /// \sa Bossa, M., Hernandez, M., & Olmos, S. Contributions to 3D diffeomorphic
430  /// atlas estimation: application to brain images. MICCAI 2007, 10(Pt 1), 667–74.
431  void EvaluateBCHFormula(int, CPImage &, double, const CPImage &, double, const CPImage &, bool = false) const;
432 
433  /// Applies the chain rule to convert spatial non-parametric gradient
434  /// to a gradient w.r.t the parameters of this transformation given the
435  /// temporal interval which the displacement gradient corresponds to
436  void ParametricGradient(const GenericImage<double> *, double *,
437  const WorldCoordsImage *,
438  const WorldCoordsImage *,
439  double, double, double) const;
440 
441 public:
442 
443  /// Applies the chain rule to convert spatial non-parametric gradient
444  /// to a gradient w.r.t the parameters of this transformation
445  virtual void ParametricGradient(const GenericImage<double> *, double *,
446  const WorldCoordsImage *,
447  const WorldCoordsImage *,
448  double = -1, double = 1) const;
449 
450  /// Applies the chain rule to convert point-wise non-parametric gradient
451  /// to a gradient w.r.t the parameters of this transformation.
452  virtual void ParametricGradient(const PointSet &, const Vector3D<double> *,
453  double *, double = 0, double = -1, double = 1) const;
454 
455  // ---------------------------------------------------------------------------
456  // I/O
457 
458  // Do not hide methods of base class
460 
461  /// Prints the parameters of the transformation
462  virtual void Print(ostream &, Indent = 0) const;
463 
464  /// Whether this transformation can read a file of specified type (i.e. format)
465  virtual bool CanRead(TransformationType) const;
466 
467 protected:
468 
469  /// Reads transformation parameters from a file stream
471 
472  /// Writes transformation parameters to a file stream
473  virtual Cofstream &WriteDOFs(Cofstream &) const;
474 
475  // ---------------------------------------------------------------------------
476  // Friends
477  friend class EvaluateBSplineSVFFD2D;
478  friend class EvaluateBSplineSVFFD3D;
481 };
482 
483 ////////////////////////////////////////////////////////////////////////////////
484 // Auxiliary voxel functions for subclasses
485 ////////////////////////////////////////////////////////////////////////////////
486 
487 // -----------------------------------------------------------------------------
488 /// Evaluate global SV FFD at image voxels of vector field
490 {
491 public:
492 
493  EvaluateGlobalSVFFD(const Matrix &logA, BaseImage *output)
494  :
495  _LogA(logA), _Output(output)
496  {}
497 
498 // template <class T>
499 // void Evaluate(Vector2D<T> &v, double x, double y, double z)
500 // {
501 // v._x = static_cast<T>(_LogA(0, 0) * x + _LogA(0, 1) * y + _LogA(0, 2) * z + _LogA(0, 3));
502 // v._y = static_cast<T>(_LogA(1, 0) * x + _LogA(1, 1) * y + _LogA(1, 2) * z + _LogA(1, 3));
503 // }
504 
505  template <class T>
506  void Evaluate(Vector3D<T> &v, double x, double y, double z)
507  {
508  v._x = static_cast<T>(_LogA(0, 0) * x + _LogA(0, 1) * y + _LogA(0, 2) * z + _LogA(0, 3));
509  v._y = static_cast<T>(_LogA(1, 0) * x + _LogA(1, 1) * y + _LogA(1, 2) * z + _LogA(1, 3));
510  v._z = static_cast<T>(_LogA(2, 0) * x + _LogA(2, 1) * y + _LogA(2, 2) * z + _LogA(2, 3));
511  }
512 
513  template <class VectorType>
514  void operator()(int i, int j, int k, int, VectorType *v)
515  {
516  double x = i, y = j, z = k;
517  _Output->ImageToWorld(x, y, z);
518  Evaluate(*v, x, y, z);
519  }
520 
521 private:
522  const Matrix _LogA; ///< Input transformation
523  BaseImage *_Output; ///< Output image
524 };
525 
526 // -----------------------------------------------------------------------------
527 /// Evaluate global SV FFD at image voxels of 3D vector field
529 {
530 public:
531 
532  EvaluateGlobalSVFFD3D(const Matrix &logA, BaseImage *output)
533  :
534  _LogA (logA),
535  _Output(output),
536  _y (output->NumberOfSpatialVoxels()),
537  _z (2 * _y)
538  {}
539 
540  template <class T>
541  void operator()(int i, int j, int k, int, T *out)
542  {
543  double x = i, y = j, z = k;
544  _Output->ImageToWorld(x, y, z);
545  out[_x] = static_cast<T>(_LogA(0, 0) * x + _LogA(0, 1) * y + _LogA(0, 2) * z + _LogA(0, 3));
546  out[_y] = static_cast<T>(_LogA(1, 0) * x + _LogA(1, 1) * y + _LogA(1, 2) * z + _LogA(1, 3));
547  out[_z] = static_cast<T>(_LogA(2, 0) * x + _LogA(2, 1) * y + _LogA(2, 2) * z + _LogA(2, 3));
548  }
549 
550 private:
551  const Matrix _LogA; ///< Input transformation
552  BaseImage *_Output; ///< Output image
553 
554  static const int _x = 0; ///< Offset of x component
555  int _y; ///< Offset of y component
556  int _z; ///< Offset of z component
557 };
558 
559 // -----------------------------------------------------------------------------
560 /// Evaluate B-spline SV FFD at image voxels
562 {
563 public:
564 
566  BaseImage *output)
567  :
568  _Input(input), _Output(output)
569  {}
570 
571 // template <class T>
572 // void Put(Vector2D<T> *v, double vx, double vy, double)
573 // {
574 // v->_x = vx, v->_y = vy;
575 // }
576 
577  template <class T>
578  void Put(Vector3D<T> *v, double vx, double vy, double vz)
579  {
580  v->_x = static_cast<T>(vx);
581  v->_y = static_cast<T>(vy);
582  v->_z = static_cast<T>(vz);
583  }
584 
585  template <class VectorType>
586  void operator()(int i, int j, int k, int, VectorType *v)
587  {
588  double vx = i, vy = j, vz = k;
589  _Output->ImageToWorld (vx, vy, vz);
590  _Input ->WorldToLattice(vx, vy, vz);
591  _Input ->Evaluate (vx, vy, vz);
592  Put(v, vx, vy, vz);
593  }
594 
595 protected:
596  const BSplineFreeFormTransformationSV *_Input; ///< Input transformation
597  BaseImage *_Output; ///< Output image
598 };
599 
600 // -----------------------------------------------------------------------------
601 /// Evaluate B-spline SV FFD at image voxels
603 {
604 public:
605 
607  :
608  _Input (input),
609  _Output(output),
610  _y (output->NumberOfSpatialVoxels()),
611  _z (2 * _y)
612  {}
613 
614  template <class T>
615  void operator()(int i, int j, int k, int, T *out)
616  {
617  double x = i, y = j, z = k;
618  _Output->ImageToWorld (x, y, z);
619  _Input ->WorldToLattice(x, y, z);
620  _Input ->Evaluate (x, y, z);
621  out[_x] = static_cast<T>(x);
622  out[_y] = static_cast<T>(y);
623  out[_z] = static_cast<T>(z);
624  }
625 
626 private:
627  const BSplineFreeFormTransformationSV *_Input; ///< Input transformation
628  BaseImage *_Output; ///< Output image
629 
630  static const int _x = 0; ///< Offset of x component
631  int _y; ///< Offset of y component
632  int _z; ///< Offset of z component
633 };
634 
635 // -----------------------------------------------------------------------------
636 /// Evaluate and add B-spline SV FFD at image voxels
638 {
639 public:
640 
642  BaseImage *output)
643  :
644  _Input(input), _Output(output)
645  {}
646 
647 // template <class T>
648 // void Add(Vector2D<T> *v, double vx, double vy, double)
649 // {
650 // v->_x += static_cast<T>(vx);
651 // v->_y += static_cast<T>(vy);
652 // }
653 
654  template <class T>
655  void Add(Vector3D<T> *v, double vx, double vy, double vz)
656  {
657  v->_x += static_cast<T>(vx);
658  v->_y += static_cast<T>(vy);
659  v->_z += static_cast<T>(vz);
660  }
661 
662  template <class VectorType>
663  void operator()(int i, int j, int k, int, VectorType *v)
664  {
665  double vx = i, vy = j, vz = k;
666  _Output->ImageToWorld (vx, vy, vz);
667  _Input ->WorldToLattice(vx, vy, vz);
668  _Input ->Evaluate (vx, vy, vz);
669  Add(v, vx, vy, vz);
670  }
671 
672 protected:
673  const BSplineFreeFormTransformationSV *_Input; ///< Input transformation
674  BaseImage *_Output; ///< Output image
675 };
676 
677 // -----------------------------------------------------------------------------
678 /// Evaluate and add 3D B-spline SV FFD at image voxels
680 {
681 public:
682 
684  :
685  _Input (input),
686  _Output(output),
687  _y (output->NumberOfSpatialVoxels()),
688  _z (2 * _y)
689  {}
690 
691  template <class T>
692  void operator()(int i, int j, int k, int, T *out)
693  {
694  double x = i, y = j, z = k;
695  _Output->ImageToWorld (x, y, z);
696  _Input ->WorldToLattice(x, y, z);
697  _Input ->Evaluate (x, y, z);
698  out[_x] += static_cast<T>(x);
699  out[_y] += static_cast<T>(y);
700  out[_z] += static_cast<T>(z);
701  }
702 
703 private:
704  const BSplineFreeFormTransformationSV *_Input; ///< Input transformation
705  BaseImage *_Output; ///< Output image
706 
707  static const int _x = 0; ///< Offset of x component
708  int _y; ///< Offset of y component
709  int _z; ///< Offset of z component
710 };
711 
712 ////////////////////////////////////////////////////////////////////////////////
713 // Inline definitions
714 ////////////////////////////////////////////////////////////////////////////////
715 
716 // =============================================================================
717 // Evaluation
718 // =============================================================================
719 
720 // -----------------------------------------------------------------------------
721 inline void BSplineFreeFormTransformationSV::Evaluate(double &x, double &y, double &z, double) const
722 {
724 }
725 
726 // -----------------------------------------------------------------------------
727 inline void BSplineFreeFormTransformationSV::EvaluateJacobianWorld(Matrix &jac, double x, double y, double z, double) const
728 {
731 }
732 
733 // -----------------------------------------------------------------------------
735 ::EvaluateJacobianDOFs(TransformationJacobian &jac, double x, double y, double z, double) const
736 {
737  if (_z == 1) EvaluateJacobianDOFs(jac, x, y);
738  else EvaluateJacobianDOFs(jac, x, y, z);
739 }
740 
741 // -----------------------------------------------------------------------------
743 {
744  if (v.T() != 3) {
745  Throw(ERR_InvalidArgument, __FUNCTION__, "Output image must have 3 channels!");
746  }
747  v.PutTSize(0.);
748  ParallelForEachVoxel(EvaluateBSplineSVFFD3D(this, &v), v.Attributes(), v);
749 }
750 
751 // -----------------------------------------------------------------------------
753 {
754  if (v.T() != 3) {
755  Throw(ERR_InvalidArgument, __FUNCTION__, "Output image must have 3 channels!");
756  }
757  v.PutTSize(0.);
758  ParallelForEachVoxel(EvaluateBSplineSVFFD3D(this, &v), v.Attributes(), v);
759 }
760 
761 // =============================================================================
762 // Point transformation
763 // =============================================================================
764 
765 // -----------------------------------------------------------------------------
767 {
768  return (_IntegrationMethod == FFDIM_SS || _IntegrationMethod == FFDIM_FastSS);
769 }
770 
771 // -----------------------------------------------------------------------------
772 inline double BSplineFreeFormTransformationSV::UpperIntegrationLimit(double t, double t0) const
773 {
774  double T = t - t0;
775  return !IsNaN(T) && !AreEqual(T, 0.) ? T : _T; // if zero/NaN, return cross-sectional interval _T instead
776 }
777 
778 // -----------------------------------------------------------------------------
780 {
781  return (_TimeUnit > .0) ? static_cast<int>((abs(T) / _TimeUnit) * _NumberOfSteps + 0.5) : _NumberOfSteps;
782 }
783 
784 // -----------------------------------------------------------------------------
786 {
787  if (_TimeUnit > .0) nsteps = static_cast<int>((_TimeUnit / abs(T)) * nsteps + 0.5);
788  const_cast<BSplineFreeFormTransformationSV *>(this)->_NumberOfSteps = nsteps;
789 }
790 
791 // -----------------------------------------------------------------------------
793 {
794  if (_NumberOfSteps == 0) return .0;
795  return ((_TimeUnit > .0) ? (_TimeUnit / static_cast<double>(_NumberOfSteps))
796  : ( abs(T) / static_cast<double>(_NumberOfSteps)));
797 }
798 
799 // =============================================================================
800 // Derivatives
801 // =============================================================================
802 
803 // -----------------------------------------------------------------------------
806  const WorldCoordsImage *i2w, const WorldCoordsImage *wc,
807  double t0, double w) const
808 {
809  this->ParametricGradient(in, out, i2w, wc, in->GetTOrigin(), t0, w);
810 }
811 
812 
813 } // namespace mirtk
814 
815 #endif // MIRTK_BSplineFreeFormTransformationSV_H
virtual bool Set(const char *, const char *)
Set named (non-DoF) parameter from value as string.
virtual void ParametricGradient(const GenericImage< double > *, double *, const WorldCoordsImage *, const WorldCoordsImage *, double=NaN, double=1) const
to a gradient w.r.t the parameters of this transformation
virtual void Initialize(const ImageAttributes &)
Initialize free-form transformation constructed by default constructor.
void IntegrateVelocities(double &, double &, double &, double T=1.0) const
double StepLengthForIntervalLength(double) const
Get length of steps for a given temporal integration interval.
Evaluate and add 3D B-spline SV FFD at image voxels.
virtual Cofstream & WriteDOFs(Cofstream &) const
Writes transformation parameters to a file stream.
virtual void JacobianDetDerivative(double dJ[3], const Matrix &adj, int cp, double x, double y, double z, double t=0, double t0=NaN, bool wrt_world=true, bool use_spacing=true) const
virtual void JacobianDOFs(Matrix &, int, double, double, double, double=0, double=NaN) const
Calculates the Jacobian of the transformation w.r.t a control point.
const ImageAttributes & Attributes() const
Gets the image attributes.
Definition: BaseImage.h:856
void EvaluateJacobianWorld(Matrix &, double, double) const
virtual void Subdivide(bool=true, bool=true, bool=true, bool=true)
Subdivide FFD lattice.
void Velocity(GenericImage< float > &) const
Get stationary velocity field.
virtual void Displacement(GenericImage< double > &, double, double=NaN, const WorldCoordsImage *=NULL) const
virtual bool CanRead(TransformationType) const
Whether this transformation can read a file of specified type (i.e. format)
void EvaluateJacobianDOFs(TransformationJacobian &, double, double) const
Calculates the Jacobian of the 2D transformation w.r.t. the transformation parameters.
MIRTKCU_API bool IsNaN(double x)
Check if floating point value is not a number (NaN)
Definition: Math.h:91
const int & _x
Read-only reference to _x attribute of _CPImage.
virtual void Interpolate(const double *, const double *, const double *)
Evaluate global SV FFD at image voxels of vector field.
int T() const
Returns the number of control points in t.
Array< Pair< string, string > > ParameterList
Ordered list of parameter name/value pairs.
Definition: Object.h:38
virtual ParameterList Parameter() const
Get (non-DoF) parameters as key/value as string map.
virtual void CombineWith(const Transformation *)
virtual ~BSplineFreeFormTransformationSV()
Destructor.
virtual void Invert()
Invert this transformation.
virtual bool LocalInverse(double &, double &, double &, double=0, double=NaN) const
Transforms a single point using the inverse of the local transformation only.
virtual double ApproximateAsNew(const Transformation *, int=1, double=.0)
Approximate another transformation and return approximation error.
const BSplineFreeFormTransformationSV * _Input
Input transformation.
virtual void Add(const DOFValue *)
Add change to transformation parameters.
Definition: IOConfig.h:41
Evaluate B-spline SV FFD at image voxels.
double UpperIntegrationLimit(double t, double t0) const
void InitializeExtrapolator()
Initialize extrapolation function.
const BSplineFreeFormTransformationSV * _Input
Input transformation.
virtual int InverseDisplacement(GenericImage< double > &, double, double=NaN, const WorldCoordsImage *=NULL) const
void ParametricGradient(const GenericImage< double > *, double *, const WorldCoordsImage *, const WorldCoordsImage *, double, double, double) const
double GetTOrigin() const
Get temporal origin.
Definition: BaseImage.h:1082
virtual void LocalTransform(double &, double &, double &, double=0, double=NaN) const
Transforms a single point using the local transformation component only.
virtual void InterpolateVelocities(const double *, const double *, const double *)
virtual void Displacement(double &, double &, double &, double=0, double=NaN) const
Calculates the displacement of a single point.
void EvaluateJacobianWorld(Matrix &, double, double, double, double) const
virtual double ApproximateAsNew(const ImageAttributes &, const Transformation *, int=1, double=.0)
Approximate another transformation and return approximation error.
virtual void LocalHessian(Matrix [3], double, double, double, double=0, double=NaN) const
Calculates the Hessian for each component of the local transformation w.r.t world coordinates...
virtual ImageAttributes ApproximationDomain(const ImageAttributes &, const Transformation *)
FFDIntegrationMethod
Enumeration of implemented numerical integration methods.
virtual ParameterList Parameter() const
Get (non-DoF) parameters as key/value as string map.
Evaluate global SV FFD at image voxels of 3D vector field.
const int & _z
Read-only reference to _z attribute of _CPImage.
virtual void ApproximateDOFs(const double *, const double *, const double *, const double *, const double *, const double *, const double *, int)
void Throw(ErrorType err, const char *func, Args... args) const
Definition: Object.h:166
void Evaluate(double &, double &, double &, int, int) const
Evaluates the FFD at a lattice point.
MIRTKCU_API bool AreEqual(double a, double b, double tol=1e-12)
Determine equality of two floating point numbers.
Definition: Math.h:115
virtual void ApproximateDOFsGradient(const double *, const double *, const double *, const double *, const double *, const double *, const double *, int, double *, double=1.0) const
void PutTSize(double)
Set temporal voxel size, i.e., to zero for vector field and non-zero for temporal sequence...
Definition: BaseImage.h:976
T _z
The z component.
Definition: Vector3D.h:60
virtual void LocalJacobian(Matrix &, double, double, double, double=0, double=NaN) const
Calculates the Jacobian of the local transformation w.r.t world coordinates.
BSplineFreeFormTransformationSV()
Default constructor.
virtual double ApproximateVelocitiesAsNew(GenericImage< double > &)
T _y
The y component.
Definition: Vector3D.h:59
virtual void Print(ostream &, Indent=0) const
Prints the parameters of the transformation.
int T() const
Returns the number of voxels in the t-direction.
Definition: BaseImage.h:892
void Put(int, const Vector &)
Puts values of the parameters at a control point.
void ScalingAndSquaring(GenericImage< VoxelType > *d, double T=1.0, const WorldCoordsImage *wc=NULL) const
virtual Cifstream & ReadDOFs(Cifstream &, TransformationType)
Reads transformation parameters from a file stream.
T _x
The x component.
Definition: Vector3D.h:58
int NumberOfSpatialVoxels() const
Returns the total number of spatial voxels.
Definition: BaseImage.h:868
void EvaluateJacobianDOFs(double [3], int, int, double, double) const
virtual void Initialize(const ImageAttributes &)
Initialize free-form transformation.
void EvaluateBCHFormula(int, CPImage &, double, const CPImage &, double, const CPImage &, bool=false) const
const int & _y
Read-only reference to _y attribute of _CPImage.
void Evaluate(double &, double &, double &, double) const
Evaluates the FFD at a point in lattice coordinates.
virtual void Print(ostream &, Indent=0) const
Prints the parameters of the transformation.
virtual void JacobianDOFs(double [3], int, int, int, double, double, double) const
Calculates the Jacobian of the transformation w.r.t. the parameters of a control point.
int NumberOfStepsForIntervalLength(double) const
Get number of integration steps for a given temporal integration interval.
Evaluate B-spline SV FFD at image voxels.
Evaluate and add B-spline SV FFD at image voxels.