HomogeneousTransformation.h
1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2008-2015 Imperial College London
5  * Copyright 2008-2013 Daniel Rueckert, Julia Schnabel
6  * Copyright 2013-2015 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_HomogeneousTransformation_H
22 #define MIRTK_HomogeneousTransformation_H
23 
24 #include "mirtk/Transformation.h"
25 
26 #include "mirtk/Matrix.h"
27 
28 #include <cmath>
29 
30 
31 namespace mirtk {
32 
33 
34 class MultiThreadedImageHomogeneousTransformation;
35 
36 enum HomogeneousTransformationParameterIndex
37 {
38  TX, TY, TZ, RX, RY, RZ, SX, SY, SZ, SXY, SYZ, SXZ, SYX, SZY, SZX,
39  SG = SX // global/isotropic scaling for similarity transformation
40 };
41 
42 
43 /**
44  * Base class for homogeneous transformations.
45  *
46  * This class defines and implements homogeneous transformations which can
47  * be represented by a 4 x 4 transformation matrix. The transformation of
48  * a point is implemented by post multiplying the transformation matrix with
49  * the point in homogeneous coordinates. The transformation is parameterized
50  * by twelve degrees of freedom.
51  *
52  */
53 
55 {
56  mirtkTransformationMacro(HomogeneousTransformation);
57 
58 protected:
59 
60  // ---------------------------------------------------------------------------
61  // Types
62 
63  enum AttributeSelector { MATRIX, DOFS, DOFS_MATRIX };
64 
65  // ---------------------------------------------------------------------------
66  // Data members
67 
68  /// 4x4 transformation matrix for homogeneous coordinates
70 
71  /// Inverse 4x4 transformation matrix for homogeneous coordinates
73 
74  /// Updates transformation matrix after change of parameter
75  virtual void UpdateMatrix();
76 
77  /// Updates transformation parameters after change of matrix
78  virtual void UpdateDOFs();
79 
80  /// Update transformation parameters and/or matrices
81  void Update(AttributeSelector);
82 
83  // ---------------------------------------------------------------------------
84  // Construction/Destruction
85 
86  /// Default constructor with given number of parameters
88 
89  /// Copy constructor with given number of parameters
91 
92 public:
93 
94  /// Default constructor
96 
97  /// Construct from 4x4 transformation matrix (without checks)
99 
100  /// Copy Constructor
102 
103  /// Destructor
104  virtual ~HomogeneousTransformation();
105 
106  // ---------------------------------------------------------------------------
107  // Approximation
108 
109  // Import other overloads
112 
113  /// Approximate displacements: This function takes a set of points and a set
114  /// of displacements and finds a transformation which approximates these
115  /// displacements. After approximation, the displacements are replaced by the
116  /// residual displacement errors at the points.
117  virtual double Approximate(const ImageAttributes &, double *, double *, double *,
118  int = 1, double = .0);
119 
120  /// Approximate displacements: This function takes a set of points and a set
121  /// of displacements and finds a transformation which approximates these
122  /// displacements. After approximation, the displacements are replaced by the
123  /// residual displacement errors at the points.
124  virtual double Approximate(const double *, const double *, const double *,
125  double *, double *, double *, int,
126  int = 1, double = .0);
127 
128  /// Approximate displacements: This function takes a set of points and a set
129  /// of displacements and finds a transformation which approximates these
130  /// displacements. After approximation, the displacements are replaced by the
131  /// residual displacement errors at the points.
132  virtual double Approximate(const double *, const double *, const double *, const double *,
133  double *, double *, double *, int,
134  int = 1, double = .0);
135 
136  /// Approximate given homogeneous coordinate transformation matrix
137  virtual double Approximate(const Matrix &);
138 
139  /// Approximate given homogeneous coordinate transformation matrix
140  virtual double ApproximateAsNew(const Matrix &);
141 
142  /// Approximate displacements: This function takes a set of points and a set
143  /// of displacements and finds !new! parameters such that the resulting
144  /// transformation approximates the displacements as good as possible.
145  virtual void ApproximateDOFs(const double *, const double *, const double *, const double *,
146  const double *, const double *, const double *, int);
147 
148  // ---------------------------------------------------------------------------
149  // Transformation parameters (DOFs)
150 
151  /// Copy active transformation parameters (DoFs) from given
152  /// transformation if possible and return \c false, otherwise
153  virtual bool CopyFrom(const Transformation *);
154 
155  /// Puts a transformation parameter
156  virtual void Put(int, DOFValue);
157 
158  /// Puts transformation parameters
159  virtual void Put(const DOFValue *);
160 
161  /// Add change to transformation parameters
162  /// \note Also passive parameters are modified by this function.
163  virtual void Add(const DOFValue *);
164 
165  /// Update transformation parameters given parametric gradient
166  /// \note Only active parameters are modified by this function.
167  virtual double Update(const DOFValue *);
168 
169  /// Set transformation parameters from a homogeneous transformation matrix
170  ///
171  /// The transformation sets its parameters to match the given homogeneous
172  /// transformation matrix as good as possible and then updates its
173  /// internal transformation matrix from these parameters. The resulting
174  /// homogeneous transformations may therefore differ from the given
175  /// transformation matrix if the transformation is not a full 12 DoF
176  /// affine transformation, but has fewer free parameters.
177  ///
178  /// \note Use one of the Approximate or ApproximateAsNew functions to
179  /// minimize the mean squared error of the approximation at given
180  /// evaluation points instead for a more accurate approximation.
181  void PutMatrix(const Matrix &);
182 
183  /// Gets the transformation matrix
184  const Matrix &GetMatrix() const;
185 
186  /// Gets the inverse transformation matrix
187  const Matrix &GetInverseMatrix() const;
188 
189  /// Reset transformation
190  virtual void Reset();
191 
192  /// Inverts the transformation
193  void Invert();
194 
195  // ---------------------------------------------------------------------------
196  // Point transformation
197 
198  // Import other overloads from base class
201 
202  /// Transforms a single point using the global transformation component only
203  virtual void GlobalTransform(double &, double &, double &, double = 0, double = -1) const;
204 
205  /// Transforms a single point using the local transformation component only
206  virtual void LocalTransform(double &, double &, double &, double = 0, double = -1) const;
207 
208  /// Transforms a single point
209  virtual void Transform(double &, double &, double &, double = 0, double = -1) const;
210 
211  /// Transforms a single point using the inverse of the global transformation only
212  virtual void GlobalInverse(double &, double &, double &, double = 0, double = -1) const;
213 
214  /// Transforms a single point using the inverse of the local transformation only
215  virtual bool LocalInverse(double &, double &, double &, double = 0, double = -1) const;
216 
217  /// Transforms a single point using the inverse of the transformation
218  virtual bool Inverse(double &, double &, double &, double = 0, double = -1) const;
219 
220  // ---------------------------------------------------------------------------
221  // Derivatives
222 
223  /// Calculates the Jacobian of the global transformation w.r.t world coordinates
224  virtual void GlobalJacobian(Matrix &, double, double, double, double = 0, double = -1) const;
225 
226  /// Calculates the Jacobian of the local transformation w.r.t world coordinates
227  virtual void LocalJacobian(Matrix &, double, double, double, double = 0, double = -1) const;
228 
229  /// Calculates the Jacobian of the transformation w.r.t world coordinates
230  virtual void Jacobian(Matrix &, double, double, double, double = 0, double = -1) const;
231 
232  // ---------------------------------------------------------------------------
233  // Properties
234 
235  /// Checks whether transformation is an identity mapping
236  virtual bool IsIdentity() const;
237 
238  // ---------------------------------------------------------------------------
239  // I/O
240 
241  // Do not hide methods of base class
242  using Transformation::Print;
243 
244  /// Prints the parameters of the transformation
245  virtual void Print(ostream &, Indent = 0) const;
246 
247 protected:
248 
249  /// Reads transformation parameters from a file stream
251 
252  // ---------------------------------------------------------------------------
253  // Backwards compatibility
254 
255 public:
256 
257  /// \deprecated Replaced by UpdateDOFs, which, however, does not have to be
258  /// called explicitly. The parameters are automatically updated
259  /// when the transformation matrix changed. Some older code may
260  /// yet call this deprecated method after PutMatrix, which no
261  /// longer does anything and the call should be optimized away.
262  void UpdateParameter() {}
263 
264  friend class MultiThreadedImageHomogeneousTransformation;
265 };
266 
267 ////////////////////////////////////////////////////////////////////////////////
268 // Inline definitions
269 ////////////////////////////////////////////////////////////////////////////////
270 
271 // =============================================================================
272 // Transformation parameters
273 // =============================================================================
274 
275 // -----------------------------------------------------------------------------
276 inline void HomogeneousTransformation::Update(AttributeSelector what)
277 {
278  // Update attributes
279  switch (what) {
280  case MATRIX:
281  this->UpdateMatrix();
282  break;
283  case DOFS:
284  this->UpdateDOFs();
285  break;
286  case DOFS_MATRIX:
287  this->UpdateDOFs();
288  this->UpdateMatrix();
289  break;
290  }
291  // Mark transformation as changed
292  this->Changed(true);
293  // Notify observers, keeping changed state intact
295 }
296 
297 // -----------------------------------------------------------------------------
299 {
300  Transformation::Put(i, x);
301  this->Update(MATRIX);
302 }
303 
304 // -----------------------------------------------------------------------------
306 {
308  this->Update(MATRIX);
309 }
310 
311 // -----------------------------------------------------------------------------
313 {
314  double delta, max_delta = .0;
315  for (int idx = 0; idx < _NumberOfDOFs; ++idx) {
316  // Add change also to passive parameters
317  _Param[idx] += dx[idx];
318  delta = abs(dx[idx]);
319  if (delta > max_delta) max_delta = delta;
320  }
321  if (max_delta > .0) this->Update(MATRIX);
322 }
323 
324 // -----------------------------------------------------------------------------
326 {
327  double delta, max_delta = .0;
328  for (int idx = 0; idx < _NumberOfDOFs; ++idx) {
329  if (_Status[idx] == Active) {
330  _Param[idx] += dx[idx];
331  delta = abs(dx[idx]);
332  if (delta > max_delta) max_delta = delta;
333  }
334  }
335  if (max_delta > .0) this->Update(MATRIX);
336  return max_delta;
337 }
338 
339 // -----------------------------------------------------------------------------
341 {
342  if (matrix.Rows() == 3 && matrix.Cols() == 3) {
343  for (int c = 0; c < 3; ++c)
344  for (int r = 0; r < 3; ++r) {
345  _matrix(r, c) = matrix(r, c);
346  }
347  for (int r = 0; r < 3; ++r) {
348  _matrix(r, 3) = 0.;
349  }
350  } else if (matrix.Rows() == 4 && matrix.Cols() == 4) {
351  _matrix = matrix;
352  } else {
353  Throw(ERR_InvalidArgument, __FUNCTION__, "Transformation matrix must be 3x3 or 4x4");
354  }
355  _matrix(3, 0) = _matrix(3, 1) = _matrix(3, 2) = 0., _matrix(3, 3) = 1.;
356  this->Update(DOFS_MATRIX); // also updates _inverse
357 }
358 
359 // -----------------------------------------------------------------------------
361 {
362  return _matrix;
363 }
364 
365 // -----------------------------------------------------------------------------
367 {
368  return _inverse;
369 }
370 
371 // =============================================================================
372 // Virtual Transformation "Impl" methods
373 // =============================================================================
374 
375 // -----------------------------------------------------------------------------
376 inline void HomogeneousTransformation::GlobalTransform(double &x, double &y, double &z, double, double) const
377 {
378  double a = _matrix(0, 0) * x + _matrix(0, 1) * y + _matrix(0, 2) * z + _matrix(0, 3);
379  double b = _matrix(1, 0) * x + _matrix(1, 1) * y + _matrix(1, 2) * z + _matrix(1, 3);
380  double c = _matrix(2, 0) * x + _matrix(2, 1) * y + _matrix(2, 2) * z + _matrix(2, 3);
381 
382  x = a;
383  y = b;
384  z = c;
385 }
386 
387 // -----------------------------------------------------------------------------
388 inline void HomogeneousTransformation::LocalTransform(double &, double &, double &, double, double) const
389 {
390  // No local component
391 }
392 
393 // -----------------------------------------------------------------------------
394 inline void HomogeneousTransformation::Transform(double &x, double &y, double &z, double t, double t0) const
395 {
396  this->GlobalTransform(x, y, z, t, t0);
397 }
398 
399 // -----------------------------------------------------------------------------
400 inline void HomogeneousTransformation::GlobalInverse(double &x, double &y, double &z, double, double) const
401 {
402  double a = _inverse(0, 0) * x + _inverse(0, 1) * y + _inverse(0, 2) * z + _inverse(0, 3);
403  double b = _inverse(1, 0) * x + _inverse(1, 1) * y + _inverse(1, 2) * z + _inverse(1, 3);
404  double c = _inverse(2, 0) * x + _inverse(2, 1) * y + _inverse(2, 2) * z + _inverse(2, 3);
405 
406  x = a;
407  y = b;
408  z = c;
409 }
410 
411 // -----------------------------------------------------------------------------
412 inline bool HomogeneousTransformation::LocalInverse(double &, double &, double &, double, double) const
413 {
414  // No local component
415  return true;
416 }
417 
418 // -----------------------------------------------------------------------------
419 inline bool HomogeneousTransformation::Inverse(double &x, double &y, double &z, double t, double t0) const
420 {
421  this->GlobalInverse(x, y, z, t, t0);
422  return true;
423 }
424 
425 // -----------------------------------------------------------------------------
426 inline void HomogeneousTransformation::GlobalJacobian(Matrix &jac, double, double, double, double, double) const
427 {
428  jac.Initialize(3, 3);
429  for (int i = 0; i < 3; i++) {
430  for (int j = 0; j < 3; j++) {
431  jac(i, j) = _matrix(i, j);
432  }
433  }
434 }
435 
436 // -----------------------------------------------------------------------------
437 inline void HomogeneousTransformation::LocalJacobian(Matrix &jac, double, double, double, double, double) const
438 {
439  jac.Initialize(3, 3);
440  jac(0, 0) = 1;
441  jac(1, 1) = 1;
442  jac(2, 2) = 1;
443 }
444 
445 // -----------------------------------------------------------------------------
446 inline void HomogeneousTransformation::Jacobian(Matrix &jac, double x, double y, double z, double t, double t0) const
447 {
448  this->GlobalJacobian(jac, x, y, z, t, t0);
449 }
450 
451 
452 } // namespace mirtk
453 
454 #endif // MIRTK_HomogeneousTransformation_H
virtual void ApproximateDOFs(const double *, const double *, const double *, const double *, const double *, const double *, const double *, int)
virtual bool LocalInverse(double &, double &, double &, double=0, double=-1) const
Transforms a single point using the inverse of the local transformation only.
void Broadcast(Event, const void *=NULL)
Broadcast event to observers.
Definition: Observable.h:195
double DOFValue
Type of transformation parameter value.
virtual double ApproximateAsNew(const Matrix &)
Approximate given homogeneous coordinate transformation matrix.
virtual bool IsIdentity() const
Checks whether transformation is an identity mapping.
virtual void UpdateMatrix()
Updates transformation matrix after change of parameter.
virtual bool Inverse(double &, double &, double &, double=0, double=-1) const
Transforms a single point using the inverse of the transformation.
virtual void Put(int, DOFValue)
Puts a transformation parameter.
DOFStatus * _Status
Status of each transformation parameter (Active or Passive)
virtual void Put(int, double)
Put value of transformation parameter.
Matrix _matrix
4x4 transformation matrix for homogeneous coordinates
int Cols() const
Get number of columns.
Definition: Matrix.h:561
virtual void UpdateDOFs()
Updates transformation parameters after change of matrix.
const Matrix & GetMatrix() const
Gets the transformation matrix.
int _NumberOfDOFs
Number of transformation parameters.
void Initialize(int, int=-1, double *=NULL)
Initialize matrix with number of rows and columns.
const Matrix & GetInverseMatrix() const
Gets the inverse transformation matrix.
Observable has modified state/parameters.
Definition: Event.h:36
Definition: IOConfig.h:41
virtual double Approximate(const ImageAttributes &, const Transformation *, int=1, double=.0)
Approximate another transformation and return approximation error.
virtual void Add(const DOFValue *)
virtual ~HomogeneousTransformation()
Destructor.
virtual void GlobalInverse(double &, double &, double &, double=0, double=-1) const
Transforms a single point using the inverse of the global transformation only.
DOFValue * _Param
Value of each transformation parameter.
virtual void Reset()
Reset transformation.
Matrix _inverse
Inverse 4x4 transformation matrix for homogeneous coordinates.
virtual void Transform(double &, double &, double &, double=0, double=NaN) const =0
Transforms a single point.
virtual Cifstream & ReadDOFs(Cifstream &, TransformationType)
Reads transformation parameters from a file stream.
virtual double Approximate(const ImageAttributes &, double *, double *, double *, int=1, double=.0)
void Throw(ErrorType err, const char *func, Args... args) const
Definition: Object.h:166
virtual double ApproximateAsNew(const ImageAttributes &, const Transformation *, int=1, double=.0)
Approximate another transformation and return approximation error.
void Update(AttributeSelector)
Update transformation parameters and/or matrices.
virtual void Print(ostream &, Indent=0) const
Prints the parameters of the transformation.
virtual void Transform(double &, double &, double &, double=0, double=-1) const
Transforms a single point.
virtual void LocalTransform(double &, double &, double &, double=0, double=-1) const
Transforms a single point using the local transformation component only.
virtual bool Inverse(double &, double &, double &, double=0, double=NaN) const
Transforms a single point using the inverse of the transformation.
void Invert()
Inverts the transformation.
virtual bool CopyFrom(const Transformation *)
int Rows() const
Get number of rows.
Definition: Matrix.h:555
void Print(Indent=0) const
Prints information about the transformation.
HomogeneousTransformation()
Default constructor.
virtual void Jacobian(Matrix &, double, double, double, double=0, double=-1) const
Calculates the Jacobian of the transformation w.r.t world coordinates.
virtual void GlobalJacobian(Matrix &, double, double, double, double=0, double=-1) const
Calculates the Jacobian of the global transformation w.r.t world coordinates.
virtual void GlobalTransform(double &, double &, double &, double=0, double=-1) const
Transforms a single point using the global transformation component only.
virtual void LocalJacobian(Matrix &, double, double, double, double=0, double=-1) const
Calculates the Jacobian of the local transformation w.r.t world coordinates.