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配準
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.