[Insight-users] MutualInformationEuler2DRegistration
Luis Ibanez
luis.ibanez at kitware.com
Thu Jul 1 17:22:39 EDT 2004
Hi Ying,
The best remedy agains "parameter sensitivity" is "user-training" :-)
What we use to call 'sensitivity' simply reflects the uncertainty
on the ranges and values that should be used for configuring the
registration. The solution is simply for you to follow the examples
in the SoftwareGuide, that introduce you progressively to the
trade-offs involved in Image Registration. This training will help
you understand how to fine tune the parameters of registration
algorithms in order to get reasonable results.
Please look at the CenteredRidig2D example in the Software Guide
http://www.itk.org/ItkSoftwareGuide.pdf
Section 8.5, pdf-page 263. The source code associated with this
example is in
Insight/Examples/Registration/
ImageRegistration5.cxx
You will find there a discussion on the importance of selecting
the center of rotation for the image.
Note that your images have the interesting characteristic of
having a center of rotation that is far from the corner of the
image *and* far from the center for the image.
It is very important that you initialize the center of rotation
of the transform. That will reduce a lot the sensibility to
the initial parameters of the registration.
Attached you will find a source code file that registers your
images in 200 iterations (about 7 seconds in a Pentium 4 at 2.4Ghz)
Note that it is using MattesMutualInformation and the Centered
transform initializer class.
The same file has been commited as a new example in
Insight/Examples/Registration/
ImageRegistration13.cxx
Please let us know if you have further questions.
Regards,
Luis
-------------------
Ying Du wrote:
> Hi,
>
> I am trying to register some 2D images using the InsightApplication
> "MutualInformationEuler2DRegistration". It seems like that the result is
> very very sensitive to the parameters (the learning rate, rotation
> scale, translation scale).
>
> I tried the application on a very simple case, but could not find a set
> of parameters to get a very good aligned result (there are always a
> small part of the boundaries that are not aligned). The input fixed
> image and moving image are shown at the following link:
> http://www.sciutah.edu/~ydu/img_registration.htm
> <http://www.sci.utah.edu/~ydu/img_registration.htm>
>
> The moving image is rotated for 5 degree and translated for a few
> pixels. This is a pretty simple case, and Mutual information should be
> able to handle it well. I am wondering why the
> MutualInformationEuler2DRegistration application is so difficult to get
> a good registration result for such a simple case, and why it is so
> sensitive to the parameters? Is there any way to solve this "super
> sensitive to parameters" problem?
>
> Many thanks.
>
> Ying
>
>
>
> ------------------------------------------------------------------------
>
-------------- next part --------------
// Please do NOT edit this file.
// It has been automatically generated
// by a perl script from the original cxx sources
// in the Insight/Examples directory
// Any changes should be made in the file
// ImageRegistration4.cxx
/*=========================================================================
Program: Insight Segmentation & Registration Toolkit
Module: $RCSfile: ImageRegistration4.cxx,v $
Language: C++
Date: $Date: 2004/04/03 13:01:42 $
Version: $Revision: 1.23 $
Copyright (c) Insight Software Consortium. All rights reserved.
See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#include "itkImageRegistrationMethod.h"
#include "itkCenteredRigid2DTransform.h"
#include "itkCenteredTransformInitializer.h"
#include "itkMattesMutualInformationImageToImageMetric.h"
#include "itkLinearInterpolateImageFunction.h"
#include "itkRegularStepGradientDescentOptimizer.h"
#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkResampleImageFilter.h"
#include "itkCastImageFilter.h"
// The following section of code implements a Command observer
// used to monitor the evolution of the registration process.
//
#include "itkCommand.h"
class CommandIterationUpdate : public itk::Command
{
public:
typedef CommandIterationUpdate Self;
typedef itk::Command Superclass;
typedef itk::SmartPointer<Self> Pointer;
itkNewMacro( Self );
protected:
CommandIterationUpdate() {};
public:
typedef itk::RegularStepGradientDescentOptimizer OptimizerType;
typedef const OptimizerType * OptimizerPointer;
void Execute(itk::Object *caller, const itk::EventObject & event)
{
Execute( (const itk::Object *)caller, event);
}
void Execute(const itk::Object * object, const itk::EventObject & event)
{
OptimizerPointer optimizer =
dynamic_cast< OptimizerPointer >( object );
if( typeid( event ) != typeid( itk::IterationEvent ) )
{
return;
}
std::cout << optimizer->GetCurrentIteration() << " ";
std::cout << optimizer->GetValue() << " ";
std::cout << optimizer->GetCurrentPosition() << std::endl;
}
};
int main( int argc, char *argv[] )
{
if( argc < 3 )
{
std::cerr << "Missing Parameters " << std::endl;
std::cerr << "Usage: " << argv[0];
std::cerr << " fixedImageFile movingImageFile ";
std::cerr << "outputImagefile [differenceImage]" << std::endl;
return 1;
}
const unsigned int Dimension = 2;
typedef unsigned short PixelType;
typedef itk::Image< PixelType, Dimension > FixedImageType;
typedef itk::Image< PixelType, Dimension > MovingImageType;
typedef itk::CenteredRigid2DTransform< double > TransformType;
typedef itk::RegularStepGradientDescentOptimizer OptimizerType;
typedef itk::LinearInterpolateImageFunction<
MovingImageType,
double > InterpolatorType;
typedef itk::ImageRegistrationMethod<
FixedImageType,
MovingImageType > RegistrationType;
typedef itk::MattesMutualInformationImageToImageMetric<
FixedImageType,
MovingImageType > MetricType;
TransformType::Pointer transform = TransformType::New();
OptimizerType::Pointer optimizer = OptimizerType::New();
InterpolatorType::Pointer interpolator = InterpolatorType::New();
RegistrationType::Pointer registration = RegistrationType::New();
registration->SetOptimizer( optimizer );
registration->SetTransform( transform );
registration->SetInterpolator( interpolator );
MetricType::Pointer metric = MetricType::New();
registration->SetMetric( metric );
metric->SetNumberOfHistogramBins( 20 );
metric->SetNumberOfSpatialSamples( 10000 );
typedef itk::ImageFileReader< FixedImageType > FixedImageReaderType;
typedef itk::ImageFileReader< MovingImageType > MovingImageReaderType;
FixedImageReaderType::Pointer fixedImageReader = FixedImageReaderType::New();
MovingImageReaderType::Pointer movingImageReader = MovingImageReaderType::New();
fixedImageReader->SetFileName( argv[1] );
movingImageReader->SetFileName( argv[2] );
registration->SetFixedImage( fixedImageReader->GetOutput() );
registration->SetMovingImage( movingImageReader->GetOutput() );
fixedImageReader->Update();
registration->SetFixedImageRegion(
fixedImageReader->GetOutput()->GetBufferedRegion() );
typedef itk::CenteredTransformInitializer<
TransformType,
FixedImageType,
MovingImageType > TransformInitializerType;
TransformInitializerType::Pointer initializer = TransformInitializerType::New();
initializer->SetTransform( transform );
initializer->SetFixedImage( fixedImageReader->GetOutput() );
initializer->SetMovingImage( movingImageReader->GetOutput() );
initializer->GeometryOn();
initializer->InitializeTransform();
transform->SetAngle( 0.0 );
registration->SetInitialTransformParameters( transform->GetParameters() );
typedef OptimizerType::ScalesType OptimizerScalesType;
OptimizerScalesType optimizerScales( transform->GetNumberOfParameters() );
const double translationScale = 1.0 / 1000.0;
optimizerScales[0] = 1.0;
optimizerScales[1] = translationScale;
optimizerScales[2] = translationScale;
optimizerScales[3] = translationScale;
optimizerScales[4] = translationScale;
optimizer->SetScales( optimizerScales );
optimizer->SetMaximumStepLength( 0.1 );
optimizer->SetMinimumStepLength( 0.001 );
optimizer->SetNumberOfIterations( 200 );
// Create the Command observer and register it with the optimizer.
//
CommandIterationUpdate::Pointer observer = CommandIterationUpdate::New();
optimizer->AddObserver( itk::IterationEvent(), observer );
try
{
registration->StartRegistration();
}
catch( itk::ExceptionObject & err )
{
std::cout << "ExceptionObject caught !" << std::endl;
std::cout << err << std::endl;
return -1;
}
typedef RegistrationType::ParametersType ParametersType;
ParametersType finalParameters = registration->GetLastTransformParameters();
const double finalAngle = finalParameters[0];
const double finalRotationCenterX = finalParameters[1];
const double finalRotationCenterY = finalParameters[2];
const double finalTranslationX = finalParameters[3];
const double finalTranslationY = finalParameters[4];
unsigned int numberOfIterations = optimizer->GetCurrentIteration();
double bestValue = optimizer->GetValue();
// Print out results
//
const double finalAngleInDegrees = finalAngle * 45.0 / atan(1.0);
std::cout << "Result = " << std::endl;
std::cout << " Angle (radians) " << finalAngle << std::endl;
std::cout << " Angle (degrees) " << finalAngleInDegrees << std::endl;
std::cout << " Center X = " << finalRotationCenterX << std::endl;
std::cout << " Center Y = " << finalRotationCenterY << std::endl;
std::cout << " Translation X = " << finalTranslationX << std::endl;
std::cout << " Translation Y = " << finalTranslationY << std::endl;
std::cout << " Iterations = " << numberOfIterations << std::endl;
std::cout << " Metric value = " << bestValue << std::endl;
typedef itk::ResampleImageFilter<
MovingImageType,
FixedImageType > ResampleFilterType;
TransformType::Pointer finalTransform = TransformType::New();
finalTransform->SetParameters( finalParameters );
ResampleFilterType::Pointer resample = ResampleFilterType::New();
resample->SetTransform( finalTransform );
resample->SetInput( movingImageReader->GetOutput() );
FixedImageType::Pointer fixedImage = fixedImageReader->GetOutput();
resample->SetSize( fixedImage->GetLargestPossibleRegion().GetSize() );
resample->SetOutputOrigin( fixedImage->GetOrigin() );
resample->SetOutputSpacing( fixedImage->GetSpacing() );
resample->SetDefaultPixelValue( 100 );
typedef unsigned char OutputPixelType;
typedef itk::Image< OutputPixelType, Dimension > OutputImageType;
typedef itk::CastImageFilter<
FixedImageType,
OutputImageType > CastFilterType;
typedef itk::ImageFileWriter< OutputImageType > WriterType;
WriterType::Pointer writer = WriterType::New();
CastFilterType::Pointer caster = CastFilterType::New();
writer->SetFileName( argv[3] );
caster->SetInput( resample->GetOutput() );
writer->SetInput( caster->GetOutput() );
writer->Update();
return 0;
}
More information about the Insight-users
mailing list