Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
mitkWeightedPointTransform.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
13 // MITK
16 #include <vtkLandmarkTransform.h>
17 #include <vtkMatrix4x4.h>
18 #include <vtkPoints.h>
19 
20 typedef itk::Matrix<double, 3, 3> Matrix3x3;
21 typedef std::vector<Matrix3x3> Matrix3x3List;
22 
24 // forward declarations of private functions
26 
27 static double ComputeWeightedFRE(vtkPoints *X,
28  vtkPoints *Y,
29  const Matrix3x3List &CovarianceMatricesMoving,
30  const Matrix3x3List &CovarianceMatricesFixed,
31  double FRENormalizationFactor,
32  Matrix3x3List &WeightMatrices,
33  const Matrix3x3 &rotation,
34  const itk::Vector<double, 3> &translation);
35 
36 static void calculateWeightMatrices(const Matrix3x3List &X,
37  const Matrix3x3List &Y,
38  Matrix3x3List &result,
39  const Matrix3x3 &rotation);
40 
41 static void IsotropicRegistration(vtkPoints *X,
42  vtkPoints *Y,
43  vtkLandmarkTransform *landmarkTransform,
45  itk::Vector<double, 3> &translation);
46 
48  : m_Threshold(1.0e-4),
49  m_MaxIterations(1000),
50  m_Iterations(-1),
51  m_FRE(-1.0),
52  m_FRENormalizationFactor(1.0),
53  m_LandmarkTransform(vtkSmartPointer<vtkLandmarkTransform>::New())
54 {
55 }
56 
58 {
59  m_FixedPointSet = nullptr;
60  m_MovingPointSet = nullptr;
61  m_LandmarkTransform = nullptr;
62 }
63 
65 {
72  m_Rotation,
74  m_FRE,
75  m_Iterations);
76 }
77 
78 // computes the weightmatrix with 2 covariance matrices
79 // and a given transformation
81  const Matrix3x3List &Y,
82  Matrix3x3List &result,
83  const Matrix3x3 &rotation)
84 {
85  const vnl_matrix_fixed<double, 3, 3> rotation_T = rotation.GetTranspose();
86 
87 #pragma omp parallel for
88  for (int i = 0; i < static_cast<int>(X.size()); ++i)
89  {
90  const Matrix3x3 w = rotation * X[i] * rotation_T;
92  }
93 }
94 
95 // computes the weighted fiducial registration error
96 double ComputeWeightedFRE(vtkPoints *X,
97  vtkPoints *Y,
98  const Matrix3x3List &CovarianceMatricesMoving,
99  const Matrix3x3List &CovarianceMatricesFixed,
100  double FRENormalizationFactor,
101  Matrix3x3List &WeightMatrices,
102  const Matrix3x3 &rotation,
103  const itk::Vector<double, 3> &translation)
104 {
105  double FRE = 0;
106  // compute weighting matrices
107  calculateWeightMatrices(CovarianceMatricesMoving, CovarianceMatricesFixed, WeightMatrices, rotation);
108 
109 #pragma omp parallel for
110  for (int i = 0; i < static_cast<int>(WeightMatrices.size()); ++i)
111  {
112  // convert to itk data types (nessecary since itk 4 migration)
113  itk::Vector<double, 3> converted_MovingPoint;
114  double point[3];
115  X->GetPoint(i, point);
116  converted_MovingPoint[0] = point[0];
117  converted_MovingPoint[1] = point[1];
118  converted_MovingPoint[2] = point[2];
119 
120  // transform point
121  itk::Vector<double, 3> p = rotation * converted_MovingPoint + translation;
122 
123  Y->GetPoint(i, point);
124  p[0] -= point[0];
125  p[1] -= point[1];
126  p[2] -= point[2];
127 
128  // do calculation
129  const itk::Vector<double, 3> D = WeightMatrices.at(i) * p;
130 #pragma omp critical
131  FRE += (D[0] * D[0] + D[1] * D[1] + D[2] * D[2]);
132  }
133 
134  FRE /= WeightMatrices.size();
135  FRE = FRENormalizationFactor * sqrt(FRE);
136 
137  return FRE;
138 }
139 
140 // registers two pointsets with an isotropic landmark transform
141 void IsotropicRegistration(vtkPoints *X,
142  vtkPoints *Y,
143  vtkLandmarkTransform *landmarkTransform,
145  itk::Vector<double, 3> &translation)
146 {
147  landmarkTransform->SetSourceLandmarks(X);
148  landmarkTransform->SetTargetLandmarks(Y);
149  landmarkTransform->SetModeToRigidBody();
150  landmarkTransform->Modified();
151  landmarkTransform->Update();
152 
153  vtkMatrix4x4 *m = landmarkTransform->GetMatrix();
154 
155  for (int i = 0; i < 3; ++i)
156  for (int j = 0; j < 3; ++j)
157  rotation[i][j] = m->GetElement(i, j);
158 
159  translation[0] = m->GetElement(0, 3);
160  translation[1] = m->GetElement(1, 3);
161  translation[2] = m->GetElement(2, 3);
162 }
163 
165  const WeightMatrixList &W,
166  itk::VariableSizeMatrix<double> &returnValue)
167 {
168 #pragma omp parallel for
169  for (int i = 0; i < X->GetNumberOfPoints(); ++i)
170  {
171  unsigned int index = 3u * i;
172  double point[3];
173  X->GetPoint(i, point);
174 
175  for (int j = 0; j < 3; ++j)
176  {
177  returnValue[index][0] = -W.at(i)[j][1] * point[2] + W.at(i)[j][2] * point[1];
178  returnValue[index][1] = W.at(i)[j][0] * point[2] - W.at(i)[j][2] * point[0];
179  returnValue[index][2] = -W.at(i)[j][0] * point[1] + W.at(i)[j][1] * point[0];
180  returnValue[index][3] = W.at(i)[j][0];
181  returnValue[index][4] = W.at(i)[j][1];
182  returnValue[index][5] = W.at(i)[j][2];
183  index += 1;
184  }
185  }
186 }
187 
189  vtkPoints *Y,
190  const WeightMatrixList &W,
191  vnl_vector<double> &returnValue)
192 {
193 #pragma omp parallel for
194  for (int i = 0; i < X->GetNumberOfPoints(); ++i)
195  {
196  unsigned int index = 3u * i;
197  double pX[3];
198  double pY[3];
199  Matrix3x3 M;
200 
201  X->GetPoint(i, pX);
202  Y->GetPoint(i, pY);
203 
204  M[0][0] = pY[0] - pX[0];
205  M[0][1] = pY[1] - pX[1];
206  M[0][2] = pY[2] - pX[2];
207  M[1][0] = M[0][0];
208  M[1][1] = M[0][1];
209  M[1][2] = M[0][2];
210  M[2][0] = M[0][0];
211  M[2][1] = M[0][1];
212  M[2][2] = M[0][2];
213 
214  for (unsigned int j = 0; j < 3; ++j)
215  {
216  returnValue[index + j] = W.at(i)[j][0] * M[j][0] + W.at(i)[j][1] * M[j][1] + W.at(i)[j][2] * M[j][2];
217  }
218  }
219 }
220 
222  vtkPoints *Y,
223  const CovarianceMatrixList &Sigma_X,
224  const CovarianceMatrixList &Sigma_Y,
225  double Threshold,
226  int MaxIterations,
227  Rotation &TransformationR,
228  Translation &TransformationT,
229  double &FRE,
230  int &n)
231 {
232  double FRE_identity = 0.0;
233  double FRE_isotropic_weighted = 0.0;
234  double initialFRE = 0.0;
235  // set config_change to infinite (max double) at start
236  double config_change = std::numeric_limits<double>::max();
237  Rotation initial_TransformationR;
238  initial_TransformationR.SetIdentity();
239  Translation initial_TransformationT;
240  initial_TransformationT.Fill(0.0);
241  // Weightmatrices
242  Matrix3x3List W;
243  vtkPoints *X_transformed = vtkPoints::New();
244  vtkPoints *X_transformedNew = vtkPoints::New();
245  vnl_vector<double> oldq;
246  itk::VariableSizeMatrix<double> iA;
247  vnl_vector<double> iB;
248 
249  // initialize memory
250  W.resize(X->GetNumberOfPoints());
251  X_transformed->SetNumberOfPoints(X->GetNumberOfPoints());
252  X_transformedNew->SetNumberOfPoints(X->GetNumberOfPoints());
253  iA.SetSize(3u * X->GetNumberOfPoints(), 6u);
254  iB.set_size(3u * X->GetNumberOfPoints());
255 
256  // calculate FRE_0 with identity transform
257  FRE_identity = ComputeWeightedFRE(
258  X, Y, Sigma_X, Sigma_Y, m_FRENormalizationFactor, W, initial_TransformationR, initial_TransformationT);
259 
260  MITK_DEBUG << "FRE for identity transform: " << FRE_identity;
261 
262  // compute isotropic transformation as initial estimate
263  IsotropicRegistration(X, Y, m_LandmarkTransform, initial_TransformationR, initial_TransformationT);
264 
265  // result of unweighted registration algorithm
266  TransformationR = initial_TransformationR;
267  TransformationT = initial_TransformationT;
268 
269  // calculate FRE_0 with isotropic transform
270  FRE_isotropic_weighted =
271  ComputeWeightedFRE(X, Y, Sigma_X, Sigma_Y, m_FRENormalizationFactor, W, TransformationR, TransformationT);
272  MITK_DEBUG << "FRE for transform obtained with unweighted registration: " << FRE_isotropic_weighted;
273 
274  // if R,t is worse than the identity, use the identity as initial transform
275  if (FRE_isotropic_weighted < FRE_identity)
276  {
277  initialFRE = FRE_isotropic_weighted;
278  }
279  else
280  {
281  initialFRE = FRE_identity;
282  TransformationR.SetIdentity(); // set rotation to identity element
283  TransformationT.Fill(0.0); // set translation to identity element
284  initial_TransformationR.SetIdentity();
285  initial_TransformationT.Fill(0.0);
286  }
287 
288  // apply transform to moving set:
289  mitk::AnisotropicRegistrationCommon::TransformPoints(X, X_transformed, TransformationR, TransformationT);
290 
291  // start with iteration 0
292  n = 0;
293 
294  do
295  {
296  n++;
297 
298  calculateWeightMatrices(Sigma_X, Sigma_Y, W, TransformationR);
299 
300  //'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
301  // PROBLEM: no square matrix but the backslash operator in matlab does solve the system anyway. How to convert this
302  // to C++?
303  // good descriptons to the "backslash"-operator (in german):
304  // http://www.tm-mathe.de/Themen/html/matlab__zauberstab__backslash-.html
305  // http://www.tm-mathe.de/Themen/html/matlab__matrix-division__vorsi.html#HoheMatrixA
306  //
307  // current method: treat the problem as a minimization problem, because this is what the
308  // "backslash"-operator also does with "high" matrices.
309  // (and we will have those matrices in most cases)
310 
311  C_maker(X_transformed, W, iA);
312  E_maker(X_transformed, Y, W, iB);
313 
314  vnl_matrix_inverse<double> myInverse(iA.GetVnlMatrix());
315  vnl_vector<double> q = myInverse.pinverse(iB.size()) * iB;
316  //'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
317 
318  if (n > 1)
319  q = (q + oldq) / 2;
320  oldq = q;
321 
322  itk::Vector<double, 3> delta_t;
323  delta_t[0] = q[3];
324  delta_t[1] = q[4];
325  delta_t[2] = q[5];
326 
327  Matrix3x3 delta_theta;
328  delta_theta[0][0] = 1;
329  delta_theta[0][1] = -q[2];
330  delta_theta[0][2] = q[1];
331  delta_theta[1][0] = q[2];
332  delta_theta[1][1] = 1;
333  delta_theta[1][2] = -q[0];
334  delta_theta[2][0] = -q[1];
335  delta_theta[2][1] = q[0];
336  delta_theta[2][2] = 1;
337 
338  vnl_svd<double> svd_delta_theta(delta_theta.GetVnlMatrix());
339 
340  // convert vnl matrices to itk matrices...
341  Matrix3x3 U;
342  Matrix3x3 V;
343 
344  for (int i = 0; i < 3; ++i)
345  {
346  for (int j = 0; j < 3; ++j)
347  {
348  U[i][j] = svd_delta_theta.U()[i][j];
349  V[i][j] = svd_delta_theta.V()[i][j];
350  }
351  }
352 
353  Matrix3x3 delta_R = U * V.GetTranspose();
354 
355  // update rotation
356  TransformationR = delta_R * TransformationR;
357 
358  // update translation
359  TransformationT = delta_R * TransformationT + delta_t;
360 
361  // update moving points
362  mitk::AnisotropicRegistrationCommon::TransformPoints(X, X_transformedNew, TransformationR, TransformationT);
363  // calculate config change
364  config_change = CalculateConfigChange(X_transformed, X_transformedNew);
365 
366  // swap the pointers the old set for the next iteration is
367  // the new set of the last iteration
368  vtkPoints *tmp = X_transformed;
369  X_transformed = X_transformedNew;
370  X_transformedNew = tmp;
371 
372  } while (config_change > Threshold && n < MaxIterations);
373 
374  // calculate FRE with current transform
375  FRE = ComputeWeightedFRE(X, Y, Sigma_X, Sigma_Y, m_FRENormalizationFactor, W, TransformationR, TransformationT);
376 
377  MITK_DEBUG << "FRE after algorithm (prior to check with initial): " << FRE;
378 
379  // compare with FRE_initial
380  if (initialFRE < FRE)
381  {
382  MITK_WARN << "FRE did not improve in anisotropic point registration function";
383  TransformationR = initial_TransformationR;
384  TransformationT = initial_TransformationT;
385  FRE = initialFRE;
386  }
387 
388  MITK_DEBUG << "FRE final: " << FRE;
389 
390  X_transformed->Delete();
391  X_transformedNew->Delete();
392 }
393 
394 void mitk::WeightedPointTransform::SetMovingPointSet(vtkSmartPointer<vtkPoints> p)
395 {
396  m_MovingPointSet = p;
397 }
398 
399 void mitk::WeightedPointTransform::SetCovarianceMatricesMoving(const CovarianceMatrixList &matrices)
400 {
401  m_CovarianceMatricesMoving = matrices;
402 }
403 
404 void mitk::WeightedPointTransform::SetFixedPointSet(vtkSmartPointer<vtkPoints> p)
405 {
406  m_FixedPointSet = p;
407 }
408 
409 void mitk::WeightedPointTransform::SetCovarianceMatricesFixed(const CovarianceMatrixList &matrices)
410 {
411  m_CovarianceMatricesFixed = matrices;
412 }
413 
414 double mitk::WeightedPointTransform::CalculateConfigChange(vtkPoints *X, vtkPoints *X_new)
415 {
416  double sum[3] = {0.0, 0.0, 0.0};
417  double mean[3] = {0.0, 0.0, 0.0};
418  double pX[3] = {0.0, 0.0, 0.0};
419  double pX_new[3] = {0.0, 0.0, 0.0};
420 
421  // compute mean of the old point set and the first sum
422  for (vtkIdType i = 0; i < X->GetNumberOfPoints(); ++i)
423  {
424  X->GetPoint(i, pX);
425  X_new->GetPoint(i, pX_new);
426 
427  // first sum
428  sum[0] += (pX_new[0] - pX[0]) * (pX_new[0] - pX[0]);
429  sum[1] += (pX_new[1] - pX[1]) * (pX_new[1] - pX[1]);
430  sum[2] += (pX_new[2] - pX[2]) * (pX_new[2] - pX[2]);
431 
432  // mean
433  mean[0] += pX[0];
434  mean[1] += pX[1];
435  mean[2] += pX[2];
436  }
437 
438  mean[0] /= X->GetNumberOfPoints();
439  mean[1] /= X->GetNumberOfPoints();
440  mean[2] /= X->GetNumberOfPoints();
441 
442  const double us = sum[0] + sum[1] + sum[2];
443 
444  // reset sum
445  sum[0] = sum[1] = sum[2] = 0.0;
446 
447  for (vtkIdType i = 0; i < X->GetNumberOfPoints(); ++i)
448  {
449  X->GetPoint(i, pX);
450 
451  sum[0] += (pX[0] - mean[0]) * (pX[0] - mean[0]);
452  sum[1] += (pX[1] - mean[1]) * (pX[1] - mean[1]);
453  sum[2] += (pX[2] - mean[2]) * (pX[2] - mean[2]);
454  }
455 
456  const double ls = sum[0] + sum[1] + sum[2];
457 
458  return sqrt(us / ls);
459 }
#define ls
Definition: MitkMCxyz.cpp:57
double CalculateConfigChange(vtkPoints *X, vtkPoints *X_new)
vtkSmartPointer< vtkLandmarkTransform > m_LandmarkTransform
void SetCovarianceMatricesMoving(const CovarianceMatrixList &matrices)
vtkSmartPointer< vtkPoints > m_MovingPointSet
void C_maker(vtkPoints *X, const WeightMatrixList &W, itk::VariableSizeMatrix< double > &returnValue)
#define MITK_DEBUG
Definition: mitkLogMacros.h:22
CovarianceMatrixList m_CovarianceMatricesFixed
static Matrix3D rotation
std::vector< Matrix3x3 > Matrix3x3List
void ComputeTransformation()
Method which registers both point sets.
#define MITK_WARN
Definition: mitkLogMacros.h:19
static void IsotropicRegistration(vtkPoints *X, vtkPoints *Y, vtkLandmarkTransform *landmarkTransform, Matrix3x3 &rotation, itk::Vector< double, 3 > &translation)
void SetMovingPointSet(vtkSmartPointer< vtkPoints > p)
void E_maker(vtkPoints *X, vtkPoints *Y, const WeightMatrixList &W, vnl_vector< double > &returnValue)
void SetCovarianceMatricesFixed(const CovarianceMatrixList &matrices)
static T max(T x, T y)
Definition: svm.cpp:56
void WeightedPointRegister(vtkPoints *X, vtkPoints *Y, const CovarianceMatrixList &Sigma_X, const CovarianceMatrixList &Sigma_Y, double Threshold, int MaxIterations, Rotation &TransformationR, Translation &TransformationT, double &FRE, int &n)
This method performs a variant of the weighted point register algorithm presented by A...
vtkSmartPointer< vtkPoints > m_FixedPointSet
static void TransformPoints(vtkPoints *src, vtkPoints *dst, const Rotation &rotation, const Translation &translation)
Transforms a point cloud with a Rotation and Translation.
itk::Matrix< double, 3, 3 > Matrix3x3
static WeightMatrix CalculateWeightMatrix(const CovarianceMatrix &sigma_X, const CovarianceMatrix &sigma_Y)
Method that computes a WeightMatrix with two CovarianceMatrices.
static void calculateWeightMatrices(const Matrix3x3List &X, const Matrix3x3List &Y, Matrix3x3List &result, const Matrix3x3 &rotation)
void SetFixedPointSet(vtkSmartPointer< vtkPoints > p)
static double ComputeWeightedFRE(vtkPoints *X, vtkPoints *Y, const Matrix3x3List &CovarianceMatricesMoving, const Matrix3x3List &CovarianceMatricesFixed, double FRENormalizationFactor, Matrix3x3List &WeightMatrices, const Matrix3x3 &rotation, const itk::Vector< double, 3 > &translation)
CovarianceMatrixList m_CovarianceMatricesMoving