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
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.