/* * Copyright (c) ICG. All rights reserved. * * Institute for Computer Graphics and Vision * Graz, University of Technology / Austria * * * 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. * * * Project : projects * Module : MIPViewer - plugins * Class : $RCSfile: CTNonlinearRegistrationBase.txx,v $ * Language : C++ * Description : * * Author : Martin Urschler * EMail : urschler@icg.tu-graz.ac.at * Date : $Date: 2007-07-02 15:15:04 $ * Version : $Revision: 1.22 $ * Full Id : $Id: CTNonlinearRegistrationBase.txx,v 1.22 2007-07-02 15:15:04 urschler Exp $ * */ #ifndef _CTNONLINEARREGISTRATIONBASE_TXX_ #define _CTNONLINEARREGISTRATIONBASE_TXX_ #include "RawDataBufferImporter.h" #include "VolumeIOWrapper.h" #include "itkWarpImageFilter.h" #include "itkImageFileWriter.h" template CTNonlinearRegistrationBase:: CTNonlinearRegistrationBase() : m_fixedImage(0), m_movingImage(0), m_displacementFieldFilename(""), m_imageOutputFilename(""), m_resampleDefaultValue(0) { } template CTNonlinearRegistrationBase:: ~CTNonlinearRegistrationBase() { m_fixedImage = 0; m_movingImage = 0; } template void CTNonlinearRegistrationBase:: setDisplacementFieldFilename( const std::string& displacementFieldFilename ) { m_displacementFieldFilename = displacementFieldFilename; } template void CTNonlinearRegistrationBase:: setImageFilenames( const std::string& fixedFilename, const std::string& movingFilename ) { m_fixedFilename = fixedFilename; m_movingFilename = movingFilename; } template void CTNonlinearRegistrationBase:: setImageOutputFilename( const std::string& outputFilename ) { m_imageOutputFilename = (outputFilename == "") ? m_movingFilename : outputFilename; } template void CTNonlinearRegistrationBase:: setResampleDefaultValue( const int resampleDefaultValue ) { m_resampleDefaultValue = resampleDefaultValue; } template CTNonlinearRegistration::ReturnType CTNonlinearRegistrationBase:: setFixedImage(TPixelDataType * ptr, const int nrSlices, const int nrRows, const int nrCols, const float sliceDistance, const float inplaneVoxelSizeX, const float inplaneVoxelSizeY ) { // now try to import the actual image data EXCEPTION_CHECKED_ITK_FILTER_UPDATE( "Importing fixed image!", m_fixedImage = RawDataBufferImporter::import( ptr, nrSlices, nrRows, nrCols, sliceDistance, inplaneVoxelSizeX, inplaneVoxelSizeY ) ); m_fixedImageSize = m_fixedImage->GetLargestPossibleRegion().GetSize(); m_fixedImageSpacing = m_fixedImage->GetSpacing(); // perform a gauss smoothing with sigma = 1 voxel // m_fixedImage = NonlinearRegistrationUtilities:: // efficientDiscreteGaussSmoothVolume( m_fixedImage, // m_fixedImageSpacing[0], // m_fixedImageSpacing[1], // m_fixedImageSpacing[2] ); return CTNonlinearRegistration::OK; } template CTNonlinearRegistration::ReturnType CTNonlinearRegistrationBase:: setMovingImage(TPixelDataType * ptr, const int nrSlices, const int nrRows, const int nrCols, const float sliceDistance, const float inplaneVoxelSizeX, const float inplaneVoxelSizeY ) { // now try to import the actual image data EXCEPTION_CHECKED_ITK_FILTER_UPDATE( "Importing moving image!", m_movingImage = RawDataBufferImporter::import( ptr, nrSlices, nrRows, nrCols, sliceDistance, inplaneVoxelSizeX, inplaneVoxelSizeY ) ); m_movingImageSize = m_movingImage->GetLargestPossibleRegion().GetSize(); m_movingImageSpacing = m_movingImage->GetSpacing(); // perform a gauss smoothing with sigma = 1 voxel // m_movingImage = NonlinearRegistrationUtilities:: // efficientDiscreteGaussSmoothVolume( m_movingImage, // m_movingImageSpacing[0], // m_movingImageSpacing[1], // m_movingImageSpacing[2] ); return CTNonlinearRegistration::OK; } template CTNonlinearRegistration::ReturnType CTNonlinearRegistrationBase:: calculateFixedAndMovingPyramid( PyramidType& fixedPyramid, PyramidType& movingPyramid ) { // first of all we look at the image sizes to be able to argue about // the downsample strategy ( and the associated upsample strategy ) typename InputImageType::SizeType fixedFullSize = m_fixedImage->GetLargestPossibleRegion().GetSize(); typename InputImageType::SizeType movingFullSize = m_movingImage->GetLargestPossibleRegion().GetSize(); // FIXXME: this is very restrictive - find a weaker condition by adapting // the algorithms if (fixedFullSize != movingFullSize) { return CTNonlinearRegistration::VOLUME_SIZES_MISMATCH_EXCEPTION; } // if( testPowerOfTwoImageSize(fixedFullSize) == false || // testPowerOfTwoImageSize(movingFullSize) == false ) // { // return CTNonlinearRegistration::VOLUME_SIZES_NOT_POWER_OF_TWO_EXCEPTION; // } // the pyramid includes all scale levels, also the original one, therefore -1 const unsigned int nrDivisions = fixedPyramid.size() - 1; if( testDivisibleByTwoImageSize(fixedFullSize,nrDivisions) == false || testDivisibleByTwoImageSize(movingFullSize,nrDivisions) == false ) { return CTNonlinearRegistration:: VOLUME_SIZES_NOT_DIVISIBLE_BY_TWO_ENOUGH_TIMES_EXCEPTION; } CTNonlinearRegistration::ReturnType pyramidReturnValue = calculateImagePyramid( m_fixedImage, fixedPyramid, m_fixedDownsampleModeSequence ); if (pyramidReturnValue != CTNonlinearRegistration::OK) return pyramidReturnValue; pyramidReturnValue = calculateImagePyramid( m_movingImage, movingPyramid, m_movingDownsampleModeSequence ); if (pyramidReturnValue != CTNonlinearRegistration::OK) return pyramidReturnValue; #if 0 const unsigned int maxNrShrinkLevels = fixedPyramid.size(); for ( unsigned int pyramidLevel=1; pyramidLevel::writeITKVolume16Bit( fixedPyramid[pyramidLevel], m_fixedFilename, outputExtensionDiff ); VolumeIOWrapper::writeITKVolume16Bit( movingPyramid[pyramidLevel], m_movingFilename, outputExtensionDiff ); // upsample and store the original resolution InputImageType::Pointer tmpImage = fixedPyramid[pyramidLevel]; for (unsigned int i=0;i(tmpImage); } VolumeIOWrapper::writeITKVolume16Bit( tmpImage, m_fixedFilename, outputExtensionDiff2 ); tmpImage = movingPyramid[pyramidLevel]; for (unsigned int i=0;i(tmpImage); } VolumeIOWrapper::writeITKVolume16Bit( tmpImage, m_movingFilename, outputExtensionDiff2 ); } } #endif return CTNonlinearRegistration::OK; } template CTNonlinearRegistration::ReturnType CTNonlinearRegistrationBase:: calculateImagePyramid( const typename InputImageType::Pointer& image, PyramidType& pyramid, DownAndUpsampleUtilities::PyramidDownsampleModeSequence& downsampleModeSequence ) { // initialize the pyramid pyramid[0] = image; const unsigned int maxNrShrinkLevels = pyramid.size(); for ( unsigned int pyramidLevel=1; pyramidLevel( pyramid[pyramidLevel-1], downsampleModeNTuple ); pyramid[pyramidLevel] = shrinked; downsampleModeSequence.push_back( downsampleModeNTuple ); std::cout << " ... finished " << std::endl; } return CTNonlinearRegistration::OK; } template CTNonlinearRegistration::ReturnType CTNonlinearRegistrationBase:: warpMovingImage( const DeformationFieldType::Pointer deformationField ) { typedef itk::WarpImageFilter< InputImageType, InputImageType, DeformationFieldType > WarperType; typedef itk::LinearInterpolateImageFunction< InputImageType, double > InterpolatorType; typename WarperType::Pointer warper = WarperType::New(); typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); warper->SetInput( m_movingImage ); warper->SetInterpolator( interpolator ); warper->SetOutputSpacing( m_fixedImage->GetSpacing() ); warper->SetOutputOrigin( m_fixedImage->GetOrigin() ); warper->SetEdgePaddingValue( m_resampleDefaultValue ); warper->SetDeformationField( deformationField ); EXCEPTION_CHECKED_ITK_FILTER_UPDATE("perform warping", warper->Update() ); typename InputImageType::Pointer outputImage = warper->GetOutput(); // finally copy the warped output image back into the moving image ITK_IMAGE_COPY_MACRO_TN( InputImageType, outputImage, InputImageType, m_movingImage ); // if necessary write the displacement field if (m_displacementFieldFilename != std::string("")) { typedef itk::ImageFileWriter DefFieldWriter; DefFieldWriter::Pointer def_field_writer = DefFieldWriter::New(); def_field_writer->SetFileName(m_displacementFieldFilename.c_str()); def_field_writer->SetInput( deformationField ); std::cout << "writing displacement field: " << m_displacementFieldFilename.c_str() << std::endl; def_field_writer->Update(); } return CTNonlinearRegistration::OK; } template bool CTNonlinearRegistrationBase:: testPowerOfTwoImageSize( const typename InputImageType::SizeType& size ) { bool testOK = true; const unsigned int dimensions = itk::GetImageDimension::ImageDimension; for (unsigned int dim=0; dim bool CTNonlinearRegistrationBase:: testDivisibleByTwoImageSize( const typename InputImageType::SizeType& size, const unsigned int nrTimesDivisible ) { bool testOK = true; // trivial check if (nrTimesDivisible == 0) return testOK; const unsigned int dimensions = itk::GetImageDimension::ImageDimension; const unsigned int bitMask = ((1 << nrTimesDivisible) - 1); //std::cout << "nrTimesDivisible = " << nrTimesDivisible << " - bitMask: " << // bitMask << std::endl; for (unsigned int dim=0; dim