From: srit Date: Mon, 14 Jun 2010 17:32:04 +0000 (+0000) Subject: Added clitkAffineRegistration from Jef's file. Also does translations only and rigid... X-Git-Tag: v1.2.0~611 X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=commitdiff_plain;h=c18059db4f507fd31b5898667f57eced7d48c5f7;p=clitk.git Added clitkAffineRegistration from Jef's file. Also does translations only and rigid only. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index c5f31cf..3a74d43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,11 @@ IF (CLITK_BUILD_TOOLS) add_subdirectory(tools) ENDIF(CLITK_BUILD_TOOLS) +OPTION(CLITK_BUILD_REGISTRATION "Build command-line registration tools" OFF) +IF (CLITK_BUILD_REGISTRATION) + add_subdirectory(registration) +ENDIF(CLITK_BUILD_REGISTRATION) + #OPTION(CLITK_BUILD_SEGMENTATION "Build segmentation tools" OFF) #IF (CLITK_BUILD_SEGMENTATION) # add_subdirectory(segmentation) diff --git a/registration/CMakeLists.txt b/registration/CMakeLists.txt new file mode 100644 index 0000000..5963898 --- /dev/null +++ b/registration/CMakeLists.txt @@ -0,0 +1,4 @@ +WRAP_GGO(clitkAffineRegistration_GGO_C clitkAffineRegistration.ggo) +ADD_EXECUTABLE(clitkAffineRegistration clitkAffineRegistration.cxx clitkAffineRegistrationGenericFilter.cxx ${clitkAffineRegistration_GGO_C} clitkLBFGSBOptimizer.cxx clitkGenericAffineTransform.cxx) +TARGET_LINK_LIBRARIES(clitkAffineRegistration clitkCommon ITKIO clitkFilters ITKNumerics ITKStatistics) + diff --git a/registration/clitkAffineRegistration.cxx b/registration/clitkAffineRegistration.cxx new file mode 100644 index 0000000..c24bbe9 --- /dev/null +++ b/registration/clitkAffineRegistration.cxx @@ -0,0 +1,36 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +// clitk include +#include "clitkIO.h" +#include "clitkAffineRegistrationGenericFilter.h" + +int main( int argc, char *argv[] ) +{ + //init command line and check options + GGO(clitkAffineRegistration, args_info); + CLITK_INIT; + + //=========================================================================== + //Set all the options passed through the commandline + clitk::AffineRegistrationGenericFilter::Pointer genericFilter=clitk::AffineRegistrationGenericFilter::New(); + genericFilter->SetArgsInfo(args_info); + genericFilter->Update(); + + return 1; +} diff --git a/registration/clitkAffineRegistration.ggo b/registration/clitkAffineRegistration.ggo new file mode 100644 index 0000000..6c9f439 --- /dev/null +++ b/registration/clitkAffineRegistration.ggo @@ -0,0 +1,89 @@ +# file clitkAffineRegistration.ggo +package "clitk" +version "1.0" +purpose "Compute an affine registration between two images." + +option "config" - "Config file" string no + +section "Run Time" + +option "verbose" v "Verbose" flag off +option "gradient" - "If verbose, show gradient at each iteration" flag off +option "threads" - "Number of threads to use (default=min(#cores,8))" int no + + +section "Input (Both images have to be of the same dimension (2 or 3D). For 2D-3D registrations, give the 2D image a third dimension of 1 and set it to the reference image.)" + +option "reference" i "Reference or fixed image filename" string yes +option "target" j "Target or moving image filename" string yes +option "referenceMask" m "Mask to placed over the reference image" string no +option "targetMask" - "Mask to placed over the target image" string no + +section "Output" + +option "output" o "Transformed object image filename" string no +option "checker_after" - "Checherboard representation of the transformed object image and reference image" string no +option "checker_before" - "Checherboard representation of the object image and reference image" string no +option "after" - "Difference between the reference image and the transformed object" string no +option "before" - "Difference between the reference image and the original object image" string no +option "matrix" - "Affine matrix (reference to object space) filename " string no + + +section "Interpolator" + +option "interp" - "Interpolation: 0=NN, 1=Linear, 2=BSpline, 3=BLUT" int no default="1" +option "interpOrder" - "Order if BLUT or BSpline (0-5)" int no default="3" +option "interpSF" - "Sampling factor if BLUT" int no default="20" + + +section "Transform (Input and Output transformation parameters map the physical space of the fixed or reference image into the physical space of the moving or object image. Positive rotations result in a counter-clockwise rotation for the moving image. Positive translations result in shift along the negative axis for the moving image.)" + +option "transform" - "Type: 0=Identity, 1=Translation, 2=Rigid, 3=Affine" int no default="2" +option "transX" x "1-3: Initial translation in mm along the X axis" float no default="0.0" +option "transY" y "1-3: Initial translation in mm along the Y axis" float no default="0.0" +option "transZ" z "1-3: Initial translation in mm along the Z axis" float no default="0.0" +option "initMatrix" - "2-3: Initial matrix (reference to object space) filename " string no +option "moment" - "2-3: Initialize translation by aligning the center of gravities for the respective intensities" flag off + +section "Metric (optimized, threaded versions are available for *, compile ITK with REVIEW and OPT_REGISTRATION enabled)" + +option "metric" - "Type: 0=Mean-Squares*, 1=Normalized CC*, 2=Histogram CC, 3=Gradient-Difference, 4=Viola-Wells MI, 5=Histogram MI, 6=Mattes' MI*, 7=Normalized MI, 8=CR, 9=SSD for BLUT FFD**" int no default="0" +option "samples" - "Specify fraction [0, 1] of samples of the reference image used for the metric (* only). Use high fraction for detailed images (eg. 0.2, 0.5), for smooth images 0.01 might be enough." float no default="1" +option "intThreshold" - "Fixed image samples intensity threshold (* only; caution with --normalize)" float no +option "subtractMean" - "1: Subtract mean for NCC calculation (narrows optimal)" flag on +option "bins" - "2,5-8: Number of histogram bins" int no default="50" +option "random" - "4,6: Samples should be taken randomly, otherwise uniformly" flag off +option "stdDev" - "4: specify the standard deviation in mm of the gaussian kernels for both PDF estimations" float no default="0.4" +option "explicitPDFDerivatives" - "6: Calculate PDF derivatives explicitly (rigid=true; FFD=false)" flag on + + +section "Preprocessing" + +option "normalize" - "Normalize images before registration (not necessary for metric 1,2,5,6,8; caution with --intThreshold)" flag off +option "blur" - "Blur images before registration, use Gaussian with std dev (none by default) " float no default="0.0" + + +section "Optimizer" + +option "optimizer" - "0=Simplex, 1=Powell, 2=FRPR, 3=Regular Step GD, 4=VersorRigid3D, 5=Conjugated Gradient, 6=L-BFGS, 7=L-BFGS-B" int no default="0" +option "delta" - "0: Initial delta, otherwise automatic" double no +option "step" - "1,2,3,4: Initial stepsize (to be multiplied with the gradient)" double no default="2.0" +option "relax" - "3,4: Relaxation of the stepsize (multiplied each time the gradient changes sign)" double no default="0.7" +option "valueTol" - "0,1,2: Tolerance on the function" double no default="0.01" +option "stepTol" - "0,1,3,4: Tolerance on the step size" double no default="0.1" +option "gradTol" - "3,4,6,7: Tolerance on the (projected) gradient magnitude (7: 1=low->1e-10=high precision)" double no default="1e-5" +option "lineAcc" - "6: Line accuracy (eg: high=0.1, low=0.9)" double no default="0.9" +option "convFactor" - "7: Convergence factor: terminate if factor*machine_precision>reduction in cost (1e+12 low, 1e+7 moderate and 1e+1 high precision) " double no default="1e+12" +option "maxIt" - "0-7: Maximum number of iterations" int no default="500" +option "maxLineIt" - "Maximum number of line iterations" int no default="50" +option "maxEval" - "Maximum number of evaluations" int no default="500" +option "maxCorr" - "Maximum number of corrections" int no default="5" +option "selectBound" - "7: Select the type of bound: 0=none, 1=u, 2=u&l, 3=l" int no default="0" +option "lowerBound" - "7: The lower bound" double no +option "upperBound" - "7: The upper bound" double no + +option "rWeight" - "Weight of 1° of rotation during optimisation (high weight, less change)" float no default="50.0" +option "tWeight" - "Weight of 1mm of translation during optimisation (high weight, less change)" float no default="1.0" +option "levels" - "Number of resolution levels" int no default="1" +option "inc" - "Increment factor (x) previous step/tol = new step/tol at next resolution level" float no default="1.2" +option "dec" - "Decrement factor (:) previous step/tol = new step/tol at next resolution level" float no default="4" diff --git a/registration/clitkAffineRegistrationGenericFilter.cxx b/registration/clitkAffineRegistrationGenericFilter.cxx new file mode 100644 index 0000000..d9bfd28 --- /dev/null +++ b/registration/clitkAffineRegistrationGenericFilter.cxx @@ -0,0 +1,98 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef CLITKAFFINEREGISTRATIONGENERICFILTER_CXX +#define CLITKAFFINEREGISTRATIONGENERICFILTER_CXX + +#include "clitkAffineRegistrationGenericFilter.h" + +namespace clitk +{ + +//==================================================================== +// Constructor +AffineRegistrationGenericFilter::AffineRegistrationGenericFilter() +{ + m_Verbose=false; +} + +//==================================================================== +// Update +void AffineRegistrationGenericFilter::Update() +{ + //Read the PixelType and dimension of the reference image + int Dimension; + std::string PixelType; + clitk::ReadImageDimensionAndPixelType(m_ArgsInfo.reference_arg, Dimension, PixelType); + if (Dimension == 2) UpdateWithDim<2>(PixelType); + else if (Dimension == 3) UpdateWithDim<3>(PixelType); + else { + itkExceptionMacro(<< "Reference Image dimension is " << Dimension + << " but I can only work on 2D and 3D images."); + } +} + +//void AffineRegistrationGenericFilter::SetTransfo( itk::MultiResolutionImageRegistrationMethod,itk::Image >::Pointer & registration ) +//{ +// typedef itk::Euler2DTransform < double > Transform2DType; +// Transform2DType::Pointer t2 =Transform2DType::New(); +// +// //Initializing the transform +// Transform2DType::OutputVectorType translation; +// translation[0] = m_ArgsInfo.transX_arg; +// translation[1] = m_ArgsInfo.transY_arg; +// t2->SetTranslation(translation); +// t2->SetRotation(m_ArgsInfo.rotX_arg); +// +// //For simplicity we set the center to the top left corner +// Transform2DType::InputPointType center; +// center[0] = 0.; +// center[1] = 0.; +// t2->SetCenter(center); +// registration->SetTransform(t2); +// registration->SetInitialTransformParameters(t2->GetParameters()); +//} + +//void clitk::AffineRegistrationGenericFilter::SetTransfo( itk::MultiResolutionImageRegistrationMethod,itk::Image >::Pointer & registration ) +//{ +// typedef itk::Euler3DTransform < double > Transform3DType; +// Transform3DType::Pointer t3 = Transform3DType::New(); +// +// t3->SetComputeZYX(true); +// //Initializing the transform +// Transform3DType::OutputVectorType translation; +// translation[0] = m_ArgsInfo.transX_arg; +// translation[1] = m_ArgsInfo.transY_arg; +// translation[2] = m_ArgsInfo.transZ_arg; +// t3->SetTranslation(translation); +// t3->SetRotation(m_ArgsInfo.rotX_arg, m_ArgsInfo.rotY_arg, m_ArgsInfo.rotZ_arg); +// +// //For simplicity we set the center to the top left corner +// Transform3DType::InputPointType center; +// center[0] = 0.; +// center[1] = 0.; +// center[2] = 0.; +// t3->SetCenter(center); +// registration->SetTransform(t3); +// registration->SetInitialTransformParameters( t3->GetParameters()); +//} + +} +//==================================================================== + +#endif //#define CLITKAFFINEREGISTRATIONCGENERICFILTER_CXX diff --git a/registration/clitkAffineRegistrationGenericFilter.h b/registration/clitkAffineRegistrationGenericFilter.h new file mode 100644 index 0000000..830046f --- /dev/null +++ b/registration/clitkAffineRegistrationGenericFilter.h @@ -0,0 +1,115 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef CLITKAFFINEREGISTRATIONGENERICFILTER_H +#define CLITKAFFINEREGISTRATIONGENERICFILTER_H + +// clitk include +#include "clitkIO.h" +#include "clitkCommon.h" +#include "clitkAffineRegistration_ggo.h" +#include "clitkCorrelationRatioImageToImageMetric.h" +#include "clitkTransformUtilities.h" +#include "clitkGenericMetric.h" +#include "clitkGenericOptimizer.h" +#include "clitkGenericInterpolator.h" +#include "clitkGenericAffineTransform.h" + +//itk include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// other includes +#include +#include +#include + +namespace clitk +{ + +//==================================================================== +class AffineRegistrationGenericFilter: public itk::LightObject +{ +public: + + //================================================ + typedef AffineRegistrationGenericFilter Self; + typedef itk::LightObject Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + //================================================ + itkNewMacro(Self); + + //==================================================================== + // Set methods + void SetArgsInfo(const args_info_clitkAffineRegistration args_info) { + m_ArgsInfo=args_info; + m_Verbose=m_ArgsInfo.verbose_flag; + } + + //==================================================================== + // Update + void Update(); + +protected: + const char * GetNameOfClass() const { + return "AffineRegistrationGenericFilter"; + } + + //==================================================================== + // Constructor & Destructor + AffineRegistrationGenericFilter(); + ~AffineRegistrationGenericFilter() { + ; + } + + //==================================================================== + //Templated member functions + template void UpdateWithDim(std::string PixelType); + template void UpdateWithDimAndPixelType(); + + //==================================================================== + //Member Data +public: + bool m_Verbose; + args_info_clitkAffineRegistration m_ArgsInfo; +}; +} + +#ifndef ITK_MANUAL_INSTANTIATION +#include "clitkAffineRegistrationGenericFilter.txx" +#endif + + +#endif //#define CLITKAFFINEREGISTRATIONGENERICFILTER__H diff --git a/registration/clitkAffineRegistrationGenericFilter.txx b/registration/clitkAffineRegistrationGenericFilter.txx new file mode 100644 index 0000000..90fb6e4 --- /dev/null +++ b/registration/clitkAffineRegistrationGenericFilter.txx @@ -0,0 +1,664 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef CLITKAFFINEREGISTRATIONGENERICFILTER_TXX +#define CLITKAFFINEREGISTRATIONGENERICFILTER_TXX + +namespace clitk +{ + +//============================================================================== +//Creating an observer class that allows us to monitor the registration +//================================================================================ +class CommandIterationUpdate : public itk::Command +{ +public: + typedef CommandIterationUpdate Self; + typedef itk::Command Superclass; + typedef itk::SmartPointer Pointer; + itkNewMacro( Self ); +protected: + CommandIterationUpdate() {}; +public: + typedef clitk::GenericOptimizer OptimizerType; + typedef const OptimizerType * OptimizerPointer; + + // Set the generic optimizer + void SetOptimizer(OptimizerPointer o) { + m_Optimizer=o; + } + + // Execute + 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) { + if( !(itk::IterationEvent().CheckEvent( &event )) ) { + return; + } + + m_Optimizer->OutputIterationInfo(); + } + + OptimizerPointer m_Optimizer; +}; + + +//============================================================================== +//Creating an observer class that allows us to change parameters at subsequent levels +//============================================================================== +template +class RegistrationInterfaceCommand : public itk::Command +{ +public: + typedef RegistrationInterfaceCommand Self; + typedef itk::Command Superclass; + typedef itk::SmartPointer Pointer; + itkNewMacro( Self ); +protected: + RegistrationInterfaceCommand() {}; +public: + + // Registration + typedef TRegistration RegistrationType; + typedef RegistrationType * RegistrationPointer; + + // Metric + typedef typename RegistrationType::FixedImageType InternalImageType; + typedef clitk::GenericMetric GenericMetricType; + + // Two arguments are passed to the Execute() method: the first + // is the pointer to the object which invoked the event and the + // second is the event that was invoked. + void Execute(itk::Object * object, const itk::EventObject & event) { + if( !(itk::IterationEvent().CheckEvent( &event )) ) { + return; + } + + // Get the levels + RegistrationPointer registration = dynamic_cast( object ); + unsigned int numberOfLevels=registration->GetNumberOfLevels(); + unsigned int currentLevel=registration->GetCurrentLevel()+1; + + // Output the levels + std::cout<1) { + // Reinitialize the metric (!= number of samples) + typename GenericMetricType::Pointer genericMetric= GenericMetricType::New(); + genericMetric->SetArgsInfo(m_ArgsInfo); + genericMetric->SetFixedImage(registration->GetFixedImagePyramid()->GetOutput(registration->GetCurrentLevel())); + if (m_ArgsInfo.referenceMask_given) genericMetric->SetFixedImageMask(registration->GetMetric()->GetFixedImageMask()); + typedef itk::ImageToImageMetric< InternalImageType, InternalImageType > MetricType; + typename MetricType::Pointer metric=genericMetric->GetMetricPointer(); + registration->SetMetric(metric); + } + } + + void Execute(const itk::Object * , const itk::EventObject & ) { + return; + } + + void SetArgsInfo(args_info_clitkAffineRegistration a) { + m_ArgsInfo=a; + } + args_info_clitkAffineRegistration m_ArgsInfo; +}; + + +//============================================================================== +// Update with the number of dimensions +//============================================================================== +template +void AffineRegistrationGenericFilter::UpdateWithDim(std::string PixelType) +{ + if (m_Verbose) std::cout << "Images were detected to be "<< Dimension << "D and " << PixelType << "..." << std::endl; + if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and float..." << std::endl; + UpdateWithDimAndPixelType(); + // } + +} + + +//============================================================================== +// Update with the number of dimensions and pixeltype +//============================================================================== +template +void +AffineRegistrationGenericFilter::UpdateWithDimAndPixelType() +{ + //============================================================================= + //Input + //============================================================================= + bool threadsGiven=m_ArgsInfo.threads_given; + int threads=m_ArgsInfo.threads_arg; + + //Coordinate Representation + typedef double TCoordRep; + + //The pixeltype of the fixed image will be used for output + typedef itk::Image< PixelType, Dimension > FixedImageType; + + //Whatever the pixel type, internally we work with an image represented in float + typedef PixelType InternalPixelType; + typedef itk::Image< InternalPixelType, Dimension > InternalImageType; + + //Read in the reference/fixed image + typedef itk::ImageFileReader< InternalImageType > ReaderType; + typename ReaderType::Pointer fixedImageReader = ReaderType::New(); + fixedImageReader->SetFileName( m_ArgsInfo.reference_arg); + + + //Read in the object/moving image + typename ReaderType::Pointer movingImageReader = ReaderType::New(); + movingImageReader->SetFileName( m_ArgsInfo.target_arg ); + if (m_Verbose) std::cout<<"Reading images..."<Update(); + movingImageReader->Update(); + + if (m_Verbose) std::cout << "Reading images... " << std::endl; + + //we connect pointers to these internal images + typename InternalImageType::Pointer fixedImage= fixedImageReader->GetOutput(); + typename InternalImageType::Pointer movingImage= movingImageReader->GetOutput(); + + //We keep the images used for input for possible output + typename InternalImageType::Pointer inputFixedImage= fixedImageReader->GetOutput(); + typename InternalImageType::Pointer inputMovingImage= movingImageReader->GetOutput(); + + + //============================================================================ + // Preprocessing + //============================================================================ + + //If given, the intensities of both images are first normalized to a zero mean and SD of 1 + // (usefull for MI, not necessary for Mattes' MI but performed anyway for the ouput) + if ( m_ArgsInfo.normalize_flag ) { + typedef itk::NormalizeImageFilter< InternalImageType,InternalImageType > NormalizeFilterType; + + typename NormalizeFilterType::Pointer fixedNormalizeFilter = NormalizeFilterType::New(); + typename NormalizeFilterType::Pointer movingNormalizeFilter = NormalizeFilterType::New(); + + fixedNormalizeFilter->SetInput( fixedImage ); + movingNormalizeFilter->SetInput( movingImage ); + + fixedNormalizeFilter->Update(); + movingNormalizeFilter->Update(); + + //We keep the images used for input for possible output + inputFixedImage= fixedNormalizeFilter->GetOutput(); + inputMovingImage= movingNormalizeFilter->GetOutput(); + + //the pointers are reconnected for further output + fixedImage=fixedNormalizeFilter->GetOutput(); + movingImage=movingNormalizeFilter->GetOutput(); + + if (m_Verbose) std::cout << "Normalizing image intensities to zero mean and SD of 1..." << std::endl; + } + + + //If given, the images are blurred before processing + if ( m_ArgsInfo.blur_arg!= 0) { + typedef itk::DiscreteGaussianImageFilter GaussianFilterType; + typename GaussianFilterType::Pointer fixedSmoother = GaussianFilterType::New(); + typename GaussianFilterType::Pointer movingSmoother = GaussianFilterType::New(); + fixedSmoother->SetVariance( m_ArgsInfo.blur_arg ); + movingSmoother->SetVariance(m_ArgsInfo.blur_arg ); + + fixedSmoother->SetInput( fixedImage ); + movingSmoother->SetInput( movingImage ); + + fixedSmoother->Update(); + movingSmoother->Update(); + + fixedImage=fixedSmoother->GetOutput(); + movingImage=movingSmoother->GetOutput(); + + if (m_Verbose) std::cout << "Blurring images with a Gaussian with standard deviation of " << m_ArgsInfo.blur_arg <<"..." << std::endl; + } + + + //============================================================================ + // Setting up the moving image in a reference system + //============================================================================ + const itk::Vector movingResolution = movingImage->GetSpacing(); + typename InternalImageType::RegionType movingRegion = movingImage->GetLargestPossibleRegion(); + typename InternalImageType::RegionType::SizeType movingSize = movingRegion.GetSize(); + + // Print the parameters of the moving image + if (m_Verbose) { + std::cout << "Object or Moving image:"< fixedResolution = fixedImage->GetSpacing(); + typename InternalImageType::RegionType fixedRegion = fixedImage->GetLargestPossibleRegion(); + typename InternalImageType::RegionType::SizeType fixedSize = fixedRegion.GetSize(); + + // Print the parameters of the moving image and the transform + if (m_Verbose) { + std::cout << "Target or Moving image:"< MaskType; + typename MaskType::Pointer fixedMask=NULL; + if (m_ArgsInfo.referenceMask_given) { + fixedMask= MaskType::New(); + typedef itk::Image< unsigned char, Dimension > ImageMaskType; + typedef itk::ImageFileReader< ImageMaskType > MaskReaderType; + typename MaskReaderType::Pointer maskReader = MaskReaderType::New(); + maskReader->SetFileName(m_ArgsInfo.referenceMask_arg); + try { + maskReader->Update(); + } catch( itk::ExceptionObject & err ) { + std::cerr << "ExceptionObject caught while reading mask !" << std::endl; + std::cerr << err << std::endl; + return; + } + if (m_Verbose)std::cout <<"Reference image mask was read..." <SetImage( maskReader->GetOutput() ); + } + + typedef itk::ImageMaskSpatialObject< Dimension > MaskType; + typename MaskType::Pointer movingMask=NULL; + if (m_ArgsInfo.targetMask_given) { + movingMask= MaskType::New(); + typedef itk::Image< unsigned char, Dimension > ImageMaskType; + typedef itk::ImageFileReader< ImageMaskType > MaskReaderType; + typename MaskReaderType::Pointer maskReader = MaskReaderType::New(); + maskReader->SetFileName(m_ArgsInfo.targetMask_arg); + try { + maskReader->Update(); + } catch( itk::ExceptionObject & err ) { + std::cerr << "ExceptionObject caught !" << std::endl; + std::cerr << err << std::endl; + } + if (m_Verbose)std::cout <<"Target image mask was read..." <SetImage( maskReader->GetOutput() ); + } + + + //============================================================================ + // The image pyramids + //============================================================================ + typedef itk::RecursiveMultiResolutionPyramidImageFilter FixedImagePyramidType; + typedef itk::RecursiveMultiResolutionPyramidImageFilter MovingImagePyramidType; + typename FixedImagePyramidType::Pointer fixedImagePyramid = FixedImagePyramidType::New(); + typename MovingImagePyramidType::Pointer movingImagePyramid = MovingImagePyramidType::New(); + fixedImagePyramid->SetUseShrinkImageFilter(false); + fixedImagePyramid->SetInput(fixedImage); + fixedImagePyramid->SetNumberOfLevels(m_ArgsInfo.levels_arg); + movingImagePyramid->SetUseShrinkImageFilter(false); + movingImagePyramid->SetInput(movingImage); + movingImagePyramid->SetNumberOfLevels(m_ArgsInfo.levels_arg); + if (m_Verbose) std::cout<<"Creating the image pyramid..."<Update(); + movingImagePyramid->Update(); + + + + //============================================================================ + // We retrieve the type of metric from the command line + //============================================================================ + typedef clitk::GenericMetric GenericMetricType; + typename GenericMetricType::Pointer genericMetric=GenericMetricType::New(); + genericMetric->SetArgsInfo(m_ArgsInfo); + genericMetric->SetFixedImage(fixedImagePyramid->GetOutput(0)); + if (fixedMask) genericMetric->SetFixedImageMask(fixedMask); + typedef itk::ImageToImageMetric< InternalImageType, InternalImageType > MetricType; + typename MetricType::Pointer metric=genericMetric->GetMetricPointer(); + if (movingMask) metric->SetMovingImageMask(movingMask); + +#ifdef ITK_USE_OPTIMIZED_REGISTRATION_METHODS + if (threadsGiven) metric->SetNumberOfThreads( threads ); +#else + if (m_Verbose) std::cout<<"Not setting the number of threads (not compiled with USE_OPTIMIZED_REGISTRATION_METHODS)..."< CalculatorType; + typename CalculatorType::Pointer fixedCalculator= CalculatorType::New(); + + typename InternalImageType::Pointer fixedThresh; + if (m_ArgsInfo.intThreshold_given) { + typedef itk::ThresholdImageFilter ThresholdImageFilterType; + typename ThresholdImageFilterType::Pointer thresholder = ThresholdImageFilterType::New(); + thresholder->SetInput(fixedImage); + thresholder->SetLower(m_ArgsInfo.intThreshold_arg); + thresholder->Update(); + fixedThresh=thresholder->GetOutput(); + } else fixedThresh=fixedImage; + + fixedCalculator->SetImage(fixedThresh); + fixedCalculator->Compute(); + Vector fixedCenter=fixedCalculator->GetCenterOfGravity(); + if(m_Verbose)std::cout<<"The fixed center of gravity is "< CalculatorType; + typename CalculatorType::Pointer movingCalculator= CalculatorType::New(); + + typename InternalImageType::Pointer movingThresh; + if (m_ArgsInfo.intThreshold_given) { + typedef itk::ThresholdImageFilter ThresholdImageFilterType; + typename ThresholdImageFilterType::Pointer thresholder = ThresholdImageFilterType::New(); + thresholder->SetInput(movingImage); + thresholder->SetLower(m_ArgsInfo.intThreshold_arg); + thresholder->Update(); + movingThresh=thresholder->GetOutput(); + } else movingThresh=movingImage; + + movingCalculator->SetImage(movingThresh); + movingCalculator->Compute(); + Vector movingCenter=movingCalculator->GetCenterOfGravity(); + if(m_Verbose)std::cout<<"The moving center of gravity is "< shift= movingCenter-fixedCenter; + if(m_Verbose)std::cout<<"The initial shift applied is "< GenericAffineTransformType; + typename GenericAffineTransformType::Pointer genericAffineTransform = GenericAffineTransformType::New(); + genericAffineTransform->SetArgsInfo(m_ArgsInfo); + typedef itk::Transform< double, Dimension, Dimension > TransformType; + typename TransformType::Pointer transform = genericAffineTransform->GetTransform(); + + + //======================================================= + // Interpolator + //======================================================= + typedef clitk::GenericInterpolator GenericInterpolatorType; + typename GenericInterpolatorType::Pointer genericInterpolator=GenericInterpolatorType::New(); + genericInterpolator->SetArgsInfo(m_ArgsInfo); + typedef itk::InterpolateImageFunction< InternalImageType, TCoordRep > InterpolatorType; + typename InterpolatorType::Pointer interpolator=genericInterpolator->GetInterpolatorPointer(); + + + //============================================================================ + // Optimizer + //============================================================================ + typedef clitk::GenericOptimizer GenericOptimizerType; + unsigned int nParam = transform->GetNumberOfParameters(); + GenericOptimizerType::Pointer genericOptimizer=GenericOptimizerType::New(); + genericOptimizer->SetArgsInfo(m_ArgsInfo); + genericOptimizer->SetOutputIteration(m_Verbose); + genericOptimizer->SetOutputPosition(m_Verbose); + genericOptimizer->SetOutputValue(m_Verbose); + genericOptimizer->SetOutputGradient(m_ArgsInfo.gradient_flag); + genericOptimizer->SetMaximize(genericMetric->GetMaximize()); + genericOptimizer->SetNumberOfParameters(nParam); + typedef itk::SingleValuedNonLinearOptimizer OptimizerType; + OptimizerType::Pointer optimizer=genericOptimizer->GetOptimizerPointer(); + + // Scales + itk::Optimizer::ScalesType scales( nParam ); + for (unsigned int i=nParam-Dimension; iSetScales(scales); + + //============================================================================ + // Multiresolution registration + //============================================================================ + typedef itk::MultiResolutionImageRegistrationMethod< InternalImageType,InternalImageType > RegistrationType; + typename RegistrationType::Pointer registration = RegistrationType::New(); + registration->SetFixedImage( fixedImage ); + registration->SetFixedImageRegion(fixedImage->GetLargestPossibleRegion()); + registration->SetMovingImage( movingImage ); + registration->SetFixedImagePyramid( fixedImagePyramid ); + registration->SetMovingImagePyramid( movingImagePyramid ); + registration->SetTransform( transform ); + registration->SetInitialTransformParameters( transform->GetParameters() ); + registration->SetInterpolator( interpolator ); + registration->SetMetric(metric); + registration->SetOptimizer(optimizer); + registration->SetNumberOfLevels( m_ArgsInfo.levels_arg ); + if (m_Verbose) std::cout << "Setting "<< m_ArgsInfo.levels_arg <<" resolution levels..." << std::endl; + if (m_Verbose) std::cout << "Initial Transform: "<< registration->GetInitialTransformParameters()<SetOptimizer(genericOptimizer); + optimizer->AddObserver( itk::IterationEvent(), observer ); + + + // Output level info + typedef RegistrationInterfaceCommand CommandType; + typename CommandType::Pointer command = CommandType::New(); + command->SetArgsInfo(m_ArgsInfo); + registration->AddObserver( itk::IterationEvent(), command ); + + } + + + //============================================================================ + // Finally we can start the registration with the given amount of multiresolution levels + //============================================================================ + if (m_Verbose) std::cout << "Starting the registration now..." << std::endl; + + try { + registration->StartRegistration(); + } catch( itk::ExceptionObject & err ) { + std::cerr << "ExceptionObject caught !" << std::endl; + std::cerr << err << std::endl; + } + + + //============================================================================ + // Processing the result of the registration + //============================================================================ + OptimizerType::ParametersType finalParameters = registration->GetLastTransformParameters(); + std::cout<< "Result : " < +#include + +namespace clitk +{ +//========================================================================================================================= +template<> +itk::MatrixOffsetTransformBase::Pointer +clitk::GenericAffineTransform::GetNewEulerTransform() +{ + itk::Euler2DTransform< double >::Pointer p = itk::Euler2DTransform< double >::New(); + itk::MatrixOffsetTransformBase::Pointer pp; + pp = p; + return pp; +} + +//========================================================================================================================= +template<> +itk::MatrixOffsetTransformBase::Pointer +clitk::GenericAffineTransform::GetNewEulerTransform() +{ + itk::Euler3DTransform< double >::Pointer p = itk::Euler3DTransform< double >::New(); + itk::MatrixOffsetTransformBase::Pointer pp; + pp = p; + return pp; +} +} diff --git a/registration/clitkGenericAffineTransform.h b/registration/clitkGenericAffineTransform.h new file mode 100644 index 0000000..a403074 --- /dev/null +++ b/registration/clitkGenericAffineTransform.h @@ -0,0 +1,95 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef __clitkGenericAffineTransform_h +#define __clitkGenericAffineTransform_h + + +//itk include +#include +#include +#include + +/* + +Requires at least the following section is the .ggo file + +option "transform" - "Type: 0=Identity, 1=Translation, 2=Rigid, 3=Affine" int no default="2" +option "transX" x "1-3: Initial translation in mm along the X axis" float no default="0.0" +option "transY" y "1-3: Initial translation in mm along the Y axis" float no default="0.0" +option "transZ" z "1-3: Initial translation in mm along the Z axis" float no default="0.0" +option "initMatrix" - "2-3: Initial matrix (reference to object space) filename " string no +option "moment" - "2-3: Initialize translation by aligning the center of gravities for the respective intensities" flag off + +The use will look something like + + typedef clitk::GenericAffineTransform GenericAffineTransformType; + typename GenericAffineTransformType::Pointer genericAffineTransform = GenericAffineTransformType::New(); + genericAffineTransform->SetArgsInfo(m_ArgsInfo); + typedef itk::Transform< double, Dimension, Dimension > TransformType; + typename TransformType::Pointer transform = genericAffineTransform->GetTransform(); +*/ + +namespace clitk +{ +template +class GenericAffineTransform : public itk::LightObject +{ +public: + //============================================== + typedef GenericAffineTransform Self; + typedef itk::LightObject Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + typedef itk::Transform< TCoordRep, Dimension, Dimension> TransformType; + typedef typename TransformType::Pointer TransformPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + //============================================== + //Set members + void SetArgsInfo(args_info_type args_info) { + m_ArgsInfo = args_info; + m_Verbose = m_ArgsInfo.verbose_flag; + } + + //============================================== + //Get members + TransformPointer GetTransform(void); + + //============================================== +protected: + GenericAffineTransform(); + ~GenericAffineTransform() {}; + + typename itk::MatrixOffsetTransformBase::Pointer GetNewEulerTransform(); + +private: + args_info_type m_ArgsInfo; + TransformPointer m_Transform; + bool m_Verbose; +}; +} + +#ifndef ITK_MANUAL_INSTANTIATION +#include "clitkGenericAffineTransform.txx" +#endif + +#endif // #define __clitkGenericAffineTransform_h diff --git a/registration/clitkGenericAffineTransform.txx b/registration/clitkGenericAffineTransform.txx new file mode 100644 index 0000000..eb4c6f9 --- /dev/null +++ b/registration/clitkGenericAffineTransform.txx @@ -0,0 +1,109 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef __clitkGenericAffineTransform_txx +#define __clitkGenericAffineTransform_txx + +#include "clitkTransformUtilities.h" + +namespace clitk +{ + +//========================================================================================================================= +//constructor +template +GenericAffineTransform::GenericAffineTransform() +{ + m_Transform=NULL; + m_Verbose=false; +} + +//========================================================================================================================= +//Get the pointer +template +typename GenericAffineTransform::TransformPointer +GenericAffineTransform::GetTransform() +{ + //============================================================================ + // We retrieve the type of transform from the command line + //============================================================================ + typedef itk::MatrixOffsetTransformBase MatrixXType; + typename MatrixXType::Pointer tMatrix; + typedef itk::TranslationTransform< TCoordRep, Dimension> TranslationXType; + typename TranslationXType::Pointer tTranslation; + + switch ( m_ArgsInfo.transform_arg ) { + case 0: + m_Transform= itk::IdentityTransform< TCoordRep, Dimension>::New(); + if (m_Verbose) std::cout<<"Using identity transform..."<::New(); + if (m_Verbose) std::cout<<"Using affine transform..."<0) { + //Initialize translations + offset[0] = m_ArgsInfo.transX_arg; + offset[1] = m_ArgsInfo.transY_arg; + if(Dimension>2) offset[2] = m_ArgsInfo.transZ_arg; + if(m_ArgsInfo.initMatrix_given) { + itk::Matrix matHom = ReadMatrix(m_ArgsInfo.initMatrix_arg); + offset = GetTranslationPartMatrix(matHom); + } + } + switch(m_ArgsInfo.transform_arg) { + case 1: + tTranslation->SetOffset(offset); + m_Transform=tTranslation; + break; + case 2: + case 3: + //Init rigid and affine transform center + itk::Point center; + for (unsigned int i=0; iSetCenter(center); + + //Initialize transform + if(m_ArgsInfo.initMatrix_given) { + itk::Matrix matHom = ReadMatrix(m_ArgsInfo.initMatrix_arg); + typename MatrixXType::MatrixType matrix = GetRotationalPartMatrix(matHom); + tMatrix->SetOffset(offset); + tMatrix->SetMatrix(matrix); + } + m_Transform=tMatrix; + break; + } + + return m_Transform; +} +} + +#endif diff --git a/registration/clitkGenericInterpolator.h b/registration/clitkGenericInterpolator.h new file mode 100644 index 0000000..a0b2faf --- /dev/null +++ b/registration/clitkGenericInterpolator.h @@ -0,0 +1,93 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef __clitkGenericInterpolator_h +#define __clitkGenericInterpolator_h + +//itk include +#include "itkNearestNeighborInterpolateImageFunction.h" +#include "itkLinearInterpolateImageFunction.h" +#include "itkBSplineInterpolateImageFunction.h" +#include "itkBSplineInterpolateImageFunctionWithLUT.h" + + +/* + +Requires at least the following section is the .ggo file + +option "interp" - "Interpolation: 0=NN, 1=Linear, 2=BSpline, 3=BLUT" int no default="1" +option "interpOrder" - "Order if BLUT or BSpline (0-5)" int no default="3" +option "interpSF" - "Sampling factor if BLUT" int no default="20" + +The use will look something like + +typedef clitk::GenericInterpolator GenericInterpolatorType; +typename GenericInterpolatorType::Pointer genericInterpolator=GenericInterpolatorType::New(); +genericInterpolator->SetArgsInfo(m_ArgsInfo); +typedef itk::InterpolateImageFunction InterpolatorType; +typename InterpolatorType::Pointer interpolator=genericInterpolator->GetInterpolatorPointer(); + +*/ + +namespace clitk +{ + +template +class GenericInterpolator : public itk::LightObject +{ +public: + //============================================== + typedef GenericInterpolator Self; + typedef itk::LightObject Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + typedef itk::InterpolateImageFunction InterpolatorType; + typedef typename InterpolatorType::Pointer InterpolatorPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + //============================================== + //Set members + void SetArgsInfo(args_info_type args_info) { + m_ArgsInfo= args_info; + m_Verbose=m_ArgsInfo.verbose_flag; + } + + //============================================== + //Get members + InterpolatorPointer GetInterpolatorPointer(void); + + //============================================== +protected: + GenericInterpolator(); + ~GenericInterpolator() {}; + +private: + args_info_type m_ArgsInfo; + InterpolatorPointer m_Interpolator; + bool m_Verbose; +}; + +} // end namespace clitk +#ifndef ITK_MANUAL_INSTANTIATION +#include "clitkGenericInterpolator.txx" +#endif + +#endif // #define __clitkGenericInterpolator_h diff --git a/registration/clitkGenericInterpolator.txx b/registration/clitkGenericInterpolator.txx new file mode 100644 index 0000000..c5deffe --- /dev/null +++ b/registration/clitkGenericInterpolator.txx @@ -0,0 +1,90 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef __clitkGenericInterpolator_txx +#define __clitkGenericInterpolator_txx + +#include "clitkGenericInterpolator.h" + + +namespace clitk +{ + +//========================================================================================================================= +//constructor +template +GenericInterpolator::GenericInterpolator() +{ + m_Interpolator=NULL; + m_Verbose=false; +} + + +//========================================================================================================================= +//Get the pointer +template +typename GenericInterpolator::InterpolatorPointer +GenericInterpolator::GetInterpolatorPointer() +{ + //============================================================================ + // We retrieve the type of interpolation from the command line + //============================================================================ + typename InterpolatorType::Pointer interpolator; + + switch ( m_ArgsInfo.interp_arg ) { + case 0: + + interpolator= itk::NearestNeighborInterpolateImageFunction< ImageType,TCoordRep >::New(); + if (m_Verbose) std::cout<<"Using nearestneighbor interpolation..."<::New(); + if (m_Verbose) std::cout<<"Using linear interpolation..."<::Pointer m =itk::BSplineInterpolateImageFunction< ImageType,TCoordRep >::New(); + m->SetSplineOrder(m_ArgsInfo.interpOrder_arg); + interpolator=m; + if (m_Verbose) std::cout<<"Using Bspline interpolation..."<::Pointer m =itk::BSplineInterpolateImageFunctionWithLUT< ImageType,TCoordRep >::New(); + m->SetSplineOrder(m_ArgsInfo.interpOrder_arg); + m->SetLUTSamplingFactor(m_ArgsInfo.interpSF_arg); + interpolator=m; + if (m_Verbose) std::cout<<"Using BLUT interpolation..."< GenericMetricType; +typename GenericMetricType::Pointer genericMetric=GenericMetricType::New(); +genericMetric->SetArgsInfo(m_ArgsInfo); +genericMetric->SetFixedImageRegion(fixedImage->GetLargestPossibleRegion()); +genericMetric->SetFixedImage(fixedImage); +genericMetric->SetFixedImageMask(fixedImageMask); +typedef itk::ImageToImageMetric MetricType; +typename MetricType::Pointer metric=genericMetric->GetMetricPointer(); + +*/ + + +namespace clitk +{ + +template < class args_info_type, class FixedImageType, class MovingImageType > +class GenericMetric : public itk::LightObject +{ +public: + //============================================== + typedef GenericMetric Self; + typedef itk::LightObject Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Determine the image dimension. */ + itkStaticConstMacro(FixedImageDimension, unsigned int, + FixedImageType::ImageDimension ); + itkStaticConstMacro(MovingImageDimension, unsigned int, + MovingImageType::ImageDimension ); + + // Typedef + typedef itk::ImageToImageMetric MetricType; + typedef typename MetricType::Pointer MetricPointer; + typedef typename FixedImageType::RegionType FixedImageRegionType; + typedef itk::SpatialObject FixedImageMaskType; + typedef typename FixedImageMaskType::Pointer MaskPointer; + typedef typename FixedImageType::PixelType FixedImagePixelType; + typedef typename FixedImageType::IndexType FixedImageIndexType; + typedef typename FixedImageType::PointType FixedImagePointType; + + //============================================== + //Set members + void SetArgsInfo(args_info_type args_info) { + m_ArgsInfo= args_info; + m_Verbose=m_ArgsInfo.verbose_flag; + } + void SetFixedImageRegion(const FixedImageRegionType f) { + m_FixedImageRegion=f; + m_FixedImageRegionGiven=true; + } + void SetFixedImage(typename FixedImageType::Pointer f) { + m_FixedImage=f; + } + void SetFixedImageMask( const FixedImageMaskType* f) { + m_FixedImageMask=f; + } + + //============================================== + //Get members + MetricPointer GetMetricPointer(void); + bool GetMaximize(void) { + return m_Maximize; + } + + + //============================================== +protected: + GenericMetric(); + ~GenericMetric() {}; + +private: + args_info_type m_ArgsInfo; + MetricPointer m_Metric; + bool m_Maximize; + bool m_Verbose; + bool m_FixedImageRegionGiven; + FixedImageRegionType m_FixedImageRegion; + typename FixedImageType::Pointer m_FixedImage; + typename FixedImageMaskType::ConstPointer m_FixedImageMask; + +#ifdef ITK_USE_OPTIMIZED_REGISTRATION_METHODS + FixedImagePixelType m_FixedImageSamplesIntensityThreshold; + bool m_UseFixedImageSamplesIntensityThreshold; +#endif + +}; + +} // end namespace clitk +#ifndef ITK_MANUAL_INSTANTIATION +#include "clitkGenericMetric.txx" +#endif + +#endif // #define __clitkGenericMetric_h diff --git a/registration/clitkGenericMetric.txx b/registration/clitkGenericMetric.txx new file mode 100644 index 0000000..27c68ed --- /dev/null +++ b/registration/clitkGenericMetric.txx @@ -0,0 +1,401 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef __clitkGenericMetric_txx +#define __clitkGenericMetric_txx + +#include "clitkGenericMetric.h" + + +namespace clitk +{ + +//========================================================================================================================= +//constructor +template +GenericMetric::GenericMetric() +{ + m_Metric=NULL; + m_Maximize=false; + m_Verbose=false; + m_FixedImageRegionGiven=false; + m_FixedImageSamplesIntensityThreshold=0; + m_UseFixedImageSamplesIntensityThreshold=false; + m_FixedImageMask=NULL; +} + + +//========================================================================================================================= +//Get the pointer +template +typename GenericMetric::MetricPointer +GenericMetric::GetMetricPointer() +{ + //============================================================================ + // Sanity check: + // The image is required for the region and spacing + if( ! this->m_FixedImage ) { + itkExceptionMacro( "Fixed Image has not been set" ); + } + + // If the image come from a filter, then update that filter. + if( this->m_FixedImage->GetSource() ) { + this->m_FixedImage->Update(); + } + + // The metric region + if( ! m_FixedImageRegionGiven ) { + m_FixedImageRegion=m_FixedImage->GetLargestPossibleRegion(); + } + + + //============================================================================ + //switch on the metric type chosen adn set metric specific members + switch (m_ArgsInfo.metric_arg) { + + case 0: { + typename itk::MeanSquaresImageToImageMetric< FixedImageType, MovingImageType >::Pointer m = + itk::MeanSquaresImageToImageMetric< FixedImageType, MovingImageType >::New(); + + //Set Parameters for this metric + m_Maximize=false; + if (m_Verbose) std::cout<<"Using the mean squares metric..."<::Pointer m + =clitk::NormalizedCorrelationImageToImageMetric< FixedImageType, MovingImageType >::New(); + + //Set Parameters for this metric + m->SetSubtractMean(m_ArgsInfo.subtractMean_flag); + m_Maximize=false; + if (m_Verbose) { + if ( !m_ArgsInfo.subtractMean_flag) std::cout<<"Using the normalized correlation metric without subtracting the mean..."<::Pointer m + = itk::CorrelationCoefficientHistogramImageToImageMetric< FixedImageType, MovingImageType >::New(); + + //Set parameters for this metric + typename itk::CorrelationCoefficientHistogramImageToImageMetric::HistogramSizeType size; + for (unsigned int i=0; i < FixedImageDimension; i++)size[i]=m_ArgsInfo.bins_arg; + m->SetHistogramSize(size); + m_Maximize=false; + if (m_Verbose) std::cout<<"Using the histogram correlation coefficient metric..."<::Pointer m + = itk::GradientDifferenceImageToImageMetric< FixedImageType, MovingImageType >::New(); + + //Set parameters for this metric + m_Maximize=false; + if (m_Verbose) std::cout<<"Using the gradient difference metric..."<::Pointer m + = itk::MutualInformationImageToImageMetric< FixedImageType, MovingImageType >::New(); + + //Set parameters for this metric + m->SetFixedImageStandardDeviation( m_ArgsInfo.stdDev_arg ); + m->SetMovingImageStandardDeviation( m_ArgsInfo.stdDev_arg); + m_Maximize=true; + + //Randomize samples if demanded + if (m_ArgsInfo.random_flag ) m->ReinitializeSeed(); + else m->ReinitializeSeed(0); + if (m_Verbose) std::cout<<"Using Viola-Wells MI..."<::Pointer m + = itk::MutualInformationHistogramImageToImageMetric< FixedImageType, MovingImageType>::New(); + + //Set parameters for this metric + typename itk::MutualInformationHistogramImageToImageMetric::HistogramSizeType size; + for (unsigned int i=0; i < FixedImageDimension; i++)size[i]=m_ArgsInfo.bins_arg; + m->SetHistogramSize(size); + m_Maximize=true; + if (m_Verbose) std::cout<<"Using the histogram MI with "<::Pointer m + = itk::MattesMutualInformationImageToImageMetric< FixedImageType, MovingImageType >::New(); + + //Set parameters for this metric + m_Maximize=false; + m->SetNumberOfHistogramBins(m_ArgsInfo.bins_arg); + + //Randomize samples if demanded + if (m_ArgsInfo.random_flag ) m->ReinitializeSeed(); + else m->ReinitializeSeed(0); + + // Two ways of calculating the derivatives + m->SetUseExplicitPDFDerivatives(m_ArgsInfo.explicitPDFDerivatives_flag); + + + if (m_Verbose) { + std::cout<<"Using Mattes' MI with "<::Pointer m + =itk::NormalizedMutualInformationHistogramImageToImageMetric< FixedImageType, MovingImageType >::New(); + + //Set parameters for this metric + typename itk::NormalizedMutualInformationHistogramImageToImageMetric::HistogramSizeType size; + for (unsigned int i=0; i < FixedImageDimension; i++)size[i]=m_ArgsInfo.bins_arg; + m->SetHistogramSize(size); + m_Maximize=false; + if (m_Verbose) std::cout<<"Using the normalized MI with "<::Pointer m + = clitk::CorrelationRatioImageToImageMetric< FixedImageType, MovingImageType >::New(); + + //Set parameters for this metric + m_Maximize=false; + m->SetNumberOfBins(m_ArgsInfo.bins_arg); + if (m_Verbose) std::cout<<"Using the correlation ratio..."<::Pointer m + = itk::MeanSquaresImageToImageMetricFor3DBLUTFFD< FixedImageType, MovingImageType >::New(); + + //Set Parameters for this metric + m_Maximize=false; + if (m_Verbose) std::cout<<"Using the mean squares metric for 3D BLUT FFD..."<::Pointer m + =clitk::NormalizedCorrelationImageToImageMetricFor3DBLUTFFD< FixedImageType, MovingImageType >::New(); + + //Set Parameters for this metric + m->SetSubtractMean(m_ArgsInfo.subtractMean_flag); + m_Maximize=false; + if (m_Verbose) { + if ( !m_ArgsInfo.subtractMean_flag) std::cout<<"Using the normalized correlation metric for 3D BLUT FFD without subtracting the mean..."<::Pointer m + = itk::MattesMutualInformationImageToImageMetricFor3DBLUTFFD< FixedImageType, MovingImageType >::New(); + + //Set parameters for this metric + m_Maximize=false; + m->SetNumberOfHistogramBins(m_ArgsInfo.bins_arg); + + //Randomize samples if demanded + if (m_ArgsInfo.random_flag ) m->ReinitializeSeed(); + else m->ReinitializeSeed(0); + + // Two ways of calculating the derivatives + m->SetUseExplicitPDFDerivatives(m_ArgsInfo.explicitPDFDerivatives_flag); + + + if (m_Verbose) { + std::cout<<"Using Mattes' MI for 3D BLUT FFD with "<SetFixedImageMask(m_FixedImageMask); + m_Metric->SetFixedImageRegion(m_FixedImageRegion); + + +#ifdef ITK_USE_OPTIMIZED_REGISTRATION_METHODS + + //============================================================================ + // Set the lower intensity threshold + if (m_ArgsInfo.intThreshold_given) { + m_UseFixedImageSamplesIntensityThreshold=true; + m_FixedImageSamplesIntensityThreshold=m_ArgsInfo.intThreshold_arg; + m_Metric->SetFixedImageSamplesIntensityThreshold(m_FixedImageSamplesIntensityThreshold); + if (m_Verbose) std::cout<<"Setting the fixed image intensity threshold to "<SetUseAllPixels(true); + if (m_Verbose) std::cout<<"Using all pixels (a fraction of "< fiic;// fixedImageindexContainer + unsigned int numberOfValidPixels=0; + FixedImageIndexType index; + FixedImagePointType inputPoint; + + // Calculate the number + const unsigned int totalNumberOfPixels = m_FixedImageRegion.GetNumberOfPixels(); + const unsigned int numberOfDemandedPixels = static_cast< unsigned int >( (double) totalNumberOfPixels *m_ArgsInfo.samples_arg ); + + // -------------------------------------------------- + // Sample whole image sequentially and pass the indexes + if (m_ArgsInfo.samples_arg==1.0) { + if (m_Verbose) std::cout<<"Sequentially scanning the image for valid pixels..."< RegionIterator; + RegionIterator regionIter( m_FixedImage, m_FixedImageRegion ); + + // go over the whole region + regionIter.GoToBegin(); + while(!regionIter.IsAtEnd() ) { + + // Get sampled index + index = regionIter.GetIndex(); + + // Mask? + if( m_FixedImageMask.IsNotNull() ) { + // Check if the Index is inside the mask, translate index to point + m_FixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + // If not inside the mask, ignore the point + if( !m_FixedImageMask->IsInside( inputPoint ) ) { + ++regionIter; // jump to next pixel + continue; + } + } + + // Intensity? + if( m_UseFixedImageSamplesIntensityThreshold && + ( regionIter.Get() < m_FixedImageSamplesIntensityThreshold) ) { + ++regionIter; // jump to next pixel + continue; + } + + // Add point to the numbers + fiic.push_back(index); + ++numberOfValidPixels; + ++regionIter; + } + } + + // -------------------------------------------------- + // Sample randomly + else { + if (m_Verbose) std::cout<<"Randomly scanning the image for valid pixels..."< RandomIterator; + RandomIterator randIter( m_FixedImage, m_FixedImageRegion ); + + // Randomly sample the image + randIter.SetNumberOfSamples( numberOfDemandedPixels * 1000 ); + randIter.GoToBegin(); + while( (!randIter.IsAtEnd()) && (numberOfValidPixels<=numberOfDemandedPixels) ) { + // Get sampled index + index = randIter.GetIndex(); + + // Mask? + if( m_FixedImageMask.IsNotNull() ) { + + // Check if the Index is inside the mask, translate index to point + m_FixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + // If not inside the mask, ignore the point + if( !m_FixedImageMask->IsInside( inputPoint ) ) { + ++randIter; // jump to next pixel + continue; + } + + } + + // Intensity? + if( m_UseFixedImageSamplesIntensityThreshold && + randIter.Get() < m_FixedImageSamplesIntensityThreshold ) { + ++randIter; + continue; + } + + // Add point to the numbers + fiic.push_back(index); + ++numberOfValidPixels; + ++randIter; + } + } + + + // Set the indexes of valid samples + m_Metric->SetFixedImageIndexes(fiic); + // m_Metric->SetNumberOfSpatialSamples( numberOfValidPixels); + if (m_Verbose) std::cout<<"A fraction of "< + +/*Requires at least this section in the associated *.ggo file: (modify defaults to fit application) + +option "optimizer" - "0=Simplex, 1=Powell, 2=FRPR, 3=Regular Step GD, 4=VersorRigid3D, 5=Conjugated Gradient, 6=L-BFGS, 7=L-BFGS-B" int no default="0" +option "delta" - "0: Initial delta, otherwise automatic" double no +option "step" - "1,2,3,4: Initial stepsize (to be multiplied with the gradient)" double no default="2.0" +option "relax" - "3,4: Relaxation of the stepsize (multiplied each time the gradient changes sign)" double no default="0.7" +option "valueTol" - "0,1,2: Tolerance on the function" double no default="0.01" +option "stepTol" - "0,1,3,4: Tolerance on the step size" double no default="0.1" +option "gradTol" - "3,4,6,7: Tolerance on the (projected) gradient magnitude (7: 1=low->1e-10=high precision)" double no default="1e-5" +option "lineAcc" - "6: Line accuracy (eg: high=0.1, low=0.9)" double no default="0.9" +option "convFactor" - "7: Convergence factor: terminate if factor*machine_precision>reduction in cost (1e+12 low -> 1e+1 high precision) " double no default="1e+7" +option "maxIt" - "0-7: Maximum number of iterations" int no default="500" +option "maxLineIt" - "1,2: Maximum number of line iterations" int no default="50" +option "maxEval" - "6,7: Maximum number of evaluations" int no default="500" +option "maxCorr" - "7: Maximum number of corrections" int no default="5" +option "selectBound" - "7: Select the type of bound: 0=none, 1=u, 2=u&l, 3=l" int no default="0" +option "lowerBound" - "7: The lower bound" double no +option "upperBound" - "7: The upper bound" double no + +*/ + +namespace clitk +{ +template +class GenericOptimizer : public itk::LightObject +{ +public: + //============================================== + typedef GenericOptimizer Self; + typedef itk::LightObject Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + + typedef itk::SingleValuedNonLinearOptimizer OptimizerType; + typedef OptimizerType::Pointer OptimizerPointer; + + //============================================== + //Set members + void SetArgsInfo(args_info_type args_info) { + m_ArgsInfo= args_info; + m_Verbose=m_ArgsInfo.verbose_flag; + } + void SetMaximize(bool f) { + m_Maximize=f; + m_MaximizeGiven=true; + } + + void SetNumberOfParameters(unsigned int n) { + m_NumberOfParameters=n; + } + + //============================================== + //Get members + OptimizerPointer GetOptimizerPointer(void) { + + if (!m_MaximizeGiven)std::cerr<<"Warning: Maximize/Minimize was not given to the optimizer!!"<SetMaximize(m_Maximize); + if (m_ArgsInfo.delta_given) { + itk::Array delta(m_NumberOfParameters); + for (unsigned int i =0 ; i < m_NumberOfParameters; i ++) + delta[i]=m_ArgsInfo.delta_arg; + m->SetInitialSimplexDelta(delta); + m->SetAutomaticInitialSimplex(false); + } else m->SetAutomaticInitialSimplex(true); + m->SetMaximumNumberOfIterations(m_ArgsInfo.maxIt_arg); + m->SetFunctionConvergenceTolerance(m_ArgsInfo.valueTol_arg); + m->SetParametersConvergenceTolerance(m_ArgsInfo.stepTol_arg); + if (m_Verbose) std::cout<<"Using Simplex Optimizer..."<SetMaximize(m_Maximize); + m->SetStepLength(m_ArgsInfo.step_arg); + m->SetStepTolerance(m_ArgsInfo.stepTol_arg); + m->SetValueTolerance (m_ArgsInfo.valueTol_arg); + m->SetMaximumIteration(m_ArgsInfo.maxIt_arg); + m->SetMaximumLineIteration(m_ArgsInfo.maxLineIt_arg); + if (m_Verbose) std::cout<<"Using Powell Optimizer..."<SetMaximize(m_Maximize); + m->SetStepLength(m_ArgsInfo.step_arg); + m->SetStepTolerance(m_ArgsInfo.stepTol_arg); + m->SetValueTolerance (m_ArgsInfo.valueTol_arg); + m->SetMaximumIteration(m_ArgsInfo.maxIt_arg); + m->SetMaximumLineIteration(m_ArgsInfo.maxLineIt_arg); + if (m_Verbose) std::cout<<"Using Powell Optimizer..."<SetMaximize(m_Maximize); + m->SetMinimumStepLength( m_ArgsInfo.stepTol_arg ); + m->SetRelaxationFactor( m_ArgsInfo.relax_arg ); + m->SetMaximumStepLength( m_ArgsInfo.step_arg ); + m->SetNumberOfIterations(m_ArgsInfo.maxIt_arg ); + m->SetGradientMagnitudeTolerance(m_ArgsInfo.gradTol_arg ); + if (m_Verbose) std::cout<<"Using Regular Step Gradient Descent Optimizer..."<SetMaximize(m_Maximize); + m->SetMinimumStepLength( m_ArgsInfo.stepTol_arg ); + m->SetRelaxationFactor( m_ArgsInfo.relax_arg ); + m->SetMaximumStepLength( m_ArgsInfo.step_arg ); + m->SetNumberOfIterations(m_ArgsInfo.maxIt_arg ); + m->SetGradientMagnitudeTolerance(m_ArgsInfo.gradTol_arg ); + if (m_Verbose) std::cout<<"Using VersorRigid3DTransform Optimizer..."<SetMaximize(m_Maximize); + if (m_Verbose) std::cout<<"Using Conjugated Gradient Optimizer..."<SetMaximize(m_Maximize); + m->SetGradientConvergenceTolerance(m_ArgsInfo.gradTol_arg); + m->SetLineSearchAccuracy(m_ArgsInfo.lineAcc_arg); + m->SetMaximumNumberOfFunctionEvaluations(m_ArgsInfo.maxEval_arg); + m->SetTrace(true); + if (m_Verbose) std::cout<<"Using L-BFGS Optimizer..."<SetMaximize(m_Maximize); + m->SetMaximumNumberOfEvaluations(m_ArgsInfo.maxEval_arg); + m->SetMaximumNumberOfIterations(m_ArgsInfo.maxIt_arg); + m->SetMaximumNumberOfCorrections(m_ArgsInfo.maxCorr_arg); + m->SetCostFunctionConvergenceFactor( m_ArgsInfo.convFactor_arg); + m->SetProjectedGradientTolerance( m_ArgsInfo.gradTol_arg); + + // Bounds + clitk::LBFGSBOptimizer::BoundSelectionType boundSelect( m_NumberOfParameters ); + clitk::LBFGSBOptimizer::BoundValueType upperBound( m_NumberOfParameters ); + clitk::LBFGSBOptimizer::BoundValueType lowerBound( m_NumberOfParameters ); + boundSelect.Fill( m_ArgsInfo.selectBound_arg ); + upperBound.Fill( m_ArgsInfo.upperBound_arg ); + lowerBound.Fill( m_ArgsInfo.lowerBound_arg ); + m->SetBoundSelection( boundSelect ); + m->SetUpperBound( upperBound ); + m->SetLowerBound( lowerBound ); + + if (m_Verbose) std::cout<<"Using L-BFGS-B Optimizer..."<SetMaximize(m_Maximize); + // m->SetGamma(m_ArgsInfo.gamma_arg); + // m->SetAlpha(m_ArgsInfo.gamma_arg); + // m->SetA(m_ArgsInfo.bigA_arg); + // m->Seta(m_ArgsInfo.littleA_arg); + // m->Setc(m_ArgsInfo.littleC_arg); + // m->SetMaximumNumberOfIterations(m_ArgsInfo.maxIt_arg); + // m->SetMinimumNumberOfIterations(m_ArgsInfo.minIt_arg); + // m->SetNumberOfPerturbations(m_ArgsInfo.per_arg); + // m->SetStateOfConvergenceDecayRate(args_inf.conv_arg); + // if (m_Verbose) std::cout<<"Using SPSA Optimizer..."<(m_Optimizer.GetPointer()); + std::cout<GetIteration()<GetCachedCurrentPosition()<GetCachedValue()<GetCachedDerivative()<(m_Optimizer.GetPointer()); + std::cout<GetCurrentIteration()<GetCurrentLineIteration()<GetCurrentPosition()<GetCurrentCost()<GetDerivative()<(m_Optimizer.GetPointer()); + std::cout<GetCurrentIteration()<GetCurrentLineIteration()<GetCurrentPosition()<GetCurrentCost()<GetDerivative()<(m_Optimizer.GetPointer()); + std::cout<GetCurrentIteration()<GetCurrentPosition()<GetCurrentStepLength()<GetValue()<GetGradient()<(m_Optimizer.GetPointer()); + std::cout<GetCurrentIteration()<GetCurrentPosition()<GetCurrentStepLength()<GetValue()<GetGradient()<(m_Optimizer.GetPointer()); + std::cout<GetCurrentIteration()<GetCachedCurrentPosition()<GetCachedValue()<GetCachedDerivative()<(m_Optimizer.GetPointer()); + std::cout<GetCurrentIteration()<GetCachedCurrentPosition()<GetCachedValue()<GetInfinityNormOfProjectedGradient()<GetCachedDerivative()<(m_Optimizer.GetPointer()); + std::cout<GetCurrentIteration()<GetCachedCurrentPosition()<GetValue()<GetInfinityNormOfProjectedGradient()<GetCachedDerivative()<(m_Optimizer.GetPointer()); + // std::cout<GetIteration()<GetCurrentPosition()<GetCurrentCost()<GetDerivative()< + + +namespace clitk +{ + + +/** \class LBFGSBOptimizerHelper + * \brief Wrapper helper around vnl_lbfgsb. + * + * This class is used to translate iteration events, etc, from + * vnl_lbfgsb into iteration events in ITK. + */ +class ITK_EXPORT LBFGSBOptimizerHelper : + public vnl_lbfgsb +{ +public: + typedef LBFGSBOptimizerHelper Self; + typedef vnl_lbfgsb Superclass; + + /** Create with a reference to the ITK object */ + LBFGSBOptimizerHelper( vnl_cost_function& f, + LBFGSBOptimizer* itkObj ); + + /** Handle new iteration event */ + virtual bool report_iter(); + +private: + LBFGSBOptimizer* m_ItkObj; +}; + + + + +/** + * Constructor + */ +LBFGSBOptimizer +::LBFGSBOptimizer() +{ + m_OptimizerInitialized = false; + m_VnlOptimizer = 0; + + m_LowerBound = InternalBoundValueType(0); + m_UpperBound = InternalBoundValueType(0); + m_BoundSelection = InternalBoundSelectionType(0); + + m_CostFunctionConvergenceFactor = 1e+7; + m_ProjectedGradientTolerance = 1e-5; + m_MaximumNumberOfIterations = 500; + m_MaximumNumberOfEvaluations = 500; + m_MaximumNumberOfCorrections = 5; + m_CurrentIteration = 0; + m_Value = 0.0; + m_InfinityNormOfProjectedGradient = 0.0; + +} + + +/** + * Destructor + */ +LBFGSBOptimizer +::~LBFGSBOptimizer() +{ + delete m_VnlOptimizer; +} + +/** + * PrintSelf + */ +void +LBFGSBOptimizer +::PrintSelf( std::ostream& os, itk::Indent indent) const +{ + Superclass::PrintSelf(os, indent); + + os << indent << "LowerBound: " << m_LowerBound << std::endl; + os << indent << "UpperBound: " << m_UpperBound << std::endl; + os << indent << "BoundSelection: " << m_BoundSelection << std::endl; + + os << indent << "CostFunctionConvergenceFactor: " << + m_CostFunctionConvergenceFactor << std::endl; + + os << indent << "ProjectedGradientTolerance: " << + m_ProjectedGradientTolerance << std::endl; + + os << indent << "MaximumNumberOfIterations: " << + m_MaximumNumberOfIterations << std::endl; + + os << indent << "MaximumNumberOfEvaluations: " << + m_MaximumNumberOfEvaluations << std::endl; + + os << indent << "MaximumNumberOfCorrections: " << + m_MaximumNumberOfCorrections << std::endl; + + os << indent << "CurrentIteration: " << + m_CurrentIteration << std::endl; + + os << indent << "Value: " << + m_Value << std::endl; + + os << indent << "InfinityNormOfProjectedGradient: " << + m_InfinityNormOfProjectedGradient << std::endl; + + if( this->m_VnlOptimizer ) { + os << indent << "Vnl LBFGSB Failure Code: " << + this->m_VnlOptimizer->get_failure_code() << std::endl; + } +} + +/** + * Set lower bound + */ +void +LBFGSBOptimizer +::SetLowerBound( + const BoundValueType& value ) +{ + m_LowerBound = value; + if( m_OptimizerInitialized ) { + m_VnlOptimizer->set_lower_bound( m_LowerBound ); + } + + this->Modified(); +} + +/** + * Get lower bound + */ +const +LBFGSBOptimizer +::BoundValueType & +LBFGSBOptimizer +::GetLowerBound() +{ + return m_LowerBound; +} + +/** + * Set upper bound + */ +void +LBFGSBOptimizer +::SetUpperBound( + const BoundValueType& value ) +{ + m_UpperBound = value; + if( m_OptimizerInitialized ) { + m_VnlOptimizer->set_upper_bound( m_UpperBound ); + } + + this->Modified(); +} + +/** + * Get upper bound + */ +const +LBFGSBOptimizer +::BoundValueType & +LBFGSBOptimizer +::GetUpperBound() +{ + return m_UpperBound; +} + + +/** + * Set bound selection array + */ +void +LBFGSBOptimizer +::SetBoundSelection( + const BoundSelectionType& value ) +{ + m_BoundSelection = value; + if( m_OptimizerInitialized ) { + m_VnlOptimizer->set_bound_selection( m_BoundSelection ); + } + this->Modified(); +} + +/** + * Get bound selection array + */ +const +LBFGSBOptimizer +::BoundSelectionType & +LBFGSBOptimizer +::GetBoundSelection() +{ + return m_BoundSelection; +} + + +/** Set/Get the CostFunctionConvergenceFactor. Algorithm terminates + * when the reduction in cost function is less than factor * epsmcj + * where epsmch is the machine precision. + * Typical values for factor: 1e+12 for low accuracy; + * 1e+7 for moderate accuracy and 1e+1 for extremely high accuracy. + */ +void +LBFGSBOptimizer +::SetCostFunctionConvergenceFactor( double value ) +{ + if( value < 1.0 ) { + itkExceptionMacro("Value " << value + << " is too small for SetCostFunctionConvergenceFactor()" + << "a typical range would be from 1.0 to 1e+12"); + } + m_CostFunctionConvergenceFactor = value; + if( m_OptimizerInitialized ) { + m_VnlOptimizer->set_cost_function_convergence_factor( + m_CostFunctionConvergenceFactor ); + } + this->Modified(); +} + + +/** Set/Get the ProjectedGradientTolerance. Algorithm terminates + * when the project gradient is below the tolerance. Default value + * is 1e-5. + */ +void +LBFGSBOptimizer +::SetProjectedGradientTolerance( double value ) +{ + m_ProjectedGradientTolerance = value; + if( m_OptimizerInitialized ) { + m_VnlOptimizer->set_projected_gradient_tolerance( + m_ProjectedGradientTolerance ); + } + this->Modified(); +} + + +/** Set/Get the MaximumNumberOfIterations. Default is 500 */ +void +LBFGSBOptimizer +::SetMaximumNumberOfIterations( unsigned int value ) +{ + m_MaximumNumberOfIterations = value; + this->Modified(); +} + + +/** Set/Get the MaximumNumberOfEvaluations. Default is 500 */ +void +LBFGSBOptimizer +::SetMaximumNumberOfEvaluations( unsigned int value ) +{ + m_MaximumNumberOfEvaluations = value; + if( m_OptimizerInitialized ) { + m_VnlOptimizer->set_max_function_evals( m_MaximumNumberOfEvaluations ); + } + this->Modified(); +} + + +/** Set/Get the MaximumNumberOfCorrections. Default is 5 */ +void +LBFGSBOptimizer +::SetMaximumNumberOfCorrections( unsigned int value ) +{ + m_MaximumNumberOfCorrections = value; + if( m_OptimizerInitialized ) { + m_VnlOptimizer->set_max_variable_metric_corrections( + m_MaximumNumberOfCorrections ); + } + this->Modified(); +} + + +/** + * Connect a Cost Function + */ +void +LBFGSBOptimizer:: +SetCostFunction( itk::SingleValuedCostFunction * costFunction ) +{ + m_CostFunction = costFunction; + + const unsigned int numberOfParameters = + costFunction->GetNumberOfParameters(); + + CostFunctionAdaptorType * adaptor = + new CostFunctionAdaptorType( numberOfParameters ); + + adaptor->SetCostFunction( costFunction ); + + if( m_OptimizerInitialized ) { + delete m_VnlOptimizer; + } + + this->SetCostFunctionAdaptor( adaptor ); + + m_VnlOptimizer = new InternalOptimizerType( *adaptor, this ); + + // set the optimizer parameters + m_VnlOptimizer->set_lower_bound( m_LowerBound ); + m_VnlOptimizer->set_upper_bound( m_UpperBound ); + m_VnlOptimizer->set_bound_selection( m_BoundSelection ); + m_VnlOptimizer->set_cost_function_convergence_factor( + m_CostFunctionConvergenceFactor ); + m_VnlOptimizer->set_projected_gradient_tolerance( + m_ProjectedGradientTolerance ); + m_VnlOptimizer->set_max_function_evals( m_MaximumNumberOfEvaluations ); + m_VnlOptimizer->set_max_variable_metric_corrections( + m_MaximumNumberOfCorrections ); + + m_OptimizerInitialized = true; + + this->Modified(); +} + + +/** + * Start the optimization + */ +void +LBFGSBOptimizer +::StartOptimization( void ) +{ + + // Check if all the bounds parameters are the same size as the initial parameters. + unsigned int numberOfParameters = m_CostFunction->GetNumberOfParameters(); + + if ( this->GetInitialPosition().Size() < numberOfParameters ) { + itkExceptionMacro( << "InitialPosition array does not have sufficient number of elements" ); + } + + if ( m_LowerBound.size() < numberOfParameters ) { + itkExceptionMacro( << "LowerBound array does not have sufficient number of elements" ); + } + + if ( m_UpperBound.size() < numberOfParameters ) { + itkExceptionMacro( << "UppperBound array does not have sufficient number of elements" ); + } + + if ( m_BoundSelection.size() < numberOfParameters ) { + itkExceptionMacro( << "BoundSelection array does not have sufficient number of elements" ); + } + + if( this->GetMaximize() ) { + this->GetNonConstCostFunctionAdaptor()->NegateCostFunctionOn(); + } + + this->SetCurrentPosition( this->GetInitialPosition() ); + + ParametersType parameters( this->GetInitialPosition() ); + + // vnl optimizers return the solution by reference + // in the variable provided as initial position + m_VnlOptimizer->minimize( parameters ); + + if ( parameters.size() != this->GetInitialPosition().Size() ) { + // set current position to initial position and throw an exception + this->SetCurrentPosition( this->GetInitialPosition() ); + itkExceptionMacro( << "Error occured in optimization" ); + } + + this->SetCurrentPosition( parameters ); + + this->InvokeEvent( itk::EndEvent() ); + +} + + +/*------------------------------------------------------------------------- + * helper class + *------------------------------------------------------------------------- + */ + +/** Create with a reference to the ITK object */ +LBFGSBOptimizerHelper +::LBFGSBOptimizerHelper( vnl_cost_function& f, + LBFGSBOptimizer* itkObj ) + : vnl_lbfgsb( f ), + m_ItkObj( itkObj ) +{ +} + + +/** Handle new iteration event */ +bool +LBFGSBOptimizerHelper +::report_iter() +{ + Superclass::report_iter(); + + m_ItkObj->m_InfinityNormOfProjectedGradient = + this->get_inf_norm_projected_gradient(); + + m_ItkObj->InvokeEvent( itk::IterationEvent() ); + + m_ItkObj->m_CurrentIteration = this->num_iterations_; + + //JV + m_ItkObj->m_Value = m_ItkObj->GetCachedValue(); + + + // Return true to terminate the optimization loop. + if( this->num_iterations_ > m_ItkObj->m_MaximumNumberOfIterations ) { + return true; + } else { + return false; + } +} + + +} // end namespace clitk + +#endif diff --git a/registration/clitkLBFGSBOptimizer.h b/registration/clitkLBFGSBOptimizer.h new file mode 100644 index 0000000..5485b76 --- /dev/null +++ b/registration/clitkLBFGSBOptimizer.h @@ -0,0 +1,215 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: clitkLBFGSBOptimizer.h,v $ + Language: C++ + Date: $Date: 2010/06/14 17:32:07 $ + Version: $Revision: 1.1 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + 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. + +=========================================================================*/ +#ifndef __clitkLBFGSBOptimizer_h +#define __clitkLBFGSBOptimizer_h + +#include "itkSingleValuedNonLinearVnlOptimizer.h" + +namespace clitk +{ + +/** \class LBFGSBOptimizerHelper + * \brief Wrapper helper around vnl_lbfgsb. + * + * This class is used to translate iteration events, etc, from + * vnl_lbfgsb into iteration events in ITK. + */ +class ITK_EXPORT LBFGSBOptimizerHelper; + + +/** \class LBFGSBOptimizer + * \brief Limited memory Broyden Fletcher Goldfarb Shannon minimization with simple bounds. + * + * This class is a wrapper for converted fortan code for performing limited + * memory Broyden Fletcher Goldfarb Shannon minimization with simple bounds. + * The algorithm miminizes a nonlinear function f(x) of n variables subject to + * simple bound constraints of l <= x <= u. + * + * See also the documentation in Numerics/lbfgsb.c + * + * References: + * + * [1] R. H. Byrd, P. Lu and J. Nocedal. + * A Limited Memory Algorithm for Bound Constrained Optimization, (1995), + * SIAM Journal on Scientific and Statistical Computing , + * 16, 5, pp. 1190-1208. + * + * [2] C. Zhu, R. H. Byrd and J. Nocedal. + * L-BFGS-B: Algorithm 778: L-BFGS-B, FORTRAN routines for large scale + * bound constrained optimization (1997), + * ACM Transactions on Mathematical Software, + * Vol 23, Num. 4, pp. 550 - 560. + * + * \ingroup Numerics Optimizers + */ +class ITK_EXPORT LBFGSBOptimizer : + public itk::SingleValuedNonLinearVnlOptimizer +{ +public: + /** Standard "Self" typedef. */ + typedef LBFGSBOptimizer Self; + typedef itk::SingleValuedNonLinearVnlOptimizer Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro( LBFGSBOptimizer, SingleValuedNonLinearVnlOptimizer ); + + /** BoundValue type. + * Use for defining the lower and upper bounds on the variables. + */ + typedef itk::Array BoundValueType; + + /** BoundSelection type + * Use for defining the boundary condition for each variables. + */ + typedef itk::Array BoundSelectionType; + + /** Internal boundary value storage type */ + typedef vnl_vector InternalBoundValueType; + + /** Internal boundary selection storage type */ + typedef vnl_vector InternalBoundSelectionType; + + /** The vnl optimizer */ + typedef LBFGSBOptimizerHelper InternalOptimizerType; + + + /** Start optimization with an initial value. */ + void StartOptimization( void ); + + /** Plug in a Cost Function into the optimizer */ + virtual void SetCostFunction( itk::SingleValuedCostFunction * costFunction ); + + /** Set the lower bound value for each variable. */ + virtual void SetLowerBound( const BoundValueType & value ); + virtual const BoundValueType & GetLowerBound(); + + /** Set the upper bound value for each variable. */ + virtual void SetUpperBound( const BoundValueType & value ); + virtual const BoundValueType & GetUpperBound(); + + /** Set the boundary condition for each variable, where + * select[i] = 0 if x[i] is unbounded, + * = 1 if x[i] has only a lower bound, + * = 2 if x[i] has both lower and upper bounds, and + * = 3 if x[1] has only an upper bound + */ + virtual void SetBoundSelection( const BoundSelectionType & select ); + virtual const BoundSelectionType & GetBoundSelection(); + + /** Set/Get the CostFunctionConvergenceFactor. Algorithm terminates + * when the reduction in cost function is less than factor * epsmcj + * where epsmch is the machine precision. + * Typical values for factor: 1e+12 for low accuracy; + * 1e+7 for moderate accuracy and 1e+1 for extremely high accuracy. + */ + virtual void SetCostFunctionConvergenceFactor( double ); + itkGetMacro( CostFunctionConvergenceFactor, double ); + + /** Set/Get the ProjectedGradientTolerance. Algorithm terminates + * when the project gradient is below the tolerance. Default value + * is 1e-5. + */ + virtual void SetProjectedGradientTolerance( double ); + itkGetMacro( ProjectedGradientTolerance, double ); + + /** Set/Get the MaximumNumberOfIterations. Default is 500 */ + virtual void SetMaximumNumberOfIterations( unsigned int ); + itkGetMacro( MaximumNumberOfIterations, unsigned int ); + + /** Set/Get the MaximumNumberOfEvaluations. Default is 500 */ + virtual void SetMaximumNumberOfEvaluations( unsigned int ); + itkGetMacro( MaximumNumberOfEvaluations, unsigned int ); + + /** Set/Get the MaximumNumberOfCorrections. Default is 5 */ + virtual void SetMaximumNumberOfCorrections( unsigned int ); + itkGetMacro( MaximumNumberOfCorrections, unsigned int ); + + /** This optimizer does not support scaling of the derivatives. */ + void SetScales( const ScalesType & ) { + itkExceptionMacro( << "This optimizer does not support scales." ); + } + + /** Get the current iteration number. */ + itkGetConstReferenceMacro( CurrentIteration, unsigned int ); + + /** Get the current cost function value. */ + itkGetConstReferenceMacro( Value, MeasureType ); + + /** Get the current infinity norm of the project gradient of the cost + * function. */ + itkGetConstReferenceMacro( InfinityNormOfProjectedGradient, double ); + +protected: + LBFGSBOptimizer(); + virtual ~LBFGSBOptimizer(); + void PrintSelf(std::ostream& os, itk::Indent indent) const; + + typedef Superclass::CostFunctionAdaptorType CostFunctionAdaptorType; + +private: + LBFGSBOptimizer(const Self&); //purposely not implemented + void operator=(const Self&); //purposely not implemented + + // give the helper access to member variables, to update iteration + // counts, etc. + friend class LBFGSBOptimizerHelper; + + bool m_OptimizerInitialized; + InternalOptimizerType * m_VnlOptimizer; + + BoundValueType m_LowerBound; + BoundValueType m_UpperBound; + BoundSelectionType m_BoundSelection; + + double m_CostFunctionConvergenceFactor; + double m_ProjectedGradientTolerance; + unsigned int m_MaximumNumberOfIterations; + unsigned int m_MaximumNumberOfEvaluations; + unsigned int m_MaximumNumberOfCorrections; + + unsigned int m_CurrentIteration; + MeasureType m_Value; + double m_InfinityNormOfProjectedGradient; + +}; + +} // end namespace clitk + +#endif diff --git a/registration/clitkNormalizedCorrelationImageToImageMetric.h b/registration/clitkNormalizedCorrelationImageToImageMetric.h new file mode 100644 index 0000000..1ef894f --- /dev/null +++ b/registration/clitkNormalizedCorrelationImageToImageMetric.h @@ -0,0 +1,119 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef _clitkNormalizedCorrelationImageToImageMetric_h +#define _clitkNormalizedCorrelationImageToImageMetric_h + +// First make sure that the configuration is available. +// This line can be removed once the optimized versions +// gets integrated into the main directories. +#include "itkConfigure.h" + +#ifdef ITK_USE_OPTIMIZED_REGISTRATION_METHODS +#include "clitkOptNormalizedCorrelationImageToImageMetric.h" +#else + +#include "itkImageToImageMetric.h" +#include "itkCovariantVector.h" +#include "itkPoint.h" + + +namespace clitk +{ + +template < class TFixedImage, class TMovingImage > +class ITK_EXPORT NormalizedCorrelationImageToImageMetric : + public itk::ImageToImageMetric< TFixedImage, TMovingImage> +{ +public: + + /** Standard class typedefs. */ + typedef NormalizedCorrelationImageToImageMetric Self; + typedef itk::ImageToImageMetric Superclass; + + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(NormalizedCorrelationImageToImageMetric, itk::Object); + + + /** Types transferred from the base class */ + typedef typename Superclass::RealType RealType; + typedef typename Superclass::TransformType TransformType; + typedef typename Superclass::TransformPointer TransformPointer; + typedef typename Superclass::TransformParametersType TransformParametersType; + typedef typename Superclass::TransformJacobianType TransformJacobianType; + typedef typename Superclass::GradientPixelType GradientPixelType; + typedef typename Superclass::OutputPointType OutputPointType; + typedef typename Superclass::InputPointType InputPointType; + + typedef typename Superclass::MeasureType MeasureType; + typedef typename Superclass::DerivativeType DerivativeType; + typedef typename Superclass::FixedImageType FixedImageType; + typedef typename Superclass::MovingImageType MovingImageType; + typedef typename Superclass::FixedImageConstPointer FixedImageConstPointer; + typedef typename Superclass::MovingImageConstPointer MovingImageConstPointer; + + + /** Get the derivatives of the match measure. */ + void GetDerivative( const TransformParametersType & parameters, + DerivativeType & Derivative ) const; + + /** Get the value for single valued optimizers. */ + MeasureType GetValue( const TransformParametersType & parameters ) const; + + /** Get value and derivatives for multiple valued optimizers. */ + void GetValueAndDerivative( const TransformParametersType & parameters, + MeasureType& Value, DerivativeType& Derivative ) const; + + /** Set/Get SubtractMean boolean. If true, the sample mean is subtracted + * from the sample values in the cross-correlation formula and + * typically results in narrower valleys in the cost fucntion. + * Default value is false. */ + itkSetMacro( SubtractMean, bool ); + itkGetConstReferenceMacro( SubtractMean, bool ); + itkBooleanMacro( SubtractMean ); + +protected: + NormalizedCorrelationImageToImageMetric(); + virtual ~NormalizedCorrelationImageToImageMetric() {}; + void PrintSelf(std::ostream& os, itk::Indent indent) const; + +private: + NormalizedCorrelationImageToImageMetric(const Self&); //purposely not implemented + void operator=(const Self&); //purposely not implemented + + bool m_SubtractMean; + +}; + +} // end namespace clitk + +#ifndef ITK_MANUAL_INSTANTIATION +#include "clitkNormalizedCorrelationImageToImageMetric.txx" +#endif + +#endif // opt + +#endif // _clitkNormalizedCorrelationImageToImageMetric.txx + + diff --git a/registration/clitkNormalizedCorrelationImageToImageMetric.txx b/registration/clitkNormalizedCorrelationImageToImageMetric.txx new file mode 100644 index 0000000..db21bbe --- /dev/null +++ b/registration/clitkNormalizedCorrelationImageToImageMetric.txx @@ -0,0 +1,505 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef _clitkNormalizedCorrelationImageToImageMetric_txx +#define _clitkNormalizedCorrelationImageToImageMetric_txx + +// First make sure that the configuration is available. +// This line can be removed once the optimized versions +// gets integrated into the main directories. +#include "itkConfigure.h" + +#ifdef ITK_USE_OPTIMIZED_REGISTRATION_METHODS +#include "clitkOptNormalizedCorrelationImageToImageMetric.txx" +#else + + +#include "clitkNormalizedCorrelationImageToImageMetric.h" + +#include "itkImageRegionConstIteratorWithIndex.h" + + +namespace clitk +{ + +/* + * Constructor + */ +template +NormalizedCorrelationImageToImageMetric +::NormalizedCorrelationImageToImageMetric() +{ + m_SubtractMean = false; +} + +/* + * Get the match Measure + */ +template +typename NormalizedCorrelationImageToImageMetric::MeasureType +NormalizedCorrelationImageToImageMetric +::GetValue( const TransformParametersType & parameters ) const +{ + + FixedImageConstPointer fixedImage = this->m_FixedImage; + + if( !fixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + typedef itk::ImageRegionConstIteratorWithIndex FixedIteratorType; + + FixedIteratorType ti( fixedImage, this->GetFixedImageRegion() ); + + typename FixedImageType::IndexType index; + + MeasureType measure; + + this->m_NumberOfPixelsCounted = 0; + + this->SetTransformParameters( parameters ); + + typedef typename itk::NumericTraits< MeasureType >::AccumulateType AccumulateType; + + AccumulateType sff = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType smm = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sfm = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sf = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sm = itk::NumericTraits< AccumulateType >::Zero; + + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + const RealType fixedValue = ti.Get(); + sff += fixedValue * fixedValue; + smm += movingValue * movingValue; + sfm += fixedValue * movingValue; + if ( this->m_SubtractMean ) { + sf += fixedValue; + sm += movingValue; + } + this->m_NumberOfPixelsCounted++; + } + + ++ti; + } + + if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sff -= ( sf * sf / this->m_NumberOfPixelsCounted ); + smm -= ( sm * sm / this->m_NumberOfPixelsCounted ); + sfm -= ( sf * sm / this->m_NumberOfPixelsCounted ); + } + + const RealType denom = -1.0 * vcl_sqrt(sff * smm ); + + if( this->m_NumberOfPixelsCounted > 0 && denom != 0.0) { + measure = sfm / denom; + } else { + measure = itk::NumericTraits< MeasureType >::Zero; + } + + return measure; + +} + + + + + +/* + * Get the Derivative Measure + */ +template < class TFixedImage, class TMovingImage> +void +NormalizedCorrelationImageToImageMetric +::GetDerivative( const TransformParametersType & parameters, + DerivativeType & derivative ) const +{ + + if( !this->GetGradientImage() ) { + itkExceptionMacro(<<"The gradient image is null, maybe you forgot to call Initialize()"); + } + + FixedImageConstPointer fixedImage = this->m_FixedImage; + + if( !fixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + const unsigned int dimension = FixedImageType::ImageDimension; + + typedef itk::ImageRegionConstIteratorWithIndex FixedIteratorType; + + FixedIteratorType ti( fixedImage, this->GetFixedImageRegion() ); + + typename FixedImageType::IndexType index; + + this->m_NumberOfPixelsCounted = 0; + + this->SetTransformParameters( parameters ); + + typedef typename itk::NumericTraits< MeasureType >::AccumulateType AccumulateType; + + AccumulateType sff = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType smm = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sfm = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sf = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sm = itk::NumericTraits< AccumulateType >::Zero; + + const unsigned int ParametersDimension = this->GetNumberOfParameters(); + derivative = DerivativeType( ParametersDimension ); + derivative.Fill( itk::NumericTraits::Zero ); + + DerivativeType derivativeF = DerivativeType( ParametersDimension ); + derivativeF.Fill( itk::NumericTraits::Zero ); + + DerivativeType derivativeM = DerivativeType( ParametersDimension ); + derivativeM.Fill( itk::NumericTraits::Zero ); + + ti.GoToBegin(); + // First compute the sums + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + const RealType fixedValue = ti.Get(); + sff += fixedValue * fixedValue; + smm += movingValue * movingValue; + sfm += fixedValue * movingValue; + if ( this->m_SubtractMean ) { + sf += fixedValue; + sm += movingValue; + } + this->m_NumberOfPixelsCounted++; + } + + ++ti; + } + + // Compute contributions to derivatives + ti.GoToBegin(); + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if ( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if ( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + const RealType fixedValue = ti.Get(); + + const TransformJacobianType & jacobian = + this->m_Transform->GetJacobian( inputPoint ); + + // Get the gradient by NearestNeighboorInterpolation: + // which is equivalent to round up the point components. + typedef typename OutputPointType::CoordRepType CoordRepType; + typedef itk::ContinuousIndex + MovingImageContinuousIndexType; + + MovingImageContinuousIndexType tempIndex; + this->m_MovingImage->TransformPhysicalPointToContinuousIndex( transformedPoint, tempIndex ); + + typename MovingImageType::IndexType mappedIndex; + mappedIndex.CopyWithRound( tempIndex ); + + const GradientPixelType gradient = + this->GetGradientImage()->GetPixel( mappedIndex ); + + for(unsigned int par=0; par::Zero; + RealType sumM = itk::NumericTraits< RealType >::Zero; + for(unsigned int dim=0; dimm_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sumF -= differential * sf / this->m_NumberOfPixelsCounted; + sumM -= differential * sm / this->m_NumberOfPixelsCounted; + } + } + derivativeF[par] += sumF; + derivativeM[par] += sumM; + } + } + + ++ti; + } + + if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sff -= ( sf * sf / this->m_NumberOfPixelsCounted ); + smm -= ( sm * sm / this->m_NumberOfPixelsCounted ); + sfm -= ( sf * sm / this->m_NumberOfPixelsCounted ); + } + + const RealType denom = -1.0 * vcl_sqrt(sff * smm ); + + if( this->m_NumberOfPixelsCounted > 0 && denom != 0.0) { + for(unsigned int i=0; i::Zero; + } + } + +} + + +/* + * Get both the match Measure and theDerivative Measure + */ +template +void +NormalizedCorrelationImageToImageMetric +::GetValueAndDerivative(const TransformParametersType & parameters, + MeasureType & value, DerivativeType & derivative) const +{ + + + if( !this->GetGradientImage() ) { + itkExceptionMacro(<<"The gradient image is null, maybe you forgot to call Initialize()"); + } + + FixedImageConstPointer fixedImage = this->m_FixedImage; + + if( !fixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + const unsigned int dimension = FixedImageType::ImageDimension; + + typedef itk::ImageRegionConstIteratorWithIndex FixedIteratorType; + + FixedIteratorType ti( fixedImage, this->GetFixedImageRegion() ); + + typename FixedImageType::IndexType index; + + this->m_NumberOfPixelsCounted = 0; + + this->SetTransformParameters( parameters ); + + typedef typename itk::NumericTraits< MeasureType >::AccumulateType AccumulateType; + + AccumulateType sff = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType smm = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sfm = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sf = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sm = itk::NumericTraits< AccumulateType >::Zero; + + const unsigned int ParametersDimension = this->GetNumberOfParameters(); + derivative = DerivativeType( ParametersDimension ); + derivative.Fill( itk::NumericTraits::Zero ); + + DerivativeType derivativeF = DerivativeType( ParametersDimension ); + derivativeF.Fill( itk::NumericTraits::Zero ); + + DerivativeType derivativeM = DerivativeType( ParametersDimension ); + derivativeM.Fill( itk::NumericTraits::Zero ); + + DerivativeType derivativeM1 = DerivativeType( ParametersDimension ); + derivativeM1.Fill( itk::NumericTraits::Zero ); + + ti.GoToBegin(); + // First compute the sums + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + const RealType fixedValue = ti.Get(); + sff += fixedValue * fixedValue; + smm += movingValue * movingValue; + sfm += fixedValue * movingValue; + if ( this->m_SubtractMean ) { + sf += fixedValue; + sm += movingValue; + } + this->m_NumberOfPixelsCounted++; + } + + ++ti; + } + + + // Compute contributions to derivatives + ti.GoToBegin(); + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + const RealType fixedValue = ti.Get(); + + const TransformJacobianType & jacobian = + this->m_Transform->GetJacobian( inputPoint ); + + // Get the gradient by NearestNeighboorInterpolation: + // which is equivalent to round up the point components. + typedef typename OutputPointType::CoordRepType CoordRepType; + typedef itk::ContinuousIndex + MovingImageContinuousIndexType; + + MovingImageContinuousIndexType tempIndex; + this->m_MovingImage->TransformPhysicalPointToContinuousIndex( transformedPoint, tempIndex ); + + typename MovingImageType::IndexType mappedIndex; + mappedIndex.CopyWithRound( tempIndex ); + + const GradientPixelType gradient = + this->GetGradientImage()->GetPixel( mappedIndex ); + + for(unsigned int par=0; par::Zero; + RealType sumM = itk::NumericTraits< RealType >::Zero; + for(unsigned int dim=0; dimm_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sumF -= differential * sf / this->m_NumberOfPixelsCounted; + sumM -= differential * sm / this->m_NumberOfPixelsCounted; + } + } + derivativeF[par] += sumF; + derivativeM[par] += sumM; + } + } + ++ti; + } + + if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sff -= ( sf * sf / this->m_NumberOfPixelsCounted ); + smm -= ( sm * sm / this->m_NumberOfPixelsCounted ); + sfm -= ( sf * sm / this->m_NumberOfPixelsCounted ); + } + + const RealType denom = -1.0 * vcl_sqrt(sff * smm ); + + if( this->m_NumberOfPixelsCounted > 0 && denom != 0.0) { + for(unsigned int i=0; i::Zero; + } + value = itk::NumericTraits< MeasureType >::Zero; + } + + + +} + +template < class TFixedImage, class TMovingImage> +void +NormalizedCorrelationImageToImageMetric +::PrintSelf(std::ostream& os, itk::Indent indent) const +{ + Superclass::PrintSelf(os, indent); + os << indent << "SubtractMean: " << m_SubtractMean << std::endl; +} + +} // end namespace itk + + +#endif // opt + +#endif // _clitkNormalizedCorrelationImageToImageMetric.txx diff --git a/registration/clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.h b/registration/clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.h new file mode 100644 index 0000000..053ad93 --- /dev/null +++ b/registration/clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.h @@ -0,0 +1,119 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef _clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD_h +#define _clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD_h + +// First make sure that the configuration is available. +// This line can be removed once the optimized versions +// gets integrated into the main directories. +#include "itkConfigure.h" + +#ifdef ITK_USE_OPTIMIZED_REGISTRATION_METHODS +#include "clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.h" +#else + +#include "itkImageToImageMetric.h" +#include "itkCovariantVector.h" +#include "itkPoint.h" + + +namespace clitk +{ + +template < class TFixedImage, class TMovingImage > +class ITK_EXPORT NormalizedCorrelationImageToImageMetricFor3DBLUTFFD : + public itk::ImageToImageMetric< TFixedImage, TMovingImage> +{ +public: + + /** Standard class typedefs. */ + typedef NormalizedCorrelationImageToImageMetricFor3DBLUTFFD Self; + typedef itk::ImageToImageMetric Superclass; + + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(NormalizedCorrelationImageToImageMetricFor3DBLUTFFD, itk::Object); + + + /** Types transferred from the base class */ + typedef typename Superclass::RealType RealType; + typedef typename Superclass::TransformType TransformType; + typedef typename Superclass::TransformPointer TransformPointer; + typedef typename Superclass::TransformParametersType TransformParametersType; + typedef typename Superclass::TransformJacobianType TransformJacobianType; + typedef typename Superclass::GradientPixelType GradientPixelType; + typedef typename Superclass::OutputPointType OutputPointType; + typedef typename Superclass::InputPointType InputPointType; + + typedef typename Superclass::MeasureType MeasureType; + typedef typename Superclass::DerivativeType DerivativeType; + typedef typename Superclass::FixedImageType FixedImageType; + typedef typename Superclass::MovingImageType MovingImageType; + typedef typename Superclass::FixedImageConstPointer FixedImageConstPointer; + typedef typename Superclass::MovingImageConstPointer MovingImageConstPointer; + + + /** Get the derivatives of the match measure. */ + void GetDerivative( const TransformParametersType & parameters, + DerivativeType & Derivative ) const; + + /** Get the value for single valued optimizers. */ + MeasureType GetValue( const TransformParametersType & parameters ) const; + + /** Get value and derivatives for multiple valued optimizers. */ + void GetValueAndDerivative( const TransformParametersType & parameters, + MeasureType& Value, DerivativeType& Derivative ) const; + + /** Set/Get SubtractMean boolean. If true, the sample mean is subtracted + * from the sample values in the cross-correlation formula and + * typically results in narrower valleys in the cost fucntion. + * Default value is false. */ + itkSetMacro( SubtractMean, bool ); + itkGetConstReferenceMacro( SubtractMean, bool ); + itkBooleanMacro( SubtractMean ); + +protected: + NormalizedCorrelationImageToImageMetricFor3DBLUTFFD(); + virtual ~NormalizedCorrelationImageToImageMetricFor3DBLUTFFD() {}; + void PrintSelf(std::ostream& os, itk::Indent indent) const; + +private: + NormalizedCorrelationImageToImageMetricFor3DBLUTFFD(const Self&); //purposely not implemented + void operator=(const Self&); //purposely not implemented + + bool m_SubtractMean; + +}; + +} // end namespace clitk + +#ifndef ITK_MANUAL_INSTANTIATION +#include "clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.txx" +#endif + +#endif // opt + +#endif // _clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.txx + + diff --git a/registration/clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.txx b/registration/clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.txx new file mode 100644 index 0000000..5fee9e5 --- /dev/null +++ b/registration/clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.txx @@ -0,0 +1,505 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef _clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD_txx +#define _clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD_txx + +// First make sure that the configuration is available. +// This line can be removed once the optimized versions +// gets integrated into the main directories. +#include "itkConfigure.h" + +#ifdef ITK_USE_OPTIMIZED_REGISTRATION_METHODS +#include "clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.txx" +#else + + +#include "clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.h" + +#include "itkImageRegionConstIteratorWithIndex.h" + + +namespace clitk +{ + +/* + * Constructor + */ +template +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::NormalizedCorrelationImageToImageMetricFor3DBLUTFFD() +{ + m_SubtractMean = false; +} + +/* + * Get the match Measure + */ +template +typename NormalizedCorrelationImageToImageMetricFor3DBLUTFFD::MeasureType +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::GetValue( const TransformParametersType & parameters ) const +{ + + FixedImageConstPointer fixedImage = this->m_FixedImage; + + if( !fixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + typedef itk::ImageRegionConstIteratorWithIndex FixedIteratorType; + + FixedIteratorType ti( fixedImage, this->GetFixedImageRegion() ); + + typename FixedImageType::IndexType index; + + MeasureType measure; + + this->m_NumberOfPixelsCounted = 0; + + this->SetTransformParameters( parameters ); + + typedef typename itk::NumericTraits< MeasureType >::AccumulateType AccumulateType; + + AccumulateType sff = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType smm = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sfm = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sf = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sm = itk::NumericTraits< AccumulateType >::Zero; + + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + const RealType fixedValue = ti.Get(); + sff += fixedValue * fixedValue; + smm += movingValue * movingValue; + sfm += fixedValue * movingValue; + if ( this->m_SubtractMean ) { + sf += fixedValue; + sm += movingValue; + } + this->m_NumberOfPixelsCounted++; + } + + ++ti; + } + + if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sff -= ( sf * sf / this->m_NumberOfPixelsCounted ); + smm -= ( sm * sm / this->m_NumberOfPixelsCounted ); + sfm -= ( sf * sm / this->m_NumberOfPixelsCounted ); + } + + const RealType denom = -1.0 * vcl_sqrt(sff * smm ); + + if( this->m_NumberOfPixelsCounted > 0 && denom != 0.0) { + measure = sfm / denom; + } else { + measure = itk::NumericTraits< MeasureType >::Zero; + } + + return measure; + +} + + + + + +/* + * Get the Derivative Measure + */ +template < class TFixedImage, class TMovingImage> +void +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::GetDerivative( const TransformParametersType & parameters, + DerivativeType & derivative ) const +{ + + if( !this->GetGradientImage() ) { + itkExceptionMacro(<<"The gradient image is null, maybe you forgot to call Initialize()"); + } + + FixedImageConstPointer fixedImage = this->m_FixedImage; + + if( !fixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + const unsigned int dimension = FixedImageType::ImageDimension; + + typedef itk::ImageRegionConstIteratorWithIndex FixedIteratorType; + + FixedIteratorType ti( fixedImage, this->GetFixedImageRegion() ); + + typename FixedImageType::IndexType index; + + this->m_NumberOfPixelsCounted = 0; + + this->SetTransformParameters( parameters ); + + typedef typename itk::NumericTraits< MeasureType >::AccumulateType AccumulateType; + + AccumulateType sff = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType smm = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sfm = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sf = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sm = itk::NumericTraits< AccumulateType >::Zero; + + const unsigned int ParametersDimension = this->GetNumberOfParameters(); + derivative = DerivativeType( ParametersDimension ); + derivative.Fill( itk::NumericTraits::Zero ); + + DerivativeType derivativeF = DerivativeType( ParametersDimension ); + derivativeF.Fill( itk::NumericTraits::Zero ); + + DerivativeType derivativeM = DerivativeType( ParametersDimension ); + derivativeM.Fill( itk::NumericTraits::Zero ); + + ti.GoToBegin(); + // First compute the sums + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + const RealType fixedValue = ti.Get(); + sff += fixedValue * fixedValue; + smm += movingValue * movingValue; + sfm += fixedValue * movingValue; + if ( this->m_SubtractMean ) { + sf += fixedValue; + sm += movingValue; + } + this->m_NumberOfPixelsCounted++; + } + + ++ti; + } + + // Compute contributions to derivatives + ti.GoToBegin(); + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if ( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if ( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + const RealType fixedValue = ti.Get(); + + const TransformJacobianType & jacobian = + this->m_Transform->GetJacobian( inputPoint ); + + // Get the gradient by NearestNeighboorInterpolation: + // which is equivalent to round up the point components. + typedef typename OutputPointType::CoordRepType CoordRepType; + typedef itk::ContinuousIndex + MovingImageContinuousIndexType; + + MovingImageContinuousIndexType tempIndex; + this->m_MovingImage->TransformPhysicalPointToContinuousIndex( transformedPoint, tempIndex ); + + typename MovingImageType::IndexType mappedIndex; + mappedIndex.CopyWithRound( tempIndex ); + + const GradientPixelType gradient = + this->GetGradientImage()->GetPixel( mappedIndex ); + + for(unsigned int par=0; par::Zero; + RealType sumM = itk::NumericTraits< RealType >::Zero; + for(unsigned int dim=0; dimm_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sumF -= differential * sf / this->m_NumberOfPixelsCounted; + sumM -= differential * sm / this->m_NumberOfPixelsCounted; + } + } + derivativeF[par] += sumF; + derivativeM[par] += sumM; + } + } + + ++ti; + } + + if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sff -= ( sf * sf / this->m_NumberOfPixelsCounted ); + smm -= ( sm * sm / this->m_NumberOfPixelsCounted ); + sfm -= ( sf * sm / this->m_NumberOfPixelsCounted ); + } + + const RealType denom = -1.0 * vcl_sqrt(sff * smm ); + + if( this->m_NumberOfPixelsCounted > 0 && denom != 0.0) { + for(unsigned int i=0; i::Zero; + } + } + +} + + +/* + * Get both the match Measure and theDerivative Measure + */ +template +void +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::GetValueAndDerivative(const TransformParametersType & parameters, + MeasureType & value, DerivativeType & derivative) const +{ + + + if( !this->GetGradientImage() ) { + itkExceptionMacro(<<"The gradient image is null, maybe you forgot to call Initialize()"); + } + + FixedImageConstPointer fixedImage = this->m_FixedImage; + + if( !fixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + const unsigned int dimension = FixedImageType::ImageDimension; + + typedef itk::ImageRegionConstIteratorWithIndex FixedIteratorType; + + FixedIteratorType ti( fixedImage, this->GetFixedImageRegion() ); + + typename FixedImageType::IndexType index; + + this->m_NumberOfPixelsCounted = 0; + + this->SetTransformParameters( parameters ); + + typedef typename itk::NumericTraits< MeasureType >::AccumulateType AccumulateType; + + AccumulateType sff = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType smm = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sfm = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sf = itk::NumericTraits< AccumulateType >::Zero; + AccumulateType sm = itk::NumericTraits< AccumulateType >::Zero; + + const unsigned int ParametersDimension = this->GetNumberOfParameters(); + derivative = DerivativeType( ParametersDimension ); + derivative.Fill( itk::NumericTraits::Zero ); + + DerivativeType derivativeF = DerivativeType( ParametersDimension ); + derivativeF.Fill( itk::NumericTraits::Zero ); + + DerivativeType derivativeM = DerivativeType( ParametersDimension ); + derivativeM.Fill( itk::NumericTraits::Zero ); + + DerivativeType derivativeM1 = DerivativeType( ParametersDimension ); + derivativeM1.Fill( itk::NumericTraits::Zero ); + + ti.GoToBegin(); + // First compute the sums + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + const RealType fixedValue = ti.Get(); + sff += fixedValue * fixedValue; + smm += movingValue * movingValue; + sfm += fixedValue * movingValue; + if ( this->m_SubtractMean ) { + sf += fixedValue; + sm += movingValue; + } + this->m_NumberOfPixelsCounted++; + } + + ++ti; + } + + + // Compute contributions to derivatives + ti.GoToBegin(); + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + const RealType fixedValue = ti.Get(); + + const TransformJacobianType & jacobian = + this->m_Transform->GetJacobian( inputPoint ); + + // Get the gradient by NearestNeighboorInterpolation: + // which is equivalent to round up the point components. + typedef typename OutputPointType::CoordRepType CoordRepType; + typedef itk::ContinuousIndex + MovingImageContinuousIndexType; + + MovingImageContinuousIndexType tempIndex; + this->m_MovingImage->TransformPhysicalPointToContinuousIndex( transformedPoint, tempIndex ); + + typename MovingImageType::IndexType mappedIndex; + mappedIndex.CopyWithRound( tempIndex ); + + const GradientPixelType gradient = + this->GetGradientImage()->GetPixel( mappedIndex ); + + for(unsigned int par=0; par::Zero; + RealType sumM = itk::NumericTraits< RealType >::Zero; + for(unsigned int dim=0; dimm_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sumF -= differential * sf / this->m_NumberOfPixelsCounted; + sumM -= differential * sm / this->m_NumberOfPixelsCounted; + } + } + derivativeF[par] += sumF; + derivativeM[par] += sumM; + } + } + ++ti; + } + + if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sff -= ( sf * sf / this->m_NumberOfPixelsCounted ); + smm -= ( sm * sm / this->m_NumberOfPixelsCounted ); + sfm -= ( sf * sm / this->m_NumberOfPixelsCounted ); + } + + const RealType denom = -1.0 * vcl_sqrt(sff * smm ); + + if( this->m_NumberOfPixelsCounted > 0 && denom != 0.0) { + for(unsigned int i=0; i::Zero; + } + value = itk::NumericTraits< MeasureType >::Zero; + } + + + +} + +template < class TFixedImage, class TMovingImage> +void +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::PrintSelf(std::ostream& os, itk::Indent indent) const +{ + Superclass::PrintSelf(os, indent); + os << indent << "SubtractMean: " << m_SubtractMean << std::endl; +} + +} // end namespace itk + + +#endif // opt + +#endif // _clitkNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.txx diff --git a/registration/clitkOptNormalizedCorrelationImageToImageMetric.h b/registration/clitkOptNormalizedCorrelationImageToImageMetric.h new file mode 100644 index 0000000..c4bda86 --- /dev/null +++ b/registration/clitkOptNormalizedCorrelationImageToImageMetric.h @@ -0,0 +1,159 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef __clitkOptNormalizedCorrelationImageToImageMetric_h +#define __clitkOptNormalizedCorrelationImageToImageMetric_h + +#include "itkOptImageToImageMetric.h" +#include "itkCovariantVector.h" +#include "itkPoint.h" +#include "itkIndex.h" + +#include "itkMultiThreader.h" + +namespace clitk +{ + +template +class ITK_EXPORT NormalizedCorrelationImageToImageMetric : + public itk::ImageToImageMetric< TFixedImage, TMovingImage > +{ +public: + + /** Standard class typedefs. */ + typedef NormalizedCorrelationImageToImageMetric Self; + typedef itk::ImageToImageMetric< TFixedImage, TMovingImage > Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(NormalizedCorrelationImageToImageMetric, ImageToImageMetric); + + /** Types inherited from Superclass. */ + typedef typename Superclass::RealType RealType; + typedef typename Superclass::TransformType TransformType; + typedef typename Superclass::TransformPointer TransformPointer; + typedef typename Superclass::TransformJacobianType TransformJacobianType; + typedef typename Superclass::InterpolatorType InterpolatorType; + typedef typename Superclass::MeasureType MeasureType; + typedef typename Superclass::DerivativeType DerivativeType; + typedef typename Superclass::ParametersType ParametersType; + typedef typename Superclass::FixedImageType FixedImageType; + typedef typename Superclass::MovingImageType MovingImageType; + typedef typename Superclass::MovingImagePointType MovingImagePointType; + typedef typename Superclass::FixedImageConstPointer FixedImageConstPointer; + typedef typename Superclass::MovingImageConstPointer MovingImageConstPointer; + typedef typename Superclass::CoordinateRepresentationType + CoordinateRepresentationType; + typedef typename Superclass::FixedImageSampleContainer + FixedImageSampleContainer; + typedef typename Superclass::ImageDerivativesType ImageDerivativesType; + typedef typename Superclass::WeightsValueType WeightsValueType; + typedef typename Superclass::IndexValueType IndexValueType; + + // Needed for evaluation of Jacobian. + typedef typename Superclass::FixedImagePointType FixedImagePointType; + + //Accumulators + typedef typename itk::NumericTraits< MeasureType >::AccumulateType AccumulateType; + + /** The moving image dimension. */ + itkStaticConstMacro( MovingImageDimension, unsigned int, + MovingImageType::ImageDimension ); + + + /** Set/Get SubtractMean boolean. If true, the sample mean is subtracted + * from the sample values in the cross-correlation formula and + * typically results in narrower valleys in the cost fucntion. + * Default value is false. */ + itkSetMacro( SubtractMean, bool ); + itkGetConstReferenceMacro( SubtractMean, bool ); + itkBooleanMacro( SubtractMean ); + + /** + * Initialize the Metric by + * (1) making sure that all the components are present and plugged + * together correctly, + * (2) uniformly select NumberOfSpatialSamples within + * the FixedImageRegion, and + * (3) allocate memory for pdf data structures. */ + virtual void Initialize(void) throw ( itk::ExceptionObject ); + + /** Get the value. */ + MeasureType GetValue( const ParametersType & parameters ) const; + + /** Get the derivatives of the match measure. */ + void GetDerivative( const ParametersType & parameters, + DerivativeType & Derivative ) const; + + /** Get the value and derivatives for single valued optimizers. */ + void GetValueAndDerivative( const ParametersType & parameters, + MeasureType & Value, + DerivativeType & Derivative ) const; + +protected: + + NormalizedCorrelationImageToImageMetric(); + virtual ~NormalizedCorrelationImageToImageMetric(); + void PrintSelf(std::ostream& os, itk::Indent indent) const; + +private: + + //purposely not implemented + NormalizedCorrelationImageToImageMetric(const Self &); + //purposely not implemented + void operator=(const Self &); + + /** Get the value for the derivative computation. */ + MeasureType ComputeSums( const ParametersType & parameters ) const; + + + inline bool GetValueThreadProcessSample( unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & mappedPoint, + double movingImageValue ) const; + + + inline bool GetValueAndDerivativeThreadProcessSample( unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & mappedPoint, + double movingImageValue, + const ImageDerivativesType & + movingImageGradientValue ) const; + + AccumulateType *m_ThreaderSFF, *m_ThreaderSMM, *m_ThreaderSFM, *m_ThreaderSF, *m_ThreaderSM; + mutable AccumulateType m_SFF, m_SMM, m_SFM, m_SF, m_SM; + mutable RealType m_Denom, m_FixedMean, m_MovingMean; + DerivativeType * m_ThreaderDerivativeF, *m_ThreaderDerivativeM; + + + bool m_SubtractMean; +}; + +} // end namespace clitk + +#ifndef ITK_MANUAL_INSTANTIATION +#include "clitkOptNormalizedCorrelationImageToImageMetric.txx" +#endif + +#endif + + diff --git a/registration/clitkOptNormalizedCorrelationImageToImageMetric.txx b/registration/clitkOptNormalizedCorrelationImageToImageMetric.txx new file mode 100644 index 0000000..41a1af0 --- /dev/null +++ b/registration/clitkOptNormalizedCorrelationImageToImageMetric.txx @@ -0,0 +1,495 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef __clitkOptNormalizedCorrelationImageToImageMetric_txx +#define __clitkOptNormalizedCorrelationImageToImageMetric_txx + +#include "clitkOptNormalizedCorrelationImageToImageMetric.h" +#include "itkCovariantVector.h" +#include "itkImageRandomConstIteratorWithIndex.h" +#include "itkImageRegionConstIterator.h" +#include "itkImageRegionIterator.h" +#include "itkImageIterator.h" +#include "vnl/vnl_math.h" + +namespace clitk +{ + +/** + * Constructor + */ +template < class TFixedImage, class TMovingImage > +NormalizedCorrelationImageToImageMetric +::NormalizedCorrelationImageToImageMetric() +{ + this->SetComputeGradient(true); + + m_ThreaderSFF = NULL; + m_ThreaderSMM = NULL; + m_ThreaderSFM = NULL; + m_ThreaderSF = NULL; + m_ThreaderSM = NULL; + m_ThreaderDerivativeF = NULL; + m_ThreaderDerivativeM = NULL; + this->m_WithinThreadPreProcess = false; + this->m_WithinThreadPostProcess = false; + + // For backward compatibility, the default behavior is to use all the pixels + // in the fixed image. + this->UseAllPixelsOn(); + + m_SubtractMean=false; + +} + +template < class TFixedImage, class TMovingImage > +NormalizedCorrelationImageToImageMetric +::~NormalizedCorrelationImageToImageMetric() +{ + if(m_ThreaderSFF != NULL) { + delete [] m_ThreaderSFF; + } + m_ThreaderSFF = NULL; + + if(m_ThreaderSMM != NULL) { + delete [] m_ThreaderSMM; + } + m_ThreaderSMM = NULL; + + if(m_ThreaderSFM != NULL) { + delete [] m_ThreaderSFM; + } + m_ThreaderSFM = NULL; + + if(m_ThreaderSF != NULL) { + delete [] m_ThreaderSF; + } + m_ThreaderSF = NULL; + + if(m_ThreaderSM != NULL) { + delete [] m_ThreaderSM; + } + m_ThreaderSM = NULL; + + if(m_ThreaderDerivativeF != NULL) { + delete [] m_ThreaderDerivativeF; + } + m_ThreaderDerivativeF = NULL; + + if(m_ThreaderDerivativeM != NULL) { + delete [] m_ThreaderDerivativeM; + } + m_ThreaderDerivativeM = NULL; +} + +/** + * Print out internal information about this class + */ +template < class TFixedImage, class TMovingImage > +void +NormalizedCorrelationImageToImageMetric +::PrintSelf(std::ostream& os, itk::Indent indent) const +{ + + Superclass::PrintSelf(os, indent); + +} + + +/** + * Initialize + */ +template +void +NormalizedCorrelationImageToImageMetric +::Initialize(void) throw ( itk::ExceptionObject ) +{ + + this->Superclass::Initialize(); + this->Superclass::MultiThreadingInitialize(); + + + /** + * Allocate memory for the accumulators (set to zero in GetValue) + */ + if(m_ThreaderSFF != NULL) { + delete [] m_ThreaderSFF; + } + m_ThreaderSFF = new double[this->m_NumberOfThreads]; + + + if(m_ThreaderSMM != NULL) { + delete [] m_ThreaderSMM; + } + m_ThreaderSMM = new double[this->m_NumberOfThreads]; + + if(m_ThreaderSFM != NULL) { + delete [] m_ThreaderSFM; + } + m_ThreaderSFM = new double[this->m_NumberOfThreads]; + + if(this->m_SubtractMean) { + if(m_ThreaderSF != NULL) { + delete [] m_ThreaderSF; + } + m_ThreaderSF = new double[this->m_NumberOfThreads]; + + if(m_ThreaderSM != NULL) { + delete [] m_ThreaderSM; + } + m_ThreaderSM = new double[this->m_NumberOfThreads]; + } + + if(m_ThreaderDerivativeF != NULL) { + delete [] m_ThreaderDerivativeF; + } + m_ThreaderDerivativeF = new DerivativeType[this->m_NumberOfThreads]; + for(unsigned int threadID=0; threadIDm_NumberOfThreads; threadID++) { + m_ThreaderDerivativeF[threadID].SetSize( this->m_NumberOfParameters ); + } + + if(m_ThreaderDerivativeM != NULL) { + delete [] m_ThreaderDerivativeM; + } + m_ThreaderDerivativeM = new DerivativeType[this->m_NumberOfThreads]; + for(unsigned int threadID=0; threadIDm_NumberOfThreads; threadID++) { + m_ThreaderDerivativeM[threadID].SetSize( this->m_NumberOfParameters ); + } +} + + +template < class TFixedImage, class TMovingImage > +inline bool +NormalizedCorrelationImageToImageMetric +::GetValueThreadProcessSample( + unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & itkNotUsed(mappedPoint), + double movingImageValue) const +{ + const RealType fixedImageValue= this->m_FixedImageSamples[fixedImageSample].value; + m_ThreaderSFF[threadID] += fixedImageValue * fixedImageValue; + m_ThreaderSMM[threadID] += movingImageValue * movingImageValue; + m_ThreaderSFM[threadID] += fixedImageValue * movingImageValue; + if ( this->m_SubtractMean ) { + m_ThreaderSF[threadID] += fixedImageValue; + m_ThreaderSM[threadID] += movingImageValue; + } + + return true; +} + +template < class TFixedImage, class TMovingImage > +typename NormalizedCorrelationImageToImageMetric +::MeasureType +NormalizedCorrelationImageToImageMetric +::GetValue( const ParametersType & parameters ) const +{ + itkDebugMacro("GetValue( " << parameters << " ) "); + + if( !this->m_FixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + + //Reset the accumulators + memset( m_ThreaderSFF, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + memset( m_ThreaderSMM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + memset( m_ThreaderSFM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + if(this->m_SubtractMean) { + memset( m_ThreaderSF, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + memset( m_ThreaderSM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + } + + + // Set up the parameters in the transform + this->m_Transform->SetParameters( parameters ); + this->m_Parameters = parameters; + + // MUST BE CALLED TO INITIATE PROCESSING + this->GetValueMultiThreadedInitiate(); + + itkDebugMacro( "Ratio of voxels mapping into moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + + if( this->m_NumberOfPixelsCounted < + this->m_NumberOfFixedImageSamples / 4 ) { + itkExceptionMacro( "Too many samples map outside moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + } + + // Accumulate the threads + AccumulateType sff, smm, sfm, sf, sm; + sff = m_ThreaderSFF[0]; + smm = m_ThreaderSMM[0]; + sfm = m_ThreaderSFM[0]; + sf = m_ThreaderSF[0]; + sm = m_ThreaderSM[0]; + + for(unsigned int t=1; tm_NumberOfThreads; t++) { + sff += m_ThreaderSFF[t]; + smm += m_ThreaderSMM[t]; + sfm += m_ThreaderSFM[t]; + if ( this->m_SubtractMean ) { + sf += m_ThreaderSF[t]; + sm += m_ThreaderSM[t]; + } + } + + if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sff -= ( sf * sf / this->m_NumberOfPixelsCounted ); + smm -= ( sm * sm / this->m_NumberOfPixelsCounted ); + sfm -= ( sf * sm / this->m_NumberOfPixelsCounted ); + } + + + const RealType denom = -1.0 * vcl_sqrt(sff * smm ); + MeasureType measure; + if( this->m_NumberOfPixelsCounted > 0 && denom != 0.0) { + measure = sfm / denom; + } else { + measure = itk::NumericTraits< MeasureType >::Zero; + } + + + return measure; +} + + +template < class TFixedImage, class TMovingImage > +typename NormalizedCorrelationImageToImageMetric +::MeasureType +NormalizedCorrelationImageToImageMetric +::ComputeSums( const ParametersType & parameters ) const +{ + //No checking for the fixed image, done in the caller + //Reset the accumulators + memset( m_ThreaderSFF, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + memset( m_ThreaderSMM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + memset( m_ThreaderSFM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + if(this->m_SubtractMean) { + memset( m_ThreaderSF, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + memset( m_ThreaderSM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + } + + + // Set up the parameters in the transform + this->m_Transform->SetParameters( parameters ); + this->m_Parameters = parameters; + + // MUST BE CALLED TO INITIATE PROCESSING + this->GetValueMultiThreadedInitiate(); + + itkDebugMacro( "Ratio of voxels mapping into moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + + if( this->m_NumberOfPixelsCounted < + this->m_NumberOfFixedImageSamples / 4 ) { + itkExceptionMacro( "Too many samples map outside moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + } + + // Accumulate the threads + m_SFF = m_ThreaderSFF[0]; + m_SMM = m_ThreaderSMM[0]; + m_SFM = m_ThreaderSFM[0]; + m_SF = m_ThreaderSF[0]; + m_SM = m_ThreaderSM[0]; + + for(unsigned int t=1; tm_NumberOfThreads; t++) { + m_SFF += m_ThreaderSFF[t]; + m_SMM += m_ThreaderSMM[t]; + m_SFM += m_ThreaderSFM[t]; + if ( this->m_SubtractMean ) { + m_SF += m_ThreaderSF[t]; + m_SM += m_ThreaderSM[t]; + } + } + + if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + m_SFF -= ( m_SF * m_SF / this->m_NumberOfPixelsCounted ); + m_SMM -= ( m_SM * m_SM / this->m_NumberOfPixelsCounted ); + m_SFM -= ( m_SF * m_SM / this->m_NumberOfPixelsCounted ); + m_FixedMean=m_SF / this->m_NumberOfPixelsCounted; + m_MovingMean=m_SM / this->m_NumberOfPixelsCounted; + } + + + m_Denom = -1.0 * vcl_sqrt(m_SFF * m_SMM ); + MeasureType measure; + if( this->m_NumberOfPixelsCounted > 0 && m_Denom != 0.0) { + measure = m_SFM / m_Denom; + } else { + measure = itk::NumericTraits< MeasureType >::Zero; + } + + return measure; +} + + +template < class TFixedImage, class TMovingImage > +inline bool +NormalizedCorrelationImageToImageMetric +::GetValueAndDerivativeThreadProcessSample( + unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & itkNotUsed(mappedPoint), + double movingImageValue, + const ImageDerivativesType & + movingImageGradientValue +) const +{ + + const RealType fixedImageValue=this->m_FixedImageSamples[fixedImageSample].value; + const FixedImagePointType fixedImagePoint = this->m_FixedImageSamples[fixedImageSample].point; + + // Need to use one of the threader transforms if we're + // not in thread 0. + // + // Use a raw pointer here to avoid the overhead of smart pointers. + // For instance, Register and UnRegister have mutex locks around + // the reference counts. + TransformType* transform; + + if (threadID > 0) { + transform = this->m_ThreaderTransform[threadID - 1]; + } else { + transform = this->m_Transform; + } + + // Jacobian should be evaluated at the unmapped (fixed image) point. + const TransformJacobianType & jacobian = transform + ->GetJacobian( fixedImagePoint ); + + for(unsigned int par=0; parm_NumberOfParameters; par++) { + RealType sumF = itk::NumericTraits< RealType >::Zero; + RealType sumM = itk::NumericTraits< RealType >::Zero; + for(unsigned int dim=0; dimm_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sumF += (fixedImageValue-m_FixedMean) * differential; + sumM += (movingImageValue-m_MovingMean) * differential; + } else { + sumF += differential * fixedImageValue; + sumM += differential * movingImageValue; + } + } + m_ThreaderDerivativeF[threadID][par] += sumF; + m_ThreaderDerivativeM[threadID][par] += sumM; + } + + return true; +} + + +/** + * Get the both Value and Derivative Measure + */ +template < class TFixedImage, class TMovingImage > +void +NormalizedCorrelationImageToImageMetric +::GetValueAndDerivative( const ParametersType & parameters, + MeasureType & value, + DerivativeType & derivative) const +{ + + if( !this->m_FixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + // Set up the parameters in the transform + this->m_Transform->SetParameters( parameters ); + this->m_Parameters = parameters; + + //We need the sums and the value to be calculated first + value=this->ComputeSums(parameters); + + //Set output values to zero + if(derivative.GetSize() != this->m_NumberOfParameters) { + derivative = DerivativeType( this->m_NumberOfParameters ); + } + memset( derivative.data_block(), + 0, + this->m_NumberOfParameters * sizeof(ITK_TYPENAME DerivativeType::ValueType) ); + + for( unsigned int threadID = 0; threadIDm_NumberOfThreads; threadID++ ) { + memset( m_ThreaderDerivativeF[threadID].data_block(), + 0, + this->m_NumberOfParameters * sizeof(ITK_TYPENAME DerivativeType::ValueType) ); + + memset( m_ThreaderDerivativeM[threadID].data_block(), + 0, + this->m_NumberOfParameters * sizeof(ITK_TYPENAME DerivativeType::ValueType) ); + } + + // MUST BE CALLED TO INITIATE PROCESSING + this->GetValueAndDerivativeMultiThreadedInitiate(); + + // Accumulate over the threads + DerivativeType derivativeF(this->m_NumberOfParameters), derivativeM(this->m_NumberOfParameters); + for(unsigned int t=0; tm_NumberOfThreads; t++) { + for(unsigned int parameter = 0; parameter < this->m_NumberOfParameters; parameter++) { + derivativeF[parameter] += m_ThreaderDerivativeF[t][parameter]; + derivativeM[parameter] += m_ThreaderDerivativeM[t][parameter]; + } + } + + //Compute derivatives + if( this->m_NumberOfPixelsCounted > 0 && m_Denom != 0.0) { + for(unsigned int i=0; im_NumberOfParameters; i++) { + derivative[i] = ( derivativeF[i] - (m_SFM/m_SMM)* derivativeM[i] ) / m_Denom; + } + } else { + for(unsigned int i=0; im_NumberOfParameters; i++) { + derivative[i] = itk::NumericTraits< MeasureType >::Zero; + } + } + +} + + +/** + * Get the match measure derivative + */ +template < class TFixedImage, class TMovingImage > +void +NormalizedCorrelationImageToImageMetric +::GetDerivative( const ParametersType & parameters, + DerivativeType & derivative ) const +{ + if( !this->m_FixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + MeasureType value; + // call the combined version + this->GetValueAndDerivative( parameters, value, derivative ); +} + +} // end namespace clitk + + +#endif diff --git a/registration/clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.h b/registration/clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.h new file mode 100644 index 0000000..54503c5 --- /dev/null +++ b/registration/clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.h @@ -0,0 +1,159 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef __clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD_h +#define __clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD_h + +#include "itkOptImageToImageMetric.h" +#include "itkCovariantVector.h" +#include "itkPoint.h" +#include "itkIndex.h" + +#include "itkMultiThreader.h" + +namespace clitk +{ + +template +class ITK_EXPORT NormalizedCorrelationImageToImageMetricFor3DBLUTFFD : + public itk::ImageToImageMetric< TFixedImage, TMovingImage > +{ +public: + + /** Standard class typedefs. */ + typedef NormalizedCorrelationImageToImageMetricFor3DBLUTFFD Self; + typedef itk::ImageToImageMetric< TFixedImage, TMovingImage > Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(NormalizedCorrelationImageToImageMetricFor3DBLUTFFD, ImageToImageMetric); + + /** Types inherited from Superclass. */ + typedef typename Superclass::RealType RealType; + typedef typename Superclass::TransformType TransformType; + typedef typename Superclass::TransformPointer TransformPointer; + typedef typename Superclass::TransformJacobianType TransformJacobianType; + typedef typename Superclass::InterpolatorType InterpolatorType; + typedef typename Superclass::MeasureType MeasureType; + typedef typename Superclass::DerivativeType DerivativeType; + typedef typename Superclass::ParametersType ParametersType; + typedef typename Superclass::FixedImageType FixedImageType; + typedef typename Superclass::MovingImageType MovingImageType; + typedef typename Superclass::MovingImagePointType MovingImagePointType; + typedef typename Superclass::FixedImageConstPointer FixedImageConstPointer; + typedef typename Superclass::MovingImageConstPointer MovingImageConstPointer; + typedef typename Superclass::CoordinateRepresentationType + CoordinateRepresentationType; + typedef typename Superclass::FixedImageSampleContainer + FixedImageSampleContainer; + typedef typename Superclass::ImageDerivativesType ImageDerivativesType; + typedef typename Superclass::WeightsValueType WeightsValueType; + typedef typename Superclass::IndexValueType IndexValueType; + + // Needed for evaluation of Jacobian. + typedef typename Superclass::FixedImagePointType FixedImagePointType; + + //Accumulators + typedef typename itk::NumericTraits< MeasureType >::AccumulateType AccumulateType; + + /** The moving image dimension. */ + itkStaticConstMacro( MovingImageDimension, unsigned int, + MovingImageType::ImageDimension ); + + + /** Set/Get SubtractMean boolean. If true, the sample mean is subtracted + * from the sample values in the cross-correlation formula and + * typically results in narrower valleys in the cost fucntion. + * Default value is false. */ + itkSetMacro( SubtractMean, bool ); + itkGetConstReferenceMacro( SubtractMean, bool ); + itkBooleanMacro( SubtractMean ); + + /** + * Initialize the Metric by + * (1) making sure that all the components are present and plugged + * together correctly, + * (2) uniformly select NumberOfSpatialSamples within + * the FixedImageRegion, and + * (3) allocate memory for pdf data structures. */ + virtual void Initialize(void) throw ( itk::ExceptionObject ); + + /** Get the value. */ + MeasureType GetValue( const ParametersType & parameters ) const; + + /** Get the derivatives of the match measure. */ + void GetDerivative( const ParametersType & parameters, + DerivativeType & Derivative ) const; + + /** Get the value and derivatives for single valued optimizers. */ + void GetValueAndDerivative( const ParametersType & parameters, + MeasureType & Value, + DerivativeType & Derivative ) const; + +protected: + + NormalizedCorrelationImageToImageMetricFor3DBLUTFFD(); + virtual ~NormalizedCorrelationImageToImageMetricFor3DBLUTFFD(); + void PrintSelf(std::ostream& os, itk::Indent indent) const; + +private: + + //purposely not implemented + NormalizedCorrelationImageToImageMetricFor3DBLUTFFD(const Self &); + //purposely not implemented + void operator=(const Self &); + + /** Get the value for the derivative computation. */ + MeasureType ComputeSums( const ParametersType & parameters ) const; + + + inline bool GetValueThreadProcessSample( unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & mappedPoint, + double movingImageValue ) const; + + + inline bool GetValueAndDerivativeThreadProcessSample( unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & mappedPoint, + double movingImageValue, + const ImageDerivativesType & + movingImageGradientValue ) const; + + AccumulateType *m_ThreaderSFF, *m_ThreaderSMM, *m_ThreaderSFM, *m_ThreaderSF, *m_ThreaderSM; + mutable AccumulateType m_SFF, m_SMM, m_SFM, m_SF, m_SM; + mutable RealType m_Denom, m_FixedMean, m_MovingMean; + DerivativeType * m_ThreaderDerivativeF, *m_ThreaderDerivativeM; + + + bool m_SubtractMean; +}; + +} // end namespace clitk + +#ifndef ITK_MANUAL_INSTANTIATION +#include "clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.txx" +#endif + +#endif + + diff --git a/registration/clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.txx b/registration/clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.txx new file mode 100644 index 0000000..1d2b8cc --- /dev/null +++ b/registration/clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.txx @@ -0,0 +1,519 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +#ifndef __clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD_txx +#define __clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD_txx + +#include "clitkOptNormalizedCorrelationImageToImageMetricFor3DBLUTFFD.h" +#include "itkCovariantVector.h" +#include "itkImageRandomConstIteratorWithIndex.h" +#include "itkImageRegionConstIterator.h" +#include "itkImageRegionIterator.h" +#include "itkImageIterator.h" +#include "vnl/vnl_math.h" + +namespace clitk +{ + +/** + * Constructor + */ +template < class TFixedImage, class TMovingImage > +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::NormalizedCorrelationImageToImageMetricFor3DBLUTFFD() +{ + this->SetComputeGradient(true); + + m_ThreaderSFF = NULL; + m_ThreaderSMM = NULL; + m_ThreaderSFM = NULL; + m_ThreaderSF = NULL; + m_ThreaderSM = NULL; + m_ThreaderDerivativeF = NULL; + m_ThreaderDerivativeM = NULL; + this->m_WithinThreadPreProcess = false; + this->m_WithinThreadPostProcess = false; + + // For backward compatibility, the default behavior is to use all the pixels + // in the fixed image. + this->UseAllPixelsOn(); + + m_SubtractMean=false; + +} + +template < class TFixedImage, class TMovingImage > +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::~NormalizedCorrelationImageToImageMetricFor3DBLUTFFD() +{ + if(m_ThreaderSFF != NULL) { + delete [] m_ThreaderSFF; + } + m_ThreaderSFF = NULL; + + if(m_ThreaderSMM != NULL) { + delete [] m_ThreaderSMM; + } + m_ThreaderSMM = NULL; + + if(m_ThreaderSFM != NULL) { + delete [] m_ThreaderSFM; + } + m_ThreaderSFM = NULL; + + if(m_ThreaderSF != NULL) { + delete [] m_ThreaderSF; + } + m_ThreaderSF = NULL; + + if(m_ThreaderSM != NULL) { + delete [] m_ThreaderSM; + } + m_ThreaderSM = NULL; + + if(m_ThreaderDerivativeF != NULL) { + delete [] m_ThreaderDerivativeF; + } + m_ThreaderDerivativeF = NULL; + + if(m_ThreaderDerivativeM != NULL) { + delete [] m_ThreaderDerivativeM; + } + m_ThreaderDerivativeM = NULL; +} + +/** + * Print out internal information about this class + */ +template < class TFixedImage, class TMovingImage > +void +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::PrintSelf(std::ostream& os, itk::Indent indent) const +{ + + Superclass::PrintSelf(os, indent); + +} + + +/** + * Initialize + */ +template +void +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::Initialize(void) throw ( itk::ExceptionObject ) +{ + + this->Superclass::Initialize(); + this->Superclass::MultiThreadingInitialize(); + + + /** + * Allocate memory for the accumulators (set to zero in GetValue) + */ + if(m_ThreaderSFF != NULL) { + delete [] m_ThreaderSFF; + } + m_ThreaderSFF = new double[this->m_NumberOfThreads]; + + + if(m_ThreaderSMM != NULL) { + delete [] m_ThreaderSMM; + } + m_ThreaderSMM = new double[this->m_NumberOfThreads]; + + if(m_ThreaderSFM != NULL) { + delete [] m_ThreaderSFM; + } + m_ThreaderSFM = new double[this->m_NumberOfThreads]; + + if(this->m_SubtractMean) { + if(m_ThreaderSF != NULL) { + delete [] m_ThreaderSF; + } + m_ThreaderSF = new double[this->m_NumberOfThreads]; + + if(m_ThreaderSM != NULL) { + delete [] m_ThreaderSM; + } + m_ThreaderSM = new double[this->m_NumberOfThreads]; + } + + if(m_ThreaderDerivativeF != NULL) { + delete [] m_ThreaderDerivativeF; + } + m_ThreaderDerivativeF = new DerivativeType[this->m_NumberOfThreads]; + for(unsigned int threadID=0; threadIDm_NumberOfThreads; threadID++) { + m_ThreaderDerivativeF[threadID].SetSize( this->m_NumberOfParameters ); + } + + if(m_ThreaderDerivativeM != NULL) { + delete [] m_ThreaderDerivativeM; + } + m_ThreaderDerivativeM = new DerivativeType[this->m_NumberOfThreads]; + for(unsigned int threadID=0; threadIDm_NumberOfThreads; threadID++) { + m_ThreaderDerivativeM[threadID].SetSize( this->m_NumberOfParameters ); + } +} + + +template < class TFixedImage, class TMovingImage > +inline bool +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::GetValueThreadProcessSample( + unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & itkNotUsed(mappedPoint), + double movingImageValue) const +{ + const RealType fixedImageValue= this->m_FixedImageSamples[fixedImageSample].value; + m_ThreaderSFF[threadID] += fixedImageValue * fixedImageValue; + m_ThreaderSMM[threadID] += movingImageValue * movingImageValue; + m_ThreaderSFM[threadID] += fixedImageValue * movingImageValue; + if ( this->m_SubtractMean ) { + m_ThreaderSF[threadID] += fixedImageValue; + m_ThreaderSM[threadID] += movingImageValue; + } + + return true; +} + +template < class TFixedImage, class TMovingImage > +typename NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::MeasureType +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::GetValue( const ParametersType & parameters ) const +{ + itkDebugMacro("GetValue( " << parameters << " ) "); + + if( !this->m_FixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + + //Reset the accumulators + memset( m_ThreaderSFF, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + memset( m_ThreaderSMM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + memset( m_ThreaderSFM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + if(this->m_SubtractMean) { + memset( m_ThreaderSF, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + memset( m_ThreaderSM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + } + + + // Set up the parameters in the transform + this->m_Transform->SetParameters( parameters ); + this->m_Parameters = parameters; + + // MUST BE CALLED TO INITIATE PROCESSING + this->GetValueMultiThreadedInitiate(); + + itkDebugMacro( "Ratio of voxels mapping into moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + + if( this->m_NumberOfPixelsCounted < + this->m_NumberOfFixedImageSamples / 4 ) { + itkExceptionMacro( "Too many samples map outside moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + } + + // Accumulate the threads + AccumulateType sff, smm, sfm, sf, sm; + sff = m_ThreaderSFF[0]; + smm = m_ThreaderSMM[0]; + sfm = m_ThreaderSFM[0]; + sf = m_ThreaderSF[0]; + sm = m_ThreaderSM[0]; + + for(unsigned int t=1; tm_NumberOfThreads; t++) { + sff += m_ThreaderSFF[t]; + smm += m_ThreaderSMM[t]; + sfm += m_ThreaderSFM[t]; + if ( this->m_SubtractMean ) { + sf += m_ThreaderSF[t]; + sm += m_ThreaderSM[t]; + } + } + + if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + sff -= ( sf * sf / this->m_NumberOfPixelsCounted ); + smm -= ( sm * sm / this->m_NumberOfPixelsCounted ); + sfm -= ( sf * sm / this->m_NumberOfPixelsCounted ); + } + + + const RealType denom = -1.0 * vcl_sqrt(sff * smm ); + MeasureType measure; + if( this->m_NumberOfPixelsCounted > 0 && denom != 0.0) { + measure = sfm / denom; + } else { + measure = itk::NumericTraits< MeasureType >::Zero; + } + + + return measure; +} + + +template < class TFixedImage, class TMovingImage > +typename NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::MeasureType +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::ComputeSums( const ParametersType & parameters ) const +{ + //No checking for the fixed image, done in the caller + //Reset the accumulators + memset( m_ThreaderSFF, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + memset( m_ThreaderSMM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + memset( m_ThreaderSFM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + if(this->m_SubtractMean) { + memset( m_ThreaderSF, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + memset( m_ThreaderSM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) ); + } + + + // Set up the parameters in the transform + this->m_Transform->SetParameters( parameters ); + this->m_Parameters = parameters; + + // MUST BE CALLED TO INITIATE PROCESSING + this->GetValueMultiThreadedInitiate(); + + itkDebugMacro( "Ratio of voxels mapping into moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + + if( this->m_NumberOfPixelsCounted < + this->m_NumberOfFixedImageSamples / 4 ) { + itkExceptionMacro( "Too many samples map outside moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + } + + // Accumulate the threads + m_SFF = m_ThreaderSFF[0]; + m_SMM = m_ThreaderSMM[0]; + m_SFM = m_ThreaderSFM[0]; + m_SF = m_ThreaderSF[0]; + m_SM = m_ThreaderSM[0]; + + for(unsigned int t=1; tm_NumberOfThreads; t++) { + m_SFF += m_ThreaderSFF[t]; + m_SMM += m_ThreaderSMM[t]; + m_SFM += m_ThreaderSFM[t]; + if ( this->m_SubtractMean ) { + m_SF += m_ThreaderSF[t]; + m_SM += m_ThreaderSM[t]; + } + } + + if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + m_SFF -= ( m_SF * m_SF / this->m_NumberOfPixelsCounted ); + m_SMM -= ( m_SM * m_SM / this->m_NumberOfPixelsCounted ); + m_SFM -= ( m_SF * m_SM / this->m_NumberOfPixelsCounted ); + m_FixedMean=m_SF / this->m_NumberOfPixelsCounted; + m_MovingMean=m_SM / this->m_NumberOfPixelsCounted; + } + + + m_Denom = -1.0 * vcl_sqrt(m_SFF * m_SMM ); + MeasureType measure; + if( this->m_NumberOfPixelsCounted > 0 && m_Denom != 0.0) { + measure = m_SFM / m_Denom; + } else { + measure = itk::NumericTraits< MeasureType >::Zero; + } + + return measure; +} + + +template < class TFixedImage, class TMovingImage > +inline bool +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::GetValueAndDerivativeThreadProcessSample( + unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & itkNotUsed(mappedPoint), + double movingImageValue, + const ImageDerivativesType & + movingImageGradientValue +) const +{ + + const RealType fixedImageValue=this->m_FixedImageSamples[fixedImageSample].value; + const FixedImagePointType fixedImagePoint = this->m_FixedImageSamples[fixedImageSample].point; + + // Need to use one of the threader transforms if we're + // not in thread 0. + // + // Use a raw pointer here to avoid the overhead of smart pointers. + // For instance, Register and UnRegister have mutex locks around + // the reference counts. + TransformType* transform; + + if (threadID > 0) { + transform = this->m_ThreaderTransform[threadID - 1]; + } else { + transform = this->m_Transform; + } + + // Jacobian should be evaluated at the unmapped (fixed image) point. + const TransformJacobianType & jacobian = transform->GetJacobian( fixedImagePoint ); + +// for(unsigned int par=0; parm_NumberOfParameters; par++) +// { +// RealType sumF = itk::NumericTraits< RealType >::Zero; +// RealType sumM = itk::NumericTraits< RealType >::Zero; +// for(unsigned int dim=0; dimm_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) +// { +// sumF += (fixedImageValue-m_FixedMean) * differential; +// sumM += (movingImageValue-m_MovingMean) * differential; +// } +// else +// { +// sumF += differential * fixedImageValue; +// sumM += differential * movingImageValue; +// } +// } +// m_ThreaderDerivativeF[threadID][par] += sumF; +// m_ThreaderDerivativeM[threadID][par] += sumM; +// } + + // JV + unsigned int par, dim; + RealType differential; + for( par=0; parm_NumberOfParameters; par+=3) { + // JV only for 3D Space BLUT FFD: if J(0, par)=0, then J(1,par+1)=0 & ... + if (jacobian( 0, par ) ) { + for(dim=0; dim<3; dim++) { + differential = jacobian( dim, par+dim ) * movingImageGradientValue[dim]; + + if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) { + m_ThreaderDerivativeF[threadID][par+dim]+= (fixedImageValue-m_FixedMean) * differential; + m_ThreaderDerivativeM[threadID][par+dim]+= (movingImageValue-m_MovingMean) * differential; + } else { + m_ThreaderDerivativeF[threadID][par+dim]+= differential * fixedImageValue; + m_ThreaderDerivativeM[threadID][par+dim]+= differential * movingImageValue; + } + } + } + } + + return true; +} + + +/** + * Get the both Value and Derivative Measure + */ +template < class TFixedImage, class TMovingImage > +void +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::GetValueAndDerivative( const ParametersType & parameters, + MeasureType & value, + DerivativeType & derivative) const +{ + + if( !this->m_FixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + // Set up the parameters in the transform + this->m_Transform->SetParameters( parameters ); + this->m_Parameters = parameters; + + //We need the sums and the value to be calculated first + value=this->ComputeSums(parameters); + + //Set output values to zero + if(derivative.GetSize() != this->m_NumberOfParameters) { + derivative = DerivativeType( this->m_NumberOfParameters ); + } + memset( derivative.data_block(), + 0, + this->m_NumberOfParameters * sizeof(ITK_TYPENAME DerivativeType::ValueType) ); + + for( unsigned int threadID = 0; threadIDm_NumberOfThreads; threadID++ ) { + memset( m_ThreaderDerivativeF[threadID].data_block(), + 0, + this->m_NumberOfParameters * sizeof(ITK_TYPENAME DerivativeType::ValueType) ); + + memset( m_ThreaderDerivativeM[threadID].data_block(), + 0, + this->m_NumberOfParameters * sizeof(ITK_TYPENAME DerivativeType::ValueType) ); + } + + // MUST BE CALLED TO INITIATE PROCESSING + this->GetValueAndDerivativeMultiThreadedInitiate(); + + // Accumulate over the threads + DerivativeType derivativeF(this->m_NumberOfParameters), derivativeM(this->m_NumberOfParameters); + for(unsigned int t=0; tm_NumberOfThreads; t++) { + for(unsigned int parameter = 0; parameter < this->m_NumberOfParameters; parameter++) { + derivativeF[parameter] += m_ThreaderDerivativeF[t][parameter]; + derivativeM[parameter] += m_ThreaderDerivativeM[t][parameter]; + } + } + + //Compute derivatives + if( this->m_NumberOfPixelsCounted > 0 && m_Denom != 0.0) { + for(unsigned int i=0; im_NumberOfParameters; i++) { + derivative[i] = ( derivativeF[i] - (m_SFM/m_SMM)* derivativeM[i] ) / m_Denom; + } + } else { + for(unsigned int i=0; im_NumberOfParameters; i++) { + derivative[i] = itk::NumericTraits< MeasureType >::Zero; + } + } + +} + + +/** + * Get the match measure derivative + */ +template < class TFixedImage, class TMovingImage > +void +NormalizedCorrelationImageToImageMetricFor3DBLUTFFD +::GetDerivative( const ParametersType & parameters, + DerivativeType & derivative ) const +{ + if( !this->m_FixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + MeasureType value; + // call the combined version + this->GetValueAndDerivative( parameters, value, derivative ); +} + +} // end namespace clitk + + +#endif diff --git a/registration/itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD.h b/registration/itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD.h new file mode 100644 index 0000000..d2f6f53 --- /dev/null +++ b/registration/itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD.h @@ -0,0 +1,535 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD.h,v $ + Language: C++ + Date: $Date: 2010/06/14 17:32:07 $ + Version: $Revision: 1.1 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + 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. + +=========================================================================*/ +#ifndef __itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD_h +#define __itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD_h + +// First make sure that the configuration is available. +// This line can be removed once the optimized versions +// gets integrated into the main directories. +#include "itkConfigure.h" + +#ifdef ITK_USE_OPTIMIZED_REGISTRATION_METHODS +#include "itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.h" +#else + +#include "itkImageToImageMetric.h" +#include "itkCovariantVector.h" +#include "itkPoint.h" +#include "itkIndex.h" +#include "itkBSplineKernelFunction.h" +#include "itkBSplineDerivativeKernelFunction.h" +#include "itkCentralDifferenceImageFunction.h" +#include "itkBSplineInterpolateImageFunction.h" +#include "itkBSplineDeformableTransform.h" +#include "itkArray2D.h" + +namespace itk +{ + +/** \class MattesMutualInformationImageToImageMetricFor3DBLUTFFD + * \brief Computes the mutual information between two images to be + * registered using the method of Mattes et al. + * + * MattesMutualInformationImageToImageMetricFor3DBLUTFFD computes the mutual + * information between a fixed and moving image to be registered. + * + * This class is templated over the FixedImage type and the MovingImage + * type. + * + * The fixed and moving images are set via methods SetFixedImage() and + * SetMovingImage(). This metric makes use of user specified Transform and + * Interpolator. The Transform is used to map points from the fixed image to + * the moving image domain. The Interpolator is used to evaluate the image + * intensity at user specified geometric points in the moving image. + * The Transform and Interpolator are set via methods SetTransform() and + * SetInterpolator(). + * + * If a BSplineInterpolationFunction is used, this class obtain + * image derivatives from the BSpline interpolator. Otherwise, + * image derivatives are computed using central differencing. + * + * \warning This metric assumes that the moving image has already been + * connected to the interpolator outside of this class. + * + * The method GetValue() computes of the mutual information + * while method GetValueAndDerivative() computes + * both the mutual information and its derivatives with respect to the + * transform parameters. + * + * The calculations are based on the method of Mattes et al [1,2] + * where the probability density distribution are estimated using + * Parzen histograms. Since the fixed image PDF does not contribute + * to the derivatives, it does not need to be smooth. Hence, + * a zero order (box car) BSpline kernel is used + * for the fixed image intensity PDF. On the other hand, to ensure + * smoothness a third order BSpline kernel is used for the + * moving image intensity PDF. + * + * On Initialize(), the FixedImage is uniformly sampled within + * the FixedImageRegion. The number of samples used can be set + * via SetNumberOfSpatialSamples(). Typically, the number of + * spatial samples used should increase with the image size. + * + * The option UseAllPixelOn() disables the random sampling and uses + * all the pixels of the FixedImageRegion in order to estimate the + * joint intensity PDF. + * + * During each call of GetValue(), GetDerivatives(), + * GetValueAndDerivatives(), marginal and joint intensity PDF's + * values are estimated at discrete position or bins. + * The number of bins used can be set via SetNumberOfHistogramBins(). + * To handle data with arbitray magnitude and dynamic range, + * the image intensity is scale such that any contribution to the + * histogram will fall into a valid bin. + * + * One the PDF's have been contructed, the mutual information + * is obtained by doubling summing over the discrete PDF values. + * + * + * Notes: + * 1. This class returns the negative mutual information value. + * 2. This class in not thread safe due the private data structures + * used to the store the sampled points and the marginal and joint pdfs. + * + * References: + * [1] "Nonrigid multimodality image registration" + * D. Mattes, D. R. Haynor, H. Vesselle, T. Lewellen and W. Eubank + * Medical Imaging 2001: Image Processing, 2001, pp. 1609-1620. + * [2] "PET-CT Image Registration in the Chest Using Free-form Deformations" + * D. Mattes, D. R. Haynor, H. Vesselle, T. Lewellen and W. Eubank + * IEEE Transactions in Medical Imaging. Vol.22, No.1, + January 2003. pp.120-128. + * [3] "Optimization of Mutual Information for MultiResolution Image + * Registration" + * P. Thevenaz and M. Unser + * IEEE Transactions in Image Processing, 9(12) December 2000. + * + * \ingroup RegistrationMetrics + * \ingroup ThreadUnSafe + */ +template +class ITK_EXPORT MattesMutualInformationImageToImageMetricFor3DBLUTFFD : + public ImageToImageMetric< TFixedImage, TMovingImage > +{ +public: + + /** Standard class typedefs. */ + typedef MattesMutualInformationImageToImageMetricFor3DBLUTFFD Self; + typedef ImageToImageMetric< TFixedImage, TMovingImage > Superclass; + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(MattesMutualInformationImageToImageMetricFor3DBLUTFFD, ImageToImageMetric); + + /** Types inherited from Superclass. */ + typedef typename Superclass::TransformType TransformType; + typedef typename Superclass::TransformPointer TransformPointer; + typedef typename Superclass::TransformJacobianType TransformJacobianType; + typedef typename Superclass::InterpolatorType InterpolatorType; + typedef typename Superclass::MeasureType MeasureType; + typedef typename Superclass::DerivativeType DerivativeType; + typedef typename Superclass::ParametersType ParametersType; + typedef typename Superclass::FixedImageType FixedImageType; + typedef typename Superclass::MovingImageType MovingImageType; + typedef typename Superclass::FixedImageConstPointer FixedImageConstPointer; + typedef typename Superclass::MovingImageConstPointer MovingImageCosntPointer; + typedef typename Superclass::InputPointType InputPointType; + typedef typename Superclass::OutputPointType OutputPointType; + + typedef typename Superclass::CoordinateRepresentationType + CoordinateRepresentationType; + + /** Index and Point typedef support. */ + typedef typename FixedImageType::IndexType FixedImageIndexType; + typedef typename FixedImageIndexType::IndexValueType FixedImageIndexValueType; + typedef typename MovingImageType::IndexType MovingImageIndexType; + typedef typename TransformType::InputPointType FixedImagePointType; + typedef typename TransformType::OutputPointType MovingImagePointType; + + /** The moving image dimension. */ + itkStaticConstMacro( MovingImageDimension, unsigned int, + MovingImageType::ImageDimension ); + + /** + * Initialize the Metric by + * (1) making sure that all the components are present and plugged + * together correctly, + * (2) uniformly select NumberOfSpatialSamples within + * the FixedImageRegion, and + * (3) allocate memory for pdf data structures. */ + virtual void Initialize(void) throw ( ExceptionObject ); + + /** Get the derivatives of the match measure. */ + void GetDerivative( const ParametersType& parameters, + DerivativeType & Derivative ) const; + + /** Get the value. */ + MeasureType GetValue( const ParametersType& parameters ) const; + + /** Get the value and derivatives for single valued optimizers. */ + void GetValueAndDerivative( const ParametersType& parameters, + MeasureType& Value, + DerivativeType& Derivative ) const; + + /** Number of spatial samples to used to compute metric */ + itkSetClampMacro( NumberOfSpatialSamples, unsigned long, + 1, NumericTraits::max() ); + itkGetConstReferenceMacro( NumberOfSpatialSamples, unsigned long); + + /** Number of bins to used in the histogram. Typical value is 50. */ + itkSetClampMacro( NumberOfHistogramBins, unsigned long, + 1, NumericTraits::max() ); + itkGetConstReferenceMacro( NumberOfHistogramBins, unsigned long); + + /** Reinitialize the seed of the random number generator that selects the + * sample of pixels used for estimating the image histograms and the joint + * histogram. By nature, this metric is not deterministic, since at each run + * it may select a different set of pixels. By initializing the random number + * generator seed to the same value you can restore determinism. On the other + * hand, calling the method ReinitializeSeed() without arguments will use the + * clock from your machine in order to have a very random initialization of + * the seed. This will indeed increase the non-deterministic behavior of the + * metric. */ + void ReinitializeSeed(); + void ReinitializeSeed(int); + + /** Select whether the metric will be computed using all the pixels on the + * fixed image region, or only using a set of randomly selected pixels. */ + itkSetMacro(UseAllPixels,bool); + itkGetConstReferenceMacro(UseAllPixels,bool); + itkBooleanMacro(UseAllPixels); + + /** This variable selects the method to be used for computing the Metric + * derivatives with respect to the Transform parameters. Two modes of + * computation are available. The choice between one and the other is a + * trade-off between computation speed and memory allocations. The two modes + * are described in detail below: + * + * UseExplicitPDFDerivatives = True + * will compute the Metric derivative by first calculating the derivatives of + * each one of the Joint PDF bins with respect to each one of the Transform + * parameters and then accumulating these contributions in the final metric + * derivative array by using a bin-specific weight. The memory required for + * storing the intermediate derivatives is a 3D array of doubles with size + * equals to the product of (number of histogram bins)^2 times number of + * transform parameters. This method is well suited for Transform with a small + * number of parameters. + * + * UseExplicitPDFDerivatives = False will compute the Metric derivative by + * first computing the weights for each one of the Joint PDF bins and caching + * them into an array. Then it will revisit each one of the PDF bins for + * computing its weighted contribution to the full derivative array. In this + * method an extra 2D array is used for storing the weights of each one of + * the PDF bins. This is an array of doubles with size equals to (number of + * histogram bins)^2. This method is well suited for Transforms with a large + * number of parameters, such as, BSplineDeformableTransforms. */ + itkSetMacro(UseExplicitPDFDerivatives,bool); + itkGetConstReferenceMacro(UseExplicitPDFDerivatives,bool); + itkBooleanMacro(UseExplicitPDFDerivatives); + + /** This boolean flag is only relevant when this metric is used along + * with a BSplineDeformableTransform. The flag enables/disables the + * caching of values computed when a physical point is mapped through + * the BSplineDeformableTransform. In particular it will cache the + * values of the BSpline weights for that points, and the indexes + * indicating what BSpline-grid nodes are relevant for that specific + * point. This caching is made optional due to the fact that the + * memory arrays used for the caching can reach large sizes even for + * moderate image size problems. For example, for a 3D image of + * 256^3, using 20% of pixels, these arrays will take about 1 + * Gigabyte of RAM for storage. The ratio of computing time between + * using the cache or not using the cache can reach 1:5, meaning that + * using the caching can provide a five times speed up. It is + * therefore, interesting to enable the caching, if enough memory is + * available for it. The caching is enabled by default, in order to + * preserve backward compatibility with previous versions of ITK. */ + itkSetMacro(UseCachingOfBSplineWeights,bool); + itkGetConstReferenceMacro(UseCachingOfBSplineWeights,bool); + itkBooleanMacro(UseCachingOfBSplineWeights); + +protected: + + MattesMutualInformationImageToImageMetricFor3DBLUTFFD(); + virtual ~MattesMutualInformationImageToImageMetricFor3DBLUTFFD() {}; + void PrintSelf(std::ostream& os, Indent indent) const; + + /** \class FixedImageSpatialSample + * A fixed image spatial sample consists of the fixed domain point + * and the fixed image value at that point. */ + /// @cond + class FixedImageSpatialSample + { + public: + FixedImageSpatialSample():FixedImageValue(0.0) { + FixedImagePointValue.Fill(0.0); + } + ~FixedImageSpatialSample() {}; + + FixedImagePointType FixedImagePointValue; + double FixedImageValue; + unsigned int FixedImageParzenWindowIndex; + }; + /// @endcond + + /** FixedImageSpatialSample typedef support. */ + typedef std::vector + FixedImageSpatialSampleContainer; + + /** Container to store a set of points and fixed image values. */ + FixedImageSpatialSampleContainer m_FixedImageSamples; + + /** Uniformly select a sample set from the fixed image domain. */ + virtual void SampleFixedImageDomain( + FixedImageSpatialSampleContainer& samples); + + /** Gather all the pixels from the fixed image domain. */ + virtual void SampleFullFixedImageDomain( + FixedImageSpatialSampleContainer& samples); + + /** Transform a point from FixedImage domain to MovingImage domain. + * This function also checks if mapped point is within support region. */ + virtual void TransformPoint( unsigned int sampleNumber, + const ParametersType& parameters, + MovingImagePointType& mappedPoint, + bool& sampleWithinSupportRegion, + double& movingImageValue ) const; + +private: + + //purposely not implemented + MattesMutualInformationImageToImageMetricFor3DBLUTFFD(const Self&); + //purposely not implemented + void operator=(const Self&); + + + /** The marginal PDFs are stored as std::vector. */ + typedef float PDFValueType; + typedef std::vector MarginalPDFType; + + /** The fixed image marginal PDF. */ + mutable MarginalPDFType m_FixedImageMarginalPDF; + + /** The moving image marginal PDF. */ + mutable MarginalPDFType m_MovingImageMarginalPDF; + + /** Helper array for storing the values of the JointPDF ratios. */ + typedef double PRatioType; + typedef Array2D< PRatioType > PRatioArrayType; + mutable PRatioArrayType m_PRatioArray; + + /** Helper variable for accumulating the derivative of the metric. */ + mutable DerivativeType m_MetricDerivative; + + /** Typedef for the joint PDF and PDF derivatives are stored as ITK Images. */ + typedef Image JointPDFType; + typedef JointPDFType::IndexType JointPDFIndexType; + typedef JointPDFType::PixelType JointPDFValueType; + typedef JointPDFType::RegionType JointPDFRegionType; + typedef JointPDFType::SizeType JointPDFSizeType; + typedef Image JointPDFDerivativesType; + typedef JointPDFDerivativesType::IndexType JointPDFDerivativesIndexType; + typedef JointPDFDerivativesType::PixelType JointPDFDerivativesValueType; + typedef JointPDFDerivativesType::RegionType JointPDFDerivativesRegionType; + typedef JointPDFDerivativesType::SizeType JointPDFDerivativesSizeType; + + /** The joint PDF and PDF derivatives. */ + typename JointPDFType::Pointer m_JointPDF; + typename JointPDFDerivativesType::Pointer m_JointPDFDerivatives; + + unsigned long m_NumberOfSpatialSamples; + unsigned long m_NumberOfParameters; + + /** Variables to define the marginal and joint histograms. */ + unsigned long m_NumberOfHistogramBins; + double m_MovingImageNormalizedMin; + double m_FixedImageNormalizedMin; + double m_MovingImageTrueMin; + double m_MovingImageTrueMax; + double m_FixedImageBinSize; + double m_MovingImageBinSize; + + /** Typedefs for BSpline kernel and derivative functions. */ + typedef BSplineKernelFunction<3> CubicBSplineFunctionType; + typedef BSplineDerivativeKernelFunction<3> CubicBSplineDerivativeFunctionType; + + /** Cubic BSpline kernel for computing Parzen histograms. */ + typename CubicBSplineFunctionType::Pointer m_CubicBSplineKernel; + typename CubicBSplineDerivativeFunctionType::Pointer + m_CubicBSplineDerivativeKernel; + + /** Precompute fixed image parzen window indices. */ + virtual void ComputeFixedImageParzenWindowIndices( + FixedImageSpatialSampleContainer& samples ); + + /** + * Types and variables related to image derivative calculations. + * If a BSplineInterpolationFunction is used, this class obtain + * image derivatives from the BSpline interpolator. Otherwise, + * image derivatives are computed using central differencing. + */ + typedef CovariantVector< double, + itkGetStaticConstMacro(MovingImageDimension) > + ImageDerivativesType; + + /** Compute image derivatives at a point. */ + virtual void ComputeImageDerivatives( const MovingImagePointType& mappedPoint, + ImageDerivativesType& gradient ) const; + + /** Boolean to indicate if the interpolator BSpline. */ + bool m_InterpolatorIsBSpline; + + /** Typedefs for using BSpline interpolator. */ + typedef + BSplineInterpolateImageFunction + BSplineInterpolatorType; + + /** Pointer to BSplineInterpolator. */ + typename BSplineInterpolatorType::Pointer m_BSplineInterpolator; + + /** Typedefs for using central difference calculator. */ + typedef CentralDifferenceImageFunction + DerivativeFunctionType; + + /** Pointer to central difference calculator. */ + typename DerivativeFunctionType::Pointer m_DerivativeCalculator; + + + /** Compute PDF derivative contribution for each parameter. */ + virtual void ComputePDFDerivatives( unsigned int sampleNumber, + int movingImageParzenWindowIndex, + const ImageDerivativesType& + movingImageGradientValue, + double cubicBSplineDerivativeValue + ) const; + + /** + * Types and variables related to BSpline deformable transforms. + * If the transform is of type third order BSplineDeformableTransform, + * then we can speed up the metric derivative calculation by + * only inspecting the parameters within the support region + * of a mapped point. + */ + + /** Boolean to indicate if the transform is BSpline deformable. */ + bool m_TransformIsBSpline; + + /** The number of BSpline parameters per image dimension. */ + long m_NumParametersPerDim; + + /** + * The number of BSpline transform weights is the number of + * of parameter in the support region (per dimension ). */ + unsigned long m_NumBSplineWeights; + + /** The fixed image dimension. */ + itkStaticConstMacro( FixedImageDimension, unsigned int, + FixedImageType::ImageDimension ); + + /** + * Enum of the deformabtion field spline order. + */ + enum { DeformationSplineOrder = 3 }; + + /** + * Typedefs for the BSplineDeformableTransform. + */ + typedef BSplineDeformableTransform< + CoordinateRepresentationType, + ::itk::GetImageDimension::ImageDimension, + DeformationSplineOrder> BSplineTransformType; + typedef typename BSplineTransformType::WeightsType + BSplineTransformWeightsType; + typedef typename BSplineTransformType::ParameterIndexArrayType + BSplineTransformIndexArrayType; + + /** + * Variables used when transform is of type BSpline deformable. + */ + typename BSplineTransformType::Pointer m_BSplineTransform; + + /** + * Cache pre-transformed points, weights, indices and + * within support region flag. + */ + typedef typename BSplineTransformWeightsType::ValueType WeightsValueType; + typedef Array2D BSplineTransformWeightsArrayType; + typedef typename BSplineTransformIndexArrayType::ValueType IndexValueType; + typedef Array2D BSplineTransformIndicesArrayType; + typedef std::vector MovingImagePointArrayType; + typedef std::vector BooleanArrayType; + + BSplineTransformWeightsArrayType m_BSplineTransformWeightsArray; + BSplineTransformIndicesArrayType m_BSplineTransformIndicesArray; + MovingImagePointArrayType m_PreTransformPointsArray; + BooleanArrayType m_WithinSupportRegionArray; + + typedef FixedArray::ImageDimension> + ParametersOffsetType; + ParametersOffsetType m_ParametersOffset; + + bool m_UseAllPixels; + + virtual void PreComputeTransformValues(); + + bool m_ReseedIterator; + int m_RandomSeed; + + // Selection of explicit or implicit computation of PDF derivatives + // with respect to Transform parameters. + bool m_UseExplicitPDFDerivatives; + + // Variables needed for optionally caching values when using a BSpline transform. + bool m_UseCachingOfBSplineWeights; + mutable BSplineTransformWeightsType m_Weights; + mutable BSplineTransformIndexArrayType m_Indices; + +}; + +} // end namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD.txx" +#endif + +#endif + +#endif diff --git a/registration/itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD.txx b/registration/itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD.txx new file mode 100644 index 0000000..171ed47 --- /dev/null +++ b/registration/itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD.txx @@ -0,0 +1,1586 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD.txx,v $ + Language: C++ + Date: $Date: 2010/06/14 17:32:07 $ + Version: $Revision: 1.1 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + 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. + +=========================================================================*/ +#ifndef __itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD_txx +#define __itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD_txx + + +// First make sure that the configuration is available. +// This line can be removed once the optimized versions +// gets integrated into the main directories. +#include "itkConfigure.h" + +#ifdef ITK_USE_OPTIMIZED_REGISTRATION_METHODS +#include "itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.txx" +#else + + +#include "itkMattesMutualInformationImageToImageMetricFor3DBLUTFFD.h" +#include "itkBSplineInterpolateImageFunction.h" +#include "itkCovariantVector.h" +#include "itkImageRandomConstIteratorWithIndex.h" +#include "itkImageRegionConstIterator.h" +#include "itkImageRegionIterator.h" +#include "itkImageIterator.h" +#include "vnl/vnl_math.h" +#include "itkBSplineDeformableTransform.h" + +namespace itk +{ + + +/** + * Constructor + */ +template < class TFixedImage, class TMovingImage > +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::MattesMutualInformationImageToImageMetricFor3DBLUTFFD() +{ + + m_NumberOfSpatialSamples = 500; + m_NumberOfHistogramBins = 50; + + this->SetComputeGradient(false); // don't use the default gradient for now + + m_InterpolatorIsBSpline = false; + m_TransformIsBSpline = false; + + // Initialize PDFs to NULL + m_JointPDF = NULL; + m_JointPDFDerivatives = NULL; + + m_UseExplicitPDFDerivatives = true; + + typename BSplineTransformType::Pointer transformer = + BSplineTransformType::New(); + this->SetTransform (transformer); + + typename BSplineInterpolatorType::Pointer interpolator = + BSplineInterpolatorType::New(); + this->SetInterpolator (interpolator); + + // Initialize memory + m_MovingImageNormalizedMin = 0.0; + m_FixedImageNormalizedMin = 0.0; + m_MovingImageTrueMin = 0.0; + m_MovingImageTrueMax = 0.0; + m_FixedImageBinSize = 0.0; + m_MovingImageBinSize = 0.0; + m_CubicBSplineDerivativeKernel = NULL; + m_BSplineInterpolator = NULL; + m_DerivativeCalculator = NULL; + m_NumParametersPerDim = 0; + m_NumBSplineWeights = 0; + m_BSplineTransform = NULL; + m_NumberOfParameters = 0; + m_UseAllPixels = false; + m_ReseedIterator = false; + m_RandomSeed = -1; + m_UseCachingOfBSplineWeights = true; +} + + +/** + * Print out internal information about this class + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::PrintSelf(std::ostream& os, Indent indent) const +{ + + Superclass::PrintSelf(os, indent); + + os << indent << "NumberOfSpatialSamples: "; + os << m_NumberOfSpatialSamples << std::endl; + os << indent << "NumberOfHistogramBins: "; + os << m_NumberOfHistogramBins << std::endl; + os << indent << "UseAllPixels: "; + os << m_UseAllPixels << std::endl; + + // Debugging information + os << indent << "NumberOfParameters: "; + os << m_NumberOfParameters << std::endl; + os << indent << "FixedImageNormalizedMin: "; + os << m_FixedImageNormalizedMin << std::endl; + os << indent << "MovingImageNormalizedMin: "; + os << m_MovingImageNormalizedMin << std::endl; + os << indent << "MovingImageTrueMin: "; + os << m_MovingImageTrueMin << std::endl; + os << indent << "MovingImageTrueMax: "; + os << m_MovingImageTrueMax << std::endl; + os << indent << "FixedImageBinSize: "; + os << m_FixedImageBinSize << std::endl; + os << indent << "MovingImageBinSize: "; + os << m_MovingImageBinSize << std::endl; + os << indent << "InterpolatorIsBSpline: "; + os << m_InterpolatorIsBSpline << std::endl; + os << indent << "TransformIsBSpline: "; + os << m_TransformIsBSpline << std::endl; + os << indent << "UseCachingOfBSplineWeights: "; + os << m_UseCachingOfBSplineWeights << std::endl; + os << indent << "UseExplicitPDFDerivatives: "; + os << m_UseExplicitPDFDerivatives << std::endl; + +} + + +/** + * Initialize + */ +template +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::Initialize(void) throw ( ExceptionObject ) +{ + this->Superclass::Initialize(); + + // Cache the number of transformation parameters + m_NumberOfParameters = this->m_Transform->GetNumberOfParameters(); + + /** + * Compute the minimum and maximum for the FixedImage over + * the FixedImageRegion. + * + * NB: We can't use StatisticsImageFilter to do this because + * the filter computes the min/max for the largest possible region + */ + double fixedImageMin = NumericTraits::max(); + double fixedImageMax = NumericTraits::NonpositiveMin(); + + typedef ImageRegionConstIterator FixedIteratorType; + FixedIteratorType fixedImageIterator( + this->m_FixedImage, this->GetFixedImageRegion() ); + + for ( fixedImageIterator.GoToBegin(); + !fixedImageIterator.IsAtEnd(); ++fixedImageIterator ) { + + double sample = static_cast( fixedImageIterator.Get() ); + + if ( sample < fixedImageMin ) { + fixedImageMin = sample; + } + + if ( sample > fixedImageMax ) { + fixedImageMax = sample; + } + } + + + /** + * Compute the minimum and maximum for the entire moving image + * in the buffer. + */ + double movingImageMin = NumericTraits::max(); + double movingImageMax = NumericTraits::NonpositiveMin(); + + typedef ImageRegionConstIterator MovingIteratorType; + MovingIteratorType movingImageIterator( + this->m_MovingImage, this->m_MovingImage->GetBufferedRegion() ); + + for ( movingImageIterator.GoToBegin(); + !movingImageIterator.IsAtEnd(); ++movingImageIterator) { + double sample = static_cast( movingImageIterator.Get() ); + + if ( sample < movingImageMin ) { + movingImageMin = sample; + } + + if ( sample > movingImageMax ) { + movingImageMax = sample; + } + } + + m_MovingImageTrueMin = movingImageMin; + m_MovingImageTrueMax = movingImageMax; + + itkDebugMacro( " FixedImageMin: " << fixedImageMin << + " FixedImageMax: " << fixedImageMax << std::endl ); + itkDebugMacro( " MovingImageMin: " << movingImageMin << + " MovingImageMax: " << movingImageMax << std::endl ); + + + /** + * Compute binsize for the histograms. + * + * The binsize for the image intensities needs to be adjusted so that + * we can avoid dealing with boundary conditions using the cubic + * spline as the Parzen window. We do this by increasing the size + * of the bins so that the joint histogram becomes "padded" at the + * borders. Because we are changing the binsize, + * we also need to shift the minimum by the padded amount in order to + * avoid minimum values filling in our padded region. + * + * Note that there can still be non-zero bin values in the padded region, + * it's just that these bins will never be a central bin for the Parzen + * window. + * + */ + const int padding = 2; // this will pad by 2 bins + + m_FixedImageBinSize = ( fixedImageMax - fixedImageMin ) / + static_cast( m_NumberOfHistogramBins - 2 * padding ); + m_FixedImageNormalizedMin = fixedImageMin / m_FixedImageBinSize - + static_cast( padding ); + + m_MovingImageBinSize = ( movingImageMax - movingImageMin ) / + static_cast( m_NumberOfHistogramBins - 2 * padding ); + m_MovingImageNormalizedMin = movingImageMin / m_MovingImageBinSize - + static_cast( padding ); + + + itkDebugMacro( "FixedImageNormalizedMin: " << m_FixedImageNormalizedMin ); + itkDebugMacro( "MovingImageNormalizedMin: " << m_MovingImageNormalizedMin ); + itkDebugMacro( "FixedImageBinSize: " << m_FixedImageBinSize ); + itkDebugMacro( "MovingImageBinSize; " << m_MovingImageBinSize ); + + if( m_UseAllPixels ) { + m_NumberOfSpatialSamples = + this->GetFixedImageRegion().GetNumberOfPixels(); + } + + /** + * Allocate memory for the fixed image sample container. + */ + m_FixedImageSamples.resize( m_NumberOfSpatialSamples ); + + + /** + * Allocate memory for the marginal PDF and initialize values + * to zero. The marginal PDFs are stored as std::vector. + */ + m_FixedImageMarginalPDF.resize( m_NumberOfHistogramBins, 0.0 ); + m_MovingImageMarginalPDF.resize( m_NumberOfHistogramBins, 0.0 ); + + /** + * Allocate memory for the joint PDF and joint PDF derivatives. + * The joint PDF and joint PDF derivatives are store as itk::Image. + */ + m_JointPDF = JointPDFType::New(); + + // Instantiate a region, index, size + JointPDFRegionType jointPDFRegion; + JointPDFIndexType jointPDFIndex; + JointPDFSizeType jointPDFSize; + + // Deallocate the memory that may have been allocated for + // previous runs of the metric. + this->m_JointPDFDerivatives = NULL; // by destroying the dynamic array + this->m_PRatioArray.SetSize( 1, 1 ); // and by allocating very small the static ones + this->m_MetricDerivative = DerivativeType( 1 ); + + // + // Now allocate memory according to the user-selected method. + // + if( this->m_UseExplicitPDFDerivatives ) { + this->m_JointPDFDerivatives = JointPDFDerivativesType::New(); + JointPDFDerivativesRegionType jointPDFDerivativesRegion; + JointPDFDerivativesIndexType jointPDFDerivativesIndex; + JointPDFDerivativesSizeType jointPDFDerivativesSize; + + // For the derivatives of the joint PDF define a region starting from {0,0,0} + // with size {m_NumberOfParameters,m_NumberOfHistogramBins, + // m_NumberOfHistogramBins}. The dimension represents transform parameters, + // fixed image parzen window index and moving image parzen window index, + // respectively. + jointPDFDerivativesIndex.Fill( 0 ); + jointPDFDerivativesSize[0] = m_NumberOfParameters; + jointPDFDerivativesSize[1] = m_NumberOfHistogramBins; + jointPDFDerivativesSize[2] = m_NumberOfHistogramBins; + + jointPDFDerivativesRegion.SetIndex( jointPDFDerivativesIndex ); + jointPDFDerivativesRegion.SetSize( jointPDFDerivativesSize ); + + // Set the regions and allocate + m_JointPDFDerivatives->SetRegions( jointPDFDerivativesRegion ); + m_JointPDFDerivatives->Allocate(); + } else { + /** Allocate memory for helper array that will contain the pRatios + * for each bin of the joint histogram. This is part of the effort + * for flattening the computation of the PDF Jacobians. + */ + this->m_PRatioArray.SetSize( this->m_NumberOfHistogramBins, this->m_NumberOfHistogramBins ); + this->m_MetricDerivative = DerivativeType( this->GetNumberOfParameters() ); + } + + // For the joint PDF define a region starting from {0,0} + // with size {m_NumberOfHistogramBins, m_NumberOfHistogramBins}. + // The dimension represents fixed image parzen window index + // and moving image parzen window index, respectively. + jointPDFIndex.Fill( 0 ); + jointPDFSize.Fill( m_NumberOfHistogramBins ); + + jointPDFRegion.SetIndex( jointPDFIndex ); + jointPDFRegion.SetSize( jointPDFSize ); + + // Set the regions and allocate + m_JointPDF->SetRegions( jointPDFRegion ); + m_JointPDF->Allocate(); + + + /** + * Setup the kernels used for the Parzen windows. + */ + m_CubicBSplineKernel = CubicBSplineFunctionType::New(); + m_CubicBSplineDerivativeKernel = CubicBSplineDerivativeFunctionType::New(); + + + if( m_UseAllPixels ) { + /** + * Take all the pixels within the fixed image region) + * to create the sample points list. + */ + this->SampleFullFixedImageDomain( m_FixedImageSamples ); + } else { + /** + * Uniformly sample the fixed image (within the fixed image region) + * to create the sample points list. + */ + this->SampleFixedImageDomain( m_FixedImageSamples ); + } + + /** + * Pre-compute the fixed image parzen window index for + * each point of the fixed image sample points list. + */ + this->ComputeFixedImageParzenWindowIndices( m_FixedImageSamples ); + + /** + * Check if the interpolator is of type BSplineInterpolateImageFunction. + * If so, we can make use of its EvaluateDerivatives method. + * Otherwise, we instantiate an external central difference + * derivative calculator. + * + * TODO: Also add it the possibility of using the default gradient + * provided by the superclass. + * + */ + m_InterpolatorIsBSpline = true; + + BSplineInterpolatorType * testPtr = dynamic_cast( + this->m_Interpolator.GetPointer() ); + if ( !testPtr ) { + m_InterpolatorIsBSpline = false; + + m_DerivativeCalculator = DerivativeFunctionType::New(); + +#ifdef ITK_USE_ORIENTED_IMAGE_DIRECTION + m_DerivativeCalculator->UseImageDirectionOn(); +#endif + + m_DerivativeCalculator->SetInputImage( this->m_MovingImage ); + + m_BSplineInterpolator = NULL; + itkDebugMacro( "Interpolator is not BSpline" ); + } else { + m_BSplineInterpolator = testPtr; + +#ifdef ITK_USE_ORIENTED_IMAGE_DIRECTION + m_BSplineInterpolator->UseImageDirectionOn(); +#endif + + m_DerivativeCalculator = NULL; + itkDebugMacro( "Interpolator is BSpline" ); + } + + /** + * Check if the transform is of type BSplineDeformableTransform. + * + * If so, several speed up features are implemented. + * [1] Precomputing the results of bulk transform for each sample point. + * [2] Precomputing the BSpline weights for each sample point, + * to be used later to directly compute the deformation vector + * [3] Precomputing the indices of the parameters within the + * the support region of each sample point. + */ + m_TransformIsBSpline = true; + + BSplineTransformType * testPtr2 = dynamic_cast( + this->m_Transform.GetPointer() ); + if( !testPtr2 ) { + m_TransformIsBSpline = false; + m_BSplineTransform = NULL; + itkDebugMacro( "Transform is not BSplineDeformable" ); + } else { + m_BSplineTransform = testPtr2; + m_NumParametersPerDim = + m_BSplineTransform->GetNumberOfParametersPerDimension(); + m_NumBSplineWeights = m_BSplineTransform->GetNumberOfWeights(); + itkDebugMacro( "Transform is BSplineDeformable" ); + } + + if( this->m_TransformIsBSpline ) { + // First, deallocate memory that may have been used from previous run of the Metric + this->m_BSplineTransformWeightsArray.SetSize( 1, 1 ); + this->m_BSplineTransformIndicesArray.SetSize( 1, 1 ); + this->m_PreTransformPointsArray.resize( 1 ); + this->m_WithinSupportRegionArray.resize( 1 ); + this->m_Weights.SetSize( 1 ); + this->m_Indices.SetSize( 1 ); + + + if( this->m_UseCachingOfBSplineWeights ) { + m_BSplineTransformWeightsArray.SetSize( + m_NumberOfSpatialSamples, m_NumBSplineWeights ); + m_BSplineTransformIndicesArray.SetSize( + m_NumberOfSpatialSamples, m_NumBSplineWeights ); + m_PreTransformPointsArray.resize( m_NumberOfSpatialSamples ); + m_WithinSupportRegionArray.resize( m_NumberOfSpatialSamples ); + + this->PreComputeTransformValues(); + } else { + this->m_Weights.SetSize( this->m_NumBSplineWeights ); + this->m_Indices.SetSize( this->m_NumBSplineWeights ); + } + + for ( unsigned int j = 0; j < FixedImageDimension; j++ ) { + m_ParametersOffset[j] = j * + m_BSplineTransform->GetNumberOfParametersPerDimension(); + } + } + +} + + +/** + * Uniformly sample the fixed image domain using a random walk + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::SampleFixedImageDomain( FixedImageSpatialSampleContainer& samples ) +{ + + // Set up a random interator within the user specified fixed image region. + typedef ImageRandomConstIteratorWithIndex RandomIterator; + RandomIterator randIter( this->m_FixedImage, this->GetFixedImageRegion() ); + + randIter.SetNumberOfSamples( m_NumberOfSpatialSamples ); + randIter.GoToBegin(); + + typename FixedImageSpatialSampleContainer::iterator iter; + typename FixedImageSpatialSampleContainer::const_iterator end=samples.end(); + + if( this->m_FixedImageMask ) { + + InputPointType inputPoint; + + iter=samples.begin(); + int count = 0; + int samples_found = 0; + int maxcount = m_NumberOfSpatialSamples * 10; + while( iter != end ) { + + if ( count > maxcount ) { +#if 0 + itkExceptionMacro( + "Drew too many samples from the mask (is it too small?): " + << maxcount << std::endl ); +#else +samples.resize(samples_found); +// this->SetNumberOfSpatialSamples(sample_found); +break; +#endif + } + count++; + + // Get sampled index + FixedImageIndexType index = randIter.GetIndex(); + // Check if the Index is inside the mask, translate index to point + this->m_FixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + // If not inside the mask, ignore the point + if( !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++randIter; // jump to another random position + continue; + } + + // Get sampled fixed image value + (*iter).FixedImageValue = randIter.Get(); + // Translate index to point + (*iter).FixedImagePointValue = inputPoint; + samples_found++; + // Jump to random position + ++randIter; + ++iter; + } + } else { + for( iter=samples.begin(); iter != end; ++iter ) { + // Get sampled index + FixedImageIndexType index = randIter.GetIndex(); + // Get sampled fixed image value + (*iter).FixedImageValue = randIter.Get(); + // Translate index to point + this->m_FixedImage->TransformIndexToPhysicalPoint( index, + (*iter).FixedImagePointValue ); + // Jump to random position + ++randIter; + + } + } +} + +/** + * Sample the fixed image domain using all pixels in the Fixed image region + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::SampleFullFixedImageDomain( FixedImageSpatialSampleContainer& samples ) +{ + + // Set up a region interator within the user specified fixed image region. + typedef ImageRegionConstIteratorWithIndex RegionIterator; + RegionIterator regionIter( this->m_FixedImage, this->GetFixedImageRegion() ); + + regionIter.GoToBegin(); + + typename FixedImageSpatialSampleContainer::iterator iter; + typename FixedImageSpatialSampleContainer::const_iterator end=samples.end(); + + if( this->m_FixedImageMask ) { + InputPointType inputPoint; + + iter=samples.begin(); + unsigned long nSamplesPicked = 0; + + while( iter != end && !regionIter.IsAtEnd() ) { + // Get sampled index + FixedImageIndexType index = regionIter.GetIndex(); + // Check if the Index is inside the mask, translate index to point + this->m_FixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + // If not inside the mask, ignore the point + if( !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++regionIter; // jump to next pixel + continue; + } + + // Get sampled fixed image value + (*iter).FixedImageValue = regionIter.Get(); + // Translate index to point + (*iter).FixedImagePointValue = inputPoint; + + ++regionIter; + ++iter; + ++nSamplesPicked; + } + + // If we picked fewer samples than the desired number, + // resize the container + if (nSamplesPicked != this->m_NumberOfSpatialSamples) { + this->m_NumberOfSpatialSamples = nSamplesPicked; + samples.resize(this->m_NumberOfSpatialSamples); + } + } else { // not restricting sample throwing to a mask + + // cannot sample more than the number of pixels in the image region + if ( this->m_NumberOfSpatialSamples + > this->GetFixedImageRegion().GetNumberOfPixels()) { + this->m_NumberOfSpatialSamples + = this->GetFixedImageRegion().GetNumberOfPixels(); + samples.resize(this->m_NumberOfSpatialSamples); + } + + for( iter=samples.begin(); iter != end; ++iter ) { + // Get sampled index + FixedImageIndexType index = regionIter.GetIndex(); + // Get sampled fixed image value + (*iter).FixedImageValue = regionIter.Get(); + // Translate index to point + this->m_FixedImage->TransformIndexToPhysicalPoint( index, + (*iter).FixedImagePointValue ); + ++regionIter; + } + } +} + +/** + * Uniformly sample the fixed image domain using a random walk + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::ComputeFixedImageParzenWindowIndices( + FixedImageSpatialSampleContainer& samples ) +{ + + typename FixedImageSpatialSampleContainer::iterator iter; + typename FixedImageSpatialSampleContainer::const_iterator end=samples.end(); + + for( iter=samples.begin(); iter != end; ++iter ) { + + // Determine parzen window arguments (see eqn 6 of Mattes paper [2]). + double windowTerm = + static_cast( (*iter).FixedImageValue ) / m_FixedImageBinSize - + m_FixedImageNormalizedMin; + unsigned int pindex = static_cast( vcl_floor(windowTerm ) ); + + // Make sure the extreme values are in valid bins + if ( pindex < 2 ) { + pindex = 2; + } else if ( pindex > (m_NumberOfHistogramBins - 3) ) { + pindex = m_NumberOfHistogramBins - 3; + } + + (*iter).FixedImageParzenWindowIndex = pindex; + + } + +} + +/** + * Get the match Measure + */ +template < class TFixedImage, class TMovingImage > +typename MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::MeasureType +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::GetValue( const ParametersType& parameters ) const +{ + + // Reset marginal pdf to all zeros. + // Assumed the size has already been set to NumberOfHistogramBins + // in Initialize(). + for ( unsigned int j = 0; j < m_NumberOfHistogramBins; j++ ) { + m_FixedImageMarginalPDF[j] = 0.0; + m_MovingImageMarginalPDF[j] = 0.0; + } + + // Reset the joint pdfs to zero + m_JointPDF->FillBuffer( 0.0 ); + + // Set up the parameters in the transform + this->m_Transform->SetParameters( parameters ); + + + // Declare iterators for iteration over the sample container + typename FixedImageSpatialSampleContainer::const_iterator fiter; + typename FixedImageSpatialSampleContainer::const_iterator fend = + m_FixedImageSamples.end(); + + unsigned long nSamples=0; + unsigned long nFixedImageSamples=0; + + + for ( fiter = m_FixedImageSamples.begin(); fiter != fend; ++fiter ) { + + // Get moving image value + MovingImagePointType mappedPoint; + bool sampleOk; + double movingImageValue; + + this->TransformPoint( nFixedImageSamples, parameters, mappedPoint, + sampleOk, movingImageValue ); + + ++nFixedImageSamples; + + if( sampleOk ) { + + ++nSamples; + + /** + * Compute this sample's contribution to the marginal and + * joint distributions. + * + */ + + // Determine parzen window arguments (see eqn 6 of Mattes paper [2]) + double movingImageParzenWindowTerm = + movingImageValue / m_MovingImageBinSize - m_MovingImageNormalizedMin; + unsigned int movingImageParzenWindowIndex = + static_cast( vcl_floor(movingImageParzenWindowTerm ) ); + + // Make sure the extreme values are in valid bins + if ( movingImageParzenWindowIndex < 2 ) { + movingImageParzenWindowIndex = 2; + } else if ( movingImageParzenWindowIndex > (m_NumberOfHistogramBins - 3) ) { + movingImageParzenWindowIndex = m_NumberOfHistogramBins - 3; + } + + + // Since a zero-order BSpline (box car) kernel is used for + // the fixed image marginal pdf, we need only increment the + // fixedImageParzenWindowIndex by value of 1.0. + m_FixedImageMarginalPDF[(*fiter).FixedImageParzenWindowIndex] += + static_cast( 1 ); + + /** + * The region of support of the parzen window determines which bins + * of the joint PDF are effected by the pair of image values. + * Since we are using a cubic spline for the moving image parzen + * window, four bins are affected. The fixed image parzen window is + * a zero-order spline (box car) and thus effects only one bin. + * + * The PDF is arranged so that moving image bins corresponds to the + * zero-th (column) dimension and the fixed image bins corresponds + * to the first (row) dimension. + * + */ + + // Pointer to affected bin to be updated + JointPDFValueType *pdfPtr = m_JointPDF->GetBufferPointer() + + ( (*fiter).FixedImageParzenWindowIndex + * m_JointPDF->GetOffsetTable()[1] ); + + // Move the pointer to the first affected bin + int pdfMovingIndex = static_cast( movingImageParzenWindowIndex ) - 1; + pdfPtr += pdfMovingIndex; + + for (; pdfMovingIndex <= static_cast( movingImageParzenWindowIndex ) + + 2; + pdfMovingIndex++, pdfPtr++ ) { + + // Update PDF for the current intensity pair + double movingImageParzenWindowArg = + static_cast( pdfMovingIndex ) - + static_cast( movingImageParzenWindowTerm ); + + *(pdfPtr) += static_cast( + m_CubicBSplineKernel->Evaluate( movingImageParzenWindowArg ) ); + + } //end parzen windowing for loop + + } //end if-block check sampleOk + } // end iterating over fixed image spatial sample container for loop + + itkDebugMacro( "Ratio of voxels mapping into moving image buffer: " + << nSamples << " / " << m_NumberOfSpatialSamples + << std::endl ); + + if( nSamples < m_NumberOfSpatialSamples / 16 ) { + itkExceptionMacro( "Too many samples map outside moving image buffer: " + << nSamples << " / " << m_NumberOfSpatialSamples + << std::endl ); + } + + this->m_NumberOfPixelsCounted = nSamples; + + + /** + * Normalize the PDFs, compute moving image marginal PDF + * + */ + typedef ImageRegionIterator JointPDFIteratorType; + JointPDFIteratorType jointPDFIterator ( m_JointPDF, + m_JointPDF->GetBufferedRegion() ); + + jointPDFIterator.GoToBegin(); + + // Compute joint PDF normalization factor + // (to ensure joint PDF sum adds to 1.0) + double jointPDFSum = 0.0; + + while( !jointPDFIterator.IsAtEnd() ) { + jointPDFSum += jointPDFIterator.Get(); + ++jointPDFIterator; + } + + if ( jointPDFSum == 0.0 ) { + itkExceptionMacro( "Joint PDF summed to zero" ); + } + + + // Normalize the PDF bins + jointPDFIterator.GoToEnd(); + while( !jointPDFIterator.IsAtBegin() ) { + --jointPDFIterator; + jointPDFIterator.Value() /= static_cast( jointPDFSum ); + } + + + // Normalize the fixed image marginal PDF + double fixedPDFSum = 0.0; + for( unsigned int bin = 0; bin < m_NumberOfHistogramBins; bin++ ) { + fixedPDFSum += m_FixedImageMarginalPDF[bin]; + } + + if ( fixedPDFSum == 0.0 ) { + itkExceptionMacro( "Fixed image marginal PDF summed to zero" ); + } + + for( unsigned int bin=0; bin < m_NumberOfHistogramBins; bin++ ) { + m_FixedImageMarginalPDF[bin] /= static_cast( fixedPDFSum ); + } + + + // Compute moving image marginal PDF by summing over fixed image bins. + typedef ImageLinearIteratorWithIndex JointPDFLinearIterator; + JointPDFLinearIterator linearIter( m_JointPDF, + m_JointPDF->GetBufferedRegion() ); + + linearIter.SetDirection( 1 ); + linearIter.GoToBegin(); + unsigned int movingIndex1 = 0; + + while( !linearIter.IsAtEnd() ) { + + double sum = 0.0; + + while( !linearIter.IsAtEndOfLine() ) { + sum += linearIter.Get(); + ++linearIter; + } + + m_MovingImageMarginalPDF[movingIndex1] = static_cast(sum); + + linearIter.NextLine(); + ++movingIndex1; + + } + + /** + * Compute the metric by double summation over histogram. + */ + + // Setup pointer to point to the first bin + JointPDFValueType * jointPDFPtr = m_JointPDF->GetBufferPointer(); + + // Initialze sum to zero + double sum = 0.0; + + for( unsigned int fixedIndex = 0; + fixedIndex < m_NumberOfHistogramBins; + ++fixedIndex ) { + + double fixedImagePDFValue = m_FixedImageMarginalPDF[fixedIndex]; + + for( unsigned int movingIndex = 0; + movingIndex < m_NumberOfHistogramBins; + ++movingIndex, jointPDFPtr++ ) { + + double movingImagePDFValue = m_MovingImageMarginalPDF[movingIndex]; + double jointPDFValue = *(jointPDFPtr); + + // check for non-zero bin contribution + if( jointPDFValue > 1e-16 && movingImagePDFValue > 1e-16 ) { + + double pRatio = vcl_log(jointPDFValue / movingImagePDFValue ); + if( fixedImagePDFValue > 1e-16) { + sum += jointPDFValue * ( pRatio - vcl_log(fixedImagePDFValue ) ); + } + + } // end if-block to check non-zero bin contribution + } // end for-loop over moving index + } // end for-loop over fixed index + + return static_cast( -1.0 * sum ); + +} + + +/** + * Get the both Value and Derivative Measure + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::GetValueAndDerivative( + const ParametersType& parameters, + MeasureType& value, + DerivativeType& derivative) const +{ + + // Set output values to zero + value = NumericTraits< MeasureType >::Zero; + + if( this->m_UseExplicitPDFDerivatives ) { + m_JointPDFDerivatives->FillBuffer( 0.0 ); + derivative = DerivativeType( this->GetNumberOfParameters() ); + derivative.Fill( NumericTraits< MeasureType >::Zero ); + } else { + this->m_MetricDerivative.Fill( NumericTraits< MeasureType >::Zero ); + this->m_PRatioArray.Fill( 0.0 ); + } + + // Reset marginal pdf to all zeros. + // Assumed the size has already been set to NumberOfHistogramBins + // in Initialize(). + for ( unsigned int j = 0; j < m_NumberOfHistogramBins; j++ ) { + m_FixedImageMarginalPDF[j] = 0.0; + m_MovingImageMarginalPDF[j] = 0.0; + } + + // Reset the joint pdfs to zero + m_JointPDF->FillBuffer( 0.0 ); + + + // Set up the parameters in the transform + this->m_Transform->SetParameters( parameters ); + + + // Declare iterators for iteration over the sample container + typename FixedImageSpatialSampleContainer::const_iterator fiter; + typename FixedImageSpatialSampleContainer::const_iterator fend = + m_FixedImageSamples.end(); + + unsigned long nSamples=0; + unsigned long nFixedImageSamples=0; + + for ( fiter = m_FixedImageSamples.begin(); fiter != fend; ++fiter ) { + + // Get moving image value + MovingImagePointType mappedPoint; + bool sampleOk; + double movingImageValue; + + + this->TransformPoint( nFixedImageSamples, parameters, mappedPoint, + sampleOk, movingImageValue ); + + if( sampleOk ) { + ++nSamples; + + // Get moving image derivative at the mapped position + ImageDerivativesType movingImageGradientValue; + this->ComputeImageDerivatives( mappedPoint, movingImageGradientValue ); + + + /** + * Compute this sample's contribution to the marginal + * and joint distributions. + * + */ + + // Determine parzen window arguments (see eqn 6 of Mattes paper [2]) + double movingImageParzenWindowTerm = + movingImageValue / m_MovingImageBinSize - m_MovingImageNormalizedMin; + unsigned int movingImageParzenWindowIndex = + static_cast( vcl_floor(movingImageParzenWindowTerm ) ); + + // Make sure the extreme values are in valid bins + if ( movingImageParzenWindowIndex < 2 ) { + movingImageParzenWindowIndex = 2; + } else if ( movingImageParzenWindowIndex > (m_NumberOfHistogramBins - 3) ) { + movingImageParzenWindowIndex = m_NumberOfHistogramBins - 3; + } + + + // Since a zero-order BSpline (box car) kernel is used for + // the fixed image marginal pdf, we need only increment the + // fixedImageParzenWindowIndex by value of 1.0. + m_FixedImageMarginalPDF[(*fiter).FixedImageParzenWindowIndex] += + static_cast( 1 ); + + /** + * The region of support of the parzen window determines which bins + * of the joint PDF are effected by the pair of image values. + * Since we are using a cubic spline for the moving image parzen + * window, four bins are effected. The fixed image parzen window is + * a zero-order spline (box car) and thus effects only one bin. + * + * The PDF is arranged so that moving image bins corresponds to the + * zero-th (column) dimension and the fixed image bins corresponds + * to the first (row) dimension. + * + */ + + // Pointer to affected bin to be updated + JointPDFValueType *pdfPtr = m_JointPDF->GetBufferPointer() + + ( (*fiter).FixedImageParzenWindowIndex * m_NumberOfHistogramBins ); + + // Move the pointer to the fist affected bin + int pdfMovingIndex = static_cast( movingImageParzenWindowIndex ) - 1; + pdfPtr += pdfMovingIndex; + + for (; pdfMovingIndex <= static_cast( movingImageParzenWindowIndex ) + + 2; + pdfMovingIndex++, pdfPtr++ ) { + // Update PDF for the current intensity pair + double movingImageParzenWindowArg = + static_cast( pdfMovingIndex ) - + static_cast(movingImageParzenWindowTerm); + + *(pdfPtr) += static_cast( + m_CubicBSplineKernel->Evaluate( movingImageParzenWindowArg ) ); + + if( this->m_UseExplicitPDFDerivatives ) { + // Compute the cubicBSplineDerivative for later repeated use. + double cubicBSplineDerivativeValue = + m_CubicBSplineDerivativeKernel->Evaluate( + movingImageParzenWindowArg ); + + // Compute PDF derivative contribution. + this->ComputePDFDerivatives( nFixedImageSamples, + pdfMovingIndex, + movingImageGradientValue, + cubicBSplineDerivativeValue ); + } + + } //end parzen windowing for loop + + } //end if-block check sampleOk + + ++nFixedImageSamples; + + } // end iterating over fixed image spatial sample container for loop + + itkDebugMacro( "Ratio of voxels mapping into moving image buffer: " + << nSamples << " / " << m_NumberOfSpatialSamples + << std::endl ); + + if( nSamples < m_NumberOfSpatialSamples / 16 ) { + itkExceptionMacro( "Too many samples map outside moving image buffer: " + << nSamples << " / " << m_NumberOfSpatialSamples + << std::endl ); + } + + this->m_NumberOfPixelsCounted = nSamples; + + /** + * Normalize the PDFs, compute moving image marginal PDF + * + */ + typedef ImageRegionIterator JointPDFIteratorType; + JointPDFIteratorType jointPDFIterator ( m_JointPDF, + m_JointPDF->GetBufferedRegion() ); + + jointPDFIterator.GoToBegin(); + + + // Compute joint PDF normalization factor + // (to ensure joint PDF sum adds to 1.0) + double jointPDFSum = 0.0; + + while( !jointPDFIterator.IsAtEnd() ) { + jointPDFSum += jointPDFIterator.Get(); + ++jointPDFIterator; + } + + if ( jointPDFSum == 0.0 ) { + itkExceptionMacro( "Joint PDF summed to zero" ); + } + + + // Normalize the PDF bins + jointPDFIterator.GoToEnd(); + while( !jointPDFIterator.IsAtBegin() ) { + --jointPDFIterator; + jointPDFIterator.Value() /= static_cast( jointPDFSum ); + } + + + // Normalize the fixed image marginal PDF + double fixedPDFSum = 0.0; + for( unsigned int bin = 0; bin < m_NumberOfHistogramBins; bin++ ) { + fixedPDFSum += m_FixedImageMarginalPDF[bin]; + } + + if ( fixedPDFSum == 0.0 ) { + itkExceptionMacro( "Fixed image marginal PDF summed to zero" ); + } + + for( unsigned int bin=0; bin < m_NumberOfHistogramBins; bin++ ) { + m_FixedImageMarginalPDF[bin] /= static_cast( fixedPDFSum ); + } + + + // Compute moving image marginal PDF by summing over fixed image bins. + typedef ImageLinearIteratorWithIndex JointPDFLinearIterator; + JointPDFLinearIterator linearIter( + m_JointPDF, m_JointPDF->GetBufferedRegion() ); + + linearIter.SetDirection( 1 ); + linearIter.GoToBegin(); + unsigned int movingIndex1 = 0; + + while( !linearIter.IsAtEnd() ) { + + double sum = 0.0; + + while( !linearIter.IsAtEndOfLine() ) { + sum += linearIter.Get(); + ++linearIter; + } + + m_MovingImageMarginalPDF[movingIndex1] = static_cast(sum); + + linearIter.NextLine(); + ++movingIndex1; + + } + + double nFactor = 1.0 / ( m_MovingImageBinSize + * static_cast( nSamples ) ); + + if( this->m_UseExplicitPDFDerivatives ) { + // Normalize the joint PDF derivatives by the test image binsize and nSamples + typedef ImageRegionIterator + JointPDFDerivativesIteratorType; + JointPDFDerivativesIteratorType jointPDFDerivativesIterator ( + m_JointPDFDerivatives, + m_JointPDFDerivatives->GetBufferedRegion() + ); + + jointPDFDerivativesIterator.GoToBegin(); + + while( !jointPDFDerivativesIterator.IsAtEnd() ) { + jointPDFDerivativesIterator.Value() *= nFactor; + ++jointPDFDerivativesIterator; + } + } + + /** + * Compute the metric by double summation over histogram. + */ + + // Setup pointer to point to the first bin + JointPDFValueType * jointPDFPtr = m_JointPDF->GetBufferPointer(); + + // Initialize sum to zero + double sum = 0.0; + + for( unsigned int fixedIndex = 0; + fixedIndex < m_NumberOfHistogramBins; + ++fixedIndex ) { + double fixedImagePDFValue = m_FixedImageMarginalPDF[fixedIndex]; + + for( unsigned int movingIndex = 0; movingIndex < m_NumberOfHistogramBins; + ++movingIndex, jointPDFPtr++ ) { + double movingImagePDFValue = m_MovingImageMarginalPDF[movingIndex]; + double jointPDFValue = *(jointPDFPtr); + + // check for non-zero bin contribution + if( jointPDFValue > 1e-16 && movingImagePDFValue > 1e-16 ) { + + double pRatio = vcl_log(jointPDFValue / movingImagePDFValue ); + + if( fixedImagePDFValue > 1e-16) { + sum += jointPDFValue * ( pRatio - vcl_log(fixedImagePDFValue ) ); + } + + if( this->m_UseExplicitPDFDerivatives ) { + // move joint pdf derivative pointer to the right position + JointPDFValueType * derivPtr = m_JointPDFDerivatives->GetBufferPointer() + + ( fixedIndex * m_JointPDFDerivatives->GetOffsetTable()[2] ) + + ( movingIndex * m_JointPDFDerivatives->GetOffsetTable()[1] ); + + for( unsigned int parameter=0; parameter < m_NumberOfParameters; ++parameter, derivPtr++ ) { + // Ref: eqn 23 of Thevenaz & Unser paper [3] + derivative[parameter] -= (*derivPtr) * pRatio; + } // end for-loop over parameters + } else { + this->m_PRatioArray[fixedIndex][movingIndex] = pRatio * nFactor; + } + } // end if-block to check non-zero bin contribution + } // end for-loop over moving index + } // end for-loop over fixed index + + if( !(this->m_UseExplicitPDFDerivatives ) ) { + // Second pass: This one is done for accumulating the contributions + // to the derivative array. + + nFixedImageSamples = 0; + + for ( fiter = m_FixedImageSamples.begin(); fiter != fend; ++fiter ) { + + // Get moving image value + MovingImagePointType mappedPoint; + bool sampleOk; + double movingImageValue; + + this->TransformPoint( nFixedImageSamples, parameters, mappedPoint, + sampleOk, movingImageValue ); + + if( sampleOk ) { + // Get moving image derivative at the mapped position + ImageDerivativesType movingImageGradientValue; + this->ComputeImageDerivatives( mappedPoint, movingImageGradientValue ); + + + /** + * Compute this sample's contribution to the marginal + * and joint distributions. + * + */ + + // Determine parzen window arguments (see eqn 6 of Mattes paper [2]). + double movingImageParzenWindowTerm = + movingImageValue / m_MovingImageBinSize - m_MovingImageNormalizedMin; + unsigned int movingImageParzenWindowIndex = + static_cast( vcl_floor(movingImageParzenWindowTerm ) ); + + // Make sure the extreme values are in valid bins + if ( movingImageParzenWindowIndex < 2 ) { + movingImageParzenWindowIndex = 2; + } else if ( movingImageParzenWindowIndex > (m_NumberOfHistogramBins - 3) ) { + movingImageParzenWindowIndex = m_NumberOfHistogramBins - 3; + } + + + // Move the pointer to the fist affected bin + int pdfMovingIndex = static_cast( movingImageParzenWindowIndex ) - 1; + + for (; pdfMovingIndex <= static_cast( movingImageParzenWindowIndex ) + + 2; + pdfMovingIndex++ ) { + + // Update PDF for the current intensity pair + double movingImageParzenWindowArg = + static_cast( pdfMovingIndex ) - + static_cast(movingImageParzenWindowTerm); + + // Compute the cubicBSplineDerivative for later repeated use. + double cubicBSplineDerivativeValue = + m_CubicBSplineDerivativeKernel->Evaluate( + movingImageParzenWindowArg ); + + // Compute PDF derivative contribution. + this->ComputePDFDerivatives( nFixedImageSamples, + pdfMovingIndex, + movingImageGradientValue, + cubicBSplineDerivativeValue ); + + + } //end parzen windowing for loop + + } //end if-block check sampleOk + + ++nFixedImageSamples; + + } // end iterating over fixed image spatial sample container for loop + + + derivative = this->m_MetricDerivative; + } + + value = static_cast( -1.0 * sum ); +} + + +/** + * Get the match measure derivative + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::GetDerivative( const ParametersType& parameters, + DerivativeType & derivative ) const +{ + MeasureType value; + // call the combined version + this->GetValueAndDerivative( parameters, value, derivative ); +} + + +/** + * Compute image derivatives using a central difference function + * if we are not using a BSplineInterpolator, which includes + * derivatives. + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::ComputeImageDerivatives( + const MovingImagePointType& mappedPoint, + ImageDerivativesType& gradient ) const +{ + + if( m_InterpolatorIsBSpline ) { + // Computed moving image gradient using derivative BSpline kernel. + gradient = m_BSplineInterpolator->EvaluateDerivative( mappedPoint ); + } else { + // For all generic interpolator use central differencing. + gradient = m_DerivativeCalculator->Evaluate( mappedPoint ); + } + +} + + +/** + * Transform a point from FixedImage domain to MovingImage domain. + * This function also checks if mapped point is within support region. + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::TransformPoint( + unsigned int sampleNumber, + const ParametersType& parameters, + MovingImagePointType& mappedPoint, + bool& sampleOk, + double& movingImageValue ) const +{ + + if ( !m_TransformIsBSpline ) { + // Use generic transform to compute mapped position + mappedPoint = this->m_Transform->TransformPoint( + m_FixedImageSamples[sampleNumber].FixedImagePointValue ); + + // Check if mapped point inside image buffer + sampleOk = this->m_Interpolator->IsInsideBuffer( mappedPoint ); + } else { + + if( this->m_UseCachingOfBSplineWeights ) { + // If the transform is BSplineDeformable, we can use the precomputed + // weights and indices to obtained the mapped position + // + const WeightsValueType * weights = + m_BSplineTransformWeightsArray[sampleNumber]; + const IndexValueType * indices = + m_BSplineTransformIndicesArray[sampleNumber]; + mappedPoint.Fill( 0.0 ); + + if ( m_WithinSupportRegionArray[sampleNumber] ) { + for ( unsigned int k = 0; k < m_NumBSplineWeights; k++ ) { + for ( unsigned int j = 0; j < FixedImageDimension; j++ ) { + mappedPoint[j] += weights[k] * + parameters[ indices[k] + m_ParametersOffset[j] ]; + } + } + } + + for( unsigned int j = 0; j < FixedImageDimension; j++ ) { + mappedPoint[j] += m_PreTransformPointsArray[sampleNumber][j]; + } + + // Check if mapped point inside image buffer + sampleOk = this->m_Interpolator->IsInsideBuffer( mappedPoint ); + + // Check if mapped point is within the support region of a grid point. + // This is neccessary for computing the metric gradient + sampleOk = sampleOk && m_WithinSupportRegionArray[sampleNumber]; + } else { + // If not caching values, we invoke the Transform to recompute the + // mapping of the point. + this->m_BSplineTransform->TransformPoint( + this->m_FixedImageSamples[sampleNumber].FixedImagePointValue, + mappedPoint, this->m_Weights, this->m_Indices, sampleOk); + + // Check if mapped point inside image buffer + sampleOk = sampleOk && this->m_Interpolator->IsInsideBuffer( mappedPoint ); + } + + } + + // If user provided a mask over the Moving image + if ( this->m_MovingImageMask ) { + // Check if mapped point is within the support region of the moving image + // mask + sampleOk = sampleOk && this->m_MovingImageMask->IsInside( mappedPoint ); + } + + + if ( sampleOk ) { + movingImageValue = this->m_Interpolator->Evaluate( mappedPoint ); + + if ( movingImageValue < m_MovingImageTrueMin || + movingImageValue > m_MovingImageTrueMax ) { + // need to throw out this sample as it will not fall into a valid bin + sampleOk = false; + } + } +} + + +/** + * Compute PDF derivatives contribution for each parameter + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::ComputePDFDerivatives( + unsigned int sampleNumber, + int pdfMovingIndex, + const ImageDerivativesType& movingImageGradientValue, + double cubicBSplineDerivativeValue ) const +{ + + const int pdfFixedIndex = + m_FixedImageSamples[sampleNumber].FixedImageParzenWindowIndex; + + JointPDFValueType * derivPtr = NULL; + double precomputedWeight = 0.0; + + if( this->m_UseExplicitPDFDerivatives ) { + // Update bins in the PDF derivatives for the current intensity pair + derivPtr = m_JointPDFDerivatives->GetBufferPointer() + + ( pdfFixedIndex * m_JointPDFDerivatives->GetOffsetTable()[2] ) + + ( pdfMovingIndex * m_JointPDFDerivatives->GetOffsetTable()[1] ); + } else { + // Recover the precomputed weight for this specific PDF bin + precomputedWeight = this->m_PRatioArray[pdfFixedIndex][pdfMovingIndex]; + } + + if( !m_TransformIsBSpline ) { + + /** + * Generic version which works for all transforms. + */ + + // Compute the transform Jacobian. + typedef typename TransformType::JacobianType JacobianType; + const JacobianType& jacobian = + this->m_Transform->GetJacobian( + m_FixedImageSamples[sampleNumber].FixedImagePointValue ); + + for ( unsigned int mu = 0; mu < m_NumberOfParameters; mu++ ) { + double innerProduct = 0.0; + for ( unsigned int dim = 0; dim < FixedImageDimension; dim++ ) { + innerProduct += jacobian[dim][mu] * movingImageGradientValue[dim]; + } + + const double derivativeContribution = innerProduct * cubicBSplineDerivativeValue; + + if( this->m_UseExplicitPDFDerivatives ) { + *(derivPtr) -= derivativeContribution; + ++derivPtr; + } else { + this->m_MetricDerivative[mu] += precomputedWeight * derivativeContribution; + } + } + + } else { + const WeightsValueType * weights = NULL; + const IndexValueType * indices = NULL; + + if( this->m_UseCachingOfBSplineWeights ) { + /** + * If the transform is of type BSplineDeformableTransform, + * we can obtain a speed up by only processing the affected parameters. + * Note that these pointers are just pointing to pre-allocated rows + * of the caching arrays. There is therefore, no need to free this + * memory. + */ + weights = m_BSplineTransformWeightsArray[sampleNumber]; + indices = m_BSplineTransformIndicesArray[sampleNumber]; + } else { + m_BSplineTransform->GetJacobian( + m_FixedImageSamples[sampleNumber].FixedImagePointValue, m_Weights, m_Indices ); + } + + for( unsigned int dim = 0; dim < FixedImageDimension; dim++ ) { + + double innerProduct; + int parameterIndex; + + for( unsigned int mu = 0; mu < m_NumBSplineWeights; mu++ ) { + + /* The array weights contains the Jacobian values in a 1-D array + * (because for each parameter the Jacobian is non-zero in only 1 of the + * possible dimensions) which is multiplied by the moving image + * gradient. */ + if( this->m_UseCachingOfBSplineWeights ) { + innerProduct = movingImageGradientValue[dim] * weights[mu]; + parameterIndex = indices[mu] + m_ParametersOffset[dim]; + } else { + innerProduct = movingImageGradientValue[dim] * this->m_Weights[mu]; + parameterIndex = this->m_Indices[mu] + this->m_ParametersOffset[dim]; + } + + const double derivativeContribution = innerProduct * cubicBSplineDerivativeValue; + + if( this->m_UseExplicitPDFDerivatives ) { + JointPDFValueType * ptr = derivPtr + parameterIndex; + *(ptr) -= derivativeContribution; + } else { + this->m_MetricDerivative[parameterIndex] += precomputedWeight * derivativeContribution; + } + + } //end mu for loop + } //end dim for loop + + } // end if-block transform is BSpline + +} + + +// Method to reinitialize the seed of the random number generator +template < class TFixedImage, class TMovingImage > void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::ReinitializeSeed() +{ + Statistics::MersenneTwisterRandomVariateGenerator::GetInstance()->SetSeed(); +} + +// Method to reinitialize the seed of the random number generator +template < class TFixedImage, class TMovingImage > void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::ReinitializeSeed(int seed) +{ + Statistics::MersenneTwisterRandomVariateGenerator::GetInstance()->SetSeed( + seed); +} + + +/** + * Cache pre-transformed points, weights and indices. + * This method is only called if the flag UseCachingOfBSplineWeights is ON. + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::PreComputeTransformValues() +{ + // Create all zero dummy transform parameters + ParametersType dummyParameters( this->m_Transform->GetNumberOfParameters() ); + dummyParameters.Fill( 0.0 ); + this->m_Transform->SetParameters( dummyParameters ); + + // Cycle through each sampled fixed image point + BSplineTransformWeightsType weights( m_NumBSplineWeights ); + BSplineTransformIndexArrayType indices( m_NumBSplineWeights ); + bool valid; + MovingImagePointType mappedPoint; + + // Declare iterators for iteration over the sample container + typename FixedImageSpatialSampleContainer::const_iterator fiter; + typename FixedImageSpatialSampleContainer::const_iterator fend = + m_FixedImageSamples.end(); + unsigned long counter = 0; + + for( fiter = m_FixedImageSamples.begin(); fiter != fend; ++fiter, counter++ ) { + m_BSplineTransform->TransformPoint( + m_FixedImageSamples[counter].FixedImagePointValue, + mappedPoint, weights, indices, valid ); + + for( unsigned long k = 0; k < m_NumBSplineWeights; k++ ) { + m_BSplineTransformWeightsArray[counter][k] = weights[k]; + m_BSplineTransformIndicesArray[counter][k] = indices[k]; + } + + m_PreTransformPointsArray[counter] = mappedPoint; + m_WithinSupportRegionArray[counter] = valid; + + } + +} + + +} // end namespace itk + + +#endif + +#endif diff --git a/registration/itkMeanSquaresImageToImageMetricFor3DBLUTFFD.h b/registration/itkMeanSquaresImageToImageMetricFor3DBLUTFFD.h new file mode 100644 index 0000000..17b6379 --- /dev/null +++ b/registration/itkMeanSquaresImageToImageMetricFor3DBLUTFFD.h @@ -0,0 +1,151 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkMeanSquaresImageToImageMetricFor3DBLUTFFD.h,v $ + Language: C++ + Date: $Date: 2010/06/14 17:32:07 $ + Version: $Revision: 1.1 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + 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. + +=========================================================================*/ +#ifndef __itkMeanSquaresImageToImageMetricFor3DBLUTFFD_h +#define __itkMeanSquaresImageToImageMetricFor3DBLUTFFD_h + +// First make sure that the configuration is available. +// This line can be removed once the optimized versions +// gets integrated into the main directories. +#include "itkConfigure.h" + +#ifdef ITK_USE_OPTIMIZED_REGISTRATION_METHODS +#include "itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD.h" +#else + +#include "itkImageToImageMetric.h" +#include "itkCovariantVector.h" +#include "itkPoint.h" + + +namespace itk +{ +/** \class MeanSquaresImageToImageMetricFor3DBLUTFFD + * \brief Computes similarity between two objects to be registered + * + * This Class is templated over the type of the fixed and moving + * images to be compared. + * + * This metric computes the sum of squared differences between pixels in + * the moving image and pixels in the fixed image. The spatial correspondance + * between both images is established through a Transform. Pixel values are + * taken from the Moving image. Their positions are mapped to the Fixed image + * and result in general in non-grid position on it. Values at these non-grid + * position of the Fixed image are interpolated using a user-selected Interpolator. + * + * \ingroup RegistrationMetrics + */ +template < class TFixedImage, class TMovingImage > +class ITK_EXPORT MeanSquaresImageToImageMetricFor3DBLUTFFD : + public ImageToImageMetric< TFixedImage, TMovingImage> +{ +public: + + /** Standard class typedefs. */ + typedef MeanSquaresImageToImageMetricFor3DBLUTFFD Self; + typedef ImageToImageMetric Superclass; + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(MeanSquaresImageToImageMetricFor3DBLUTFFD, ImageToImageMetric); + + + /** Types transferred from the base class */ + typedef typename Superclass::RealType RealType; + typedef typename Superclass::TransformType TransformType; + typedef typename Superclass::TransformPointer TransformPointer; + typedef typename Superclass::TransformParametersType TransformParametersType; + typedef typename Superclass::TransformJacobianType TransformJacobianType; + typedef typename Superclass::GradientPixelType GradientPixelType; + typedef typename Superclass::GradientImageType GradientImageType; + typedef typename Superclass::InputPointType InputPointType; + typedef typename Superclass::OutputPointType OutputPointType; + + typedef typename Superclass::MeasureType MeasureType; + typedef typename Superclass::DerivativeType DerivativeType; + typedef typename Superclass::FixedImageType FixedImageType; + typedef typename Superclass::MovingImageType MovingImageType; + typedef typename Superclass::FixedImageConstPointer FixedImageConstPointer; + typedef typename Superclass::MovingImageConstPointer MovingImageConstPointer; + + + /** Get the derivatives of the match measure. */ + void GetDerivative( const TransformParametersType & parameters, + DerivativeType & derivative ) const; + + /** Get the value for single valued optimizers. */ + MeasureType GetValue( const TransformParametersType & parameters ) const; + + /** Get value and derivatives for multiple valued optimizers. */ + void GetValueAndDerivative( const TransformParametersType & parameters, + MeasureType& Value, DerivativeType& Derivative ) const; + +#ifdef ITK_USE_CONCEPT_CHECKING + /** Begin concept checking */ + itkConceptMacro(MovingPixelTypeHasNumericTraitsCheck, + (Concept::HasNumericTraits)); + itkConceptMacro(MovingRealTypeAdditivieOperatorsCheck, + (Concept::AdditiveOperators< + typename NumericTraits::RealType>)); + itkConceptMacro(MovingRealTypeMultiplyOperatorCheck, + (Concept::MultiplyOperator< + typename NumericTraits::RealType>)); + + /** End concept checking */ +#endif + +protected: + MeanSquaresImageToImageMetricFor3DBLUTFFD(); + virtual ~MeanSquaresImageToImageMetricFor3DBLUTFFD() {}; + +private: + MeanSquaresImageToImageMetricFor3DBLUTFFD(const Self&); //purposely not implemented + void operator=(const Self&); //purposely not implemented + +}; + +} // end namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkMeanSquaresImageToImageMetricFor3DBLUTFFD.txx" +#endif + +#endif + +#endif + diff --git a/registration/itkMeanSquaresImageToImageMetricFor3DBLUTFFD.txx b/registration/itkMeanSquaresImageToImageMetricFor3DBLUTFFD.txx new file mode 100644 index 0000000..c9d7861 --- /dev/null +++ b/registration/itkMeanSquaresImageToImageMetricFor3DBLUTFFD.txx @@ -0,0 +1,370 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkMeanSquaresImageToImageMetricFor3DBLUTFFD.txx,v $ + Language: C++ + Date: $Date: 2010/06/14 17:32:07 $ + Version: $Revision: 1.1 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + 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. + +=========================================================================*/ +#ifndef __itkMeanSquaresImageToImageMetricFor3DBLUTFFD_txx +#define __itkMeanSquaresImageToImageMetricFor3DBLUTFFD_txx + +// First make sure that the configuration is available. +// This line can be removed once the optimized versions +// gets integrated into the main directories. +#include "itkConfigure.h" + +#ifdef ITK_USE_OPTIMIZED_REGISTRATION_METHODS +#include "itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD.txx" +#else + +#include "itkMeanSquaresImageToImageMetricFor3DBLUTFFD.h" +#include "itkImageRegionConstIteratorWithIndex.h" + +namespace itk +{ + +/** + * Constructor + */ +template +MeanSquaresImageToImageMetricFor3DBLUTFFD +::MeanSquaresImageToImageMetricFor3DBLUTFFD() +{ + itkDebugMacro("Constructor"); +} + +/** + * Get the match Measure + */ +template +typename MeanSquaresImageToImageMetricFor3DBLUTFFD::MeasureType +MeanSquaresImageToImageMetricFor3DBLUTFFD +::GetValue( const TransformParametersType & parameters ) const +{ + + itkDebugMacro("GetValue( " << parameters << " ) "); + + FixedImageConstPointer fixedImage = this->m_FixedImage; + + if( !fixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + typedef itk::ImageRegionConstIteratorWithIndex FixedIteratorType; + + + FixedIteratorType ti( fixedImage, this->GetFixedImageRegion() ); + + typename FixedImageType::IndexType index; + + MeasureType measure = NumericTraits< MeasureType >::Zero; + + this->m_NumberOfPixelsCounted = 0; + + this->SetTransformParameters( parameters ); + + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + const RealType fixedValue = ti.Get(); + this->m_NumberOfPixelsCounted++; + const RealType diff = movingValue - fixedValue; + measure += diff * diff; + } + + ++ti; + } + + if( !this->m_NumberOfPixelsCounted ) { + itkExceptionMacro(<<"All the points mapped to outside of the moving image"); + } else { + measure /= this->m_NumberOfPixelsCounted; + } + + return measure; + +} + +/** + * Get the Derivative Measure + */ +template < class TFixedImage, class TMovingImage> +void +MeanSquaresImageToImageMetricFor3DBLUTFFD +::GetDerivative( const TransformParametersType & parameters, + DerivativeType & derivative ) const +{ + + itkDebugMacro("GetDerivative( " << parameters << " ) "); + + if( !this->GetGradientImage() ) { + itkExceptionMacro(<<"The gradient image is null, maybe you forgot to call Initialize()"); + } + + FixedImageConstPointer fixedImage = this->m_FixedImage; + + if( !fixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + const unsigned int ImageDimension = FixedImageType::ImageDimension; + + + typedef itk::ImageRegionConstIteratorWithIndex< + FixedImageType> FixedIteratorType; + + typedef itk::ImageRegionConstIteratorWithIndex GradientIteratorType; + + + FixedIteratorType ti( fixedImage, this->GetFixedImageRegion() ); + + typename FixedImageType::IndexType index; + + this->m_NumberOfPixelsCounted = 0; + + this->SetTransformParameters( parameters ); + + const unsigned int ParametersDimension = this->GetNumberOfParameters(); + derivative = DerivativeType( ParametersDimension ); + derivative.Fill( NumericTraits::Zero ); + + ti.GoToBegin(); + + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + + const TransformJacobianType & jacobian = + this->m_Transform->GetJacobian( inputPoint ); + + + const RealType fixedValue = ti.Value(); + this->m_NumberOfPixelsCounted++; + const RealType diff = movingValue - fixedValue; + + // Get the gradient by NearestNeighboorInterpolation: + // which is equivalent to round up the point components. + typedef typename OutputPointType::CoordRepType CoordRepType; + typedef ContinuousIndex + MovingImageContinuousIndexType; + + MovingImageContinuousIndexType tempIndex; + this->m_MovingImage->TransformPhysicalPointToContinuousIndex( transformedPoint, tempIndex ); + + typename MovingImageType::IndexType mappedIndex; + mappedIndex.CopyWithRound( tempIndex ); + + const GradientPixelType gradient = + this->GetGradientImage()->GetPixel( mappedIndex ); + + for(unsigned int par=0; par::Zero; + for(unsigned int dim=0; dimm_NumberOfPixelsCounted ) { + itkExceptionMacro(<<"All the points mapped to outside of the moving image"); + } else { + for(unsigned int i=0; im_NumberOfPixelsCounted; + } + } + +} + + +/* + * Get both the match Measure and theDerivative Measure + */ +template +void +MeanSquaresImageToImageMetricFor3DBLUTFFD +::GetValueAndDerivative(const TransformParametersType & parameters, + MeasureType & value, DerivativeType & derivative) const +{ + + itkDebugMacro("GetValueAndDerivative( " << parameters << " ) "); + + if( !this->GetGradientImage() ) { + itkExceptionMacro(<<"The gradient image is null, maybe you forgot to call Initialize()"); + } + + FixedImageConstPointer fixedImage = this->m_FixedImage; + + if( !fixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + const unsigned int ImageDimension = FixedImageType::ImageDimension; + + typedef itk::ImageRegionConstIteratorWithIndex< + FixedImageType> FixedIteratorType; + + typedef itk::ImageRegionConstIteratorWithIndex GradientIteratorType; + + + FixedIteratorType ti( fixedImage, this->GetFixedImageRegion() ); + + typename FixedImageType::IndexType index; + + MeasureType measure = NumericTraits< MeasureType >::Zero; + + this->m_NumberOfPixelsCounted = 0; + + this->SetTransformParameters( parameters ); + + const unsigned int ParametersDimension = this->GetNumberOfParameters(); + derivative = DerivativeType( ParametersDimension ); + derivative.Fill( NumericTraits::Zero ); + + ti.GoToBegin(); + + while(!ti.IsAtEnd()) { + + index = ti.GetIndex(); + + InputPointType inputPoint; + fixedImage->TransformIndexToPhysicalPoint( index, inputPoint ); + + if( this->m_FixedImageMask && !this->m_FixedImageMask->IsInside( inputPoint ) ) { + ++ti; + continue; + } + + OutputPointType transformedPoint = this->m_Transform->TransformPoint( inputPoint ); + + if( this->m_MovingImageMask && !this->m_MovingImageMask->IsInside( transformedPoint ) ) { + ++ti; + continue; + } + + if( this->m_Interpolator->IsInsideBuffer( transformedPoint ) ) { + const RealType movingValue = this->m_Interpolator->Evaluate( transformedPoint ); + + const TransformJacobianType & jacobian = + this->m_Transform->GetJacobian( inputPoint ); + + + const RealType fixedValue = ti.Value(); + this->m_NumberOfPixelsCounted++; + + const RealType diff = movingValue - fixedValue; + + measure += diff * diff; + + // Get the gradient by NearestNeighboorInterpolation: + // which is equivalent to round up the point components. + typedef typename OutputPointType::CoordRepType CoordRepType; + typedef ContinuousIndex + MovingImageContinuousIndexType; + + MovingImageContinuousIndexType tempIndex; + this->m_MovingImage->TransformPhysicalPointToContinuousIndex( transformedPoint, tempIndex ); + + typename MovingImageType::IndexType mappedIndex; + mappedIndex.CopyWithRound( tempIndex ); + + const GradientPixelType gradient = + this->GetGradientImage()->GetPixel( mappedIndex ); + + for(unsigned int par=0; par::Zero; + for(unsigned int dim=0; dimm_NumberOfPixelsCounted ) { + itkExceptionMacro(<<"All the points mapped to outside of the moving image"); + } else { + for(unsigned int i=0; im_NumberOfPixelsCounted; + } + measure /= this->m_NumberOfPixelsCounted; + } + + value = measure; + +} + +} // end namespace itk + + +#endif + +#endif diff --git a/registration/itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.h b/registration/itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.h new file mode 100644 index 0000000..ea56932 --- /dev/null +++ b/registration/itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.h @@ -0,0 +1,362 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.h,v $ + Language: C++ + Date: $Date: 2010/06/14 17:32:07 $ + Version: $Revision: 1.1 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + 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. + +=========================================================================*/ +#ifndef __itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD_h +#define __itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD_h + +#include "itkOptImageToImageMetric.h" +#include "itkCovariantVector.h" +#include "itkPoint.h" +#include "itkIndex.h" +#include "itkBSplineKernelFunction.h" +#include "itkBSplineDerivativeKernelFunction.h" +#include "itkCentralDifferenceImageFunction.h" +#include "itkBSplineInterpolateImageFunction.h" +#include "itkBSplineDeformableTransform.h" +#include "itkArray2D.h" + +#include "itkMultiThreader.h" + +namespace itk +{ + +/** \class MattesMutualInformationImageToImageMetricFor3DBLUTFFD + * \brief Computes the mutual information between two images to be + * registered using the method of Mattes et al. + * + * MattesMutualInformationImageToImageMetricFor3DBLUTFFD computes the mutual + * information between a fixed and moving image to be registered. + * + * This class is templated over the FixedImage type and the MovingImage + * type. + * + * The fixed and moving images are set via methods SetFixedImage() and + * SetMovingImage(). This metric makes use of user specified Transform and + * Interpolator. The Transform is used to map points from the fixed image to + * the moving image domain. The Interpolator is used to evaluate the image + * intensity at user specified geometric points in the moving image. + * The Transform and Interpolator are set via methods SetTransform() and + * SetInterpolator(). + * + * If a BSplineInterpolationFunction is used, this class obtain + * image derivatives from the BSpline interpolator. Otherwise, + * image derivatives are computed using central differencing. + * + * \warning This metric assumes that the moving image has already been + * connected to the interpolator outside of this class. + * + * The method GetValue() computes of the mutual information + * while method GetValueAndDerivative() computes + * both the mutual information and its derivatives with respect to the + * transform parameters. + * + * The calculations are based on the method of Mattes et al [1,2] + * where the probability density distribution are estimated using + * Parzen histograms. Since the fixed image PDF does not contribute + * to the derivatives, it does not need to be smooth. Hence, + * a zero order (box car) BSpline kernel is used + * for the fixed image intensity PDF. On the other hand, to ensure + * smoothness a third order BSpline kernel is used for the + * moving image intensity PDF. + * + * On Initialize(), the FixedImage is uniformly sampled within + * the FixedImageRegion. The number of samples used can be set + * via SetNumberOfSpatialSamples(). Typically, the number of + * spatial samples used should increase with the image size. + * + * The option UseAllPixelOn() disables the random sampling and uses + * all the pixels of the FixedImageRegion in order to estimate the + * joint intensity PDF. + * + * During each call of GetValue(), GetDerivatives(), + * GetValueAndDerivatives(), marginal and joint intensity PDF's + * values are estimated at discrete position or bins. + * The number of bins used can be set via SetNumberOfHistogramBins(). + * To handle data with arbitray magnitude and dynamic range, + * the image intensity is scale such that any contribution to the + * histogram will fall into a valid bin. + * + * One the PDF's have been contructed, the mutual information + * is obtained by doubling summing over the discrete PDF values. + * + * + * Notes: + * 1. This class returns the negative mutual information value. + * 2. This class in not thread safe due the private data structures + * used to the store the sampled points and the marginal and joint pdfs. + * + * References: + * [1] "Nonrigid multimodality image registration" + * D. Mattes, D. R. Haynor, H. Vesselle, T. Lewellen and W. Eubank + * Medical Imaging 2001: Image Processing, 2001, pp. 1609-1620. + * [2] "PET-CT Image Registration in the Chest Using Free-form Deformations" + * D. Mattes, D. R. Haynor, H. Vesselle, T. Lewellen and W. Eubank + * IEEE Transactions in Medical Imaging. Vol.22, No.1, + January 2003. pp.120-128. + * [3] "Optimization of Mutual Information for MultiResolution Image + * Registration" + * P. Thevenaz and M. Unser + * IEEE Transactions in Image Processing, 9(12) December 2000. + * + * \ingroup RegistrationMetrics + */ +template +class ITK_EXPORT MattesMutualInformationImageToImageMetricFor3DBLUTFFD : + public ImageToImageMetric< TFixedImage, TMovingImage > +{ +public: + + /** Standard class typedefs. */ + typedef MattesMutualInformationImageToImageMetricFor3DBLUTFFD Self; + typedef ImageToImageMetric< TFixedImage, TMovingImage > Superclass; + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(MattesMutualInformationImageToImageMetricFor3DBLUTFFD, + ImageToImageMetric); + + /** Types inherited from Superclass. */ + typedef typename Superclass::TransformType TransformType; + typedef typename Superclass::TransformPointer TransformPointer; + typedef typename Superclass::TransformJacobianType TransformJacobianType; + typedef typename Superclass::InterpolatorType InterpolatorType; + typedef typename Superclass::MeasureType MeasureType; + typedef typename Superclass::DerivativeType DerivativeType; + typedef typename Superclass::ParametersType ParametersType; + typedef typename Superclass::FixedImageType FixedImageType; + typedef typename Superclass::MovingImageType MovingImageType; + typedef typename Superclass::MovingImagePointType MovingImagePointType; + typedef typename Superclass::FixedImageConstPointer FixedImageConstPointer; + typedef typename Superclass::MovingImageConstPointer MovingImageConstPointer; + typedef typename Superclass::BSplineTransformWeightsType + BSplineTransformWeightsType; + typedef typename Superclass::BSplineTransformIndexArrayType + BSplineTransformIndexArrayType; + + typedef typename Superclass::CoordinateRepresentationType + CoordinateRepresentationType; + typedef typename Superclass::FixedImageSampleContainer + FixedImageSampleContainer; + typedef typename Superclass::ImageDerivativesType ImageDerivativesType; + typedef typename Superclass::WeightsValueType WeightsValueType; + typedef typename Superclass::IndexValueType IndexValueType; + + /** The moving image dimension. */ + itkStaticConstMacro( MovingImageDimension, unsigned int, + MovingImageType::ImageDimension ); + + /** + * Initialize the Metric by + * (1) making sure that all the components are present and plugged + * together correctly, + * (2) uniformly select NumberOfSpatialSamples within + * the FixedImageRegion, and + * (3) allocate memory for pdf data structures. */ + virtual void Initialize(void) throw ( ExceptionObject ); + + /** Get the value. */ + MeasureType GetValue( const ParametersType & parameters ) const; + + /** Get the derivatives of the match measure. */ + void GetDerivative( const ParametersType & parameters, + DerivativeType & Derivative ) const; + + /** Get the value and derivatives for single valued optimizers. */ + void GetValueAndDerivative( const ParametersType & parameters, + MeasureType & Value, + DerivativeType & Derivative ) const; + + /** Number of bins to used in the histogram. Typical value is 50. */ + itkSetClampMacro( NumberOfHistogramBins, unsigned long, + 1, NumericTraits::max() ); + itkGetConstReferenceMacro( NumberOfHistogramBins, unsigned long); + + /** This variable selects the method to be used for computing the Metric + * derivatives with respect to the Transform parameters. Two modes of + * computation are available. The choice between one and the other is a + * trade-off between computation speed and memory allocations. The two modes + * are described in detail below: + * + * UseExplicitPDFDerivatives = True + * will compute the Metric derivative by first calculating the derivatives of + * each one of the Joint PDF bins with respect to each one of the Transform + * parameters and then accumulating these contributions in the final metric + * derivative array by using a bin-specific weight. The memory required for + * storing the intermediate derivatives is a 3D array of doubles with size + * equals to the product of (number of histogram bins)^2 times number of + * transform parameters. This method is well suited for Transform with a small + * number of parameters. + * + * UseExplicitPDFDerivatives = False will compute the Metric derivative by + * first computing the weights for each one of the Joint PDF bins and caching + * them into an array. Then it will revisit each one of the PDF bins for + * computing its weighted contribution to the full derivative array. In this + * method an extra 2D array is used for storing the weights of each one of + * the PDF bins. This is an array of doubles with size equals to (number of + * histogram bins)^2. This method is well suited for Transforms with a large + * number of parameters, such as, BSplineDeformableTransforms. */ + itkSetMacro(UseExplicitPDFDerivatives,bool); + itkGetConstReferenceMacro(UseExplicitPDFDerivatives,bool); + itkBooleanMacro(UseExplicitPDFDerivatives); + +protected: + + MattesMutualInformationImageToImageMetricFor3DBLUTFFD(); + virtual ~MattesMutualInformationImageToImageMetricFor3DBLUTFFD(); + void PrintSelf(std::ostream& os, Indent indent) const; + +private: + + //purposely not implemented + MattesMutualInformationImageToImageMetricFor3DBLUTFFD(const Self &); + //purposely not implemented + void operator=(const Self &); + + + /** The marginal PDFs are stored as std::vector. */ + typedef float PDFValueType; + typedef float * MarginalPDFType; + + mutable MarginalPDFType m_FixedImageMarginalPDF; + + /** The moving image marginal PDF. */ + mutable MarginalPDFType m_MovingImageMarginalPDF; + + /** Helper array for storing the values of the JointPDF ratios. */ + typedef double PRatioType; + typedef Array2D< PRatioType > PRatioArrayType; + mutable PRatioArrayType m_PRatioArray; + + /** Helper variable for accumulating the derivative of the metric. */ + mutable DerivativeType m_MetricDerivative; + mutable DerivativeType * m_ThreaderMetricDerivative; + + /** Typedef for the joint PDF and PDF derivatives are stored as ITK Images. */ + typedef Image JointPDFType; + typedef Image JointPDFDerivativesType; + typedef JointPDFType::IndexType JointPDFIndexType; + typedef JointPDFType::PixelType JointPDFValueType; + typedef JointPDFType::RegionType JointPDFRegionType; + typedef JointPDFType::SizeType JointPDFSizeType; + typedef JointPDFDerivativesType::IndexType JointPDFDerivativesIndexType; + typedef JointPDFDerivativesType::PixelType JointPDFDerivativesValueType; + typedef JointPDFDerivativesType::RegionType JointPDFDerivativesRegionType; + typedef JointPDFDerivativesType::SizeType JointPDFDerivativesSizeType; + + /** The joint PDF and PDF derivatives. */ + typename JointPDFType::Pointer m_JointPDF; + unsigned long m_JointPDFBufferSize; + typename JointPDFDerivativesType::Pointer m_JointPDFDerivatives; + unsigned long m_JointPDFDerivativesBufferSize; + + /** Variables to define the marginal and joint histograms. */ + unsigned long m_NumberOfHistogramBins; + double m_MovingImageNormalizedMin; + double m_FixedImageNormalizedMin; + double m_FixedImageTrueMin; + double m_FixedImageTrueMax; + double m_MovingImageTrueMin; + double m_MovingImageTrueMax; + double m_FixedImageBinSize; + double m_MovingImageBinSize; + + /** Typedefs for BSpline kernel and derivative functions. */ + typedef BSplineKernelFunction<3> CubicBSplineFunctionType; + typedef BSplineDerivativeKernelFunction<3> CubicBSplineDerivativeFunctionType; + + /** Cubic BSpline kernel for computing Parzen histograms. */ + typename CubicBSplineFunctionType::Pointer m_CubicBSplineKernel; + typename CubicBSplineDerivativeFunctionType::Pointer + m_CubicBSplineDerivativeKernel; + + /** Precompute fixed image parzen window indices. */ + virtual void ComputeFixedImageParzenWindowIndices( + FixedImageSampleContainer & samples ); + + /** Compute PDF derivative contribution for each parameter. */ + virtual void ComputePDFDerivatives( unsigned int threadID, + unsigned int sampleNumber, + int movingImageParzenWindowIndex, + const ImageDerivativesType + & movingImageGradientValue, + double cubicBSplineDerivativeValue + ) const; + + PDFValueType * m_ThreaderFixedImageMarginalPDF; + typename JointPDFType::Pointer * m_ThreaderJointPDF; + typename JointPDFDerivativesType::Pointer * m_ThreaderJointPDFDerivatives; + int * m_ThreaderJointPDFStartBin; + int * m_ThreaderJointPDFEndBin; + mutable double * m_ThreaderJointPDFSum; + mutable double m_JointPDFSum; + + bool m_UseExplicitPDFDerivatives; + mutable bool m_ImplicitDerivativesSecondPass; + + + virtual inline void GetValueThreadPreProcess( unsigned int threadID, + bool withinSampleThread ) const; + virtual inline bool GetValueThreadProcessSample( unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & mappedPoint, + double movingImageValue ) const; + virtual inline void GetValueThreadPostProcess( unsigned int threadID, + bool withinSampleThread ) const; + + virtual inline void GetValueAndDerivativeThreadPreProcess( + unsigned int threadID, + bool withinSampleThread ) const; + virtual inline bool GetValueAndDerivativeThreadProcessSample( unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & mappedPoint, + double movingImageValue, + const ImageDerivativesType & + movingImageGradientValue ) const; + virtual inline void GetValueAndDerivativeThreadPostProcess( + unsigned int threadID, + bool withinSampleThread ) const; + +}; + +} // end namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.txx" +#endif + +#endif diff --git a/registration/itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.txx b/registration/itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.txx new file mode 100644 index 0000000..6d890e3 --- /dev/null +++ b/registration/itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.txx @@ -0,0 +1,1268 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +/*========================================================================= + +Program: Insight Segmentation & Registration Toolkit +Module: $RCSfile: itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.txx,v $ +Language: C++ +Date: $Date: 2010/06/14 17:32:07 $ +Version: $Revision: 1.1 $ + +Copyright (c) Insight Software Consortium. All rights reserved. +See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + +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. + +=========================================================================*/ +#ifndef __itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD_txx +#define __itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD_txx + +#include "itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.h" +#include "itkCovariantVector.h" +#include "itkImageRandomConstIteratorWithIndex.h" +#include "itkImageRegionConstIterator.h" +#include "itkImageRegionIterator.h" +#include "itkImageIterator.h" +#include "vnl/vnl_math.h" +#include "itkStatisticsImageFilter.h" + +#include "vnl/vnl_vector.txx" +#include "vnl/vnl_c_vector.txx" + +namespace itk +{ + +/** + * Constructor + */ +template < class TFixedImage, class TMovingImage > +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::MattesMutualInformationImageToImageMetricFor3DBLUTFFD() +{ + m_NumberOfHistogramBins = 50; + + this->SetComputeGradient(false); // don't use the default gradient for now + + // Initialize PDFs to NULL + m_JointPDF = NULL; + m_JointPDFDerivatives = NULL; + + // Initialize memory + m_FixedImageMarginalPDF = NULL; + m_MovingImageMarginalPDF = NULL; + + m_MovingImageNormalizedMin = 0.0; + m_FixedImageNormalizedMin = 0.0; + m_MovingImageTrueMin = 0.0; + m_MovingImageTrueMax = 0.0; + m_FixedImageBinSize = 0.0; + m_MovingImageBinSize = 0.0; + + m_CubicBSplineDerivativeKernel = NULL; + + // For multi-threading the metric + m_ThreaderFixedImageMarginalPDF = NULL; + m_ThreaderJointPDF = NULL; + m_ThreaderJointPDFDerivatives = NULL; + m_ThreaderJointPDFStartBin = NULL; + m_ThreaderJointPDFEndBin = NULL; + m_ThreaderJointPDFSum = NULL; + this->m_WithinThreadPreProcess = true; + this->m_WithinThreadPostProcess = false; + + this->m_ThreaderMetricDerivative = NULL; + this->m_UseExplicitPDFDerivatives = true; + this->m_ImplicitDerivativesSecondPass = false; +} + +template < class TFixedImage, class TMovingImage > +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::~MattesMutualInformationImageToImageMetricFor3DBLUTFFD() +{ + + if(m_FixedImageMarginalPDF != NULL) { + delete [] m_FixedImageMarginalPDF; + } + m_FixedImageMarginalPDF = NULL; + if(m_MovingImageMarginalPDF != NULL) { + delete [] m_MovingImageMarginalPDF; + } + m_MovingImageMarginalPDF = NULL; + if(m_ThreaderJointPDF != NULL) { + delete [] m_ThreaderJointPDF; + } + m_ThreaderJointPDF = NULL; + + if(m_ThreaderJointPDFDerivatives != NULL) { + delete [] m_ThreaderJointPDFDerivatives; + } + m_ThreaderJointPDFDerivatives = NULL; + + if(m_ThreaderFixedImageMarginalPDF != NULL) { + delete [] m_ThreaderFixedImageMarginalPDF; + } + m_ThreaderFixedImageMarginalPDF = NULL; + + if(m_ThreaderJointPDFStartBin != NULL) { + delete [] m_ThreaderJointPDFStartBin; + } + m_ThreaderJointPDFStartBin = NULL; + + if(m_ThreaderJointPDFEndBin != NULL) { + delete [] m_ThreaderJointPDFEndBin; + } + m_ThreaderJointPDFEndBin = NULL; + + if(m_ThreaderJointPDFSum != NULL) { + delete [] m_ThreaderJointPDFSum; + } + m_ThreaderJointPDFSum = NULL; + + if( this->m_ThreaderMetricDerivative != NULL ) { + delete [] this->m_ThreaderMetricDerivative; + } + this->m_ThreaderMetricDerivative = NULL; +} + +/** + * Print out internal information about this class + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::PrintSelf(std::ostream& os, Indent indent) const +{ + + Superclass::PrintSelf(os, indent); + + os << indent << "NumberOfHistogramBins: "; + os << this->m_NumberOfHistogramBins << std::endl; + + // Debugging information + os << indent << "FixedImageNormalizedMin: "; + os << this->m_FixedImageNormalizedMin << std::endl; + os << indent << "MovingImageNormalizedMin: "; + os << this->m_MovingImageNormalizedMin << std::endl; + os << indent << "MovingImageTrueMin: "; + os << this->m_MovingImageTrueMin << std::endl; + os << indent << "MovingImageTrueMax: "; + os << this->m_MovingImageTrueMax << std::endl; + os << indent << "FixedImageBinSize: "; + os << this->m_FixedImageBinSize << std::endl; + os << indent << "MovingImageBinSize: "; + os << this->m_MovingImageBinSize << std::endl; + os << indent << "UseExplicitPDFDerivatives: "; + os << this->m_UseExplicitPDFDerivatives << std::endl; + os << indent << "ImplicitDerivativesSecondPass: "; + os << this->m_ImplicitDerivativesSecondPass << std::endl; + +} + + +/** + * Initialize + */ +template +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::Initialize(void) throw ( ExceptionObject ) +{ + this->Superclass::Initialize(); + this->Superclass::MultiThreadingInitialize(); + + typedef StatisticsImageFilter FixedImageStatisticsFilterType; + typename FixedImageStatisticsFilterType::Pointer fixedImageStats = + FixedImageStatisticsFilterType::New(); + fixedImageStats->SetInput( this->m_FixedImage ); + fixedImageStats->SetNumberOfThreads( this->m_NumberOfThreads ); + fixedImageStats->Update(); + + m_FixedImageTrueMin = fixedImageStats->GetMinimum(); + m_FixedImageTrueMax = fixedImageStats->GetMaximum(); + double fixedImageMin = m_FixedImageTrueMin; + double fixedImageMax = m_FixedImageTrueMax; + + typedef StatisticsImageFilter + MovingImageStatisticsFilterType; + typename MovingImageStatisticsFilterType::Pointer movingImageStats = + MovingImageStatisticsFilterType::New(); + movingImageStats->SetInput( this->m_MovingImage ); + movingImageStats->SetNumberOfThreads( this->m_NumberOfThreads ); + movingImageStats->Update(); + + m_MovingImageTrueMin = movingImageStats->GetMinimum(); + m_MovingImageTrueMax = movingImageStats->GetMaximum(); + double movingImageMin = m_MovingImageTrueMin; + double movingImageMax = m_MovingImageTrueMax; + + itkDebugMacro( " FixedImageMin: " << fixedImageMin << + " FixedImageMax: " << fixedImageMax << std::endl ); + itkDebugMacro( " MovingImageMin: " << movingImageMin << + " MovingImageMax: " << movingImageMax << std::endl ); + + + /** + * Compute binsize for the histograms. + * + * The binsize for the image intensities needs to be adjusted so that + * we can avoid dealing with boundary conditions using the cubic + * spline as the Parzen window. We do this by increasing the size + * of the bins so that the joint histogram becomes "padded" at the + * borders. Because we are changing the binsize, + * we also need to shift the minimum by the padded amount in order to + * avoid minimum values filling in our padded region. + * + * Note that there can still be non-zero bin values in the padded region, + * it's just that these bins will never be a central bin for the Parzen + * window. + * + */ + const int padding = 2; // this will pad by 2 bins + + m_FixedImageBinSize = ( fixedImageMax - fixedImageMin ) + / static_cast( m_NumberOfHistogramBins + - 2 * padding ); + m_FixedImageNormalizedMin = fixedImageMin / m_FixedImageBinSize + - static_cast( padding ); + + m_MovingImageBinSize = ( movingImageMax - movingImageMin ) + / static_cast( m_NumberOfHistogramBins + - 2 * padding ); + m_MovingImageNormalizedMin = movingImageMin / m_MovingImageBinSize + - static_cast( padding ); + + itkDebugMacro( "FixedImageNormalizedMin: " << m_FixedImageNormalizedMin ); + itkDebugMacro( "MovingImageNormalizedMin: " << m_MovingImageNormalizedMin ); + itkDebugMacro( "FixedImageBinSize: " << m_FixedImageBinSize ); + itkDebugMacro( "MovingImageBinSize; " << m_MovingImageBinSize ); + + + /** + * Allocate memory for the marginal PDF and initialize values + * to zero. The marginal PDFs are stored as std::vector. + */ + if(m_FixedImageMarginalPDF != NULL) { + delete [] m_FixedImageMarginalPDF; + } + m_FixedImageMarginalPDF = new PDFValueType[m_NumberOfHistogramBins]; + if(m_MovingImageMarginalPDF != NULL) { + delete [] m_MovingImageMarginalPDF; + } + m_MovingImageMarginalPDF = new PDFValueType[m_NumberOfHistogramBins]; + + /** + * Allocate memory for the joint PDF and joint PDF derivatives. + * The joint PDF and joint PDF derivatives are store as itk::Image. + */ + m_JointPDF = JointPDFType::New(); + m_JointPDFDerivatives = JointPDFDerivativesType::New(); + + // Instantiate a region, index, size + JointPDFRegionType jointPDFRegion; + JointPDFIndexType jointPDFIndex; + JointPDFSizeType jointPDFSize; + + // Deallocate the memory that may have been allocated for + // previous runs of the metric. + this->m_JointPDFDerivatives = NULL; // by destroying the dynamic array + this->m_PRatioArray.SetSize( 1, 1 ); // and by allocating very small the static ones + this->m_MetricDerivative = DerivativeType( 1 ); + + JointPDFDerivativesRegionType jointPDFDerivativesRegion; + + // + // Now allocate memory according to the user-selected method. + // + if( this->m_UseExplicitPDFDerivatives ) { + this->m_JointPDFDerivatives = JointPDFDerivativesType::New(); + + JointPDFDerivativesIndexType jointPDFDerivativesIndex; + JointPDFDerivativesSizeType jointPDFDerivativesSize; + + // For the derivatives of the joint PDF define a region starting from {0,0,0} + // with size {m_NumberOfParameters,m_NumberOfHistogramBins, + // m_NumberOfHistogramBins}. The dimension represents transform parameters, + // fixed image parzen window index and moving image parzen window index, + // respectively. + jointPDFDerivativesIndex.Fill( 0 ); + jointPDFDerivativesSize[0] = this->m_NumberOfParameters; + jointPDFDerivativesSize[1] = this->m_NumberOfHistogramBins; + jointPDFDerivativesSize[2] = this->m_NumberOfHistogramBins; + + jointPDFDerivativesRegion.SetIndex( jointPDFDerivativesIndex ); + jointPDFDerivativesRegion.SetSize( jointPDFDerivativesSize ); + + // Set the regions and allocate + m_JointPDFDerivatives->SetRegions( jointPDFDerivativesRegion ); + m_JointPDFDerivatives->Allocate(); + m_JointPDFDerivativesBufferSize = jointPDFDerivativesSize[0] + * jointPDFDerivativesSize[1] + * jointPDFDerivativesSize[2] + * sizeof(JointPDFDerivativesValueType); + + } else { + /** Allocate memory for helper array that will contain the pRatios + * for each bin of the joint histogram. This is part of the effort + * for flattening the computation of the PDF Jacobians. + */ + this->m_PRatioArray.SetSize( this->m_NumberOfHistogramBins, this->m_NumberOfHistogramBins ); + this->m_MetricDerivative = DerivativeType( this->GetNumberOfParameters() ); + } + + // For the joint PDF define a region starting from {0,0} + // with size {m_NumberOfHistogramBins, m_NumberOfHistogramBins}. + // The dimension represents fixed image parzen window index + // and moving image parzen window index, respectively. + jointPDFIndex.Fill( 0 ); + jointPDFSize.Fill( m_NumberOfHistogramBins ); + + jointPDFRegion.SetIndex( jointPDFIndex ); + jointPDFRegion.SetSize( jointPDFSize ); + + // Set the regions and allocate + m_JointPDF->SetRegions( jointPDFRegion ); + m_JointPDF->Allocate(); + + m_JointPDFBufferSize = jointPDFSize[0] * jointPDFSize[1] * sizeof(PDFValueType); + + + /** + * Setup the kernels used for the Parzen windows. + */ + m_CubicBSplineKernel = CubicBSplineFunctionType::New(); + m_CubicBSplineDerivativeKernel = CubicBSplineDerivativeFunctionType::New(); + + /** + * Pre-compute the fixed image parzen window index for + * each point of the fixed image sample points list. + */ + this->ComputeFixedImageParzenWindowIndices( this->m_FixedImageSamples ); + + + if(m_ThreaderFixedImageMarginalPDF != NULL) { + delete [] m_ThreaderFixedImageMarginalPDF; + } + // Assumes number of threads doesn't change between calls to Initialize + m_ThreaderFixedImageMarginalPDF = new + PDFValueType[(this->m_NumberOfThreads-1) + * m_NumberOfHistogramBins]; + + if(m_ThreaderJointPDF != NULL) { + delete [] m_ThreaderJointPDF; + } + m_ThreaderJointPDF = new typename + JointPDFType::Pointer[this->m_NumberOfThreads-1]; + + if(m_ThreaderJointPDFStartBin != NULL) { + delete [] m_ThreaderJointPDFStartBin; + } + m_ThreaderJointPDFStartBin = new int[this->m_NumberOfThreads]; + + if(m_ThreaderJointPDFEndBin != NULL) { + delete [] m_ThreaderJointPDFEndBin; + } + m_ThreaderJointPDFEndBin = new int[this->m_NumberOfThreads]; + + if(m_ThreaderJointPDFSum != NULL) { + delete [] m_ThreaderJointPDFSum; + } + m_ThreaderJointPDFSum = new double[this->m_NumberOfThreads]; + + unsigned int threadID; + + int binRange = m_NumberOfHistogramBins / this->m_NumberOfThreads; + + for(threadID = 0; threadID < this->m_NumberOfThreads-1; threadID++) { + m_ThreaderJointPDF[threadID] = JointPDFType::New(); + m_ThreaderJointPDF[threadID]->SetRegions( jointPDFRegion ); + m_ThreaderJointPDF[threadID]->Allocate(); + + m_ThreaderJointPDFStartBin[threadID] = threadID * binRange; + m_ThreaderJointPDFEndBin[threadID] = (threadID + 1) * binRange - 1; + } + + m_ThreaderJointPDFStartBin[this->m_NumberOfThreads-1] = + (this->m_NumberOfThreads - 1 ) * binRange; + + m_ThreaderJointPDFEndBin[this->m_NumberOfThreads-1] = m_NumberOfHistogramBins - 1; + + // Release memory of arrays that may have been used for + // previous executions of this metric with different settings + // of the memory caching flags. + if(m_ThreaderJointPDFDerivatives != NULL) { + delete [] m_ThreaderJointPDFDerivatives; + } + m_ThreaderJointPDFDerivatives = NULL; + + if(m_ThreaderMetricDerivative != NULL) { + delete [] m_ThreaderMetricDerivative; + } + m_ThreaderMetricDerivative = NULL; + + + if( this->m_UseExplicitPDFDerivatives ) { + m_ThreaderJointPDFDerivatives = new typename + JointPDFDerivativesType::Pointer[this->m_NumberOfThreads-1]; + + for(threadID = 0; threadID < this->m_NumberOfThreads-1; threadID++) { + m_ThreaderJointPDFDerivatives[threadID] = JointPDFDerivativesType::New(); + m_ThreaderJointPDFDerivatives[threadID]->SetRegions( + jointPDFDerivativesRegion ); + m_ThreaderJointPDFDerivatives[threadID]->Allocate(); + } + } else { + m_ThreaderMetricDerivative = new DerivativeType[this->m_NumberOfThreads-1]; + + for(threadID = 0; threadID < this->m_NumberOfThreads-1; threadID++) { + this->m_ThreaderMetricDerivative[threadID] = DerivativeType( this->GetNumberOfParameters() ); + } + } +} + +/** + * Uniformly sample the fixed image domain using a random walk + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::ComputeFixedImageParzenWindowIndices( + FixedImageSampleContainer& samples ) +{ + + typename FixedImageSampleContainer::iterator iter; + typename FixedImageSampleContainer::const_iterator end=samples.end(); + + for( iter=samples.begin(); iter != end; ++iter ) { + + // Determine parzen window arguments (see eqn 6 of Mattes paper [2]). + double windowTerm = static_cast( (*iter).value ) + / m_FixedImageBinSize + - m_FixedImageNormalizedMin; + unsigned int pindex = static_cast( windowTerm ); + + // Make sure the extreme values are in valid bins + if ( pindex < 2 ) { + pindex = 2; + } else if ( pindex > (m_NumberOfHistogramBins - 3) ) { + pindex = m_NumberOfHistogramBins - 3; + } + + (*iter).valueIndex = pindex; + } + +} + +template < class TFixedImage, class TMovingImage > +inline void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::GetValueThreadPreProcess( unsigned int threadID, + bool withinSampleThread ) const +{ + + this->Superclass::GetValueThreadPreProcess( threadID, withinSampleThread ); + + if(threadID > 0) { + memset( m_ThreaderJointPDF[threadID-1]->GetBufferPointer(), + 0, + m_JointPDFBufferSize ); + memset( &(m_ThreaderFixedImageMarginalPDF[(threadID-1) + *m_NumberOfHistogramBins]), + 0, + m_NumberOfHistogramBins*sizeof(PDFValueType) ); + } else { + // zero-th thread uses the variables directly + memset( m_JointPDF->GetBufferPointer(), + 0, + m_JointPDFBufferSize ); + memset( m_FixedImageMarginalPDF, + 0, + m_NumberOfHistogramBins*sizeof(PDFValueType) ); + } +} + +template < class TFixedImage, class TMovingImage > +inline bool +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::GetValueThreadProcessSample( unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & itkNotUsed(mappedPoint), + double movingImageValue) const +{ + /** + * Compute this sample's contribution to the marginal and + * joint distributions. + * + */ + + if(movingImageValue < m_MovingImageTrueMin) { + return false; + } else if(movingImageValue > m_MovingImageTrueMax) { + return false; + } + + // Determine parzen window arguments (see eqn 6 of Mattes paper [2]). + double movingImageParzenWindowTerm = movingImageValue + / m_MovingImageBinSize + - m_MovingImageNormalizedMin; + // Same as floor + unsigned int movingImageParzenWindowIndex = + static_cast( movingImageParzenWindowTerm ); + if( movingImageParzenWindowIndex < 2 ) { + movingImageParzenWindowIndex = 2; + } else if( movingImageParzenWindowIndex > (m_NumberOfHistogramBins - 3) ) { + movingImageParzenWindowIndex = m_NumberOfHistogramBins - 3; + } + + unsigned int fixedImageParzenWindowIndex = + this->m_FixedImageSamples[fixedImageSample].valueIndex; + if(threadID > 0) { + m_ThreaderFixedImageMarginalPDF[(threadID-1)*m_NumberOfHistogramBins + + fixedImageParzenWindowIndex] += 1; + } else { + m_FixedImageMarginalPDF[fixedImageParzenWindowIndex] += 1; + } + + // Pointer to affected bin to be updated + JointPDFValueType *pdfPtr; + if(threadID > 0) { + pdfPtr = m_ThreaderJointPDF[threadID-1]->GetBufferPointer() + + ( fixedImageParzenWindowIndex + * m_ThreaderJointPDF[threadID-1] + ->GetOffsetTable()[1] ); + } else { + pdfPtr = m_JointPDF->GetBufferPointer() + + ( fixedImageParzenWindowIndex + * m_JointPDF->GetOffsetTable()[1] ); + } + + // Move the pointer to the first affected bin + int pdfMovingIndex = static_cast( movingImageParzenWindowIndex ) - 1; + pdfPtr += pdfMovingIndex; + int pdfMovingIndexMax = static_cast(movingImageParzenWindowIndex) + 2; + + double movingImageParzenWindowArg = + static_cast( pdfMovingIndex ) + - movingImageParzenWindowTerm; + + while( pdfMovingIndex <= pdfMovingIndexMax ) { + *(pdfPtr++) += static_cast( m_CubicBSplineKernel + ->Evaluate( + movingImageParzenWindowArg ) ); + movingImageParzenWindowArg += 1; + ++pdfMovingIndex; + } + + return true; +} + +template < class TFixedImage, class TMovingImage > +inline void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::GetValueThreadPostProcess( unsigned int threadID, + bool itkNotUsed(withinSampleThread) ) const +{ + unsigned int t; + int i; + int maxI; + maxI = m_NumberOfHistogramBins + * ( m_ThreaderJointPDFEndBin[threadID] + - m_ThreaderJointPDFStartBin[threadID] + 1); + JointPDFValueType *pdfPtr; + JointPDFValueType *pdfPtrStart; + pdfPtrStart = m_JointPDF->GetBufferPointer() + + ( m_ThreaderJointPDFStartBin[threadID] + * m_JointPDF->GetOffsetTable()[1] ); + JointPDFValueType *tPdfPtr; + JointPDFValueType *tPdfPtrEnd; + unsigned int tPdfPtrOffset; + tPdfPtrOffset = ( m_ThreaderJointPDFStartBin[threadID] + * m_JointPDF->GetOffsetTable()[1] ); + for(t=0; tm_NumberOfThreads-1; t++) { + pdfPtr = pdfPtrStart; + tPdfPtr = m_ThreaderJointPDF[t]->GetBufferPointer() + tPdfPtrOffset; + tPdfPtrEnd = tPdfPtr + maxI; + //for(i=0; i < maxI; i++) + while(tPdfPtr < tPdfPtrEnd) { + *(pdfPtr++) += *(tPdfPtr++); + } + for(i = m_ThreaderJointPDFStartBin[threadID]; + i <= m_ThreaderJointPDFEndBin[threadID]; + i++) { + m_FixedImageMarginalPDF[i] += m_ThreaderFixedImageMarginalPDF[ + (t*m_NumberOfHistogramBins) + i]; + } + } + double jointPDFSum = 0.0; + pdfPtr = pdfPtrStart; + for(i = 0; i < maxI; i++) { + jointPDFSum += *(pdfPtr++); + } + if(threadID > 0) { + m_ThreaderJointPDFSum[threadID-1] = jointPDFSum; + } else { + m_JointPDFSum = jointPDFSum; + } +} + +template < class TFixedImage, class TMovingImage > +typename MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::MeasureType +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::GetValue( const ParametersType & parameters ) const +{ + // Set up the parameters in the transform + this->m_Transform->SetParameters( parameters ); + this->m_Parameters = parameters; + + // MUST BE CALLED TO INITIATE PROCESSING + this->GetValueMultiThreadedInitiate(); + + // MUST BE CALLED TO INITIATE PROCESSING + this->GetValueMultiThreadedPostProcessInitiate(); + + for(unsigned int threadID = 0; threadIDm_NumberOfThreads-1; threadID++) { + m_JointPDFSum += m_ThreaderJointPDFSum[threadID]; + } + if ( m_JointPDFSum == 0.0 ) { + itkExceptionMacro( "Joint PDF summed to zero" ); + } + + memset( m_MovingImageMarginalPDF, + 0, + m_NumberOfHistogramBins*sizeof(PDFValueType) ); + + JointPDFValueType * pdfPtr; + PDFValueType * movingMarginalPtr; + unsigned int i, j; + double fixedPDFSum = 0.0; + double nFactor = 1.0 / m_JointPDFSum; + pdfPtr = m_JointPDF->GetBufferPointer(); + for(i=0; im_NumberOfPixelsCounted < + this->m_NumberOfFixedImageSamples / 16 ) { + itkExceptionMacro( "Too many samples map outside moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + } + + // Normalize the fixed image marginal PDF + if ( fixedPDFSum == 0.0 ) { + itkExceptionMacro( "Fixed image marginal PDF summed to zero" ); + } + for( unsigned int bin=0; bin < m_NumberOfHistogramBins; bin++ ) { + m_FixedImageMarginalPDF[bin] /= fixedPDFSum; + } + + /** + * Compute the metric by double summation over histogram. + */ + + // Setup pointer to point to the first bin + JointPDFValueType * jointPDFPtr = m_JointPDF->GetBufferPointer(); + + double sum = 0.0; + + for( unsigned int fixedIndex = 0; + fixedIndex < m_NumberOfHistogramBins; + ++fixedIndex ) { + double fixedImagePDFValue = m_FixedImageMarginalPDF[fixedIndex]; + + for( unsigned int movingIndex = 0; + movingIndex < m_NumberOfHistogramBins; + ++movingIndex, jointPDFPtr++ ) { + double movingImagePDFValue = m_MovingImageMarginalPDF[movingIndex]; + double jointPDFValue = *(jointPDFPtr); + + // check for non-zero bin contribution + if( jointPDFValue > 1e-16 && movingImagePDFValue > 1e-16 ) { + + double pRatio = vcl_log(jointPDFValue / movingImagePDFValue ); + if( fixedImagePDFValue > 1e-16) { + sum += jointPDFValue * ( pRatio - vcl_log(fixedImagePDFValue ) ); + } + + } // end if-block to check non-zero bin contribution + } // end for-loop over moving index + } // end for-loop over fixed index + + return static_cast( -1.0 * sum ); + +} + + +template < class TFixedImage, class TMovingImage > +inline void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::GetValueAndDerivativeThreadPreProcess( unsigned int threadID, + bool itkNotUsed(withinSampleThread) ) const +{ + if(threadID > 0) { + memset( m_ThreaderJointPDF[threadID-1]->GetBufferPointer(), + 0, + m_JointPDFBufferSize ); + + memset( &(m_ThreaderFixedImageMarginalPDF[(threadID-1) + * m_NumberOfHistogramBins]), + 0, + m_NumberOfHistogramBins*sizeof(PDFValueType) ); + + if( this->m_UseExplicitPDFDerivatives ) { + memset( m_ThreaderJointPDFDerivatives[threadID-1]->GetBufferPointer(), + 0, + m_JointPDFDerivativesBufferSize ); + } + } else { + memset( m_JointPDF->GetBufferPointer(), + 0, + m_JointPDFBufferSize ); + memset( m_FixedImageMarginalPDF, + 0, + m_NumberOfHistogramBins*sizeof(PDFValueType) ); + + if( this->m_UseExplicitPDFDerivatives ) { + memset( m_JointPDFDerivatives->GetBufferPointer(), + 0, + m_JointPDFDerivativesBufferSize ); + } + } +} + +template < class TFixedImage, class TMovingImage > +inline bool +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::GetValueAndDerivativeThreadProcessSample( unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & itkNotUsed(mappedPoint), + double movingImageValue, + const ImageDerivativesType & + movingImageGradientValue) const +{ + /** + * Compute this sample's contribution to the marginal + * and joint distributions. + * + */ + if(movingImageValue < m_MovingImageTrueMin) { + return false; + } else if(movingImageValue > m_MovingImageTrueMax) { + return false; + } + + unsigned int fixedImageParzenWindowIndex = + this->m_FixedImageSamples[fixedImageSample].valueIndex; + + // Determine parzen window arguments (see eqn 6 of Mattes paper [2]). + double movingImageParzenWindowTerm = movingImageValue + / m_MovingImageBinSize + - m_MovingImageNormalizedMin; + unsigned int movingImageParzenWindowIndex = + static_cast( movingImageParzenWindowTerm ); + + // Make sure the extreme values are in valid bins + if ( movingImageParzenWindowIndex < 2 ) { + movingImageParzenWindowIndex = 2; + } else if ( movingImageParzenWindowIndex > (m_NumberOfHistogramBins - 3) ) { + movingImageParzenWindowIndex = m_NumberOfHistogramBins - 3; + } + + // Since a zero-order BSpline (box car) kernel is used for + // the fixed image marginal pdf, we need only increment the + // fixedImageParzenWindowIndex by value of 1.0. + if(threadID > 0) { + ++m_ThreaderFixedImageMarginalPDF[(threadID-1)*m_NumberOfHistogramBins + + fixedImageParzenWindowIndex]; + } else { + ++m_FixedImageMarginalPDF[fixedImageParzenWindowIndex]; + } + + /** + * The region of support of the parzen window determines which bins + * of the joint PDF are effected by the pair of image values. + * Since we are using a cubic spline for the moving image parzen + * window, four bins are effected. The fixed image parzen window is + * a zero-order spline (box car) and thus effects only one bin. + * + * The PDF is arranged so that moving image bins corresponds to the + * zero-th (column) dimension and the fixed image bins corresponds + * to the first (row) dimension. + * + */ + + // Pointer to affected bin to be updated + JointPDFValueType *pdfPtr; + if(threadID > 0) { + pdfPtr = m_ThreaderJointPDF[threadID-1] + ->GetBufferPointer() + + ( fixedImageParzenWindowIndex + * m_NumberOfHistogramBins ); + } else { + pdfPtr = m_JointPDF->GetBufferPointer() + + ( fixedImageParzenWindowIndex + * m_NumberOfHistogramBins ); + } + + // Move the pointer to the fist affected bin + int pdfMovingIndex = static_cast( movingImageParzenWindowIndex ) - 1; + pdfPtr += pdfMovingIndex; + int pdfMovingIndexMax = static_cast(movingImageParzenWindowIndex) + 2; + + double movingImageParzenWindowArg = static_cast( pdfMovingIndex ) + - static_cast( movingImageParzenWindowTerm ); + + while( pdfMovingIndex <= pdfMovingIndexMax ) { + *(pdfPtr++) += static_cast( m_CubicBSplineKernel + ->Evaluate( + movingImageParzenWindowArg ) ); + + if( this->m_UseExplicitPDFDerivatives || this->m_ImplicitDerivativesSecondPass ) { + // Compute the cubicBSplineDerivative for later repeated use. + double cubicBSplineDerivativeValue = + m_CubicBSplineDerivativeKernel->Evaluate( movingImageParzenWindowArg ); + + // Compute PDF derivative contribution. + this->ComputePDFDerivatives( threadID, + fixedImageSample, + pdfMovingIndex, + movingImageGradientValue, + cubicBSplineDerivativeValue ); + } + + movingImageParzenWindowArg += 1; + ++pdfMovingIndex; + } + + return true; +} + +template < class TFixedImage, class TMovingImage > +inline void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::GetValueAndDerivativeThreadPostProcess( unsigned int threadID, + bool withinSampleThread ) const +{ + this->GetValueThreadPostProcess( threadID, withinSampleThread ); + + if( this->m_UseExplicitPDFDerivatives ) { + const unsigned int rowSize = this->m_NumberOfParameters * m_NumberOfHistogramBins; + + const unsigned int maxI = + rowSize * ( m_ThreaderJointPDFEndBin[threadID] + - m_ThreaderJointPDFStartBin[threadID] + 1 ); + + JointPDFDerivativesValueType *pdfDPtr; + JointPDFDerivativesValueType *pdfDPtrStart; + pdfDPtrStart = m_JointPDFDerivatives->GetBufferPointer() + + ( m_ThreaderJointPDFStartBin[threadID] * rowSize ); + JointPDFDerivativesValueType *tPdfDPtr; + JointPDFDerivativesValueType *tPdfDPtrEnd; + unsigned int tPdfDPtrOffset; + tPdfDPtrOffset = m_ThreaderJointPDFStartBin[threadID] * rowSize; + for(unsigned int t=0; tm_NumberOfThreads-1; t++) { + pdfDPtr = pdfDPtrStart; + tPdfDPtr = m_ThreaderJointPDFDerivatives[t]->GetBufferPointer() + + tPdfDPtrOffset; + tPdfDPtrEnd = tPdfDPtr + maxI; + // for(i = 0; i < maxI; i++) + while(tPdfDPtr < tPdfDPtrEnd) { + *(pdfDPtr++) += *(tPdfDPtr++); + } + } + + double nFactor = 1.0 / (m_MovingImageBinSize + * this->m_NumberOfPixelsCounted); + + pdfDPtr = pdfDPtrStart; + tPdfDPtrEnd = pdfDPtrStart + maxI; + //for(int i = 0; i < maxI; i++) + while(pdfDPtr < tPdfDPtrEnd) { + *(pdfDPtr++) *= nFactor; + } + } +} + +/** + * Get the both Value and Derivative Measure + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::GetValueAndDerivative( const ParametersType & parameters, + MeasureType & value, + DerivativeType & derivative) const +{ + // Set output values to zero + value = NumericTraits< MeasureType >::Zero; + + if( this->m_UseExplicitPDFDerivatives ) { + // Set output values to zero + if(derivative.GetSize() != this->m_NumberOfParameters) { + derivative = DerivativeType( this->m_NumberOfParameters ); + } + memset( derivative.data_block(), + 0, + this->m_NumberOfParameters * sizeof(double) ); + } else { + this->m_PRatioArray.Fill( 0.0 ); + this->m_MetricDerivative.Fill( NumericTraits< MeasureType >::Zero ); + for(unsigned int threadID = 0; threadID < this->m_NumberOfThreads-1; threadID++ ) { + this->m_ThreaderMetricDerivative[threadID].Fill( NumericTraits< MeasureType >::Zero ); + } + this->m_ImplicitDerivativesSecondPass = false; + } + + // Set up the parameters in the transform + this->m_Transform->SetParameters( parameters ); + this->m_Parameters = parameters; + + // MUST BE CALLED TO INITIATE PROCESSING ON SAMPLES + this->GetValueAndDerivativeMultiThreadedInitiate(); + + // CALL IF DOING THREADED POST PROCESSING + this->GetValueAndDerivativeMultiThreadedPostProcessInitiate(); + + for(unsigned int threadID = 0; threadIDm_NumberOfThreads-1; threadID++) { + m_JointPDFSum += m_ThreaderJointPDFSum[threadID]; + } + if ( m_JointPDFSum == 0.0 ) { + itkExceptionMacro( "Joint PDF summed to zero" ); + } + + memset( m_MovingImageMarginalPDF, + 0, + m_NumberOfHistogramBins*sizeof(PDFValueType) ); + + JointPDFValueType * pdfPtr; + PDFValueType * movingMarginalPtr; + unsigned int i, j; + double fixedPDFSum = 0.0; + const double normalizationFactor = 1.0 / m_JointPDFSum; + + pdfPtr = m_JointPDF->GetBufferPointer(); + for(i=0; im_NumberOfPixelsCounted < + this->m_NumberOfFixedImageSamples / 16 ) { + itkExceptionMacro( "Too many samples map outside moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + } + + // Normalize the fixed image marginal PDF + if ( fixedPDFSum == 0.0 ) { + itkExceptionMacro( "Fixed image marginal PDF summed to zero" ); + } + for( unsigned int bin=0; bin < m_NumberOfHistogramBins; bin++ ) { + m_FixedImageMarginalPDF[bin] /= fixedPDFSum; + } + + /** + * Compute the metric by double summation over histogram. + */ + + // Setup pointer to point to the first bin + JointPDFValueType * jointPDFPtr = m_JointPDF->GetBufferPointer(); + + // Initialize sum to zero + double sum = 0.0; + + const double nFactor = 1.0 / (m_MovingImageBinSize + * this->m_NumberOfPixelsCounted); + + for( unsigned int fixedIndex = 0; + fixedIndex < m_NumberOfHistogramBins; + ++fixedIndex ) { + double fixedImagePDFValue = m_FixedImageMarginalPDF[fixedIndex]; + + for( unsigned int movingIndex = 0; + movingIndex < m_NumberOfHistogramBins; + ++movingIndex, jointPDFPtr++ ) { + double movingImagePDFValue = m_MovingImageMarginalPDF[movingIndex]; + double jointPDFValue = *(jointPDFPtr); + + // check for non-zero bin contribution + if( jointPDFValue > 1e-16 && movingImagePDFValue > 1e-16 ) { + + double pRatio = vcl_log(jointPDFValue / movingImagePDFValue ); + + if( fixedImagePDFValue > 1e-16) { + sum += jointPDFValue * ( pRatio - vcl_log(fixedImagePDFValue ) ); + } + + if( this->m_UseExplicitPDFDerivatives ) { + // move joint pdf derivative pointer to the right position + JointPDFValueType * derivPtr = m_JointPDFDerivatives->GetBufferPointer() + + ( fixedIndex * m_JointPDFDerivatives->GetOffsetTable()[2] ) + + ( movingIndex * m_JointPDFDerivatives->GetOffsetTable()[1] ); + + for( unsigned int parameter=0; parameter < this->m_NumberOfParameters; ++parameter, derivPtr++ ) { + + // Ref: eqn 23 of Thevenaz & Unser paper [3] + derivative[parameter] -= (*derivPtr) * pRatio; + + } // end for-loop over parameters + } else { + this->m_PRatioArray[fixedIndex][movingIndex] = pRatio * nFactor; + } + } // end if-block to check non-zero bin contribution + } // end for-loop over moving index + } // end for-loop over fixed index + + if( !(this->m_UseExplicitPDFDerivatives ) ) { + // Second pass: This one is done for accumulating the contributions + // to the derivative array. + // + this->m_ImplicitDerivativesSecondPass = true; + // + // MUST BE CALLED TO INITIATE PROCESSING ON SAMPLES + this->GetValueAndDerivativeMultiThreadedInitiate(); + + // CALL IF DOING THREADED POST PROCESSING + this->GetValueAndDerivativeMultiThreadedPostProcessInitiate(); + + // Consolidate the contributions from each one of the threads to the total + // derivative. + for(unsigned int t = 0; t < this->m_NumberOfThreads-1; t++ ) { + DerivativeType * source = &(this->m_ThreaderMetricDerivative[t]); + for(unsigned int pp=0; pp < this->m_NumberOfParameters; pp++ ) { + this->m_MetricDerivative[pp] += (*source)[pp]; + } + } + + derivative = this->m_MetricDerivative; + } + + value = static_cast( -1.0 * sum ); + +} + +/** + * Get the match measure derivative + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::GetDerivative( const ParametersType & parameters, + DerivativeType & derivative ) const +{ + MeasureType value; + // call the combined version + this->GetValueAndDerivative( parameters, value, derivative ); +} + + +/** + * Compute PDF derivatives contribution for each parameter + */ +template < class TFixedImage, class TMovingImage > +void +MattesMutualInformationImageToImageMetricFor3DBLUTFFD +::ComputePDFDerivatives( unsigned int threadID, + unsigned int sampleNumber, + int pdfMovingIndex, + const ImageDerivativesType & movingImageGradientValue, + double cubicBSplineDerivativeValue ) const +{ + // Update bins in the PDF derivatives for the current intensity pair + // Could pre-compute + JointPDFDerivativesValueType * derivPtr; + + double precomputedWeight = 0.0; + + const int pdfFixedIndex = this->m_FixedImageSamples[sampleNumber].valueIndex; + + DerivativeType * derivativeHelperArray = NULL; + + if( this->m_UseExplicitPDFDerivatives ) { + if(threadID > 0) { + derivPtr = m_ThreaderJointPDFDerivatives[threadID-1]->GetBufferPointer() + + ( pdfFixedIndex * m_JointPDFDerivatives->GetOffsetTable()[2] ) + + ( pdfMovingIndex * m_JointPDFDerivatives->GetOffsetTable()[1] ); + } else { + derivPtr = m_JointPDFDerivatives->GetBufferPointer() + + ( pdfFixedIndex * m_JointPDFDerivatives->GetOffsetTable()[2] ) + + ( pdfMovingIndex * m_JointPDFDerivatives->GetOffsetTable()[1] ); + } + } else { + derivPtr = 0; + // Recover the precomputed weight for this specific PDF bin + precomputedWeight = this->m_PRatioArray[pdfFixedIndex][pdfMovingIndex]; + if(threadID > 0) { + derivativeHelperArray = &(this->m_ThreaderMetricDerivative[threadID-1]); + } else { + derivativeHelperArray = &(this->m_MetricDerivative); + } + + } + + if( !this->m_TransformIsBSpline ) { + /** + * Generic version which works for all transforms. + */ + + // Compute the transform Jacobian. + // Should pre-compute + typedef typename TransformType::JacobianType JacobianType; + + // Need to use one of the threader transforms if we're + // not in thread 0. + // + // Use a raw pointer here to avoid the overhead of smart pointers. + // For instance, Register and UnRegister have mutex locks around + // the reference counts. + TransformType* transform; + + if (threadID > 0) { + transform = this->m_ThreaderTransform[threadID - 1]; + } else { + transform = this->m_Transform; + } + + const JacobianType& jacobian = + transform->GetJacobian( this->m_FixedImageSamples[sampleNumber].point ); + + // for ( unsigned int mu = 0; mu < this->m_NumberOfParameters; mu++ ) + // { + // double innerProduct = 0.0; + // for ( unsigned int dim = 0; dim < Superclass::FixedImageDimension; dim++ ) + // { + // innerProduct += jacobian[dim][mu] * movingImageGradientValue[dim]; + // } + + // const double derivativeContribution = innerProduct * cubicBSplineDerivativeValue; + + // if( this->m_UseExplicitPDFDerivatives ) + // { + // *(derivPtr) -= derivativeContribution; + // ++derivPtr; + // } + // else + // { + // (*derivativeHelperArray)[mu] += precomputedWeight * derivativeContribution; + // } + // } + // } + // JV + unsigned int mu, dim; + double innerProduct,derivativeContribution; + for ( mu = 0; mu < this->m_NumberOfParameters; mu+=3 ) { + // JV only for 3D Space BLUT FFD: if J(0, par)=0, then J(1,par+1)=0 & ... + if (jacobian[0][mu]) + for ( dim = 0; dim < Superclass::FixedImageDimension; dim++ ) { + innerProduct = jacobian[dim][mu+dim] * movingImageGradientValue[dim]; + derivativeContribution = innerProduct * cubicBSplineDerivativeValue; + + if( this->m_UseExplicitPDFDerivatives ) { + *(derivPtr) -= derivativeContribution; + ++derivPtr; + } else { + (*derivativeHelperArray)[mu+dim] += precomputedWeight * derivativeContribution; + } + } + else if( this->m_UseExplicitPDFDerivatives ) derivPtr+=3; + } + } else { + const WeightsValueType * weights = NULL; + const IndexValueType * indices = NULL; + + + BSplineTransformWeightsType * weightsHelper = NULL; + BSplineTransformIndexArrayType * indicesHelper = NULL; + + if( this->m_UseCachingOfBSplineWeights ) { + // + // If the transform is of type BSplineDeformableTransform, we can obtain + // a speed up by only processing the affected parameters. Note that + // these pointers are just pointing to pre-allocated rows of the caching + // arrays. There is therefore, no need to free this memory. + // + weights = this->m_BSplineTransformWeightsArray[sampleNumber]; + indices = this->m_BSplineTransformIndicesArray[sampleNumber]; + } else { + if( threadID > 0 ) { + weightsHelper = &(this->m_ThreaderBSplineTransformWeights[threadID-1]); + indicesHelper = &(this->m_ThreaderBSplineTransformIndices[threadID-1]); + } else { + weightsHelper = &(this->m_BSplineTransformWeights); + indicesHelper = &(this->m_BSplineTransformIndices); + } + + this->m_BSplineTransform->GetJacobian( + this->m_FixedImageSamples[sampleNumber].point, + *weightsHelper, *indicesHelper ); + } + + for( unsigned int dim = 0; dim < Superclass::FixedImageDimension; dim++ ) { + + double innerProduct; + int parameterIndex; + + for( unsigned int mu = 0; mu < this->m_NumBSplineWeights; mu++ ) { + + /* The array weights contains the Jacobian values in a 1-D array + * (because for each parameter the Jacobian is non-zero in only 1 of the + * possible dimensions) which is multiplied by the moving image + * gradient. */ + if( this->m_UseCachingOfBSplineWeights ) { + innerProduct = movingImageGradientValue[dim] * weights[mu]; + parameterIndex = indices[mu] + this->m_BSplineParametersOffset[dim]; + } else { + innerProduct = movingImageGradientValue[dim] * (*weightsHelper)[mu]; + parameterIndex = (*indicesHelper)[mu] + this->m_BSplineParametersOffset[dim]; + } + + const double derivativeContribution = innerProduct * cubicBSplineDerivativeValue; + + if( this->m_UseExplicitPDFDerivatives ) { + JointPDFValueType * ptr = derivPtr + parameterIndex; + *(ptr) -= derivativeContribution; + } else { + (*derivativeHelperArray)[parameterIndex] += precomputedWeight * derivativeContribution; + } + + } //end mu for loop + } //end dim for loop + + } // end if-block transform is BSpline + +} + + +} // end namespace itk + + +#endif diff --git a/registration/itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD.h b/registration/itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD.h new file mode 100644 index 0000000..78bf91c --- /dev/null +++ b/registration/itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD.h @@ -0,0 +1,151 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD.h,v $ + Language: C++ + Date: $Date: 2010/06/14 17:32:07 $ + Version: $Revision: 1.1 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + 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. + +=========================================================================*/ +#ifndef __itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD_h +#define __itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD_h + +#include "itkOptImageToImageMetric.h" +#include "itkCovariantVector.h" +#include "itkPoint.h" +#include "itkIndex.h" + +#include "itkMultiThreader.h" + +namespace itk +{ + +template +class ITK_EXPORT MeanSquaresImageToImageMetricFor3DBLUTFFD : + public ImageToImageMetric< TFixedImage, TMovingImage > +{ +public: + + /** Standard class typedefs. */ + typedef MeanSquaresImageToImageMetricFor3DBLUTFFD Self; + typedef ImageToImageMetric< TFixedImage, TMovingImage > Superclass; + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(MeanSquaresImageToImageMetricFor3DBLUTFFD, ImageToImageMetric); + + /** Types inherited from Superclass. */ + typedef typename Superclass::TransformType TransformType; + typedef typename Superclass::TransformPointer TransformPointer; + typedef typename Superclass::TransformJacobianType TransformJacobianType; + typedef typename Superclass::InterpolatorType InterpolatorType; + typedef typename Superclass::MeasureType MeasureType; + typedef typename Superclass::DerivativeType DerivativeType; + typedef typename Superclass::ParametersType ParametersType; + typedef typename Superclass::FixedImageType FixedImageType; + typedef typename Superclass::MovingImageType MovingImageType; + typedef typename Superclass::MovingImagePointType MovingImagePointType; + typedef typename Superclass::FixedImageConstPointer FixedImageConstPointer; + typedef typename Superclass::MovingImageConstPointer MovingImageConstPointer; + typedef typename Superclass::CoordinateRepresentationType + CoordinateRepresentationType; + typedef typename Superclass::FixedImageSampleContainer + FixedImageSampleContainer; + typedef typename Superclass::ImageDerivativesType ImageDerivativesType; + typedef typename Superclass::WeightsValueType WeightsValueType; + typedef typename Superclass::IndexValueType IndexValueType; + + // Needed for evaluation of Jacobian. + typedef typename Superclass::FixedImagePointType FixedImagePointType; + + /** The moving image dimension. */ + itkStaticConstMacro( MovingImageDimension, unsigned int, + MovingImageType::ImageDimension ); + + /** + * Initialize the Metric by + * (1) making sure that all the components are present and plugged + * together correctly, + * (2) uniformly select NumberOfSpatialSamples within + * the FixedImageRegion, and + * (3) allocate memory for pdf data structures. */ + virtual void Initialize(void) throw ( ExceptionObject ); + + /** Get the value. */ + MeasureType GetValue( const ParametersType & parameters ) const; + + /** Get the derivatives of the match measure. */ + void GetDerivative( const ParametersType & parameters, + DerivativeType & Derivative ) const; + + /** Get the value and derivatives for single valued optimizers. */ + void GetValueAndDerivative( const ParametersType & parameters, + MeasureType & Value, + DerivativeType & Derivative ) const; + +protected: + + MeanSquaresImageToImageMetricFor3DBLUTFFD(); + virtual ~MeanSquaresImageToImageMetricFor3DBLUTFFD(); + void PrintSelf(std::ostream& os, Indent indent) const; + +private: + + //purposely not implemented + MeanSquaresImageToImageMetricFor3DBLUTFFD(const Self &); + //purposely not implemented + void operator=(const Self &); + + inline bool GetValueThreadProcessSample( unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & mappedPoint, + double movingImageValue ) const; + + inline bool GetValueAndDerivativeThreadProcessSample( unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & mappedPoint, + double movingImageValue, + const ImageDerivativesType & + movingImageGradientValue ) const; + + MeasureType * m_ThreaderMSE; + DerivativeType * m_ThreaderMSEDerivatives; + +}; + +} // end namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD.txx" +#endif + +#endif diff --git a/registration/itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD.txx b/registration/itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD.txx new file mode 100644 index 0000000..d949e9b --- /dev/null +++ b/registration/itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD.txx @@ -0,0 +1,331 @@ +/*========================================================================= + Program: vv http://www.creatis.insa-lyon.fr/rio/vv + + Authors belong to: + - University of LYON http://www.universite-lyon.fr/ + - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr + - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the copyright notices for more information. + + It is distributed under dual licence + + - BSD See included LICENSE.txt file + - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html +======================================================================-====*/ + +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD.txx,v $ + Language: C++ + Date: $Date: 2010/06/14 17:32:07 $ + Version: $Revision: 1.1 $ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + 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. + +=========================================================================*/ +#ifndef __itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD_txx +#define __itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD_txx + +#include "itkOptMeanSquaresImageToImageMetricFor3DBLUTFFD.h" +#include "itkCovariantVector.h" +#include "itkImageRandomConstIteratorWithIndex.h" +#include "itkImageRegionConstIterator.h" +#include "itkImageRegionIterator.h" +#include "itkImageIterator.h" +#include "vnl/vnl_math.h" + +namespace itk +{ + +/** + * Constructor + */ +template < class TFixedImage, class TMovingImage > +MeanSquaresImageToImageMetricFor3DBLUTFFD +::MeanSquaresImageToImageMetricFor3DBLUTFFD() +{ + this->SetComputeGradient(true); + + m_ThreaderMSE = NULL; + m_ThreaderMSEDerivatives = NULL; + this->m_WithinThreadPreProcess = false; + this->m_WithinThreadPostProcess = false; + + // For backward compatibility, the default behavior is to use all the pixels + // in the fixed image. + this->UseAllPixelsOn(); +} + +template < class TFixedImage, class TMovingImage > +MeanSquaresImageToImageMetricFor3DBLUTFFD +::~MeanSquaresImageToImageMetricFor3DBLUTFFD() +{ + if(m_ThreaderMSE != NULL) { + delete [] m_ThreaderMSE; + } + m_ThreaderMSE = NULL; + + if(m_ThreaderMSEDerivatives != NULL) { + delete [] m_ThreaderMSEDerivatives; + } + m_ThreaderMSEDerivatives = NULL; +} + +/** + * Print out internal information about this class + */ +template < class TFixedImage, class TMovingImage > +void +MeanSquaresImageToImageMetricFor3DBLUTFFD +::PrintSelf(std::ostream& os, Indent indent) const +{ + + Superclass::PrintSelf(os, indent); + +} + + +/** + * Initialize + */ +template +void +MeanSquaresImageToImageMetricFor3DBLUTFFD +::Initialize(void) throw ( ExceptionObject ) +{ + + this->Superclass::Initialize(); + this->Superclass::MultiThreadingInitialize(); + + if(m_ThreaderMSE != NULL) { + delete [] m_ThreaderMSE; + } + m_ThreaderMSE = new double[this->m_NumberOfThreads]; + + if(m_ThreaderMSEDerivatives != NULL) { + delete [] m_ThreaderMSEDerivatives; + } + m_ThreaderMSEDerivatives = new DerivativeType[this->m_NumberOfThreads]; + for(unsigned int threadID=0; threadIDm_NumberOfThreads; threadID++) { + m_ThreaderMSEDerivatives[threadID].SetSize( this->m_NumberOfParameters ); + } +} + +template < class TFixedImage, class TMovingImage > +inline bool +MeanSquaresImageToImageMetricFor3DBLUTFFD +::GetValueThreadProcessSample( unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & itkNotUsed(mappedPoint), + double movingImageValue) const +{ + double diff = movingImageValue - this->m_FixedImageSamples[fixedImageSample].value; + + m_ThreaderMSE[threadID] += diff*diff; + + return true; +} + +template < class TFixedImage, class TMovingImage > +typename MeanSquaresImageToImageMetricFor3DBLUTFFD +::MeasureType +MeanSquaresImageToImageMetricFor3DBLUTFFD +::GetValue( const ParametersType & parameters ) const +{ + itkDebugMacro("GetValue( " << parameters << " ) "); + + if( !this->m_FixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + memset( m_ThreaderMSE, + 0, + this->m_NumberOfThreads * sizeof(MeasureType) ); + + // Set up the parameters in the transform + this->m_Transform->SetParameters( parameters ); + this->m_Parameters = parameters; + + // MUST BE CALLED TO INITIATE PROCESSING + this->GetValueMultiThreadedInitiate(); + + itkDebugMacro( "Ratio of voxels mapping into moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + + if( this->m_NumberOfPixelsCounted < + this->m_NumberOfFixedImageSamples / 4 ) { + itkExceptionMacro( "Too many samples map outside moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + } + + double mse = m_ThreaderMSE[0]; + for(unsigned int t=1; tm_NumberOfThreads; t++) { + mse += m_ThreaderMSE[t]; + } + mse /= this->m_NumberOfPixelsCounted; + + return mse; +} + + +template < class TFixedImage, class TMovingImage > +inline bool +MeanSquaresImageToImageMetricFor3DBLUTFFD +::GetValueAndDerivativeThreadProcessSample( unsigned int threadID, + unsigned long fixedImageSample, + const MovingImagePointType & itkNotUsed(mappedPoint), + double movingImageValue, + const ImageDerivativesType & + movingImageGradientValue ) const +{ + double diff = movingImageValue - this->m_FixedImageSamples[fixedImageSample].value; + + m_ThreaderMSE[threadID] += diff*diff; + + //JV + //FixedImagePointType fixedImagePoint = this->m_FixedImageSamples[fixedImageSample].point; + + // Need to use one of the threader transforms if we're + // not in thread 0. + // + // Use a raw pointer here to avoid the overhead of smart pointers. + // For instance, Register and UnRegister have mutex locks around + // the reference counts. + TransformType* transform; + + if (threadID > 0) { + transform = this->m_ThreaderTransform[threadID - 1]; + } else { + transform = this->m_Transform; + } + + // Jacobian should be evaluated at the unmapped (fixed image) point. + const TransformJacobianType & jacobian = transform ->GetJacobian( this->m_FixedImageSamples[fixedImageSample].point ); + //double sum; + unsigned int par, dim; + for( par=0; parm_NumberOfParameters; par+=3) { + // double sum = 0.0; + // for(unsigned int dim=0; dim +void +MeanSquaresImageToImageMetricFor3DBLUTFFD +::GetValueAndDerivative( const ParametersType & parameters, + MeasureType & value, + DerivativeType & derivative) const +{ + + if( !this->m_FixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + // Set up the parameters in the transform + this->m_Transform->SetParameters( parameters ); + this->m_Parameters = parameters; + + // Reset the joint pdfs to zero + memset( m_ThreaderMSE, + 0, + this->m_NumberOfThreads * sizeof(MeasureType) ); + + // Set output values to zero + if(derivative.GetSize() != this->m_NumberOfParameters) { + derivative = DerivativeType( this->m_NumberOfParameters ); + } + memset( derivative.data_block(), + 0, + this->m_NumberOfParameters * sizeof(double) ); + + for( unsigned int threadID = 0; threadIDm_NumberOfThreads; threadID++ ) { + memset( m_ThreaderMSEDerivatives[threadID].data_block(), + 0, + this->m_NumberOfParameters * sizeof(double) ); + } + + // MUST BE CALLED TO INITIATE PROCESSING + this->GetValueAndDerivativeMultiThreadedInitiate(); + + itkDebugMacro( "Ratio of voxels mapping into moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + + if( this->m_NumberOfPixelsCounted < + this->m_NumberOfFixedImageSamples / 4 ) { + itkExceptionMacro( "Too many samples map outside moving image buffer: " + << this->m_NumberOfPixelsCounted << " / " + << this->m_NumberOfFixedImageSamples + << std::endl ); + } + + value = 0; + for(unsigned int t=0; tm_NumberOfThreads; t++) { + value += m_ThreaderMSE[t]; + for(unsigned int parameter = 0; parameter < this->m_NumberOfParameters; + parameter++) { + derivative[parameter] += m_ThreaderMSEDerivatives[t][parameter]; + } + } + + value /= this->m_NumberOfPixelsCounted; + for(unsigned int parameter = 0; parameter < this->m_NumberOfParameters; + parameter++) { + derivative[parameter] /= this->m_NumberOfPixelsCounted; + // JV + //DD(parameter<<"\t"< +void +MeanSquaresImageToImageMetricFor3DBLUTFFD +::GetDerivative( const ParametersType & parameters, + DerivativeType & derivative ) const +{ + if( !this->m_FixedImage ) { + itkExceptionMacro( << "Fixed image has not been assigned" ); + } + + MeasureType value; + // call the combined version + this->GetValueAndDerivative( parameters, value, derivative ); +} + +} // end namespace itk + + +#endif diff --git a/tools/clitkRigidRegistration.cxx b/tools/clitkRigidRegistration.cxx deleted file mode 100644 index 1db3e75..0000000 --- a/tools/clitkRigidRegistration.cxx +++ /dev/null @@ -1,109 +0,0 @@ -/*========================================================================= - Program: vv http://www.creatis.insa-lyon.fr/rio/vv - - Authors belong to: - - University of LYON http://www.universite-lyon.fr/ - - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr - - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. See the copyright notices for more information. - - It is distributed under dual licence - - - BSD See included LICENSE.txt file - - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html -======================================================================-====*/ -/** - ------------------------------------------------- - * @file clitkRigidRegistration.cxx - * @author Jef Vandemeulebroucke - * @date 14 August 2007 - * - * @brief Perform a rigid registration between 2 images - * - -------------------------------------------------*/ - - -// clitk include -#include "clitkIO.h" -#include "clitkImageCommon.h" -#include "clitkRigidRegistration_ggo.h" -#include "clitkRigidRegistrationGenericFilter.h" - -using namespace clitk; -using namespace std; - - -int main( int argc, char *argv[] ) -{ - //init command line and check options - GGO(args_info); - CLITK_INIT; - - //--------------------------------------------------------------------------- - //Set all the options passed through the commandline - - RigidRegistrationGenericFilter rigidRegistration; - - rigidRegistration.SetVerbose(args_info.verbose_flag); - rigidRegistration.SetGradient(args_info.gradient_flag); - rigidRegistration.SetZeroOrigin(args_info.zero_origin_flag); - -//Input - rigidRegistration.SetFixedImageName(args_info.reference_arg); - rigidRegistration.SetMovingImageName(args_info.object_arg); - rigidRegistration.SetFixedImageMaskGiven(args_info.mask_given); - if (args_info.mask_given) rigidRegistration.SetFixedImageMaskName(args_info.mask_arg); - - //Output - rigidRegistration.SetOutputGiven(args_info.output_given); - if (args_info.output_given) rigidRegistration.SetOutputName(args_info.output_arg); - rigidRegistration.SetCheckerAfterGiven(args_info.checker_after_given); - if (args_info.checker_after_given) rigidRegistration.SetCheckerAfterName(args_info.checker_after_arg); - rigidRegistration.SetCheckerBeforeGiven(args_info.checker_before_given); - if (args_info.checker_before_given) rigidRegistration.SetCheckerBeforeName(args_info.checker_before_arg); - rigidRegistration.SetBeforeGiven(args_info.before_given); - if (args_info.before_given) rigidRegistration.SetBeforeName(args_info.before_arg); - rigidRegistration.SetAfterGiven(args_info.after_given); - if (args_info.after_given) rigidRegistration.SetAfterName(args_info.after_arg); - rigidRegistration.SetMatrixGiven(args_info.matrix_given); - if (args_info.matrix_given) rigidRegistration.SetMatrixName(args_info.matrix_arg); - - //Interp - rigidRegistration.SetInterpType(args_info.interp_arg); - - //Transform - rigidRegistration.SetRotX(args_info.rotX_arg); - rigidRegistration.SetRotY(args_info.rotY_arg); - rigidRegistration.SetRotZ(args_info.rotZ_arg); - rigidRegistration.SetTransX(args_info.transX_arg); - rigidRegistration.SetTransY(args_info.transY_arg); - rigidRegistration.SetTransZ(args_info.transZ_arg); - - //Optimizer - rigidRegistration.SetLevels(args_info.levels_arg); - rigidRegistration.SetIstep(args_info.Istep_arg); - rigidRegistration.SetFstep(args_info.Fstep_arg); - rigidRegistration.SetRelax(args_info.relax_arg); - rigidRegistration.SetInc(args_info.inc_arg); - rigidRegistration.SetDec(args_info.dec_arg); - rigidRegistration.SetIter(args_info.iter_arg); - rigidRegistration.SetRweight(args_info.Rweight_arg); - rigidRegistration.SetTweight(args_info.Tweight_arg); - - //Metric - rigidRegistration.SetMetricType(args_info.metric_arg); - rigidRegistration.SetSamples(args_info.samples_arg); - rigidRegistration.SetBins(args_info.bins_arg); - rigidRegistration.SetRandom(args_info.random_flag); - rigidRegistration.SetStdDev(args_info.stdDev_arg); - - //Preprocessing - rigidRegistration.SetBlur(args_info.blur_arg); - rigidRegistration.SetNormalize(args_info.normalize_flag); - - rigidRegistration.Update(); - -} diff --git a/tools/clitkRigidRegistration.ggo b/tools/clitkRigidRegistration.ggo deleted file mode 100644 index 5002e60..0000000 --- a/tools/clitkRigidRegistration.ggo +++ /dev/null @@ -1,55 +0,0 @@ -# file clitkRigidRegistration.ggo -Package "clitkRigidRegistration" -version "1.0" -purpose "Compute a rigid registration between two images." - -option "config" - "Config file" string no -option "verbose" v "Verbose" flag off -option "gradient" - "If verbose, show gradient at each iteration" flag off - -section "Input (Both images have to be of the same dimension (2 or 3D). For 2D-3D registrations, give the 2D image a third dimension of 1 and set it to the reference image.)" -option "reference" i "Reference or fixed image filename" string yes -option "object" j "Object or moving image filename" string yes -option "mask" m "Mask to placed over the reference image" string no - -section "Output" -option "output" o "Transformed object image filename" string no -option "checker_after" - "Checherboard representation of the transformed object image and reference image" string no -option "checker_before" - "Checherboard representation of the object image and reference image" string no -option "after" - "Difference between the reference image and the transformed object" string no -option "before" - "Difference between the reference image and the original object image" string no -option "matrix" - "Affine matrix (reference to object space) filename " string no - -section "Interpolator" -option "interp" - "Interpolator used during registration: 0=nearestneighbor, 1=linear, 2=bspline" int no default="1" - -section "Transform (Input and Output transformation parameters map the physical space of the fixed or reference image into the physical space of the moving or object image. Positive rotations result in a counter-clockwise rotation for the moving image. Positive translations result in shift along the negative axis for the moving image.)" -option "transX" x "Initial translation in mm along the X axis" float no default="0.0" -option "transY" y "Initial translation in mm along the Y axis" float no default="0.0" -option "transZ" z "Initial translation in mm along the Z axis" float no default="0.0" -option "rotX" X "Initial rotation in rad along the X axis" float no default="0.0" -option "rotY" Y "Initial rotation in rad along the Y axis" float no default="0.0" -option "rotZ" Z "Initial rotation in rad along the Z axis" float no default="0.0" - -section "Optimizer" -option "levels" l "Number of multiresolution levels" int no default="1" -option "Istep" - "Initial stepsize in mm in the first level(to be multiplied with the gradient)" float no default="2.0" -option "Fstep" - "Final stepsize in mm in the first level (to be multiplied with the gradient)" float no default="0.1" -option "relax" - "Relaxation of the stepsize (multiplied each time the gradient changes sign)" float no default="0.7" -option "inc" - "Increment factor x previous stepsize = new stepsize when going to next level" float no default="1.2" -option "dec" - "Decrement factor(:) previous stepsize = new final stepsize when going to next level" float no default="4.0" -option "iter" - "Maximum number of iterations at each level" int no default="200" -option "Rweight" - "Weight of 1° of rotation during optimisation (high weight, less change)" float no default="50.0" -option "Tweight" - "Weight of 1mm of translation during optimisation (high weight, less change)" float no default="1.0" - -section "Metric (Use a high fraction of samples for detailed images (eg. 0.2, 0.5). For smooth images 0.01 might be enough. Use enough bins to cover the dynamique range. Randomizing the samples will make each execution different.)" -option "metric" - "Metric used during registration: 0=MS, 1=MI, 2=Mattes' MI, 3=CR" int no default="0" -option "samples" - "If using MI or Mattes' MI, specify fraction [0, 1] of samples of the reference image" float no default="0.2" -option "bins" - "If using Mattes' MI, specify the number of histogram bins for the PDF estimation of the reference image" int no default="50" -option "random" - "If using Mattes' MI and MI, specify if the set of samples should be taken randomly" flag off -option "stdDev" - "If using MI, specify the standard deviation in mm of the gaussian kernels for both PDF estimations" float no default="0.4" - -section "Preprocessing" -option "normalize" - "Normalize images before registration (not necessary for Mattes' MI)" flag off -option "blur" - "Blur images before registration, use Gaussian with std dev (none by default) " float no default="0.0" -option "zero_origin" - "Reset the input images' origins to zero, to avoid additional shifts according to jef" flag off