void ITKTestClass::ImageRegistration1(std::string fixedImageFile, std::string movingImageFile, std::string outputImagefile, std::string differenceImageAfter, std::string differenceImageBefore, double initialStepLength)
{
itk::PNGImageIOFactory::RegisterOneFactory();
itk::TIFFImageIOFactory::RegisterOneFactory();
itk::BMPImageIOFactory::RegisterOneFactory();
itk::GDCMImageIOFactory::RegisterOneFactory();
itk::JPEGImageIOFactory::RegisterOneFactory();
//1.定义待配准图像类型: 维数, 像素类型
const unsigned int Dimension = 2;
typedef unsigned char PixelType;
typedef itk::Image< PixelType, Dimension > FixedImageType;
typedef itk::Image< PixelType, Dimension > MovingImageType;
//2.定义配准框架的基本组件: 变换, 优化, 测度, 插值, 配准组件
//用于 2D 图像的一个刚体变换,该变换的唯一参数为: 空间座标的类型
typedef itk::CenteredRigid2DTransform< double > TransformType;
typedef itk::RegularStepGradientDescentOptimizer OptimizerType;
typedef itk::MeanSquaresImageToImageMetric< FixedImageType,
MovingImageType > MetricType;
typedef itk::LinearInterpolateImageFunction< MovingImageType,
double > InterpolatorType;
//配准组件: 该组件用于连接其它各组件
typedef itk::ImageRegistrationMethod< FixedImageType,
MovingImageType > RegistrationType;
MetricType::Pointer metric = MetricType::New();
OptimizerType::Pointer optimizer = OptimizerType::New();
InterpolatorType::Pointer interpolator = InterpolatorType::New();
RegistrationType::Pointer registration = RegistrationType::New();
//3.使用配准组件将:变换, 优化, 测度, 插值 四个基本组件连接至一起
registration->SetMetric(metric);
registration->SetOptimizer(optimizer);
registration->SetInterpolator(interpolator);
TransformType::Pointer transform = TransformType::New();
registration->SetTransform(transform);
//4.设置待配准图像及变换区域
//定义待配准两幅输入图像的 reader
typedef itk::ImageFileReader< FixedImageType > FixedImageReaderType;
typedef itk::ImageFileReader< MovingImageType > MovingImageReaderType;
FixedImageReaderType::Pointer fixedImageReader
= FixedImageReaderType::New();
MovingImageReaderType::Pointer movingImageReader
= MovingImageReaderType::New();
fixedImageReader->SetFileName(fixedImageFile);
movingImageReader->SetFileName(movingImageFile);
//本例, 固定图像与浮动图像都来自文件
//需要 itk::ImageRegistrationMethod 从 file readers 的输出获取输入
registration->SetFixedImage(fixedImageReader->GetOutput());
registration->SetMovingImage(movingImageReader->GetOutput());
//更新 readers,确保在初始化变换时图像参数(size,origin,spacing)有效.
fixedImageReader->Update();
//可以将配准限制在固定图像的一特定区域,通过将其作为测度(metric)计算的输入.
//该区域通过方法SetFixedImageRegion() 定义. 通过使用这个特征, 可以避免图像
//中某些不需要的的对象影响配准输出,或者减少计算时间,本例中使用整幅图像.
//此区域通过 BufferedRegion 标识.
registration->SetFixedImageRegion(
fixedImageReader->GetOutput()->GetBufferedRegion());
//更新固定,浮动图像 reader, 使其 origin, size, spacing 有效,
fixedImageReader->Update();
movingImageReader->Update();
//5.设置各组件的参数,主要是变换、优化组件的参数
//本例使用的为变换为: 先进行一个旋转变换,再进行一平移变换.
typedef FixedImageType::SpacingType SpacingType;
typedef FixedImageType::PointType OriginType;
typedef FixedImageType::RegionType RegionType;
typedef FixedImageType::SizeType SizeType;
FixedImageType::Pointer fixedImage = fixedImageReader->GetOutput();
const SpacingType fixedSpacing = fixedImage->GetSpacing();
const OriginType fixedOrigin = fixedImage->GetOrigin();
const RegionType fixedRegion = fixedImage->GetLargestPossibleRegion();
const SizeType fixedSize = fixedRegion.GetSize();
//本例使用固定图像的中心点做为旋转中心
//使用固定图与浮动图中心之间距离向量作为平移量
//首先计算固定图像中心点
TransformType::InputPointType centerFixed;
centerFixed[0] = fixedOrigin[0] + fixedSpacing[0] * fixedSize[0] / 2.0;
centerFixed[1] = fixedOrigin[1] + fixedSpacing[1] * fixedSize[1] / 2.0;
MovingImageType::Pointer movingImage = movingImageReader->GetOutput();
const SpacingType movingSpacing = movingImage->GetSpacing();
const OriginType movingOrigin = movingImage->GetOrigin();
const RegionType movingRegion = movingImage->GetLargestPossibleRegion();
const SizeType movingSize = movingRegion.GetSize();
//然后计算浮动图像中心点
TransformType::InputPointType centerMoving;
centerMoving[0] = movingOrigin[0] + movingSpacing[0] * movingSize[0] / 2.0;
centerMoving[1] = movingOrigin[1] + movingSpacing[1] * movingSize[1] / 2.0;
//设置旋转中心
transform->SetCenter(centerFixed);
//设置平移量
transform->SetTranslation(centerMoving - centerFixed);
//设置初始旋转角度
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);
//本例使用的优化器是 itk::RegularStepGradientDescentOptimizer,
//梯度下降法的一种,下面定义优化参数:
//relaxation fator, initial step length, minimal step length, number of iterations
optimizer->SetRelaxationFactor(0.6); //松驰因子
optimizer->SetMaximumStepLength(initialStepLength); //最大步长
//最小步长和迭代最大次数, 用来限制优化过程迭代的执行
optimizer->SetMinimumStepLength(0.001); //最小步长
optimizer->SetNumberOfIterations(200); //最大迭代次数,以防止无限次迭代
/*
*应该注意: 配准过程是一个优化的过程, 优化过程每迭代一次,
*就与相似性测度进行一次比较
*当相似性测度值达到我们设定的预期值,或者达到设定的迭代上限时
*就停止迭代,得到最终结果.
*/
//6.实例化一 Command/Observer 对象, 监视配准过程的执行, 并触发配准过程的执行.
//实例化一个 Command/Observer 对象, 将其注册为 optimizer 的观察者
CommandIterationUpdate::Pointer observer = CommandIterationUpdate::New();
optimizer->AddObserver(itk::IterationEvent(), observer);
//取得配准执行完毕的最终参数
OptimizerType::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];
const unsigned int numberOfIterations = optimizer->GetCurrentIteration();
const double bestValue = optimizer->GetValue();
const double finalAngleInDegrees = finalAngle * 180.0 / vnl_math::pi;
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;
//7.重采样, 将变换后的浮动图像映射到固定图像空间中,保存配准结果
//一般情况,最后一步为: 利用最终的变换结果将浮动图像映射到固定图像空间
//可通过 itk::ResampleImageFilter 完成
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());
resample->SetSize(fixedImage->GetLargestPossibleRegion().GetSize());
resample->SetOutputOrigin(fixedImage->GetOrigin());
resample->SetOutputSpacing(fixedImage->GetSpacing());
resample->SetOutputDirection(fixedImage->GetDirection());
//设置默认灰度值,用来 "突出" 显示映射后在浮动图像之外的区域
resample->SetDefaultPixelValue(100);
//保存配准后图像
typedef itk::ImageFileWriter< FixedImageType > WriterFixedType;
WriterFixedType::Pointer writer = WriterFixedType::New();
writer->SetFileName(outputImagefile);
writer->SetInput(resample->GetOutput()); //重采样过滤器的输出
try
{
writer->Update();
}
catch (itk::ExceptionObject & excp)
{
std::cerr << "ExceptionObject while writing the resampled image !" << std::endl;
std::cerr << excp << std::endl;
}
//通过 itk::SubtractImageFilter 比较"经过变换的浮动图像"与"固定图像"之间的差异
typedef itk::Image< float, Dimension > DifferenceImageType;
typedef itk::SubtractImageFilter< FixedImageType,
FixedImageType, DifferenceImageType > DifferenceFilterType;
DifferenceFilterType::Pointer difference = DifferenceFilterType::New();
typedef unsigned char OutputPixelType;
typedef itk::Image< OutputPixelType, Dimension > OutputImageType;
//由于两幅图像之间的差异可能对应非常小的亮度值
//使用 itk::RescaleIntensityImageFilter 重新调节亮度值,使其更加明显
typedef itk::RescaleIntensityImageFilter< DifferenceImageType,
OutputImageType > RescalerType;
RescalerType::Pointer intensityRescaler = RescalerType::New();
intensityRescaler->SetOutputMinimum(0);
intensityRescaler->SetOutputMaximum(255);
difference->SetInput1(fixedImageReader->GetOutput());
difference->SetInput2(resample->GetOutput());
resample->SetDefaultPixelValue(1);
intensityRescaler->SetInput(difference->GetOutput());
typedef itk::ImageFileWriter< OutputImageType > WriterType;
WriterType::Pointer writer2 = WriterType::New();
writer2->SetInput(intensityRescaler->GetOutput());
try
{
//保存配准后, 浮动图与固定图之间差异的对比
writer2->SetFileName(differenceImageAfter);
writer2->Update();
//保存配准前, 浮动图与固定图之间差异的对比, 这里仍然需要重采样
//因为浮动图与固定图不一定有相同的 origin, size, spacing.
//可以过一个恒等变换(Identity Transform)完成
TransformType::Pointer identityTransform = TransformType::New();
identityTransform->SetIdentity();
resample->SetTransform(identityTransform);
writer2->SetFileName(differenceImageBefore);
writer2->Update();
}
catch (itk::ExceptionObject & excp)
{
std::cerr << "Error while writing difference images" << std::endl;
std::cerr << excp << std::endl;
}
}
ITK配准
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.