1 /*=========================================================================
2 Program: vv http://www.creatis.insa-lyon.fr/rio/vv
5 - University of LYON http://www.universite-lyon.fr/
6 - Léon Bérard cancer center http://www.centreleonberard.fr
7 - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr
9 This software is distributed WITHOUT ANY WARRANTY; without even
10 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 PURPOSE. See the copyright notices for more information.
13 It is distributed under dual licence
15 - BSD See included LICENSE.txt file
16 - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
17 ===========================================================================**/
19 /*=========================================================================
21 Program: Insight Segmentation & Registration Toolkit
22 Module: $RCSfile: itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.txx,v $
24 Date: $Date: 2010/06/14 17:32:07 $
25 Version: $Revision: 1.1 $
27 Copyright (c) Insight Software Consortium. All rights reserved.
28 See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
30 This software is distributed WITHOUT ANY WARRANTY; without even
31 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
32 PURPOSE. See the above copyright notices for more information.
34 =========================================================================*/
35 #ifndef __itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD_txx
36 #define __itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD_txx
38 #include "itkOptMattesMutualInformationImageToImageMetricFor3DBLUTFFD.h"
39 #include "itkCovariantVector.h"
40 #include "itkImageRandomConstIteratorWithIndex.h"
41 #include "itkImageRegionConstIterator.h"
42 #include "itkImageRegionIterator.h"
43 #include "itkImageIterator.h"
44 #include "vnl/vnl_math.h"
45 #include "itkStatisticsImageFilter.h"
47 #include "vnl/vnl_vector.h"
48 #include "vnl/vnl_c_vector.h"
56 template < class TFixedImage, class TMovingImage >
57 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
58 ::MattesMutualInformationImageToImageMetricFor3DBLUTFFD()
60 m_NumberOfHistogramBins = 50;
62 this->SetComputeGradient(false); // don't use the default gradient for now
64 // Initialize PDFs to NULL
66 m_JointPDFDerivatives = NULL;
69 m_FixedImageMarginalPDF = NULL;
70 m_MovingImageMarginalPDF = NULL;
72 m_MovingImageNormalizedMin = 0.0;
73 m_FixedImageNormalizedMin = 0.0;
74 m_MovingImageTrueMin = 0.0;
75 m_MovingImageTrueMax = 0.0;
76 m_FixedImageBinSize = 0.0;
77 m_MovingImageBinSize = 0.0;
79 m_CubicBSplineDerivativeKernel = NULL;
81 // For multi-threading the metric
82 m_ThreaderFixedImageMarginalPDF = NULL;
83 m_ThreaderJointPDF = NULL;
84 m_ThreaderJointPDFDerivatives = NULL;
85 m_ThreaderJointPDFStartBin = NULL;
86 m_ThreaderJointPDFEndBin = NULL;
87 m_ThreaderJointPDFSum = NULL;
88 this->m_WithinThreadPreProcess = true;
89 this->m_WithinThreadPostProcess = false;
91 this->m_ThreaderMetricDerivative = NULL;
92 this->m_UseExplicitPDFDerivatives = true;
93 this->m_ImplicitDerivativesSecondPass = false;
96 template < class TFixedImage, class TMovingImage >
97 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
98 ::~MattesMutualInformationImageToImageMetricFor3DBLUTFFD()
101 if(m_FixedImageMarginalPDF != NULL) {
102 delete [] m_FixedImageMarginalPDF;
104 m_FixedImageMarginalPDF = NULL;
105 if(m_MovingImageMarginalPDF != NULL) {
106 delete [] m_MovingImageMarginalPDF;
108 m_MovingImageMarginalPDF = NULL;
109 if(m_ThreaderJointPDF != NULL) {
110 delete [] m_ThreaderJointPDF;
112 m_ThreaderJointPDF = NULL;
114 if(m_ThreaderJointPDFDerivatives != NULL) {
115 delete [] m_ThreaderJointPDFDerivatives;
117 m_ThreaderJointPDFDerivatives = NULL;
119 if(m_ThreaderFixedImageMarginalPDF != NULL) {
120 delete [] m_ThreaderFixedImageMarginalPDF;
122 m_ThreaderFixedImageMarginalPDF = NULL;
124 if(m_ThreaderJointPDFStartBin != NULL) {
125 delete [] m_ThreaderJointPDFStartBin;
127 m_ThreaderJointPDFStartBin = NULL;
129 if(m_ThreaderJointPDFEndBin != NULL) {
130 delete [] m_ThreaderJointPDFEndBin;
132 m_ThreaderJointPDFEndBin = NULL;
134 if(m_ThreaderJointPDFSum != NULL) {
135 delete [] m_ThreaderJointPDFSum;
137 m_ThreaderJointPDFSum = NULL;
139 if( this->m_ThreaderMetricDerivative != NULL ) {
140 delete [] this->m_ThreaderMetricDerivative;
142 this->m_ThreaderMetricDerivative = NULL;
146 * Print out internal information about this class
148 template < class TFixedImage, class TMovingImage >
150 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
151 ::PrintSelf(std::ostream& os, Indent indent) const
154 Superclass::PrintSelf(os, indent);
156 os << indent << "NumberOfHistogramBins: ";
157 os << this->m_NumberOfHistogramBins << std::endl;
159 // Debugging information
160 os << indent << "FixedImageNormalizedMin: ";
161 os << this->m_FixedImageNormalizedMin << std::endl;
162 os << indent << "MovingImageNormalizedMin: ";
163 os << this->m_MovingImageNormalizedMin << std::endl;
164 os << indent << "MovingImageTrueMin: ";
165 os << this->m_MovingImageTrueMin << std::endl;
166 os << indent << "MovingImageTrueMax: ";
167 os << this->m_MovingImageTrueMax << std::endl;
168 os << indent << "FixedImageBinSize: ";
169 os << this->m_FixedImageBinSize << std::endl;
170 os << indent << "MovingImageBinSize: ";
171 os << this->m_MovingImageBinSize << std::endl;
172 os << indent << "UseExplicitPDFDerivatives: ";
173 os << this->m_UseExplicitPDFDerivatives << std::endl;
174 os << indent << "ImplicitDerivativesSecondPass: ";
175 os << this->m_ImplicitDerivativesSecondPass << std::endl;
183 template <class TFixedImage, class TMovingImage>
185 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
186 ::Initialize(void) throw ( ExceptionObject )
188 this->Superclass::Initialize();
189 this->Superclass::MultiThreadingInitialize();
191 typedef StatisticsImageFilter<FixedImageType> FixedImageStatisticsFilterType;
192 typename FixedImageStatisticsFilterType::Pointer fixedImageStats =
193 FixedImageStatisticsFilterType::New();
194 fixedImageStats->SetInput( this->m_FixedImage );
195 fixedImageStats->SetNumberOfThreads( this->m_NumberOfThreads );
196 fixedImageStats->Update();
198 m_FixedImageTrueMin = fixedImageStats->GetMinimum();
199 m_FixedImageTrueMax = fixedImageStats->GetMaximum();
200 double fixedImageMin = m_FixedImageTrueMin;
201 double fixedImageMax = m_FixedImageTrueMax;
203 typedef StatisticsImageFilter<MovingImageType>
204 MovingImageStatisticsFilterType;
205 typename MovingImageStatisticsFilterType::Pointer movingImageStats =
206 MovingImageStatisticsFilterType::New();
207 movingImageStats->SetInput( this->m_MovingImage );
208 movingImageStats->SetNumberOfThreads( this->m_NumberOfThreads );
209 movingImageStats->Update();
211 m_MovingImageTrueMin = movingImageStats->GetMinimum();
212 m_MovingImageTrueMax = movingImageStats->GetMaximum();
213 double movingImageMin = m_MovingImageTrueMin;
214 double movingImageMax = m_MovingImageTrueMax;
216 itkDebugMacro( " FixedImageMin: " << fixedImageMin <<
217 " FixedImageMax: " << fixedImageMax << std::endl );
218 itkDebugMacro( " MovingImageMin: " << movingImageMin <<
219 " MovingImageMax: " << movingImageMax << std::endl );
223 * Compute binsize for the histograms.
225 * The binsize for the image intensities needs to be adjusted so that
226 * we can avoid dealing with boundary conditions using the cubic
227 * spline as the Parzen window. We do this by increasing the size
228 * of the bins so that the joint histogram becomes "padded" at the
229 * borders. Because we are changing the binsize,
230 * we also need to shift the minimum by the padded amount in order to
231 * avoid minimum values filling in our padded region.
233 * Note that there can still be non-zero bin values in the padded region,
234 * it's just that these bins will never be a central bin for the Parzen
238 const int padding = 2; // this will pad by 2 bins
240 m_FixedImageBinSize = ( fixedImageMax - fixedImageMin )
241 / static_cast<double>( m_NumberOfHistogramBins
243 m_FixedImageNormalizedMin = fixedImageMin / m_FixedImageBinSize
244 - static_cast<double>( padding );
246 m_MovingImageBinSize = ( movingImageMax - movingImageMin )
247 / static_cast<double>( m_NumberOfHistogramBins
249 m_MovingImageNormalizedMin = movingImageMin / m_MovingImageBinSize
250 - static_cast<double>( padding );
252 itkDebugMacro( "FixedImageNormalizedMin: " << m_FixedImageNormalizedMin );
253 itkDebugMacro( "MovingImageNormalizedMin: " << m_MovingImageNormalizedMin );
254 itkDebugMacro( "FixedImageBinSize: " << m_FixedImageBinSize );
255 itkDebugMacro( "MovingImageBinSize; " << m_MovingImageBinSize );
259 * Allocate memory for the marginal PDF and initialize values
260 * to zero. The marginal PDFs are stored as std::vector.
262 if(m_FixedImageMarginalPDF != NULL) {
263 delete [] m_FixedImageMarginalPDF;
265 m_FixedImageMarginalPDF = new PDFValueType[m_NumberOfHistogramBins];
266 if(m_MovingImageMarginalPDF != NULL) {
267 delete [] m_MovingImageMarginalPDF;
269 m_MovingImageMarginalPDF = new PDFValueType[m_NumberOfHistogramBins];
272 * Allocate memory for the joint PDF and joint PDF derivatives.
273 * The joint PDF and joint PDF derivatives are store as itk::Image.
275 m_JointPDF = JointPDFType::New();
276 m_JointPDFDerivatives = JointPDFDerivativesType::New();
278 // Instantiate a region, index, size
279 JointPDFRegionType jointPDFRegion;
280 JointPDFIndexType jointPDFIndex;
281 JointPDFSizeType jointPDFSize;
283 // Deallocate the memory that may have been allocated for
284 // previous runs of the metric.
285 this->m_JointPDFDerivatives = NULL; // by destroying the dynamic array
286 this->m_PRatioArray.SetSize( 1, 1 ); // and by allocating very small the static ones
287 this->m_MetricDerivative = DerivativeType( 1 );
289 JointPDFDerivativesRegionType jointPDFDerivativesRegion;
292 // Now allocate memory according to the user-selected method.
294 if( this->m_UseExplicitPDFDerivatives ) {
295 this->m_JointPDFDerivatives = JointPDFDerivativesType::New();
297 JointPDFDerivativesIndexType jointPDFDerivativesIndex;
298 JointPDFDerivativesSizeType jointPDFDerivativesSize;
300 // For the derivatives of the joint PDF define a region starting from {0,0,0}
301 // with size {m_NumberOfParameters,m_NumberOfHistogramBins,
302 // m_NumberOfHistogramBins}. The dimension represents transform parameters,
303 // fixed image parzen window index and moving image parzen window index,
305 jointPDFDerivativesIndex.Fill( 0 );
306 jointPDFDerivativesSize[0] = this->m_NumberOfParameters;
307 jointPDFDerivativesSize[1] = this->m_NumberOfHistogramBins;
308 jointPDFDerivativesSize[2] = this->m_NumberOfHistogramBins;
310 jointPDFDerivativesRegion.SetIndex( jointPDFDerivativesIndex );
311 jointPDFDerivativesRegion.SetSize( jointPDFDerivativesSize );
313 // Set the regions and allocate
314 m_JointPDFDerivatives->SetRegions( jointPDFDerivativesRegion );
315 m_JointPDFDerivatives->Allocate();
316 m_JointPDFDerivativesBufferSize = jointPDFDerivativesSize[0]
317 * jointPDFDerivativesSize[1]
318 * jointPDFDerivativesSize[2]
319 * sizeof(JointPDFDerivativesValueType);
322 /** Allocate memory for helper array that will contain the pRatios
323 * for each bin of the joint histogram. This is part of the effort
324 * for flattening the computation of the PDF Jacobians.
326 this->m_PRatioArray.SetSize( this->m_NumberOfHistogramBins, this->m_NumberOfHistogramBins );
327 this->m_MetricDerivative = DerivativeType( this->GetNumberOfParameters() );
330 // For the joint PDF define a region starting from {0,0}
331 // with size {m_NumberOfHistogramBins, m_NumberOfHistogramBins}.
332 // The dimension represents fixed image parzen window index
333 // and moving image parzen window index, respectively.
334 jointPDFIndex.Fill( 0 );
335 jointPDFSize.Fill( m_NumberOfHistogramBins );
337 jointPDFRegion.SetIndex( jointPDFIndex );
338 jointPDFRegion.SetSize( jointPDFSize );
340 // Set the regions and allocate
341 m_JointPDF->SetRegions( jointPDFRegion );
342 m_JointPDF->Allocate();
344 m_JointPDFBufferSize = jointPDFSize[0] * jointPDFSize[1] * sizeof(PDFValueType);
348 * Setup the kernels used for the Parzen windows.
350 m_CubicBSplineKernel = CubicBSplineFunctionType::New();
351 m_CubicBSplineDerivativeKernel = CubicBSplineDerivativeFunctionType::New();
354 * Pre-compute the fixed image parzen window index for
355 * each point of the fixed image sample points list.
357 this->ComputeFixedImageParzenWindowIndices( this->m_FixedImageSamples );
360 if(m_ThreaderFixedImageMarginalPDF != NULL) {
361 delete [] m_ThreaderFixedImageMarginalPDF;
363 // Assumes number of threads doesn't change between calls to Initialize
364 m_ThreaderFixedImageMarginalPDF = new
365 PDFValueType[(this->m_NumberOfThreads-1)
366 * m_NumberOfHistogramBins];
368 if(m_ThreaderJointPDF != NULL) {
369 delete [] m_ThreaderJointPDF;
371 m_ThreaderJointPDF = new typename
372 JointPDFType::Pointer[this->m_NumberOfThreads-1];
374 if(m_ThreaderJointPDFStartBin != NULL) {
375 delete [] m_ThreaderJointPDFStartBin;
377 m_ThreaderJointPDFStartBin = new int[this->m_NumberOfThreads];
379 if(m_ThreaderJointPDFEndBin != NULL) {
380 delete [] m_ThreaderJointPDFEndBin;
382 m_ThreaderJointPDFEndBin = new int[this->m_NumberOfThreads];
384 if(m_ThreaderJointPDFSum != NULL) {
385 delete [] m_ThreaderJointPDFSum;
387 m_ThreaderJointPDFSum = new double[this->m_NumberOfThreads];
389 unsigned int threadID;
391 int binRange = m_NumberOfHistogramBins / this->m_NumberOfThreads;
393 for(threadID = 0; threadID < this->m_NumberOfThreads-1; threadID++) {
394 m_ThreaderJointPDF[threadID] = JointPDFType::New();
395 m_ThreaderJointPDF[threadID]->SetRegions( jointPDFRegion );
396 m_ThreaderJointPDF[threadID]->Allocate();
398 m_ThreaderJointPDFStartBin[threadID] = threadID * binRange;
399 m_ThreaderJointPDFEndBin[threadID] = (threadID + 1) * binRange - 1;
402 m_ThreaderJointPDFStartBin[this->m_NumberOfThreads-1] =
403 (this->m_NumberOfThreads - 1 ) * binRange;
405 m_ThreaderJointPDFEndBin[this->m_NumberOfThreads-1] = m_NumberOfHistogramBins - 1;
407 // Release memory of arrays that may have been used for
408 // previous executions of this metric with different settings
409 // of the memory caching flags.
410 if(m_ThreaderJointPDFDerivatives != NULL) {
411 delete [] m_ThreaderJointPDFDerivatives;
413 m_ThreaderJointPDFDerivatives = NULL;
415 if(m_ThreaderMetricDerivative != NULL) {
416 delete [] m_ThreaderMetricDerivative;
418 m_ThreaderMetricDerivative = NULL;
421 if( this->m_UseExplicitPDFDerivatives ) {
422 m_ThreaderJointPDFDerivatives = new typename
423 JointPDFDerivativesType::Pointer[this->m_NumberOfThreads-1];
425 for(threadID = 0; threadID < this->m_NumberOfThreads-1; threadID++) {
426 m_ThreaderJointPDFDerivatives[threadID] = JointPDFDerivativesType::New();
427 m_ThreaderJointPDFDerivatives[threadID]->SetRegions(
428 jointPDFDerivativesRegion );
429 m_ThreaderJointPDFDerivatives[threadID]->Allocate();
432 m_ThreaderMetricDerivative = new DerivativeType[this->m_NumberOfThreads-1];
434 for(threadID = 0; threadID < this->m_NumberOfThreads-1; threadID++) {
435 this->m_ThreaderMetricDerivative[threadID] = DerivativeType( this->GetNumberOfParameters() );
441 * Uniformly sample the fixed image domain using a random walk
443 template < class TFixedImage, class TMovingImage >
445 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
446 ::ComputeFixedImageParzenWindowIndices(
447 FixedImageSampleContainer& samples )
450 typename FixedImageSampleContainer::iterator iter;
451 typename FixedImageSampleContainer::const_iterator end=samples.end();
453 for( iter=samples.begin(); iter != end; ++iter ) {
455 // Determine parzen window arguments (see eqn 6 of Mattes paper [2]).
456 double windowTerm = static_cast<double>( (*iter).value )
457 / m_FixedImageBinSize
458 - m_FixedImageNormalizedMin;
459 unsigned int pindex = static_cast<unsigned int>( windowTerm );
461 // Make sure the extreme values are in valid bins
464 } else if ( pindex > (m_NumberOfHistogramBins - 3) ) {
465 pindex = m_NumberOfHistogramBins - 3;
468 (*iter).valueIndex = pindex;
473 template < class TFixedImage, class TMovingImage >
475 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
476 ::GetValueThreadPreProcess( unsigned int threadID,
477 bool withinSampleThread ) const
480 this->Superclass::GetValueThreadPreProcess( threadID, withinSampleThread );
483 memset( m_ThreaderJointPDF[threadID-1]->GetBufferPointer(),
485 m_JointPDFBufferSize );
486 memset( &(m_ThreaderFixedImageMarginalPDF[(threadID-1)
487 *m_NumberOfHistogramBins]),
489 m_NumberOfHistogramBins*sizeof(PDFValueType) );
491 // zero-th thread uses the variables directly
492 memset( m_JointPDF->GetBufferPointer(),
494 m_JointPDFBufferSize );
495 memset( m_FixedImageMarginalPDF,
497 m_NumberOfHistogramBins*sizeof(PDFValueType) );
501 template < class TFixedImage, class TMovingImage >
503 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
504 ::GetValueThreadProcessSample( unsigned int threadID,
505 unsigned long fixedImageSample,
506 const MovingImagePointType & itkNotUsed(mappedPoint),
507 double movingImageValue) const
510 * Compute this sample's contribution to the marginal and
511 * joint distributions.
515 if(movingImageValue < m_MovingImageTrueMin) {
517 } else if(movingImageValue > m_MovingImageTrueMax) {
521 // Determine parzen window arguments (see eqn 6 of Mattes paper [2]).
522 double movingImageParzenWindowTerm = movingImageValue
523 / m_MovingImageBinSize
524 - m_MovingImageNormalizedMin;
526 unsigned int movingImageParzenWindowIndex =
527 static_cast<unsigned int>( movingImageParzenWindowTerm );
528 if( movingImageParzenWindowIndex < 2 ) {
529 movingImageParzenWindowIndex = 2;
530 } else if( movingImageParzenWindowIndex > (m_NumberOfHistogramBins - 3) ) {
531 movingImageParzenWindowIndex = m_NumberOfHistogramBins - 3;
534 unsigned int fixedImageParzenWindowIndex =
535 this->m_FixedImageSamples[fixedImageSample].valueIndex;
537 m_ThreaderFixedImageMarginalPDF[(threadID-1)*m_NumberOfHistogramBins
538 + fixedImageParzenWindowIndex] += 1;
540 m_FixedImageMarginalPDF[fixedImageParzenWindowIndex] += 1;
543 // Pointer to affected bin to be updated
544 JointPDFValueType *pdfPtr;
546 pdfPtr = m_ThreaderJointPDF[threadID-1]->GetBufferPointer() +
547 ( fixedImageParzenWindowIndex
548 * m_ThreaderJointPDF[threadID-1]
549 ->GetOffsetTable()[1] );
551 pdfPtr = m_JointPDF->GetBufferPointer() +
552 ( fixedImageParzenWindowIndex
553 * m_JointPDF->GetOffsetTable()[1] );
556 // Move the pointer to the first affected bin
557 int pdfMovingIndex = static_cast<int>( movingImageParzenWindowIndex ) - 1;
558 pdfPtr += pdfMovingIndex;
559 int pdfMovingIndexMax = static_cast<int>(movingImageParzenWindowIndex) + 2;
561 double movingImageParzenWindowArg =
562 static_cast<double>( pdfMovingIndex )
563 - movingImageParzenWindowTerm;
565 while( pdfMovingIndex <= pdfMovingIndexMax ) {
566 *(pdfPtr++) += static_cast<PDFValueType>( m_CubicBSplineKernel
568 movingImageParzenWindowArg ) );
569 movingImageParzenWindowArg += 1;
576 template < class TFixedImage, class TMovingImage >
578 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
579 ::GetValueThreadPostProcess( unsigned int threadID,
580 bool itkNotUsed(withinSampleThread) ) const
585 maxI = m_NumberOfHistogramBins
586 * ( m_ThreaderJointPDFEndBin[threadID]
587 - m_ThreaderJointPDFStartBin[threadID] + 1);
588 JointPDFValueType *pdfPtr;
589 JointPDFValueType *pdfPtrStart;
590 pdfPtrStart = m_JointPDF->GetBufferPointer()
591 + ( m_ThreaderJointPDFStartBin[threadID]
592 * m_JointPDF->GetOffsetTable()[1] );
593 JointPDFValueType *tPdfPtr;
594 JointPDFValueType *tPdfPtrEnd;
595 unsigned int tPdfPtrOffset;
596 tPdfPtrOffset = ( m_ThreaderJointPDFStartBin[threadID]
597 * m_JointPDF->GetOffsetTable()[1] );
598 for(t=0; t<this->m_NumberOfThreads-1; t++) {
599 pdfPtr = pdfPtrStart;
600 tPdfPtr = m_ThreaderJointPDF[t]->GetBufferPointer() + tPdfPtrOffset;
601 tPdfPtrEnd = tPdfPtr + maxI;
602 //for(i=0; i < maxI; i++)
603 while(tPdfPtr < tPdfPtrEnd) {
604 *(pdfPtr++) += *(tPdfPtr++);
606 for(i = m_ThreaderJointPDFStartBin[threadID];
607 i <= m_ThreaderJointPDFEndBin[threadID];
609 m_FixedImageMarginalPDF[i] += m_ThreaderFixedImageMarginalPDF[
610 (t*m_NumberOfHistogramBins) + i];
613 double jointPDFSum = 0.0;
614 pdfPtr = pdfPtrStart;
615 for(i = 0; i < maxI; i++) {
616 jointPDFSum += *(pdfPtr++);
619 m_ThreaderJointPDFSum[threadID-1] = jointPDFSum;
621 m_JointPDFSum = jointPDFSum;
625 template < class TFixedImage, class TMovingImage >
626 typename MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
628 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
629 ::GetValue( const ParametersType & parameters ) const
631 // Set up the parameters in the transform
632 this->m_Transform->SetParameters( parameters );
634 // MUST BE CALLED TO INITIATE PROCESSING
635 this->GetValueMultiThreadedInitiate();
637 // MUST BE CALLED TO INITIATE PROCESSING
638 this->GetValueMultiThreadedPostProcessInitiate();
640 for(unsigned int threadID = 0; threadID<this->m_NumberOfThreads-1; threadID++) {
641 m_JointPDFSum += m_ThreaderJointPDFSum[threadID];
643 if ( m_JointPDFSum == 0.0 ) {
644 itkExceptionMacro( "Joint PDF summed to zero" );
647 memset( m_MovingImageMarginalPDF,
649 m_NumberOfHistogramBins*sizeof(PDFValueType) );
651 JointPDFValueType * pdfPtr;
652 PDFValueType * movingMarginalPtr;
654 double fixedPDFSum = 0.0;
655 double nFactor = 1.0 / m_JointPDFSum;
656 pdfPtr = m_JointPDF->GetBufferPointer();
657 for(i=0; i<m_NumberOfHistogramBins; i++) {
658 fixedPDFSum += m_FixedImageMarginalPDF[i];
659 movingMarginalPtr = m_MovingImageMarginalPDF;
660 for(j=0; j<m_NumberOfHistogramBins; j++) {
661 *(pdfPtr) *= nFactor;
662 *(movingMarginalPtr++) += *(pdfPtr++);
666 if( this->m_NumberOfPixelsCounted <
667 this->m_NumberOfFixedImageSamples / 16 ) {
668 itkExceptionMacro( "Too many samples map outside moving image buffer: "
669 << this->m_NumberOfPixelsCounted << " / "
670 << this->m_NumberOfFixedImageSamples
674 // Normalize the fixed image marginal PDF
675 if ( fixedPDFSum == 0.0 ) {
676 itkExceptionMacro( "Fixed image marginal PDF summed to zero" );
678 for( unsigned int bin=0; bin < m_NumberOfHistogramBins; bin++ ) {
679 m_FixedImageMarginalPDF[bin] /= fixedPDFSum;
683 * Compute the metric by double summation over histogram.
686 // Setup pointer to point to the first bin
687 JointPDFValueType * jointPDFPtr = m_JointPDF->GetBufferPointer();
691 for( unsigned int fixedIndex = 0;
692 fixedIndex < m_NumberOfHistogramBins;
694 double fixedImagePDFValue = m_FixedImageMarginalPDF[fixedIndex];
696 for( unsigned int movingIndex = 0;
697 movingIndex < m_NumberOfHistogramBins;
698 ++movingIndex, jointPDFPtr++ ) {
699 double movingImagePDFValue = m_MovingImageMarginalPDF[movingIndex];
700 double jointPDFValue = *(jointPDFPtr);
702 // check for non-zero bin contribution
703 if( jointPDFValue > 1e-16 && movingImagePDFValue > 1e-16 ) {
705 double pRatio = vcl_log(jointPDFValue / movingImagePDFValue );
706 if( fixedImagePDFValue > 1e-16) {
707 sum += jointPDFValue * ( pRatio - vcl_log(fixedImagePDFValue ) );
710 } // end if-block to check non-zero bin contribution
711 } // end for-loop over moving index
712 } // end for-loop over fixed index
714 return static_cast<MeasureType>( -1.0 * sum );
719 template < class TFixedImage, class TMovingImage >
721 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
722 ::GetValueAndDerivativeThreadPreProcess( unsigned int threadID,
723 bool itkNotUsed(withinSampleThread) ) const
726 memset( m_ThreaderJointPDF[threadID-1]->GetBufferPointer(),
728 m_JointPDFBufferSize );
730 memset( &(m_ThreaderFixedImageMarginalPDF[(threadID-1)
731 * m_NumberOfHistogramBins]),
733 m_NumberOfHistogramBins*sizeof(PDFValueType) );
735 if( this->m_UseExplicitPDFDerivatives ) {
736 memset( m_ThreaderJointPDFDerivatives[threadID-1]->GetBufferPointer(),
738 m_JointPDFDerivativesBufferSize );
741 memset( m_JointPDF->GetBufferPointer(),
743 m_JointPDFBufferSize );
744 memset( m_FixedImageMarginalPDF,
746 m_NumberOfHistogramBins*sizeof(PDFValueType) );
748 if( this->m_UseExplicitPDFDerivatives ) {
749 memset( m_JointPDFDerivatives->GetBufferPointer(),
751 m_JointPDFDerivativesBufferSize );
756 template < class TFixedImage, class TMovingImage >
758 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
759 ::GetValueAndDerivativeThreadProcessSample( unsigned int threadID,
760 unsigned long fixedImageSample,
761 const MovingImagePointType & itkNotUsed(mappedPoint),
762 double movingImageValue,
763 const ImageDerivativesType &
764 movingImageGradientValue) const
767 * Compute this sample's contribution to the marginal
768 * and joint distributions.
771 if(movingImageValue < m_MovingImageTrueMin) {
773 } else if(movingImageValue > m_MovingImageTrueMax) {
777 unsigned int fixedImageParzenWindowIndex =
778 this->m_FixedImageSamples[fixedImageSample].valueIndex;
780 // Determine parzen window arguments (see eqn 6 of Mattes paper [2]).
781 double movingImageParzenWindowTerm = movingImageValue
782 / m_MovingImageBinSize
783 - m_MovingImageNormalizedMin;
784 unsigned int movingImageParzenWindowIndex =
785 static_cast<unsigned int>( movingImageParzenWindowTerm );
787 // Make sure the extreme values are in valid bins
788 if ( movingImageParzenWindowIndex < 2 ) {
789 movingImageParzenWindowIndex = 2;
790 } else if ( movingImageParzenWindowIndex > (m_NumberOfHistogramBins - 3) ) {
791 movingImageParzenWindowIndex = m_NumberOfHistogramBins - 3;
794 // Since a zero-order BSpline (box car) kernel is used for
795 // the fixed image marginal pdf, we need only increment the
796 // fixedImageParzenWindowIndex by value of 1.0.
798 ++m_ThreaderFixedImageMarginalPDF[(threadID-1)*m_NumberOfHistogramBins
799 + fixedImageParzenWindowIndex];
801 ++m_FixedImageMarginalPDF[fixedImageParzenWindowIndex];
805 * The region of support of the parzen window determines which bins
806 * of the joint PDF are effected by the pair of image values.
807 * Since we are using a cubic spline for the moving image parzen
808 * window, four bins are effected. The fixed image parzen window is
809 * a zero-order spline (box car) and thus effects only one bin.
811 * The PDF is arranged so that moving image bins corresponds to the
812 * zero-th (column) dimension and the fixed image bins corresponds
813 * to the first (row) dimension.
817 // Pointer to affected bin to be updated
818 JointPDFValueType *pdfPtr;
820 pdfPtr = m_ThreaderJointPDF[threadID-1]
821 ->GetBufferPointer() +
822 ( fixedImageParzenWindowIndex
823 * m_NumberOfHistogramBins );
825 pdfPtr = m_JointPDF->GetBufferPointer() +
826 ( fixedImageParzenWindowIndex
827 * m_NumberOfHistogramBins );
830 // Move the pointer to the fist affected bin
831 int pdfMovingIndex = static_cast<int>( movingImageParzenWindowIndex ) - 1;
832 pdfPtr += pdfMovingIndex;
833 int pdfMovingIndexMax = static_cast<int>(movingImageParzenWindowIndex) + 2;
835 double movingImageParzenWindowArg = static_cast<double>( pdfMovingIndex )
836 - static_cast<double>( movingImageParzenWindowTerm );
838 while( pdfMovingIndex <= pdfMovingIndexMax ) {
839 *(pdfPtr++) += static_cast<PDFValueType>( m_CubicBSplineKernel
841 movingImageParzenWindowArg ) );
843 if( this->m_UseExplicitPDFDerivatives || this->m_ImplicitDerivativesSecondPass ) {
844 // Compute the cubicBSplineDerivative for later repeated use.
845 double cubicBSplineDerivativeValue =
846 m_CubicBSplineDerivativeKernel->Evaluate( movingImageParzenWindowArg );
848 // Compute PDF derivative contribution.
849 this->ComputePDFDerivatives( threadID,
852 movingImageGradientValue,
853 cubicBSplineDerivativeValue );
856 movingImageParzenWindowArg += 1;
863 template < class TFixedImage, class TMovingImage >
865 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
866 ::GetValueAndDerivativeThreadPostProcess( unsigned int threadID,
867 bool withinSampleThread ) const
869 this->GetValueThreadPostProcess( threadID, withinSampleThread );
871 if( this->m_UseExplicitPDFDerivatives ) {
872 const unsigned int rowSize = this->m_NumberOfParameters * m_NumberOfHistogramBins;
874 const unsigned int maxI =
875 rowSize * ( m_ThreaderJointPDFEndBin[threadID]
876 - m_ThreaderJointPDFStartBin[threadID] + 1 );
878 JointPDFDerivativesValueType *pdfDPtr;
879 JointPDFDerivativesValueType *pdfDPtrStart;
880 pdfDPtrStart = m_JointPDFDerivatives->GetBufferPointer()
881 + ( m_ThreaderJointPDFStartBin[threadID] * rowSize );
882 JointPDFDerivativesValueType *tPdfDPtr;
883 JointPDFDerivativesValueType *tPdfDPtrEnd;
884 unsigned int tPdfDPtrOffset;
885 tPdfDPtrOffset = m_ThreaderJointPDFStartBin[threadID] * rowSize;
886 for(unsigned int t=0; t<this->m_NumberOfThreads-1; t++) {
887 pdfDPtr = pdfDPtrStart;
888 tPdfDPtr = m_ThreaderJointPDFDerivatives[t]->GetBufferPointer()
890 tPdfDPtrEnd = tPdfDPtr + maxI;
891 // for(i = 0; i < maxI; i++)
892 while(tPdfDPtr < tPdfDPtrEnd) {
893 *(pdfDPtr++) += *(tPdfDPtr++);
897 double nFactor = 1.0 / (m_MovingImageBinSize
898 * this->m_NumberOfPixelsCounted);
900 pdfDPtr = pdfDPtrStart;
901 tPdfDPtrEnd = pdfDPtrStart + maxI;
902 //for(int i = 0; i < maxI; i++)
903 while(pdfDPtr < tPdfDPtrEnd) {
904 *(pdfDPtr++) *= nFactor;
910 * Get the both Value and Derivative Measure
912 template < class TFixedImage, class TMovingImage >
914 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
915 ::GetValueAndDerivative( const ParametersType & parameters,
917 DerivativeType & derivative) const
919 // Set output values to zero
920 value = NumericTraits< MeasureType >::Zero;
922 if( this->m_UseExplicitPDFDerivatives ) {
923 // Set output values to zero
924 if(derivative.GetSize() != this->m_NumberOfParameters) {
925 derivative = DerivativeType( this->m_NumberOfParameters );
927 memset( derivative.data_block(),
929 this->m_NumberOfParameters * sizeof(double) );
931 this->m_PRatioArray.Fill( 0.0 );
932 this->m_MetricDerivative.Fill( NumericTraits< MeasureType >::Zero );
933 for(unsigned int threadID = 0; threadID < this->m_NumberOfThreads-1; threadID++ ) {
934 this->m_ThreaderMetricDerivative[threadID].Fill( NumericTraits< MeasureType >::Zero );
936 this->m_ImplicitDerivativesSecondPass = false;
939 // Set up the parameters in the transform
940 this->m_Transform->SetParameters( parameters );
942 // MUST BE CALLED TO INITIATE PROCESSING ON SAMPLES
943 this->GetValueAndDerivativeMultiThreadedInitiate();
945 // CALL IF DOING THREADED POST PROCESSING
946 this->GetValueAndDerivativeMultiThreadedPostProcessInitiate();
948 for(unsigned int threadID = 0; threadID<this->m_NumberOfThreads-1; threadID++) {
949 m_JointPDFSum += m_ThreaderJointPDFSum[threadID];
951 if ( m_JointPDFSum == 0.0 ) {
952 itkExceptionMacro( "Joint PDF summed to zero" );
955 memset( m_MovingImageMarginalPDF,
957 m_NumberOfHistogramBins*sizeof(PDFValueType) );
959 JointPDFValueType * pdfPtr;
960 PDFValueType * movingMarginalPtr;
962 double fixedPDFSum = 0.0;
963 const double normalizationFactor = 1.0 / m_JointPDFSum;
965 pdfPtr = m_JointPDF->GetBufferPointer();
966 for(i=0; i<m_NumberOfHistogramBins; i++) {
967 fixedPDFSum += m_FixedImageMarginalPDF[i];
968 movingMarginalPtr = m_MovingImageMarginalPDF;
969 for(j=0; j<m_NumberOfHistogramBins; j++) {
970 *(pdfPtr) *= normalizationFactor;
971 *(movingMarginalPtr++) += *(pdfPtr++);
975 if( this->m_NumberOfPixelsCounted <
976 this->m_NumberOfFixedImageSamples / 16 ) {
977 itkExceptionMacro( "Too many samples map outside moving image buffer: "
978 << this->m_NumberOfPixelsCounted << " / "
979 << this->m_NumberOfFixedImageSamples
983 // Normalize the fixed image marginal PDF
984 if ( fixedPDFSum == 0.0 ) {
985 itkExceptionMacro( "Fixed image marginal PDF summed to zero" );
987 for( unsigned int bin=0; bin < m_NumberOfHistogramBins; bin++ ) {
988 m_FixedImageMarginalPDF[bin] /= fixedPDFSum;
992 * Compute the metric by double summation over histogram.
995 // Setup pointer to point to the first bin
996 JointPDFValueType * jointPDFPtr = m_JointPDF->GetBufferPointer();
998 // Initialize sum to zero
1001 const double nFactor = 1.0 / (m_MovingImageBinSize
1002 * this->m_NumberOfPixelsCounted);
1004 for( unsigned int fixedIndex = 0;
1005 fixedIndex < m_NumberOfHistogramBins;
1007 double fixedImagePDFValue = m_FixedImageMarginalPDF[fixedIndex];
1009 for( unsigned int movingIndex = 0;
1010 movingIndex < m_NumberOfHistogramBins;
1011 ++movingIndex, jointPDFPtr++ ) {
1012 double movingImagePDFValue = m_MovingImageMarginalPDF[movingIndex];
1013 double jointPDFValue = *(jointPDFPtr);
1015 // check for non-zero bin contribution
1016 if( jointPDFValue > 1e-16 && movingImagePDFValue > 1e-16 ) {
1018 double pRatio = vcl_log(jointPDFValue / movingImagePDFValue );
1020 if( fixedImagePDFValue > 1e-16) {
1021 sum += jointPDFValue * ( pRatio - vcl_log(fixedImagePDFValue ) );
1024 if( this->m_UseExplicitPDFDerivatives ) {
1025 // move joint pdf derivative pointer to the right position
1026 JointPDFValueType * derivPtr = m_JointPDFDerivatives->GetBufferPointer()
1027 + ( fixedIndex * m_JointPDFDerivatives->GetOffsetTable()[2] )
1028 + ( movingIndex * m_JointPDFDerivatives->GetOffsetTable()[1] );
1030 for( unsigned int parameter=0; parameter < this->m_NumberOfParameters; ++parameter, derivPtr++ ) {
1032 // Ref: eqn 23 of Thevenaz & Unser paper [3]
1033 derivative[parameter] -= (*derivPtr) * pRatio;
1035 } // end for-loop over parameters
1037 this->m_PRatioArray[fixedIndex][movingIndex] = pRatio * nFactor;
1039 } // end if-block to check non-zero bin contribution
1040 } // end for-loop over moving index
1041 } // end for-loop over fixed index
1043 if( !(this->m_UseExplicitPDFDerivatives ) ) {
1044 // Second pass: This one is done for accumulating the contributions
1045 // to the derivative array.
1047 this->m_ImplicitDerivativesSecondPass = true;
1049 // MUST BE CALLED TO INITIATE PROCESSING ON SAMPLES
1050 this->GetValueAndDerivativeMultiThreadedInitiate();
1052 // CALL IF DOING THREADED POST PROCESSING
1053 this->GetValueAndDerivativeMultiThreadedPostProcessInitiate();
1055 // Consolidate the contributions from each one of the threads to the total
1057 for(unsigned int t = 0; t < this->m_NumberOfThreads-1; t++ ) {
1058 DerivativeType * source = &(this->m_ThreaderMetricDerivative[t]);
1059 for(unsigned int pp=0; pp < this->m_NumberOfParameters; pp++ ) {
1060 this->m_MetricDerivative[pp] += (*source)[pp];
1064 derivative = this->m_MetricDerivative;
1067 value = static_cast<MeasureType>( -1.0 * sum );
1072 * Get the match measure derivative
1074 template < class TFixedImage, class TMovingImage >
1076 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
1077 ::GetDerivative( const ParametersType & parameters,
1078 DerivativeType & derivative ) const
1081 // call the combined version
1082 this->GetValueAndDerivative( parameters, value, derivative );
1087 * Compute PDF derivatives contribution for each parameter
1089 template < class TFixedImage, class TMovingImage >
1091 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
1092 ::ComputePDFDerivatives( unsigned int threadID,
1093 unsigned int sampleNumber,
1095 const ImageDerivativesType & movingImageGradientValue,
1096 double cubicBSplineDerivativeValue ) const
1098 // Update bins in the PDF derivatives for the current intensity pair
1099 // Could pre-compute
1100 JointPDFDerivativesValueType * derivPtr;
1102 double precomputedWeight = 0.0;
1104 const int pdfFixedIndex = this->m_FixedImageSamples[sampleNumber].valueIndex;
1106 DerivativeType * derivativeHelperArray = NULL;
1108 if( this->m_UseExplicitPDFDerivatives ) {
1110 derivPtr = m_ThreaderJointPDFDerivatives[threadID-1]->GetBufferPointer()
1111 + ( pdfFixedIndex * m_JointPDFDerivatives->GetOffsetTable()[2] )
1112 + ( pdfMovingIndex * m_JointPDFDerivatives->GetOffsetTable()[1] );
1114 derivPtr = m_JointPDFDerivatives->GetBufferPointer()
1115 + ( pdfFixedIndex * m_JointPDFDerivatives->GetOffsetTable()[2] )
1116 + ( pdfMovingIndex * m_JointPDFDerivatives->GetOffsetTable()[1] );
1120 // Recover the precomputed weight for this specific PDF bin
1121 precomputedWeight = this->m_PRatioArray[pdfFixedIndex][pdfMovingIndex];
1123 derivativeHelperArray = &(this->m_ThreaderMetricDerivative[threadID-1]);
1125 derivativeHelperArray = &(this->m_MetricDerivative);
1130 if( !this->m_TransformIsBSpline ) {
1132 * Generic version which works for all transforms.
1135 // Compute the transform Jacobian.
1136 // Should pre-compute
1137 typedef typename TransformType::JacobianType JacobianType;
1139 // Need to use one of the threader transforms if we're
1142 // Use a raw pointer here to avoid the overhead of smart pointers.
1143 // For instance, Register and UnRegister have mutex locks around
1144 // the reference counts.
1145 TransformType* transform;
1148 transform = this->m_ThreaderTransform[threadID - 1];
1150 transform = this->m_Transform;
1153 JacobianType jacobian;
1154 transform->ComputeJacobianWithRespectToParameters(this->m_FixedImageSamples[sampleNumber].point, jacobian);
1156 // for ( unsigned int mu = 0; mu < this->m_NumberOfParameters; mu++ )
1158 // double innerProduct = 0.0;
1159 // for ( unsigned int dim = 0; dim < Superclass::FixedImageDimension; dim++ )
1161 // innerProduct += jacobian[dim][mu] * movingImageGradientValue[dim];
1164 // const double derivativeContribution = innerProduct * cubicBSplineDerivativeValue;
1166 // if( this->m_UseExplicitPDFDerivatives )
1168 // *(derivPtr) -= derivativeContribution;
1173 // (*derivativeHelperArray)[mu] += precomputedWeight * derivativeContribution;
1178 unsigned int mu, dim;
1179 double innerProduct,derivativeContribution;
1180 for ( mu = 0; mu < this->m_NumberOfParameters; mu+=3 ) {
1181 // JV only for 3D Space BLUT FFD: if J(0, par)=0, then J(1,par+1)=0 & ...
1182 if (jacobian[0][mu])
1183 for ( dim = 0; dim < Superclass::FixedImageDimension; dim++ ) {
1184 innerProduct = jacobian[dim][mu+dim] * movingImageGradientValue[dim];
1185 derivativeContribution = innerProduct * cubicBSplineDerivativeValue;
1187 if( this->m_UseExplicitPDFDerivatives ) {
1188 *(derivPtr) -= derivativeContribution;
1191 (*derivativeHelperArray)[mu+dim] += precomputedWeight * derivativeContribution;
1194 else if( this->m_UseExplicitPDFDerivatives ) derivPtr+=3;
1197 const WeightsValueType * weights = NULL;
1198 const IndexValueType * indices = NULL;
1201 BSplineTransformWeightsType * weightsHelper = NULL;
1202 BSplineTransformIndexArrayType * indicesHelper = NULL;
1204 if( this->m_UseCachingOfBSplineWeights ) {
1206 // If the transform is of type BSplineDeformableTransform, we can obtain
1207 // a speed up by only processing the affected parameters. Note that
1208 // these pointers are just pointing to pre-allocated rows of the caching
1209 // arrays. There is therefore, no need to free this memory.
1211 weights = this->m_BSplineTransformWeightsArray[sampleNumber];
1212 indices = this->m_BSplineTransformIndicesArray[sampleNumber];
1214 if( threadID > 0 ) {
1215 weightsHelper = &(this->m_ThreaderBSplineTransformWeights[threadID-1]);
1216 indicesHelper = &(this->m_ThreaderBSplineTransformIndices[threadID-1]);
1218 weightsHelper = &(this->m_BSplineTransformWeights);
1219 indicesHelper = &(this->m_BSplineTransformIndices);
1222 this->m_BSplineTransform->ComputeJacobianFromBSplineWeightsWithRespectToPosition(
1223 this->m_FixedImageSamples[sampleNumber].point,
1224 *weightsHelper, *indicesHelper );
1227 for( unsigned int dim = 0; dim < Superclass::FixedImageDimension; dim++ ) {
1229 double innerProduct;
1232 for( unsigned int mu = 0; mu < this->m_NumBSplineWeights; mu++ ) {
1234 /* The array weights contains the Jacobian values in a 1-D array
1235 * (because for each parameter the Jacobian is non-zero in only 1 of the
1236 * possible dimensions) which is multiplied by the moving image
1238 if( this->m_UseCachingOfBSplineWeights ) {
1239 innerProduct = movingImageGradientValue[dim] * weights[mu];
1240 parameterIndex = indices[mu] + this->m_BSplineParametersOffset[dim];
1242 innerProduct = movingImageGradientValue[dim] * (*weightsHelper)[mu];
1243 parameterIndex = (*indicesHelper)[mu] + this->m_BSplineParametersOffset[dim];
1246 const double derivativeContribution = innerProduct * cubicBSplineDerivativeValue;
1248 if( this->m_UseExplicitPDFDerivatives ) {
1249 JointPDFValueType * ptr = derivPtr + parameterIndex;
1250 *(ptr) -= derivativeContribution;
1252 (*derivativeHelperArray)[parameterIndex] += precomputedWeight * derivativeContribution;
1256 } //end dim for loop
1258 } // end if-block transform is BSpline
1263 } // end namespace itk