FluidFreeFormTransformation.h
1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2008-2017 Imperial College London
5  * Copyright 2008-2013 Daniel Rueckert, Julia Schnabel
6  * Copyright 2013-2017 Andreas Schuh
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 #ifndef MIRTK_FluidFreeFormTransformation_H
22 #define MIRTK_FluidFreeFormTransformation_H
23 
24 #include "mirtk/MultiLevelTransformation.h"
25 
26 #include "mirtk/Memory.h" // swap
27 
28 
29 namespace mirtk {
30 
31 
32 /**
33  * Class for multi-level FFD where global and local transformations are composed.
34  *
35  * T_mffd(x) = T_affine ° T_local^N ° T_local^N-1 ° ... ° T_local^1 ° T_global(x)
36  *
37  * where T_global is the initial global affine transformation, T_local are
38  * the non-rigid free-form deformations (FFD), and T_affine is an optional
39  * affine transformation which is applied after the deformations.
40  */
42 {
43  mirtkTransformationMacro(FluidFreeFormTransformation);
44 
45  // ---------------------------------------------------------------------------
46  // Attributes
47 
48 protected:
49 
50  /// Affine transformation applied after the local free-form deformations.
52 
53  // ---------------------------------------------------------------------------
54  // Construction/Destruction
55 
56 public:
57 
58  /// Default constructor
60 
61  /// Construct multi-level transformation given a rigid transformation
63 
64  /// Construct multi-level transformation given an affine transformation
66 
67  /// Copy constructor
69 
70  /// Destructor
72 
73  // ---------------------------------------------------------------------------
74  // Levels
75 
76  /// Compose current transformation with another
77  virtual void PushTransformation(const Transformation *, ImageAttributes *attr = nullptr);
78 
79  /// Combine local transformations on stack
80  virtual void CombineLocalTransformation();
81 
82  /// Convert the global transformation from a matrix representation to a
83  /// FFD and incorporate it with any existing local transformation
84  virtual void MergeGlobalIntoLocalDisplacement();
85 
86  /// Get affine transformation applied after the local free-form deformations
88 
89  /// Get affine transformation applied after the local free-form deformations
91 
92 protected:
93 
94  /// Checks whether a given transformation is supported as local transformation
95  virtual void CheckTransformation(FreeFormTransformation *) const;
96 
97 public:
98 
99  // ---------------------------------------------------------------------------
100  // Bounding box
101 
102  /// Gets the spatial bounding box for a transformation parameter in image coordinates.
103  /// The last parameter specifies what fraction of the bounding box to return.
104  /// The default is 1 which equals 100% of the bounding box.
105  virtual bool DOFBoundingBox(const Image *, int, int &, int &, int &,
106  int &, int &, int &, double = 1) const;
107 
108  // ---------------------------------------------------------------------------
109  // Approximation
110 
111  /// Approximate displacements: This function takes a set of points and a set
112  /// of displacements and finds a transformation which approximates these
113  /// displacements. After approximation, the displacements are replaced by the
114  /// residual displacement errors at the points.
115  ///
116  /// \note This function does not change the global transformation and the
117  /// initial passive local levels of this multi-level FFD. Use
118  /// ApproximateAsNew to also approximate a new global transformation.
119  virtual double Approximate(const ImageAttributes &, double *, double *, double *,
120  int = 1, double = .0);
121 
122  /// Approximate displacements: This function takes a set of points and a set
123  /// of displacements and finds a transformation which approximates these
124  /// displacements. After approximation, the displacements are replaced by the
125  /// residual displacement errors at the points.
126  ///
127  /// \note This function does not change the global transformation and the
128  /// initial passive local levels of this multi-level FFD. Use
129  /// ApproximateAsNew to also approximate a new global transformation.
130  virtual double Approximate(const double *, const double *, const double *,
131  double *, double *, double *, int,
132  int = 1, double = .0);
133 
134  /// Approximate displacements: This function takes a set of points and a set
135  /// of displacements and finds a transformation which approximates these
136  /// displacements. After approximation, the displacements are replaced by the
137  /// residual displacement errors at the points.
138  ///
139  /// \note This function does not change the global transformation and the
140  /// initial passive local levels of this multi-level FFD. Use
141  /// ApproximateAsNew to also approximate a new global transformation.
142  virtual double Approximate(const double *, const double *, const double *, const double *,
143  double *, double *, double *, int,
144  int = 1, double = .0);
145 
146  /// Approximate displacements: This function takes a set of points and a set
147  /// of displacements and finds a !new! transformation which approximates these
148  /// displacements. After approximation, the displacements are replaced by the
149  /// residual displacement errors at the points.
150  ///
151  /// \note This function also modifies the global transformation.
152  /// Use Reset and Approximate instead if this is not desired.
153  virtual double ApproximateAsNew(const ImageAttributes &, double *, double *, double *,
154  int = 1, double = .0);
155 
156  /// Approximate displacements: This function takes a set of points and a set
157  /// of displacements and finds a !new! transformation which approximates these
158  /// displacements. After approximation, the displacements are replaced by the
159  /// residual displacement errors at the points.
160  ///
161  /// \note This function also modifies the global transformation.
162  /// Use Reset and Approximate instead if this is not desired.
163  virtual double ApproximateAsNew(const double *, const double *, const double *,
164  double *, double *, double *, int,
165  int = 1, double = .0);
166 
167  /// Approximate displacements: This function takes a set of points and a set
168  /// of displacements and finds a !new! transformation which approximates these
169  /// displacements. After approximation, the displacements are replaced by the
170  /// residual displacement errors at the points.
171  ///
172  /// \note This function also modifies the global transformation.
173  /// Use Reset and Approximate instead if this is not desired.
174  virtual double ApproximateAsNew(const double *, const double *, const double *, const double *,
175  double *, double *, double *, int,
176  int = 1, double = .0);
177 
178  /// Approximate displacements: This function takes a set of points and a set
179  /// of displacements and finds !new! parameters such that the resulting
180  /// transformation approximates the displacements as good as possible.
181  virtual void ApproximateDOFs(const double *, const double *, const double *, const double *,
182  const double *, const double *, const double *, int);
183 
184  /// Finds gradient of approximation error: This function takes a set of points
185  /// and a set of errors. It finds a gradient w.r.t. the transformation parameters
186  /// which minimizes the L2 norm of the approximation error and adds it to the
187  /// input gradient with the given weight.
188  virtual void ApproximateDOFsGradient(const double *, const double *, const double *, const double *,
189  const double *, const double *, const double *, int,
190  double *, double = 1.0) const;
191 
192  // ---------------------------------------------------------------------------
193  // Transformation parameters (DoFs)
194 
195  /// Copy active transformation parameters (DoFs) from given
196  /// transformation if possible and return \c false, otherwise
197  virtual bool CopyFrom(const Transformation *);
198 
199  /// Checks whether transformation is an identity mapping
200  virtual bool IsIdentity() const;
201 
202  /// Reset transformation (does not remove local transformations)
203  virtual void Reset();
204 
205  /// Reset transformation and remove all local transformations
206  virtual void Clear();
207 
208  // ---------------------------------------------------------------------------
209  // Point transformation
210 
215 
216  /// Transforms a single point
217  virtual void Transform(int, int, double &, double &, double &, double = 0, double = -1) const;
218 
219  /// Transforms a single point using the inverse of the transformation
220  virtual bool Inverse(int, int, double &, double &, double &, double = 0, double = -1) const;
221 
222  /// Calculates the displacement vectors for a whole image domain
223  ///
224  /// \attention The displacements are computed at the positions after applying the
225  /// current displacements at each voxel. These displacements are then
226  /// added to the current displacements. Therefore, set the input
227  /// displacements to zero if only interested in the displacements of
228  /// this transformation at the voxel positions.
229  virtual void Displacement(int, int, GenericImage<double> &, double, double = -1, const WorldCoordsImage * = NULL) const;
230 
231  /// Calculates the displacement vectors for a whole image domain
232  ///
233  /// \attention The displacements are computed at the positions after applying the
234  /// current displacements at each voxel. These displacements are then
235  /// added to the current displacements. Therefore, set the input
236  /// displacements to zero if only interested in the displacements of
237  /// this transformation at the voxel positions.
238  virtual void Displacement(int, int, GenericImage<float> &, double, double = -1, const WorldCoordsImage * = NULL) const;
239 
240  /// Calculates the inverse displacement vectors for a whole image domain
241  ///
242  /// \attention The displacements are computed at the positions after applying the
243  /// current displacements at each voxel. These displacements are then
244  /// added to the current displacements. Therefore, set the input
245  /// displacements to zero if only interested in the displacements of
246  /// this transformation at the voxel positions.
247  virtual int InverseDisplacement(int, int, GenericImage<double> &, double, double = -1, const WorldCoordsImage * = NULL) const;
248 
249  /// Calculates the inverse displacement vectors for a whole image domain
250  ///
251  /// \attention The displacements are computed at the positions after applying the
252  /// current displacements at each voxel. These displacements are then
253  /// added to the current displacements. Therefore, set the input
254  /// displacements to zero if only interested in the displacements of
255  /// this transformation at the voxel positions.
256  virtual int InverseDisplacement(int, int, GenericImage<float> &, double, double = -1, const WorldCoordsImage * = NULL) const;
257 
258  /// Applies the chain rule to convert spatial non-parametric gradient
259  /// to a gradient w.r.t the parameters of this transformation.
260  ///
261  /// If the transformation itself is non-parametric, the gradient will be passed through
262  /// unchanged. The default implementation uses the full Jacobian matrix computed for each
263  /// DoF separately (i.e., calls JacobianDOFs for each DoF).
264  ///
265  /// For 4D transformations, the temporal coordinate t used for the computation of the Jacobian
266  /// of the transformation w.r.t the transformation parameters for all spatial (x, y, z) voxel
267  /// coordinates in world units, is assumed to correspond to the temporal origin of the given
268  /// gradient image. For 4D transformations parameterized by velocities, a second time for the
269  /// upper integration bound can be provided as last argument to this method. This last
270  /// argument is ignored by transformations parameterized by displacements.
271  ///
272  /// \sa ImageSimilarityMetric::EvaluateGradient
273  virtual void ParametricGradient(const GenericImage<double> *, double *,
274  const WorldCoordsImage * = NULL,
275  const WorldCoordsImage * = NULL,
276  double = -1, double = 1) const;
277 
278  /// Applies the chain rule to convert point-wise non-parametric gradient
279  /// to a gradient w.r.t the parameters of this transformation.
280  virtual void ParametricGradient(const PointSet &, const Vector3D<double> *,
281  double *, double = 0, double = -1, double = 1) const;
282 
283  // ---------------------------------------------------------------------------
284  // Derivatives
285 
288 
289  /// Calculates the Jacobian of the transformation w.r.t world coordinates
290  virtual void Jacobian(int, int, Matrix &, double, double, double, double = 0, double = -1) const;
291 
292  /// Calculates the Hessian of the transformation w.r.t world coordinates
293  virtual void Hessian(int, int, Matrix [3], double, double, double, double = 0, double = -1) const;
294 
295  // ---------------------------------------------------------------------------
296  // I/O
297 
298  // Do not hide methods of base class
300 
301  /// Prints the parameters of the transformation
302  virtual void Print(ostream &, Indent = 0) const;
303 
304  /// Whether this transformation can read a file of specified type (i.e. format)
305  virtual bool CanRead(TransformationType) const;
306 
307 protected:
308 
309  /// Reads transformation parameters from a file stream
311 
312  /// Writes transformation parameters to a file stream
313  virtual Cofstream &WriteDOFs(Cofstream &) const;
314 
315 };
316 
317 ////////////////////////////////////////////////////////////////////////////////
318 // Inline definitions
319 ////////////////////////////////////////////////////////////////////////////////
320 
321 // =============================================================================
322 // Levels
323 // =============================================================================
324 
325 // -----------------------------------------------------------------------------
327 {
328  return &_AffineTransformation;
329 }
330 
331 // -----------------------------------------------------------------------------
333 {
334  return &_AffineTransformation;
335 }
336 
337 // =============================================================================
338 // Bounding box
339 // =============================================================================
340 
341 // -----------------------------------------------------------------------------
342 inline bool FluidFreeFormTransformation
343 ::DOFBoundingBox(const Image *image, int dof, int &i1, int &j1, int &k1,
344  int &i2, int &j2, int &k2, double fraction) const
345 {
346  // Get local transformation and corresponding parameter index
347  const FreeFormTransformation *ffd;
348  const int n = DOFIndexToLocalTransformation(this, dof, ffd, dof);
349 
350  // Calculate bounding box in world coordinates
351  double x1, y1, z1, x2, y2, z2;
352  ffd->BoundingBox(ffd->DOFToIndex(dof), x1, y1, z1, x2, y2, z2, fraction);
353 
354  // Apply inverse transformations which preceed this local transformation
355  this->Inverse(-1, n, x1, y1, z1);
356  this->Inverse(-1, n, x2, y2, z2);
357 
358  // Convert to image coordinates
359  image->WorldToImage(x1, y1, z1);
360  image->WorldToImage(x2, y2, z2);
361 
362  if (x2 < x1) swap(x1, x2);
363  if (y2 < y1) swap(y1, y2);
364  if (z2 < z1) swap(z1, z2);
365 
366  // Round up/down to nearest voxel in image domain
367  i1 = static_cast<int>(ceil (x1));
368  i2 = static_cast<int>(floor(x2));
369  j1 = static_cast<int>(ceil (y1));
370  j2 = static_cast<int>(floor(y2));
371  k1 = static_cast<int>(ceil (z1));
372  k2 = static_cast<int>(floor(z2));
373  // When both indices are outside in opposite directions,
374  // use the full range [0, N[. If they are both outside in
375  // the same direction, the condition i1 <= i2 is false which
376  // indicates that the bounding box is empty in this case
377  i1 = (i1 < 0 ? 0 : (i1 >= image->X() ? image->X() : i1));
378  i2 = (i2 < 0 ? -1 : (i2 >= image->X() ? image->X() - 1 : i2));
379  j1 = (j1 < 0 ? 0 : (j1 >= image->Y() ? image->Y() : j1));
380  j2 = (j2 < 0 ? -1 : (j2 >= image->Y() ? image->Y() - 1 : j2));
381  k1 = (k1 < 0 ? 0 : (k1 >= image->Z() ? image->Z() : k1));
382  k2 = (k2 < 0 ? -1 : (k2 >= image->Z() ? image->Z() - 1 : k2));
383  return i1 <= i2 && j1 <= j2 && k1 <= k2;
384 }
385 
386 
387 } // namespace mirtk
388 
389 #endif // MIRTK_FluidFreeFormTransformation_H
virtual void Hessian(int, int, Matrix [3], double, double, double, double=0, double=-1) const
Calculates the Hessian of the transformation w.r.t world coordinates.
virtual void Displacement(int, int, GenericImage< double > &, double, double=-1, const WorldCoordsImage *=NULL) const
virtual ~FluidFreeFormTransformation()
Destructor.
virtual void ParametricGradient(const GenericImage< double > *, double *, const WorldCoordsImage *=NULL, const WorldCoordsImage *=NULL, double=-1, double=1) const
virtual void Displacement(int, int, double &, double &, double &, double=0, double=NaN) const
Calculates the displacement of a single point.
virtual int InverseDisplacement(int, int, GenericImage< double > &, double, double=-1, const WorldCoordsImage *=NULL) const
virtual bool IsIdentity() const
Checks whether transformation is an identity mapping.
void WorldToImage(double &, double &) const
World to image coordinate conversion with two doubles.
Definition: BaseImage.h:1212
FluidFreeFormTransformation()
Default constructor.
virtual bool CanRead(TransformationType) const
Whether this transformation can read a file of specified type (i.e. format)
virtual double ApproximateAsNew(const ImageAttributes &, double *, double *, double *, int=1, double=.0)
virtual void Hessian(int, int, Matrix [3], double, double, double, double=0, double=NaN) const
Calculates the Hessian for each component of the transformation w.r.t world coordinates.
virtual bool InverseDisplacement(int, int, double &, double &, double &, double=0, double=NaN) const
Calculates the displacement of a single point using the inverse of the transformation.
AffineTransformation _AffineTransformation
Affine transformation applied after the local free-form deformations.
virtual void PushTransformation(const Transformation *, ImageAttributes *attr=nullptr)
Compose current transformation with another.
Definition: IOConfig.h:41
virtual bool Inverse(int, int, double &, double &, double &, double=0, double=-1) const
Transforms a single point using the inverse of the transformation.
virtual bool DOFBoundingBox(const Image *, int, int &, int &, int &, int &, int &, int &, double=1) const
virtual void Reset()
Reset transformation (does not remove local transformations)
virtual void MergeGlobalIntoLocalDisplacement()
virtual void Print(ostream &, Indent=0) const
Prints the parameters of the transformation.
void BoundingBox(double &, double &) const
Gets the temporal bounding box of the free-form deformation (in ms)
int Y() const
Returns the number of voxels in the y-direction.
Definition: BaseImage.h:880
virtual void Print(ostream &, Indent=0) const
Prints the parameters of the transformation.
virtual void CheckTransformation(FreeFormTransformation *) const
Checks whether a given transformation is supported as local transformation.
int X() const
Returns the number of voxels in the x-direction.
Definition: BaseImage.h:874
virtual void Jacobian(int, int, Matrix &, double, double, double, double=0, double=-1) const
Calculates the Jacobian of the transformation w.r.t world coordinates.
virtual void Transform(int, int, double &, double &, double &, double=0, double=-1) const
Transforms a single point.
virtual bool CopyFrom(const Transformation *)
virtual void Jacobian(int, int, Matrix &, double, double, double, double=0, double=NaN) const
Calculates the Jacobian of the transformation w.r.t world coordinates.
AffineTransformation * GetAffineTransformation()
Get affine transformation applied after the local free-form deformations.
virtual bool Inverse(int, int, double &, double &, double &, double=0, double=NaN) const
Transforms a single point using the inverse of the transformation.
virtual Cofstream & WriteDOFs(Cofstream &) const
Writes transformation parameters to a file stream.
virtual Cifstream & ReadDOFs(Cifstream &, TransformationType)
Reads transformation parameters from a file stream.
virtual void Transform(int, int, double &, double &, double &, double=0, double=NaN) const =0
Transforms a single point.
int DOFToIndex(int) const
Get index of control point corresponding to transformation parameter (DoFs)
virtual double Approximate(const ImageAttributes &, double *, double *, double *, int=1, double=.0)
virtual void Clear()
Reset transformation and remove all local transformations.
virtual void CombineLocalTransformation()
Combine local transformations on stack.
int Z() const
Returns the number of voxels in the z-direction.
Definition: BaseImage.h:886
virtual void ApproximateDOFs(const double *, const double *, const double *, const double *, const double *, const double *, const double *, int)
virtual void ApproximateDOFsGradient(const double *, const double *, const double *, const double *, const double *, const double *, const double *, int, double *, double=1.0) const