MITK-IGT
IGT Extension of MITK
Loading...
Searching...
No Matches
mitkNavigationDataLandmarkTransformFilter.cpp
Go to the documentation of this file.
1/*============================================================================
2
3The Medical Imaging Interaction Toolkit (MITK)
4
5Copyright (c) German Cancer Research Center (DKFZ)
6All rights reserved.
7
8Use of this source code is governed by a 3-clause BSD license that can be
9found 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
25m_ErrorMean(-1.0), m_ErrorStdDev(-1.0), m_ErrorRMS(-1.0), m_ErrorMin(-1.0), m_ErrorMax(-1.0), m_ErrorAbsMax(-1.0),
26m_SourcePoints(), m_TargetPoints(), m_LandmarkTransformInitializer(nullptr), m_LandmarkTransform(nullptr),
27m_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;
43 m_LandmarkTransformInitializer = 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
68void mitk::NavigationDataLandmarkTransformFilter::SetSourceLandmarks(mitk::PointSet::Pointer mitkSourcePointSet)
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())
87 this->InitializeLandmarkTransform(m_SourcePoints, m_TargetPoints);
88}
89
90
91void mitk::NavigationDataLandmarkTransformFilter::SetTargetLandmarks(mitk::PointSet::Pointer mitkTargetPointSet)
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())
108 this->InitializeLandmarkTransform(m_SourcePoints, m_TargetPoints);
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
149{
150 //mean, min, max
151 m_ErrorMean = 0.0;
152 m_ErrorMin = itk::NumericTraits<mitk::ScalarType>::max();
153 m_ErrorMax = itk::NumericTraits<mitk::ScalarType>::min();
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
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 */
219 NavigationData::OrientationType quatIn = input->GetOrientation();
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
241void 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
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 }
427 this->AccumulateStatistics(m_Errors);
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}
QuaternionTransformType::Pointer m_QuatTransform
further transform needed to rotate orientation
TransformInitializerType::Pointer m_LandmarkTransformInitializer
landmark based transform initializer
mitk::ScalarType GetAbsMaxError() const
Returns the absolute maximum registration error.
void InitializeLandmarkTransform(LandmarkPointContainer &sources, const LandmarkPointContainer &targets)
initializes the transform using source and target PointSets
const ErrorVector & GetErrorVector() const
Returns a vector with the euclidean distance of each transformed source point to its respective targe...
void PrintSelf(std::ostream &os, itk::Indent indent) const override
print object info to ostream
TransformInitializerType::LandmarkPointContainer LandmarkPointContainer
mitk::ScalarType GetRMSError() const
Returns the Root Mean Square of the registration error.
LandmarkTransformType::Pointer m_LandmarkTransform
transform calculated from source and target points
QuaternionTransformType::Pointer m_QuatLandmarkTransform
transform needed to rotate orientation
void GenerateData() override
transforms input NDs according to the calculated LandmarkTransform
void AccumulateStatistics(ErrorVector &vector)
calculate error metrics for the transforms.
mitk::ScalarType GetFREStdDev() const
Returns the standard deviation of the Fiducial Registration Error.
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 void SetTargetLandmarks(mitk::PointSet::Pointer targetPointSet)
Set points used as target points for landmark transform.
virtual void SetSourceLandmarks(mitk::PointSet::Pointer sourcePointSet)
Set points used as source points for landmark transform.
mitk::ScalarType GetMinError() const
Returns the minimum registration error / best fitting landmark distance.
mitk::ScalarType GetMaxError() const
Returns the maximum registration error / worst fitting landmark distance.
bool FindCorrespondentLandmarks(LandmarkPointContainer &sources, const LandmarkPointContainer &targets) const
perform an iterative closest point matching to find corresponding landmarks that will be used for lan...
NavigationDataToNavigationDataFilter is the base class of all filters that receive NavigationDatas as...
mitk::Point3D TransformPoint(const mitk::Point3D point) const
Transform by an affine transformation.
void Graft(const DataObject *data) override
Graft the data and information from one NavigationData to another.
mitk::Quaternion OrientationType
Type that holds the orientation part of the tracking data.
virtual bool IsDataValid() const
returns true if the object contains valid data
mitk::Point3D PositionType
Type that holds the position part of the tracking data.
itk::VersorRigid3DTransform< mitk::ScalarType > TransformType
IGT Exceptions.