/*
*多分辨策略,或称为多分辨尺度, 是图像配准中广泛应用的一种方法
*用于加快计算速度, 改进配准精度, 以及配准的健壮性
*基本思想为: 首先使用一种粗糙的尺度对少量的图像像素进行处理,然后在下一层使用一种精确的
*尺度, 并用上一层的结果对其参数进行初始化. 迭代该过程, 直到达到最精确的尺度.
*这种由粗到细, 在大尺度上看整体, 在小尺度上看细节的方法能够极大程度地提高配准成功率
*本例演示了使用:itk::MultiResolutionImageRegistrationMethod 解决一个简单的图像配准问题
*采用多分辨策略进行图像配准, 需要两个图像金字塔组件, 用来创建向下采样的图像序列.
*/
ITK 提供了一个兼容的多分辨策略配准框架,该框架有另外两个组件:固定图像及浮动图像金字塔,用于对固定图像及浮动图像向下采样。图像金字塔根据用户定义的收缩因子对图像进行平滑及二次采样处理。如下图所示:
多分辨策略通过由粗到精的方式解决配准问题,如下图所示。首先对固定及浮动图像金字塔的第一层进行处理,使用一种粗尺度方法,然后用其结果初始化下一层较精细的变换参数,循环,直至达到最精确的结果。
#if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif #include "itkMultiResolutionImageRegistrationMethod.h" #include "itkTranslationTransform.h" #include "itkMattesMutualInformationImageToImageMetric.h" #include "itkLinearInterpolateImageFunction.h" #include "itkRegularStepGradientDescentOptimizer.h" #include "itkMultiResolutionPyramidImageFilter.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkResampleImageFilter.h" #include "itkCastImageFilter.h" #include "itkCheckerBoardImageFilter.h" #include "itkCommand.h" // //典型的情况, 用户可能会在不同的层间调整组件的参数设置, 或者使用不同的组件 //这可以通过使用 ITK 实现的 Command/Observer 设计模式实现 //在每一层开始配准前, MultiResolutionImageRegistrationMethod 会调用一个 IterationEvent //配准组件的改变, 可以通过实现响应该事件的 itk::Command 来实现 //新接口的其模板参数为 multi-resolution registration type //本例通过改变优化器在不同层间的参数来演示该机制 // template <typename TRegistration> class RegistrationInterfaceCommand : public itk::Command { public: typedef RegistrationInterfaceCommand Self; typedef itk::Command Superclass; typedef itk::SmartPointer<Self> Pointer; itkNewMacro( Self ); protected: RegistrationInterfaceCommand() {}; public: typedef TRegistration RegistrationType; typedef RegistrationType * RegistrationPointer; typedef itk::RegularStepGradientDescentOptimizer OptimizerType; typedef OptimizerType * OptimizerPointer; void Execute(itk::Object * object, const itk::EventObject & event) { if( !(itk::IterationEvent().CheckEvent( &event )) ) { return; } RegistrationPointer registration = dynamic_cast<RegistrationPointer>( object ); //first resolution level 设置 step length, minimum step length 成较大值. //然后在随后的每一层, 减小 minimum step length 10 倍, 以使优化器集中在较小的关键区域 //maximum step length 设置为当前步长, 这样, 优化器下一层配准前重新初始化时 //step length 将使用上一层的最后值. //这将保证 the continuity of the path taken by the optimizer through the parameter space. OptimizerPointer optimizer = dynamic_cast< OptimizerPointer >( registration->GetOptimizer() ); std::cout << "-------------------------------------" << std::endl; std::cout << "MultiResolution Level : " << registration->GetCurrentLevel() << std::endl; std::cout << std::endl; if ( registration->GetCurrentLevel() == 0 ) { //第一层处理时设置的参数 optimizer->SetMaximumStepLength( 16.00 ); optimizer->SetMinimumStepLength( 0.01 ); } else { //以后每进行到下一层, 则根据定义的收缩因子使用上一层的参数进行初始化 //这里分别缩小 4 倍, 10 倍. optimizer->SetMaximumStepLength( optimizer->GetMaximumStepLength() / 4.0 ); optimizer->SetMinimumStepLength( optimizer->GetMinimumStepLength() / 10.0 ); } } void Execute(const itk::Object * , const itk::EventObject & ) { return; } }; 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( !(itk::IterationEvent().CheckEvent( &event )) ) { return; } std::cout << optimizer->GetCurrentIteration() << " "; std::cout << optimizer->GetValue() << " "; std::cout << optimizer->GetCurrentPosition() << std::endl; } }; int main( int argc, char *argv[] ) { if( argc < 4 ) { std::cerr << "Missing Parameters " << std::endl; std::cerr << "Usage: " << argv[0]; std::cerr << " fixedImageFile movingImageFile "; std::cerr << " outputImagefile [backgroundGrayLevel]"; std::cerr << " [checkerBoardBefore] [checkerBoardAfter]"; std::cerr << " [useExplicitPDFderivatives ] " << std::endl; std::cerr << " [numberOfBins] [numberOfSamples ] " << std::endl; return EXIT_FAILURE; } //1.定义图像类型 const unsigned int Dimension = 2; typedef unsigned short PixelType; typedef itk::Image< PixelType, Dimension > FixedImageType; typedef itk::Image< PixelType, Dimension > MovingImageType; //由于图像金字塔在计算降低采样率的图像时是递归的,所以输出图像要求 real pixel types: typedef float InternalPixelType; typedef itk::Image< InternalPixelType, Dimension > InternalImageType; //2.定义配准框架的基本组件: 变换,优化,插值,测度, 及用于连接各组件的配准方法组件 //对于多分辨策略, 这里需要另外两个组件, 固定图像及浮动图像金字塔组件 typedef itk::TranslationTransform< double, Dimension > TransformType; typedef itk::RegularStepGradientDescentOptimizer OptimizerType; typedef itk::LinearInterpolateImageFunction< InternalImageType, double > InterpolatorType; typedef itk::MattesMutualInformationImageToImageMetric< InternalImageType, InternalImageType > MetricType; //多分辨策略配准方法组件,用于连接各组件 typedef itk::MultiResolutionImageRegistrationMethod< InternalImageType, InternalImageType > RegistrationType; //在多分辨策略框架中(multi-resolution framework) //使用类 itk::MultiResolutionPyramidImageFilter //创建一个降低采样率的图像(downsampled image)金字塔. //每幅 downsampled image 的大小由用户以收缩因子的形式指定. typedef itk::MultiResolutionPyramidImageFilter< InternalImageType,InternalImageType > FixedImagePyramidType; typedef itk::MultiResolutionPyramidImageFilter< InternalImageType,InternalImageType > MovingImagePyramidType; TransformType::Pointer transform = TransformType::New(); OptimizerType::Pointer optimizer = OptimizerType::New(); InterpolatorType::Pointer interpolator = InterpolatorType::New(); RegistrationType::Pointer registration = RegistrationType::New(); MetricType::Pointer metric = MetricType::New(); FixedImagePyramidType::Pointer fixedImagePyramid = FixedImagePyramidType::New(); MovingImagePyramidType::Pointer movingImagePyramid = MovingImagePyramidType::New(); //3.使用配准方法组件,将 优化,变换,插值,测度 四个基本组件连接起来 //对于多分辨策略, 还需要连接 固定图像与浮动图像金字塔组件 registration->SetOptimizer( optimizer ); registration->SetTransform( transform ); registration->SetInterpolator( interpolator ); registration->SetMetric( metric ); registration->SetFixedImagePyramid( fixedImagePyramid ); registration->SetMovingImagePyramid( movingImagePyramid ); 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] ); //4.设置待配准图像及配准区域, 并对图像类型进行适当转换处理 //对输入图像进行额外的处理 //固定,浮动图像均来自文件,在将图像连接至配准过程前 //需要将它们转化为内部图像类型,使用 itk::CastImageFilter typedef itk::CastImageFilter< FixedImageType, InternalImageType > FixedCastFilterType; typedef itk::CastImageFilter< MovingImageType, InternalImageType > MovingCastFilterType; FixedCastFilterType::Pointer fixedCaster = FixedCastFilterType::New(); MovingCastFilterType::Pointer movingCaster = MovingCastFilterType::New(); fixedCaster->SetInput( fixedImageReader->GetOutput() ); movingCaster->SetInput( movingImageReader->GetOutput() ); registration->SetFixedImage( fixedCaster->GetOutput() ); registration->SetMovingImage( movingCaster->GetOutput() ); fixedCaster->Update(); registration->SetFixedImageRegion( fixedCaster->GetOutput()->GetBufferedRegion() ); //5.设置各组件的参数,变换、优化,测度等组件的参数 typedef RegistrationType::ParametersType ParametersType; ParametersType initialParameters( transform->GetNumberOfParameters() ); initialParameters[0] = 0.0; // Initial offset in mm along X initialParameters[1] = 0.0; // Initial offset in mm along Y registration->SetInitialTransformParameters( initialParameters ); //Matte 互信息测度需要选择两个参数: //1. number of bins: 用来计算熵值 //2. number of spatial samples: 用来计算密度估计(density estimates) //典型的情况 50 histogram bins 足够了,注意, bins 值对优化器行为有着动态的影响 //spatial samples 值依赖图像的内容, 如果图像比较光滑且没有包含大量的细节 //则可以使用总像素的 1% 作为其值. 否则, 可以使用更多的比例, 如 20% //本例设置如下: metric->SetNumberOfHistogramBins( 128 ); metric->SetNumberOfSpatialSamples( 50000 ); if( argc > 8 ) { metric->SetNumberOfHistogramBins( atoi( argv[8] ) ); } if( argc > 9 ) { metric->SetNumberOfSpatialSamples( atoi( argv[9] ) ); } // metric->ReinitializeSeed( 76926294 ); if( argc > 7 ) { metric->SetUseExplicitPDFDerivatives( atoi( argv[7] ) ); } //设置迭代次数限制及松驰因子 //关于松驰因子, 需参考 regular step gradient descent 优化方法 //当该优化器遇到参数在参数空间运动的方向改变时, 便减小 step length //缩小的比例通过松驰因子控制(relaxation factor). 该值默认为 0.5 optimizer->SetNumberOfIterations( 200 ); optimizer->SetRelaxationFactor( 0.9 ); //6.实例化一 Command/Observer 对象, 监视配准过程的执行, 并触发配准过程的执行. //多分辨策略还需要实例化另一 Command/Observer 对象, 用于改变层间组件及参数 CommandIterationUpdate::Pointer observer = CommandIterationUpdate::New(); optimizer->AddObserver( itk::IterationEvent(), observer ); typedef RegistrationInterfaceCommand<RegistrationType> CommandType; CommandType::Pointer command = CommandType::New(); registration->AddObserver( itk::IterationEvent(), command ); //对于多分辨策略, 需要设置其层数 //这里设置 multi-resolution levels 为 3, 并触发配准过程的执行. registration->SetNumberOfLevels( 3 ); try { registration->StartRegistration(); } catch( itk::ExceptionObject & err ) { std::cout << "ExceptionObject caught !" << std::endl; std::cout << err << std::endl; return EXIT_FAILURE; } //获取配准完成后的各个参数的最终值 ParametersType finalParameters = registration->GetLastTransformParameters(); double TranslationAlongX = finalParameters[0]; double TranslationAlongY = finalParameters[1]; unsigned int numberOfIterations = optimizer->GetCurrentIteration(); double bestValue = optimizer->GetValue(); std::cout << "Result = " << std::endl; std::cout << " Translation X = " << TranslationAlongX << std::endl; std::cout << " Translation Y = " << TranslationAlongY << std::endl; std::cout << " Iterations = " << numberOfIterations << std::endl; std::cout << " Metric value = " << bestValue << std::endl; //7.重采样, 将变换后的浮动图像映射到固定图像空间中,保存配准结果 typedef itk::ResampleImageFilter< MovingImageType, FixedImageType > ResampleFilterType; TransformType::Pointer finalTransform = TransformType::New(); finalTransform->SetParameters( finalParameters ); finalTransform->SetFixedParameters( transform->GetFixedParameters() ); ResampleFilterType::Pointer resample = ResampleFilterType::New(); resample->SetTransform( finalTransform ); resample->SetInput( movingImageReader->GetOutput() ); FixedImageType::Pointer fixedImage = fixedImageReader->GetOutput(); PixelType backgroundGrayLevel = 100; if( argc > 4 ) { backgroundGrayLevel = atoi( argv[4] ); } resample->SetSize( fixedImage->GetLargestPossibleRegion().GetSize() ); resample->SetOutputOrigin( fixedImage->GetOrigin() ); resample->SetOutputSpacing( fixedImage->GetSpacing() ); resample->SetOutputDirection( fixedImage->GetDirection() ); resample->SetDefaultPixelValue( backgroundGrayLevel ); //转换输出图像类型 CastImageFilter 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(); //生成配准前后, 浮动图像与固定图像的对比图, 使用一棋盘状图显示差异 typedef itk::CheckerBoardImageFilter< FixedImageType > CheckerBoardFilterType; CheckerBoardFilterType::Pointer checker = CheckerBoardFilterType::New(); checker->SetInput1( fixedImage ); checker->SetInput2( resample->GetOutput() ); caster->SetInput( checker->GetOutput() ); writer->SetInput( caster->GetOutput() ); resample->SetDefaultPixelValue( 0 ); //配准前浮动图像与固定图像间的差异 TransformType::Pointer identityTransform = TransformType::New(); identityTransform->SetIdentity(); resample->SetTransform( identityTransform ); if( argc > 5 ) { writer->SetFileName( argv[5] ); writer->Update(); } //配准后 resample->SetTransform( finalTransform ); if( argc > 6 ) { writer->SetFileName( argv[6] ); writer->Update(); } return EXIT_SUCCESS; }
CMakelists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.4)
PROJECT(MultiResImageRegistration1)
FIND_PACKAGE(ITK)
IF(ITK_FOUND)
INCLUDE(${ITK_USE_FILE})
ELSE(ITK_FOUND)
MESSAGE(FATAL_ERROR
"ITK not found. Please set ITK_DIR.")
ENDIF(ITK_FOUND)
ADD_EXECUTABLE(MultiResImageRegistration1 MultiResImageRegistration1.cxx )
TARGET_LINK_LIBRARIES(MultiResImageRegistration1 ITKIO ITKNumerics)
测试图像如下:固定图像,浮动图像
结果如下:配准后图像、配准前浮动图像与固定图像差异,配准后的浮动图像与固定图像的差异
程序中我们设置了最大层数为 3,配准过程处理了 3 层后成功并停止,最终参数为:
>MultiResImageRegistration1.exe fixed.png moving.png out.png 100 befor.png after.png
-------------------------------------
MultiResolution Level : 0
0 -0.90675 [15.6488, 3.33401]
。。。。
。。。。
73 -2.2638 [13.1505, 17.1896]
74 -2.2659 [13.1597, 17.1936]
-------------------------------------
MultiResolution Level : 1
0 -2.18173 [10.1725, 14.5334]
1 -1.29464 [13.4157, 16.0959]
2 -1.91879 [11.6505, 18.8127]
。。。。。
102 -2.25089 [13.0004, 16.9996]
103 -2.25085 [12.9996, 17.0003]
-------------------------------------
-------------------------------------
MultiResolution Level : 2
0 -2.80063 [13.9996, 17.0093]
1 -1.45265 [13.0998, 16.992]
2 -2.56817 [12.2098, 17.1264]
3 -1.61491 [13.0139, 17.0287]
4 -2.7529 [12.6293, 16.4095]
5 -1.71975 [12.943, 16.9857]
6 -2.66095 [13.5739, 17.1659]
7 -1.80306 [13.0141, 16.978]
8 -2.7611 [12.6566, 17.448]
9 -1.84529 [12.9994, 17.0419]
。。。。。
。。。。。
109 -2.80063 [12.9998, 17.0004]
110 -2.80062 [12.9998, 17.0003]
111 -2.80062 [12.9998, 17.0003]
Result =
Translation X = 12.9998
Translation Y = 17.0003
Iterations = 113
Metric value = -2.80062
可以看到,浮动图像平移了大约(13, 17)。