Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.