ITK配準

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