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