Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkPyramidImageRegistrationMethod.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 
19 #include "mitkException.h"
20 #include "mitkImageAccessByItk.h"
21 
23  : m_FixedImage(NULL),
24  m_MovingImage(NULL),
25  m_CrossModalityRegistration(true),
26  m_UseAffineTransform(true),
27  m_UseWindowedSincInterpolator(false),
28  m_UseNearestNeighborInterpolator(false),
29  m_UseMask(false),
30  m_EstimatedParameters(0),
31  m_InitialParameters(0),
32  m_Verbose(false)
33 {
34 
35 }
36 
38 {
39  if( m_EstimatedParameters != NULL)
40  {
41  delete [] m_EstimatedParameters;
42  }
43 
44 }
45 
47 {
48  if( fixed.IsNotNull() )
49  {
50  m_FixedImage = fixed;
51  }
52 }
53 
55 {
56  if( moving.IsNotNull() )
57  {
58  m_MovingImage = moving;
59  }
60 }
61 
62 
64 {
65  m_FixedImageMask = mask;
66 }
67 
69 {
70  if( m_MovingImage.IsNull() )
71  {
72  mitkThrow() << " Moving image is null";
73  }
74 
75  if( m_FixedImage.IsNull() )
76  {
77  mitkThrow() << " Moving image is null";
78  }
79 
80  unsigned int allowedDimension = 3;
81 
82  if( m_FixedImage->GetDimension() != allowedDimension ||
83  m_MovingImage->GetDimension() != allowedDimension )
84  {
85  mitkThrow() << " Only 3D Images supported.";
86  }
87 
88  //
89  // One possibility: use the FixedDimesnionByItk, but this instantiates ALL possible
90  // pixel type combinations!
91  // AccessTwoImagesFixedDimensionByItk( m_FixedImage, m_MovingImage, RegisterTwoImages, 3);
92  // in helper: TypeSubset : short, float
93  AccessTwoImagesFixedDimensionTypeSubsetByItk( m_FixedImage, m_MovingImage, RegisterTwoImagesV4, 3);
94 
95 }
96 
99 {
100  TransformMatrixType output;
101  if( m_EstimatedParameters == NULL )
102  {
103  output.set_identity();
104  return output;
105  }
106 
107  typedef itk::MatrixOffsetTransformBase< double, 3, 3> BaseTransformType;
109 
110  if( this->m_UseAffineTransform )
111  {
112  typedef itk::AffineTransform< double > TransformType;
114 
115  TransformType::ParametersType affine_params( TransformType::ParametersDimension );
116  this->GetParameters( &affine_params[0] );
117 
118  transform->SetParameters( affine_params );
119  base_transform = transform;
120  }
121  else
122  {
123  typedef itk::Euler3DTransform< double > RigidTransformType;
125 
126  RigidTransformType::ParametersType rigid_params( RigidTransformType::ParametersDimension );
127  this->GetParameters( &rigid_params[0] );
128 
129  rtransform->SetParameters( rigid_params );
130 
131  base_transform = rtransform;
132  }
133 
134  return base_transform->GetMatrix().GetVnlMatrix();
135 
136 }
137 
140 {
141 
143  //output->Initialize( this->m_FixedImage );
144 
145  AccessFixedDimensionByItk_1( this->m_MovingImage, ResampleMitkImage, 3, output );
146 
147  return output;
148 
149 }
150 
152 {
154 
155 
156  unsigned int dim = 12;
157  if( !m_UseAffineTransform )
158  dim = 6;
159 
160  if (m_EstimatedParameters == NULL)
161  m_EstimatedParameters = new double[dim];
162 
163  double tmpParams[12];
164  // save and set temporal transformation values
165  for( unsigned int i=0; i<dim; i++)
166  {
167  tmpParams[i] = m_EstimatedParameters[i];
168  m_EstimatedParameters[i] = transform[i];
169  }
170 
171  AccessFixedDimensionByItk_1( movingImage, ResampleMitkImage, 3, output );
172 
173  // Restore old values
174  for( unsigned int i=0; i<dim; i++)
175  {
176  m_EstimatedParameters[i] = tmpParams[i];
177  }
178 
179  return output;
180 
181 }
182 
183 // ITK 4 Includes
184 #include <itkImageRegistrationMethodv4.h>
185 #include <itkGradientDescentLineSearchOptimizerv4.h>
186 #include <itkMattesMutualInformationImageToImageMetricv4.h>
187 #include <itkJointHistogramMutualInformationImageToImageMetricv4.h>
188 #include <itkCorrelationImageToImageMetricv4.h>
189 #include <itkOptimizerParameterScalesEstimator.h>
190 #include <itkRegistrationParameterScalesFromPhysicalShift.h>
191 
192 template <typename TPixel1, unsigned int VImageDimension1, typename TPixel2, unsigned int VImageDimension2>
194 RegisterTwoImagesV4(itk::Image<TPixel1, VImageDimension1>* itkImage1, itk::Image<TPixel2, VImageDimension2>* itkImage2)
195 {
196  // Basic typedefs needed
197  typedef typename itk::Image<TPixel1, VImageDimension1> ItkImageTypeFixed;
198  typedef typename itk::Image<TPixel2, VImageDimension1> ItkImageTypeMoving;
199 
200  typedef itk::MatrixOffsetTransformBase< double, VImageDimension1, VImageDimension2 > BaseTransformType;
201 
202  typedef itk::Image< double, 3> ItkRegistrationImageType;
203  typedef itk::CastImageFilter< ItkImageTypeFixed, ItkRegistrationImageType> FixedCastFilterType;
204  typedef itk::CastImageFilter< ItkImageTypeMoving, ItkRegistrationImageType> MovingCastFilterType;
205 
206  typedef typename itk::ImageRegistrationMethodv4< ItkRegistrationImageType, ItkRegistrationImageType, AffineTransformType> RegistrationType;
207  typedef typename itk::MattesMutualInformationImageToImageMetricv4< ItkRegistrationImageType, ItkRegistrationImageType> MIMetricType;
208  typedef typename itk::CorrelationImageToImageMetricv4< ItkRegistrationImageType, ItkRegistrationImageType > NCMetricType;
209  typedef typename itk::ImageToImageMetricv4< ItkRegistrationImageType, ItkRegistrationImageType > BaseMetricType;
210 
211  typedef typename itk::ImageRegistrationMethodv4< ItkRegistrationImageType, ItkRegistrationImageType, RigidTransformType> RigidRegistrationType;
212 
213  typedef itk::GradientDescentLineSearchOptimizerv4 OptimizerType;
214 
215  typename ItkImageTypeFixed::Pointer referenceImage = itkImage1;
216  typename ItkImageTypeMoving::Pointer movingImage = itkImage2;
217 
218  // initial parameter dimension ( affine = default )
219  unsigned int paramDim = 12;
220  typename BaseTransformType::Pointer transform;
221  if( m_UseAffineTransform )
222  {
223  transform = AffineTransformType::New();
224  }
225  else
226  {
227  transform = RigidTransformType::New();
228  paramDim = 6;
229  }
230 
231  typename BaseTransformType::ParametersType initialParams(paramDim);
232 
233  initialParams.Fill(0);
234  if( m_UseAffineTransform )
235  {
236  initialParams[0] = initialParams[4] = initialParams[8] = 1;
237  }
238 
239 
240  // [Prepare registration]
241  // The ITKv4 Methods ( the MI Metric ) require double-type images so we need to perform cast first
244 
245  try
246  {
247  caster_f->SetInput(0, referenceImage );
248  caster_m->SetInput(0, movingImage );
249 
250  caster_f->Update();
251  caster_m->Update();
252 
253  }
254  catch( const itk::ExceptionObject &/*e*/ )
255  {
256  return ;
257  }
258 
259  // [Prepare Registration]
260  // Estimate the size of the pyramid based on image dimension
261  // here the image size on the topmost level must remain higher than the threshold
262  unsigned int max_pyramid_lvl = 4;
263  unsigned int max_schedule_val = 8;
264  const unsigned int min_required_image_size = 12;
265  typename ItkImageTypeFixed::RegionType::SizeType image_size = referenceImage->GetLargestPossibleRegion().GetSize();
266  unsigned int min_value = std::min( image_size[0], std::min( image_size[1], image_size[2]));
267 
268  // Adapt also the optimizer settings to the number of levels
269  float optmaxstep = 10;
270  float optminstep = 0.1f;
271  unsigned int iterations = 40;
272  unsigned int convergence_win_size = 8;
273  double convergence_tolerance = 1e-5;
274  while( max_schedule_val > 1.0 && (min_value / max_schedule_val < min_required_image_size) )
275  {
276  max_schedule_val /= 2;
277  max_pyramid_lvl--;
278  optmaxstep -= 2;
279  optminstep *= 0.5f;
280  convergence_win_size += 4;
281  convergence_tolerance *= 0.5;
282  }
283 
284  typename RegistrationType::ShrinkFactorsArrayType shrink_factors(max_pyramid_lvl);
285  shrink_factors.Fill(1);
286 
287  shrink_factors[0] = max_schedule_val;
288  for( unsigned int i=1; i<max_pyramid_lvl; i++)
289  {
290  shrink_factors[i] = std::max( shrink_factors[i-1]/2, 1ul);
291  }
292 
293  if(m_Verbose)
294  {
295  MITK_INFO("dw.pyramid.reg") << " : Pyramid size set to : " << max_pyramid_lvl;
296  }
297 
298  // [Prepare Registration]
299  // Initialize the optimizer
300  // (i) Use the scales estimator ( depends on metric )
301  typename OptimizerType::Pointer optimizer = OptimizerType::New();
302  typename BaseMetricType::Pointer base_metric;
303  if( m_CrossModalityRegistration )
304  {
305  typename MIMetricType::Pointer metric = MIMetricType::New();
306 
309  mi_estimator->SetMetric( metric );
310 
311  optimizer->SetScalesEstimator( mi_estimator );
312 
313  base_metric = metric;
314  }
315  else
316  {
317  typename NCMetricType::Pointer metric = NCMetricType::New();
318 
321 
322  nc_estimator->SetMetric( metric );
323 
324  optimizer->SetScalesEstimator( nc_estimator );
325 
326  base_metric = metric;
327  }
328 
329  optimizer->SetDoEstimateLearningRateOnce( false );
330  optimizer->SetDoEstimateLearningRateAtEachIteration( true );
331  optimizer->SetNumberOfIterations( iterations );
332  optimizer->SetConvergenceWindowSize( convergence_win_size );
333  optimizer->SetMaximumStepSizeInPhysicalUnits( optmaxstep );
334  optimizer->SetMinimumConvergenceValue( convergence_tolerance );
335 
336  // line search options
337  optimizer->SetEpsilon( 1e-3 );
338  optimizer->SetLowerLimit( 0.3 );
339  optimizer->SetUpperLimit( 1.7 );
340  optimizer->SetMaximumLineSearchIterations( 20 );
341 
342  // add observer tag if verbose
343  unsigned long vopt_tag = 0;
344  if(m_Verbose)
345  {
346  MITK_INFO << " :: Starting at " << initialParams;
347 
350 
351  vopt_tag = optimizer->AddObserver( itk::IterationEvent(), iterationObserver );
352  }
353 
354 
355  // Initializing by Geometry
356  if( !m_UseAffineTransform && m_InitializeByGeometry )
357  {
358  typedef itk::CenteredVersorTransformInitializer< ItkImageTypeFixed, ItkImageTypeMoving> TransformInitializerType;
360  MITK_INFO << "Initializer starting at : " << rigidTransform->GetParameters();
361 
363  initializer->SetTransform( rigidTransform);
364  initializer->SetFixedImage( referenceImage.GetPointer() );
365  initializer->SetMovingImage( movingImage.GetPointer() );
366  initializer->MomentsOn();
367  initializer->InitializeTransform();
368 
369  MITK_INFO << "Initialized Rigid position : " << rigidTransform->GetParameters();
370  initialParams[3] = rigidTransform->GetParameters()[3];
371  initialParams[4] = rigidTransform->GetParameters()[4];
372  initialParams[5] = rigidTransform->GetParameters()[5];
373  }
374 
375  // [Prepare Registration]
376  // Masking (Optional)
377  if( m_UseMask )
378  {
379  typedef itk::Image<unsigned char, 3> BinaryImageType;
380  BinaryImageType::Pointer itkFixedImageMask = BinaryImageType::New();
382 
383  CastToItkImage( m_FixedImageMask, itkFixedImageMask);
385  notFilter->SetInput(itkFixedImageMask);
386  notFilter->Update();
387  fixedMaskSpatialObject->SetImage( notFilter->GetOutput() );
388  base_metric->SetFixedImageMask( fixedMaskSpatialObject );
389  }
390 
391 
392  if( m_EstimatedParameters != NULL)
393  {
394  delete [] m_EstimatedParameters;
395  }
396 
397  m_EstimatedParameters = new double[paramDim];
398 
399  //-----------------
400  //-----------------
401 
402  // [Prepare Registration]
403  // combine all components to set-up registration
404  if( m_UseAffineTransform )
405  {
406 
407  typename RegistrationType::Pointer registration = RegistrationType::New();
408 
409  registration->SetFixedImage( 0, caster_f->GetOutput() );
410  registration->SetMovingImage( 0, caster_m->GetOutput() );
411  registration->SetMetric( base_metric );
412  registration->SetOptimizer( optimizer );
413  registration->SetMovingInitialTransform( transform.GetPointer() );
414  registration->SetNumberOfLevels(max_pyramid_lvl);
415  registration->SetShrinkFactorsPerLevel( shrink_factors );
416 
417  // observe the pyramid level change in order to adapt parameters
420  unsigned int pyramid_tag = registration->AddObserver( itk::InitializeEvent(), pyramid_observer );
421 
422 
423  try
424  {
425  registration->Update();
426  }
427  catch( const itk::ExceptionObject &e)
428  {
429  registration->Print( std::cout );
430 
431  MITK_ERROR << "[Registration Update] Caught ITK exception: ";
432  MITK_ERROR("itk.exception") << e.what();
433 
434  mitkThrow() << "Registration failed with exception: " << e.what();
435  }
436 
437  // [Post Registration]
438  // Retrieve the last transformation parameters from the performed registration task
439  typename BaseTransformType::ParametersType finalParameters = registration->GetOptimizer()->GetCurrentPosition();
440  for( unsigned int i=0; i<paramDim; i++)
441  {
442  m_EstimatedParameters[i] = finalParameters[i];
443  }
444 
445  if( m_Verbose )
446  {
447  MITK_INFO("Params") << optimizer->GetValue() << " :: " << finalParameters;
448  }
449 
450  if( pyramid_tag )
451  {
452  registration->RemoveObserver( pyramid_tag );
453  }
454 
455  }
456  //-----------
457  //-----------
458  else
459  {
460 
462 
463  registration->SetFixedImage( 0, caster_f->GetOutput() );
464  registration->SetMovingImage( 0, caster_m->GetOutput() );
465  registration->SetMetric( base_metric );
466  registration->SetOptimizer( optimizer );
467  registration->SetMovingInitialTransform( transform.GetPointer() );
468  registration->SetNumberOfLevels(max_pyramid_lvl);
469  registration->SetShrinkFactorsPerLevel( shrink_factors );
470 
471  // observe the pyramid level change in order to adapt parameters
474  unsigned int pyramid_tag = registration->AddObserver( itk::InitializeEvent(), pyramid_observer );
475 
476 
477  try
478  {
479  registration->Update();
480  }
481  catch( const itk::ExceptionObject &e)
482  {
483  registration->Print( std::cout );
484 
485  MITK_ERROR << "[Registration Update] Caught ITK exception: ";
486  MITK_ERROR("itk.exception") << e.what();
487 
488  mitkThrow() << "Registration failed with exception: " << e.what();
489  }
490 
491  // [Post Registration]
492  // Retrieve the last transformation parameters from the performed registration task
493  typename BaseTransformType::ParametersType finalParameters = registration->GetOptimizer()->GetCurrentPosition();
494  for( unsigned int i=0; i<paramDim; i++)
495  {
496  m_EstimatedParameters[i] = finalParameters[i];
497  }
498 
499  if( m_Verbose )
500  {
501  MITK_INFO("Params") << optimizer->GetValue() << " :: " << finalParameters;
502  }
503 
504  if( pyramid_tag )
505  {
506  registration->RemoveObserver( pyramid_tag );
507  }
508 
509  }
510 
511  if( m_Verbose )
512  {
513  MITK_INFO("Termination") << optimizer->GetStopConditionDescription();
514  }
515 
516  // remove optimizer tag if used
517  if( vopt_tag )
518  {
519  optimizer->RemoveObserver( vopt_tag );
520  }
521 
522 
523 
524 
525 
526 }
527 
528 template< typename TPixel, unsigned int VDimension>
530 ResampleMitkImage( itk::Image<TPixel, VDimension>* itkImage,
531  mitk::Image::Pointer& outputImage )
532 {
533  typedef typename itk::Image< TPixel, VDimension> ImageType;
534  typedef typename itk::ResampleImageFilter< ImageType, ImageType, double> ResampleImageFilterType;
535 
536  typedef itk::LinearInterpolateImageFunction< ImageType, double > InterpolatorType;
537  typename InterpolatorType::Pointer linear_interpolator = InterpolatorType::New();
538 
539 
540  typedef itk::NearestNeighborInterpolateImageFunction< ImageType, double > NearestNeighborInterpolatorType;
542 
543 
544  typedef itk::WindowedSincInterpolateImageFunction< ImageType, 7> WindowedSincInterpolatorType;
546 
547  typename ImageType::Pointer reference_image = ImageType::New();
548  CastToItkImage(m_FixedImage,reference_image);
549 
550  typedef itk::MatrixOffsetTransformBase< double, 3, 3> BaseTransformType;
552 
553  if( this->m_UseAffineTransform )
554  {
555  typedef itk::AffineTransform< double > TransformType;
557 
558  TransformType::ParametersType affine_params( TransformType::ParametersDimension );
559  this->GetParameters( &affine_params[0] );
560 
561  transform->SetParameters( affine_params );
562 
563  base_transform = transform;
564  }
565  // Rigid
566  else
567  {
568  typedef itk::Euler3DTransform< double > RigidTransformType;
570 
571  RigidTransformType::ParametersType rigid_params( RigidTransformType::ParametersDimension );
572  this->GetParameters( &rigid_params[0] );
573 
574  rtransform->SetParameters( rigid_params );
575 
576  base_transform = rtransform;
577  }
578 
580 
581  resampler->SetInterpolator( linear_interpolator );
582  if( m_UseWindowedSincInterpolator )
583  resampler->SetInterpolator( sinc_interpolator );
584  if ( m_UseNearestNeighborInterpolator)
585  resampler->SetInterpolator ( nn_interpolator );
586 
587  //resampler->SetNumberOfThreads(1);
588 
589  resampler->SetInput( itkImage );
590  resampler->SetTransform( base_transform );
591  resampler->SetReferenceImage( reference_image );
592  resampler->UseReferenceImageOn();
593 
594  resampler->Update();
595 
596  mitk::GrabItkImageMemory( resampler->GetOutput(), outputImage);
597 }
mitk::Image::Pointer GetResampledMovingImage()
Returns the moving image transformed according to the estimated transformation and resampled to the g...
itk::SmartPointer< Self > Pointer
#define MITK_INFO
Definition: mitkLogMacros.h:22
#define MITK_ERROR
Definition: mitkLogMacros.h:24
void ResampleMitkImage(itk::Image< TPixel, VDimension > *itkImage, mitk::Image::Pointer &outputImage)
ResampleMitkImage applies the functionality of an the itk::ResampleImageFilter to the given mitk::Ima...
void RegisterTwoImagesV4(itk::Image< TPixel1, VImageDimension1 > *itkImage1, itk::Image< TPixel2, VImageDimension2 > *itkImage2)
The method takes two itk::Images and performs a multi-scale registration on them. ...
Image::Pointer GrabItkImageMemory(itk::SmartPointer< ItkOutputImageType > &itkimage, mitk::Image *mitkImage=nullptr, const BaseGeometry *geometry=nullptr, bool update=true)
Grabs the memory of an itk::Image (with a specific type) and puts it into an mitk::Image.The memory is managed by the mitk::Image after calling this function. The itk::Image remains valid until the mitk::Image decides to free the memory.
map::core::discrete::Elements< 3 >::InternalImageType ImageType
#define AccessTwoImagesFixedDimensionTypeSubsetByItk(mitkImage1, mitkImage2, itkImageTypeFunction, dimension)
Provides same functionality as AccessTwoImagesFixedDimensionByItk for a subset of types...
#define mitkThrow()
itk::Image< unsigned char, 3 > BinaryImageType
static T max(T x, T y)
Definition: svm.cpp:70
static Pointer New()
static T min(T x, T y)
Definition: svm.cpp:67
TransformMatrixType GetLastRotationMatrix()
Get the rotation part of the transformation as a vnl_fixed_matrix
vnl_matrix_fixed< double, 3, 3 > TransformMatrixType
#define AccessFixedDimensionByItk_1(mitkImage, itkImageTypeFunction, dimension, arg1)
void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer< ItkOutputImageType > &itkOutputImage)
Cast an mitk::Image to an itk::Image with a specific type.
::map::core::RegistrationBase RegistrationType
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.