Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
mitkNavigationDataLandmarkTransformFilter.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 
14 #include "itkIndent.h"
15 #include "itkEuler3DTransform.h"
16 #include "itkVersorRigid3DTransform.h"
17 #include "itkEuclideanDistancePointMetric.h"
18 #include "itkLevenbergMarquardtOptimizer.h"
19 #include "itkPointSet.h"
20 #include "itkPointSetToPointSetRegistrationMethod.h"
21 #include <algorithm>
22 
23 
25 m_ErrorMean(-1.0), m_ErrorStdDev(-1.0), m_ErrorRMS(-1.0), m_ErrorMin(-1.0), m_ErrorMax(-1.0), m_ErrorAbsMax(-1.0),
26 m_SourcePoints(), m_TargetPoints(), m_LandmarkTransformInitializer(nullptr), m_LandmarkTransform(nullptr),
27 m_QuatLandmarkTransform(nullptr), m_QuatTransform(nullptr), m_Errors(), m_UseICPInitialization(false)
28 {
29  m_LandmarkTransform = LandmarkTransformType::New();
30 
31  m_LandmarkTransformInitializer = TransformInitializerType::New();
33 
34  //transform to rotate orientation
35  m_QuatLandmarkTransform = QuaternionTransformType::New();
36  m_QuatTransform = QuaternionTransformType::New();
37 }
38 
39 
41 {
42  m_LandmarkTransform = nullptr;
44  m_QuatLandmarkTransform = nullptr;
45  m_QuatTransform = nullptr;
46 }
47 
48 
50 {
51  if (m_UseICPInitialization == true)
52  {
53  if (this->FindCorrespondentLandmarks(sources, targets) == false) // determine landmark correspondences with iterative closest point optimization, sort sort landmarks accordingly
54  {
55  itkExceptionMacro("Landmark correspondence finding failed.");
56  }
57  }
58 
59  if(m_SourcePoints.size() != m_TargetPoints.size())// check whether target and source points size are equal itk registration won't work otherways
60  {
61  itkExceptionMacro("Cannot initialize Filter, number of input datas does not equal number of output datas.");
62  }
63 
64  this->UpdateLandmarkTransform(sources, targets); // if size of source and target points is equal
65 }
66 
67 
69 {
70  m_SourcePoints.clear();
71  mitk::PointSet::PointType mitkSourcePoint;
72  TransformInitializerType::LandmarkPointType lPoint;
73 
74  for (mitk::PointSet::PointsContainer::ConstIterator it = mitkSourcePointSet->GetPointSet()->GetPoints()->Begin();
75  it != mitkSourcePointSet->GetPointSet()->GetPoints()->End(); ++it)
76  {
77  mitk::FillVector3D(lPoint, it->Value().GetElement(0), it->Value().GetElement(1), it->Value().GetElement(2));
78  m_SourcePoints.push_back(lPoint);
79  }
80 
81  if (m_SourcePoints.size() < 3)
82  {
83  itkExceptionMacro("SourcePointSet must contain at least 3 points");
84  }
85 
86  if (this->IsInitialized())
88 }
89 
90 
92 {
93  m_TargetPoints.clear();
94  TransformInitializerType::LandmarkPointType lPoint;
95  for (mitk::PointSet::PointsContainer::ConstIterator it = mitkTargetPointSet->GetPointSet()->GetPoints()->Begin();
96  it != mitkTargetPointSet->GetPointSet()->GetPoints()->End(); ++it)
97  {
98  mitk::FillVector3D(lPoint, it->Value().GetElement(0), it->Value().GetElement(1), it->Value().GetElement(2));
99  m_TargetPoints.push_back(lPoint);
100  }
101 
102  if (m_TargetPoints.size() < 3)
103  {
104  itkExceptionMacro("TargetPointSet must contain at least 3 points");
105  }
106 
107  if (this->IsInitialized())
109 }
110 
111 
113 {
114  return m_ErrorMean;
115 }
116 
117 
119 {
120  return m_ErrorStdDev;
121 }
122 
123 
125 {
126  return m_ErrorRMS;
127 }
128 
129 
131 {
132  return m_ErrorMin;
133 }
134 
135 
137 {
138  return m_ErrorMax;
139 }
140 
141 
143 {
144  return m_ErrorAbsMax;
145 }
146 
147 
148 void mitk::NavigationDataLandmarkTransformFilter::AccumulateStatistics(std::vector<mitk::ScalarType>& vector)
149 {
150  //mean, min, max
151  m_ErrorMean = 0.0;
154  m_ErrorAbsMax = 0.0;
155  m_ErrorRMS = 0.0;
156  for (std::vector<mitk::ScalarType>::size_type i = 0; i < vector.size(); i++)
157  {
158  m_ErrorMean += vector[i]; // mean
159  m_ErrorRMS += pow(vector[i],2); // RMS
160  if(vector[i] < m_ErrorMin) // min
161  m_ErrorMin = vector[i];
162  if(vector[i] > m_ErrorMax) // max
163  m_ErrorMax = vector[i];
164  if(fabs(vector[i]) > fabs(m_ErrorAbsMax)) // abs_max
165  m_ErrorAbsMax = vector[i];
166  }
167  m_ErrorMean /= vector.size();
168  m_ErrorRMS = sqrt(m_ErrorRMS/vector.size());
169 
170  //standard deviation
171  m_ErrorStdDev = 0.0;
172  for (std::vector<mitk::ScalarType>::size_type i = 0; i < vector.size(); i++)
173  m_ErrorStdDev += pow(vector[i] - m_ErrorMean, 2);
174 
175  if(vector.size() > 1)
176  m_ErrorStdDev = sqrt(m_ErrorStdDev / (vector.size() - 1.0));
177  this->Modified();
178 }
179 
180 
182 {
183  this->CreateOutputsForAllInputs(); // make sure that we have the same number of outputs as inputs
184 
185  TransformInitializerType::LandmarkPointType lPointIn, lPointOut;
186 
187  /* update outputs with tracking data from tools */
188  for (unsigned int i = 0; i < this->GetNumberOfOutputs() ; ++i)
189  {
190  mitk::NavigationData* output = this->GetOutput(i);
191  assert(output);
192  const mitk::NavigationData* input = this->GetInput(i);
193  assert(input);
194 
195  if (input->IsDataValid() == false)
196  {
197  output->SetDataValid(false);
198  continue;
199  }
200  output->Graft(input); // First, copy all information from input to output
201 
202  if (this->IsInitialized() == false) // as long as there is no valid transformation matrix, only graft the outputs
203  continue;
204 
205  mitk::NavigationData::PositionType tempCoordinate;
206  tempCoordinate = input->GetPosition();
207  lPointIn[0] = tempCoordinate[0]; // convert navigation data position to transform point
208  lPointIn[1] = tempCoordinate[1];
209  lPointIn[2] = tempCoordinate[2];
210 
211  /* transform position */
212  lPointOut = m_LandmarkTransform->TransformPoint(lPointIn); // transform position
213  tempCoordinate[0] = lPointOut[0]; // convert back into navigation data position
214  tempCoordinate[1] = lPointOut[1];
215  tempCoordinate[2] = lPointOut[2];
216  output->SetPosition(tempCoordinate); // update output navigation data with new position
217 
218  /* transform orientation */
220  vnl_quaternion<double> const vnlQuatIn(quatIn.x(), quatIn.y(), quatIn.z(), quatIn.r()); // convert orientation into vnl quaternion
221  m_QuatTransform->SetRotation(vnlQuatIn); // convert orientation into transform
222 
223  m_QuatLandmarkTransform->SetMatrix(m_LandmarkTransform->GetMatrix());
224 
225  m_QuatLandmarkTransform->Compose(m_QuatTransform, true); // compose navigation data transform and landmark transform
226 
227  vnl_quaternion<double> vnlQuatOut = m_QuatLandmarkTransform->GetRotation(); // convert composed transform back into a quaternion
228  NavigationData::OrientationType quatOut(vnlQuatOut[0], vnlQuatOut[1], vnlQuatOut[2], vnlQuatOut[3]); // convert back into navigation data orientation
229  output->SetOrientation(quatOut); // update output navigation data with new orientation
230  output->SetDataValid(true); // operation was successful, therefore data of output is valid.
231  }
232 }
233 
234 
236 {
237  return (m_SourcePoints.size() >= 3) && (m_TargetPoints.size() >= 3);
238 }
239 
240 
241 void mitk::NavigationDataLandmarkTransformFilter::PrintSelf( std::ostream& os, itk::Indent indent ) const
242 {
243  Superclass::PrintSelf(os, indent);
244 
245  os << indent << this->GetNameOfClass() << ":\n";
246  os << indent << m_SourcePoints.size() << " SourcePoints exist: \n";
247  itk::Indent nextIndent = indent.GetNextIndent();
248  unsigned int i = 0;
249  for (LandmarkPointContainer::const_iterator it = m_SourcePoints.begin(); it != m_SourcePoints.end(); ++it)
250  {
251  os << nextIndent << "Point " << i++ << ": [";
252  os << it->GetElement(0);
253  for (unsigned int i = 1; i < TransformInitializerType::LandmarkPointType::GetPointDimension(); ++i)
254  {
255  os << ", " << it->GetElement(i);
256  }
257  os << "]\n";
258  }
259 
260  os << indent << m_TargetPoints.size() << " TargetPoints exist: \n";
261  i = 0;
262  for (LandmarkPointContainer::const_iterator it = m_TargetPoints.begin(); it != m_TargetPoints.end(); ++it)
263  {
264  os << nextIndent << "Point " << i++ << ": [";
265  os << it->GetElement(0);
266  for (unsigned int i = 1; i < TransformInitializerType::LandmarkPointType::GetPointDimension(); ++i)
267  {
268  os << ", " << it->GetElement(i);
269  }
270  os << "]\n";
271  }
272  os << indent << "Landmarktransform initialized: " << this->IsInitialized() << "\n";
273  if (this->IsInitialized() == true)
274  m_LandmarkTransform->Print(os, nextIndent);
275  os << indent << "Registration error statistics:\n";
276  os << nextIndent << "FRE: " << this->GetFRE() << "\n";
277  os << nextIndent << "FRE std.dev.: " << this->GetFREStdDev() << "\n";
278  os << nextIndent << "FRE RMS: " << this->GetRMSError() << "\n";
279  os << nextIndent << "Minimum registration error: " << this->GetMinError() << "\n";
280  os << nextIndent << "Maximum registration error: " << this->GetMaxError() << "\n";
281  os << nextIndent << "Absolute Maximum registration error: " << this->GetAbsMaxError() << "\n";
282 }
283 
284 
286 {
287  return m_Errors;
288 }
289 
290 
292 {
293  if (sources.size() < 6 || targets.size() < 6)
294  return false;
295  //throw std::invalid_argument("ICP correspondence finding needs at least 6 landmarks");
296 
297  /* lots of type definitions */
298  typedef itk::PointSet<mitk::ScalarType, 3> PointSetType;
299 
300  typedef itk::EuclideanDistancePointMetric< PointSetType, PointSetType> MetricType;
301  typedef itk::VersorRigid3DTransform< double > TransformType;
302  typedef itk::PointSetToPointSetRegistrationMethod< PointSetType, PointSetType > RegistrationType;
303 
304  /* copy landmarks to itk pointsets for registration */
305  PointSetType::Pointer sourcePointSet = PointSetType::New();
306  unsigned int i = 0;
307  for (LandmarkPointContainer::const_iterator it = sources.begin(); it != sources.end(); ++it)
308  {
309  PointSetType::PointType doublePoint;
310  mitk::itk2vtk(*it, doublePoint); // copy mitk::ScalarType point into double point as workaround to ITK 3.10 bug
311  sourcePointSet->SetPoint(i++, doublePoint );
312  }
313 
314  i = 0;
315  PointSetType::Pointer targetPointSet = PointSetType::New();
316  for (LandmarkPointContainer::const_iterator it = targets.begin(); it != targets.end(); ++it)
317  {
318  PointSetType::PointType doublePoint;
319  mitk::itk2vtk(*it, doublePoint); // copy mitk::ScalarType point into double point as workaround to ITK 3.10 bug
320  targetPointSet->SetPoint(i++, doublePoint );
321  }
322 
323  TransformType::Pointer transform = TransformType::New();
324  transform->SetIdentity();
325 
326  itk::LevenbergMarquardtOptimizer::Pointer optimizer = itk::LevenbergMarquardtOptimizer::New();
327  optimizer->SetUseCostFunctionGradient(false);
328 
329  RegistrationType::Pointer registration = RegistrationType::New();
330 
331  // Scale the translation components of the Transform in the Optimizer
332  itk::LevenbergMarquardtOptimizer::ScalesType scales(transform->GetNumberOfParameters());
333  const double translationScale = 5000; //sqrtf(targetBoundingBox->GetDiagonalLength2()) * 1000; // dynamic range of translations
334  const double rotationScale = 1.0; // dynamic range of rotations
335  scales[0] = 1.0 / rotationScale;
336  scales[1] = 1.0 / rotationScale;
337  scales[2] = 1.0 / rotationScale;
338  scales[3] = 1.0 / translationScale;
339  scales[4] = 1.0 / translationScale;
340  scales[5] = 1.0 / translationScale;
341 
342  unsigned long numberOfIterations = 80000;
343  double gradientTolerance = 1e-10; // convergence criterion
344  double valueTolerance = 1e-10; // convergence criterion
345  double epsilonFunction = 1e-10; // convergence criterion
346  optimizer->SetScales( scales );
347  optimizer->SetNumberOfIterations( numberOfIterations );
348  optimizer->SetValueTolerance( valueTolerance );
349  optimizer->SetGradientTolerance( gradientTolerance );
350  optimizer->SetEpsilonFunction( epsilonFunction );
351 
352 
353  registration->SetInitialTransformParameters( transform->GetParameters() );
354  //------------------------------------------------------
355  // Connect all the components required for Registration
356  //------------------------------------------------------
357  MetricType::Pointer metric = MetricType::New();
358 
359  registration->SetMetric( metric );
360  registration->SetOptimizer( optimizer );
361  registration->SetTransform( transform );
362  registration->SetFixedPointSet( targetPointSet );
363  registration->SetMovingPointSet( sourcePointSet );
364 
365  try
366  {
367  //registration->StartRegistration();
368  registration->Update();
369  }
370  catch( itk::ExceptionObject & e )
371  {
372  MITK_INFO << "Exception caught during ICP optimization: " << e;
373  return false;
374  //throw e;
375  }
376  MITK_INFO << "ICP successful: Solution = " << transform->GetParameters() << std::endl;
377  MITK_INFO << "Metric value: " << metric->GetValue(transform->GetParameters());
378 
379  /* find point correspondences */
380 
381  LandmarkPointContainer sortedSources;
382  for (LandmarkPointContainer::const_iterator targetsIt = targets.begin(); targetsIt != targets.end(); ++targetsIt)
383  {
384  double minDistance = itk::NumericTraits<double>::max();
385  LandmarkPointContainer::iterator minDistanceIterator = sources.end();
386  for (LandmarkPointContainer::iterator sourcesIt = sources.begin(); sourcesIt != sources.end(); ++sourcesIt)
387  {
388  TransformInitializerType::LandmarkPointType transformedSource = transform->TransformPoint(*sourcesIt);
389  double dist = targetsIt->EuclideanDistanceTo(transformedSource);
390  MITK_INFO << "target: " << *targetsIt << ", source: " << *sourcesIt << ", transformed source: " << transformedSource << ", dist: " << dist;
391  if (dist < minDistance )
392  {
393  minDistanceIterator = sourcesIt;
394  minDistance = dist;
395  }
396  }
397  if (minDistanceIterator == sources.end())
398  return false;
399  MITK_INFO << "minimum distance point is: " << *minDistanceIterator << " (dist: " << targetsIt->EuclideanDistanceTo(transform->TransformPoint(*minDistanceIterator)) << ", minDist: " << minDistance << ")";
400  sortedSources.push_back(*minDistanceIterator); // this point is assigned
401  sources.erase(minDistanceIterator); // erase it from sources to avoid duplicate assigns
402  }
403  //for (LandmarkPointContainer::const_iterator sortedSourcesIt = sortedSources.begin(); targetsIt != sortedSources.end(); ++targetsIt)
404  sources = sortedSources;
405  return true;
406 }
407 
408 
410 {
411  try
412  {
413  /* calculate transform from landmarks */
414  m_LandmarkTransformInitializer->SetMovingLandmarks(targets);
415  m_LandmarkTransformInitializer->SetFixedLandmarks(sources); // itk registration always maps from fixed object space to moving object space
416  m_LandmarkTransform->SetIdentity();
417  m_LandmarkTransformInitializer->InitializeTransform();
418 
419  /* Calculate error statistics for the transform */
420  TransformInitializerType::LandmarkPointType curData;
421  m_Errors.clear();
422  for (LandmarkPointContainer::size_type index = 0; index < sources.size(); index++)
423  {
424  curData = m_LandmarkTransform->TransformPoint(sources.at(index));
425  m_Errors.push_back(curData.EuclideanDistanceTo(targets.at(index)));
426  }
428  this->Modified();
429  }
430  catch (std::exception& e)
431  {
432  m_Errors.clear();
433  m_LandmarkTransform->SetIdentity();
434  itkExceptionMacro("Initializing landmark-transform failed\n. " << e.what());
435  }
436 }
mitk::ScalarType m_ErrorStdDev
standard deviation of the Fiducial Registration Error
map::core::continuous::Elements< 3 >::InternalPointSetType PointSetType
NavigationData * GetOutput(void)
return the output (output with id 0) of the filter
#define MITK_INFO
Definition: mitkLogMacros.h:18
TransformInitializerType::Pointer m_LandmarkTransformInitializer
landmark based transform initializer
NavigationDataToNavigationDataFilter is the base class of all filters that receive NavigationDatas as...
mitk::ScalarType m_ErrorMax
maximum registration error / worst fitting landmark distance
double ScalarType
mitk::ScalarType m_ErrorMin
minimum registration error / best fitting landmark distance
Navigation Data.
mitk::ScalarType GetRMSError() const
Returns the Root Mean Square of the registration error.
DataCollection - Class to facilitate loading/accessing structured data.
virtual void SetDataValid(bool _arg)
sets the dataValid flag of the NavigationData object indicating if the object contains valid data ...
void AccumulateStatistics(ErrorVector &vector)
calculate error metrics for the transforms.
mitk::ScalarType GetMaxError() const
Returns the maximum registration error / worst fitting landmark distance.
mitk::Quaternion OrientationType
Type that holds the orientation part of the tracking data.
mitk::ScalarType GetFRE() const
Returns the Fiducial Registration Error.
void UpdateLandmarkTransform(const LandmarkPointContainer &sources, const LandmarkPointContainer &targets)
calculates the transform using source and target PointSets
virtual PositionType GetPosition() const
returns position of the NavigationData object
void FillVector3D(Tout &out, mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z)
Definition: mitkArray.h:106
bool m_UseICPInitialization
find source <–> target point correspondences with iterative closest point optimization ...
const NavigationData * GetInput(void) const
Get the input of this filter.
mitk::ScalarType m_ErrorMean
Fiducial Registration Error.
LandmarkPointContainer m_SourcePoints
positions of the source points
mitk::ScalarType GetMinError() const
Returns the minimum registration error / best fitting landmark distance.
void PrintSelf(std::ostream &os, itk::Indent indent) const override
print object info to ostream
void InitializeLandmarkTransform(LandmarkPointContainer &sources, const LandmarkPointContainer &targets)
initializes the transform using source and target PointSets
virtual void SetOrientation(OrientationType _arg)
sets the orientation of the NavigationData object
ErrorVector m_Errors
stores the euclidean distance of each transformed source landmark and its respective target landmark ...
QuaternionTransformType::Pointer m_QuatTransform
further transform needed to rotate orientation
bool FindCorrespondentLandmarks(LandmarkPointContainer &sources, const LandmarkPointContainer &targets) const
perform an iterative closest point matching to find corresponding landmarks that will be used for lan...
static T max(T x, T y)
Definition: svm.cpp:56
mitk::ScalarType m_ErrorAbsMax
the absolute maximum registration error
LandmarkTransformType::Pointer m_LandmarkTransform
transform calculated from source and target points
const ErrorVector & GetErrorVector() const
Returns a vector with the euclidean distance of each transformed source point to its respective targe...
virtual void SetPosition(PositionType _arg)
sets the position of the NavigationData object
static T min(T x, T y)
Definition: svm.cpp:53
void itk2vtk(const Tin &in, Tout &out)
void Graft(const DataObject *data) override
Graft the data and information from one NavigationData to another.
TransformInitializerType::LandmarkPointContainer LandmarkPointContainer
void GenerateData() override
transforms input NDs according to the calculated LandmarkTransform
virtual OrientationType GetOrientation() const
returns the orientation of the NavigationData object
virtual bool IsDataValid() const
returns true if the object contains valid data
virtual void SetSourceLandmarks(mitk::PointSet::Pointer sourcePointSet)
Set points used as source points for landmark transform.
mitk::ScalarType GetFREStdDev() const
Returns the standard deviation of the Fiducial Registration Error.
::map::core::RegistrationBase RegistrationType
LandmarkPointContainer m_TargetPoints
positions of the target points
virtual void SetTargetLandmarks(mitk::PointSet::Pointer targetPointSet)
Set points used as target points for landmark transform.
mitk::ScalarType m_ErrorRMS
Root Mean Square of the registration error.
QuaternionTransformType::Pointer m_QuatLandmarkTransform
transform needed to rotate orientation
mitk::ScalarType GetAbsMaxError() const
Returns the absolute maximum registration error.