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.txx"
48 #include "vnl/vnl_c_vector.txx"
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 );
633 #if ITK_VERSION_MAJOR < 4
634 this->m_Parameters = parameters;
637 // MUST BE CALLED TO INITIATE PROCESSING
638 this->GetValueMultiThreadedInitiate();
640 // MUST BE CALLED TO INITIATE PROCESSING
641 this->GetValueMultiThreadedPostProcessInitiate();
643 for(unsigned int threadID = 0; threadID<this->m_NumberOfThreads-1; threadID++) {
644 m_JointPDFSum += m_ThreaderJointPDFSum[threadID];
646 if ( m_JointPDFSum == 0.0 ) {
647 itkExceptionMacro( "Joint PDF summed to zero" );
650 memset( m_MovingImageMarginalPDF,
652 m_NumberOfHistogramBins*sizeof(PDFValueType) );
654 JointPDFValueType * pdfPtr;
655 PDFValueType * movingMarginalPtr;
657 double fixedPDFSum = 0.0;
658 double nFactor = 1.0 / m_JointPDFSum;
659 pdfPtr = m_JointPDF->GetBufferPointer();
660 for(i=0; i<m_NumberOfHistogramBins; i++) {
661 fixedPDFSum += m_FixedImageMarginalPDF[i];
662 movingMarginalPtr = m_MovingImageMarginalPDF;
663 for(j=0; j<m_NumberOfHistogramBins; j++) {
664 *(pdfPtr) *= nFactor;
665 *(movingMarginalPtr++) += *(pdfPtr++);
669 if( this->m_NumberOfPixelsCounted <
670 this->m_NumberOfFixedImageSamples / 16 ) {
671 itkExceptionMacro( "Too many samples map outside moving image buffer: "
672 << this->m_NumberOfPixelsCounted << " / "
673 << this->m_NumberOfFixedImageSamples
677 // Normalize the fixed image marginal PDF
678 if ( fixedPDFSum == 0.0 ) {
679 itkExceptionMacro( "Fixed image marginal PDF summed to zero" );
681 for( unsigned int bin=0; bin < m_NumberOfHistogramBins; bin++ ) {
682 m_FixedImageMarginalPDF[bin] /= fixedPDFSum;
686 * Compute the metric by double summation over histogram.
689 // Setup pointer to point to the first bin
690 JointPDFValueType * jointPDFPtr = m_JointPDF->GetBufferPointer();
694 for( unsigned int fixedIndex = 0;
695 fixedIndex < m_NumberOfHistogramBins;
697 double fixedImagePDFValue = m_FixedImageMarginalPDF[fixedIndex];
699 for( unsigned int movingIndex = 0;
700 movingIndex < m_NumberOfHistogramBins;
701 ++movingIndex, jointPDFPtr++ ) {
702 double movingImagePDFValue = m_MovingImageMarginalPDF[movingIndex];
703 double jointPDFValue = *(jointPDFPtr);
705 // check for non-zero bin contribution
706 if( jointPDFValue > 1e-16 && movingImagePDFValue > 1e-16 ) {
708 double pRatio = vcl_log(jointPDFValue / movingImagePDFValue );
709 if( fixedImagePDFValue > 1e-16) {
710 sum += jointPDFValue * ( pRatio - vcl_log(fixedImagePDFValue ) );
713 } // end if-block to check non-zero bin contribution
714 } // end for-loop over moving index
715 } // end for-loop over fixed index
717 return static_cast<MeasureType>( -1.0 * sum );
722 template < class TFixedImage, class TMovingImage >
724 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
725 ::GetValueAndDerivativeThreadPreProcess( unsigned int threadID,
726 bool itkNotUsed(withinSampleThread) ) const
729 memset( m_ThreaderJointPDF[threadID-1]->GetBufferPointer(),
731 m_JointPDFBufferSize );
733 memset( &(m_ThreaderFixedImageMarginalPDF[(threadID-1)
734 * m_NumberOfHistogramBins]),
736 m_NumberOfHistogramBins*sizeof(PDFValueType) );
738 if( this->m_UseExplicitPDFDerivatives ) {
739 memset( m_ThreaderJointPDFDerivatives[threadID-1]->GetBufferPointer(),
741 m_JointPDFDerivativesBufferSize );
744 memset( m_JointPDF->GetBufferPointer(),
746 m_JointPDFBufferSize );
747 memset( m_FixedImageMarginalPDF,
749 m_NumberOfHistogramBins*sizeof(PDFValueType) );
751 if( this->m_UseExplicitPDFDerivatives ) {
752 memset( m_JointPDFDerivatives->GetBufferPointer(),
754 m_JointPDFDerivativesBufferSize );
759 template < class TFixedImage, class TMovingImage >
761 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
762 ::GetValueAndDerivativeThreadProcessSample( unsigned int threadID,
763 unsigned long fixedImageSample,
764 const MovingImagePointType & itkNotUsed(mappedPoint),
765 double movingImageValue,
766 const ImageDerivativesType &
767 movingImageGradientValue) const
770 * Compute this sample's contribution to the marginal
771 * and joint distributions.
774 if(movingImageValue < m_MovingImageTrueMin) {
776 } else if(movingImageValue > m_MovingImageTrueMax) {
780 unsigned int fixedImageParzenWindowIndex =
781 this->m_FixedImageSamples[fixedImageSample].valueIndex;
783 // Determine parzen window arguments (see eqn 6 of Mattes paper [2]).
784 double movingImageParzenWindowTerm = movingImageValue
785 / m_MovingImageBinSize
786 - m_MovingImageNormalizedMin;
787 unsigned int movingImageParzenWindowIndex =
788 static_cast<unsigned int>( movingImageParzenWindowTerm );
790 // Make sure the extreme values are in valid bins
791 if ( movingImageParzenWindowIndex < 2 ) {
792 movingImageParzenWindowIndex = 2;
793 } else if ( movingImageParzenWindowIndex > (m_NumberOfHistogramBins - 3) ) {
794 movingImageParzenWindowIndex = m_NumberOfHistogramBins - 3;
797 // Since a zero-order BSpline (box car) kernel is used for
798 // the fixed image marginal pdf, we need only increment the
799 // fixedImageParzenWindowIndex by value of 1.0.
801 ++m_ThreaderFixedImageMarginalPDF[(threadID-1)*m_NumberOfHistogramBins
802 + fixedImageParzenWindowIndex];
804 ++m_FixedImageMarginalPDF[fixedImageParzenWindowIndex];
808 * The region of support of the parzen window determines which bins
809 * of the joint PDF are effected by the pair of image values.
810 * Since we are using a cubic spline for the moving image parzen
811 * window, four bins are effected. The fixed image parzen window is
812 * a zero-order spline (box car) and thus effects only one bin.
814 * The PDF is arranged so that moving image bins corresponds to the
815 * zero-th (column) dimension and the fixed image bins corresponds
816 * to the first (row) dimension.
820 // Pointer to affected bin to be updated
821 JointPDFValueType *pdfPtr;
823 pdfPtr = m_ThreaderJointPDF[threadID-1]
824 ->GetBufferPointer() +
825 ( fixedImageParzenWindowIndex
826 * m_NumberOfHistogramBins );
828 pdfPtr = m_JointPDF->GetBufferPointer() +
829 ( fixedImageParzenWindowIndex
830 * m_NumberOfHistogramBins );
833 // Move the pointer to the fist affected bin
834 int pdfMovingIndex = static_cast<int>( movingImageParzenWindowIndex ) - 1;
835 pdfPtr += pdfMovingIndex;
836 int pdfMovingIndexMax = static_cast<int>(movingImageParzenWindowIndex) + 2;
838 double movingImageParzenWindowArg = static_cast<double>( pdfMovingIndex )
839 - static_cast<double>( movingImageParzenWindowTerm );
841 while( pdfMovingIndex <= pdfMovingIndexMax ) {
842 *(pdfPtr++) += static_cast<PDFValueType>( m_CubicBSplineKernel
844 movingImageParzenWindowArg ) );
846 if( this->m_UseExplicitPDFDerivatives || this->m_ImplicitDerivativesSecondPass ) {
847 // Compute the cubicBSplineDerivative for later repeated use.
848 double cubicBSplineDerivativeValue =
849 m_CubicBSplineDerivativeKernel->Evaluate( movingImageParzenWindowArg );
851 // Compute PDF derivative contribution.
852 this->ComputePDFDerivatives( threadID,
855 movingImageGradientValue,
856 cubicBSplineDerivativeValue );
859 movingImageParzenWindowArg += 1;
866 template < class TFixedImage, class TMovingImage >
868 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
869 ::GetValueAndDerivativeThreadPostProcess( unsigned int threadID,
870 bool withinSampleThread ) const
872 this->GetValueThreadPostProcess( threadID, withinSampleThread );
874 if( this->m_UseExplicitPDFDerivatives ) {
875 const unsigned int rowSize = this->m_NumberOfParameters * m_NumberOfHistogramBins;
877 const unsigned int maxI =
878 rowSize * ( m_ThreaderJointPDFEndBin[threadID]
879 - m_ThreaderJointPDFStartBin[threadID] + 1 );
881 JointPDFDerivativesValueType *pdfDPtr;
882 JointPDFDerivativesValueType *pdfDPtrStart;
883 pdfDPtrStart = m_JointPDFDerivatives->GetBufferPointer()
884 + ( m_ThreaderJointPDFStartBin[threadID] * rowSize );
885 JointPDFDerivativesValueType *tPdfDPtr;
886 JointPDFDerivativesValueType *tPdfDPtrEnd;
887 unsigned int tPdfDPtrOffset;
888 tPdfDPtrOffset = m_ThreaderJointPDFStartBin[threadID] * rowSize;
889 for(unsigned int t=0; t<this->m_NumberOfThreads-1; t++) {
890 pdfDPtr = pdfDPtrStart;
891 tPdfDPtr = m_ThreaderJointPDFDerivatives[t]->GetBufferPointer()
893 tPdfDPtrEnd = tPdfDPtr + maxI;
894 // for(i = 0; i < maxI; i++)
895 while(tPdfDPtr < tPdfDPtrEnd) {
896 *(pdfDPtr++) += *(tPdfDPtr++);
900 double nFactor = 1.0 / (m_MovingImageBinSize
901 * this->m_NumberOfPixelsCounted);
903 pdfDPtr = pdfDPtrStart;
904 tPdfDPtrEnd = pdfDPtrStart + maxI;
905 //for(int i = 0; i < maxI; i++)
906 while(pdfDPtr < tPdfDPtrEnd) {
907 *(pdfDPtr++) *= nFactor;
913 * Get the both Value and Derivative Measure
915 template < class TFixedImage, class TMovingImage >
917 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
918 ::GetValueAndDerivative( const ParametersType & parameters,
920 DerivativeType & derivative) const
922 // Set output values to zero
923 value = NumericTraits< MeasureType >::Zero;
925 if( this->m_UseExplicitPDFDerivatives ) {
926 // Set output values to zero
927 if(derivative.GetSize() != this->m_NumberOfParameters) {
928 derivative = DerivativeType( this->m_NumberOfParameters );
930 memset( derivative.data_block(),
932 this->m_NumberOfParameters * sizeof(double) );
934 this->m_PRatioArray.Fill( 0.0 );
935 this->m_MetricDerivative.Fill( NumericTraits< MeasureType >::Zero );
936 for(unsigned int threadID = 0; threadID < this->m_NumberOfThreads-1; threadID++ ) {
937 this->m_ThreaderMetricDerivative[threadID].Fill( NumericTraits< MeasureType >::Zero );
939 this->m_ImplicitDerivativesSecondPass = false;
942 // Set up the parameters in the transform
943 this->m_Transform->SetParameters( parameters );
944 #if ITK_VERSION_MAJOR < 4
945 this->m_Parameters = parameters;
948 // MUST BE CALLED TO INITIATE PROCESSING ON SAMPLES
949 this->GetValueAndDerivativeMultiThreadedInitiate();
951 // CALL IF DOING THREADED POST PROCESSING
952 this->GetValueAndDerivativeMultiThreadedPostProcessInitiate();
954 for(unsigned int threadID = 0; threadID<this->m_NumberOfThreads-1; threadID++) {
955 m_JointPDFSum += m_ThreaderJointPDFSum[threadID];
957 if ( m_JointPDFSum == 0.0 ) {
958 itkExceptionMacro( "Joint PDF summed to zero" );
961 memset( m_MovingImageMarginalPDF,
963 m_NumberOfHistogramBins*sizeof(PDFValueType) );
965 JointPDFValueType * pdfPtr;
966 PDFValueType * movingMarginalPtr;
968 double fixedPDFSum = 0.0;
969 const double normalizationFactor = 1.0 / m_JointPDFSum;
971 pdfPtr = m_JointPDF->GetBufferPointer();
972 for(i=0; i<m_NumberOfHistogramBins; i++) {
973 fixedPDFSum += m_FixedImageMarginalPDF[i];
974 movingMarginalPtr = m_MovingImageMarginalPDF;
975 for(j=0; j<m_NumberOfHistogramBins; j++) {
976 *(pdfPtr) *= normalizationFactor;
977 *(movingMarginalPtr++) += *(pdfPtr++);
981 if( this->m_NumberOfPixelsCounted <
982 this->m_NumberOfFixedImageSamples / 16 ) {
983 itkExceptionMacro( "Too many samples map outside moving image buffer: "
984 << this->m_NumberOfPixelsCounted << " / "
985 << this->m_NumberOfFixedImageSamples
989 // Normalize the fixed image marginal PDF
990 if ( fixedPDFSum == 0.0 ) {
991 itkExceptionMacro( "Fixed image marginal PDF summed to zero" );
993 for( unsigned int bin=0; bin < m_NumberOfHistogramBins; bin++ ) {
994 m_FixedImageMarginalPDF[bin] /= fixedPDFSum;
998 * Compute the metric by double summation over histogram.
1001 // Setup pointer to point to the first bin
1002 JointPDFValueType * jointPDFPtr = m_JointPDF->GetBufferPointer();
1004 // Initialize sum to zero
1007 const double nFactor = 1.0 / (m_MovingImageBinSize
1008 * this->m_NumberOfPixelsCounted);
1010 for( unsigned int fixedIndex = 0;
1011 fixedIndex < m_NumberOfHistogramBins;
1013 double fixedImagePDFValue = m_FixedImageMarginalPDF[fixedIndex];
1015 for( unsigned int movingIndex = 0;
1016 movingIndex < m_NumberOfHistogramBins;
1017 ++movingIndex, jointPDFPtr++ ) {
1018 double movingImagePDFValue = m_MovingImageMarginalPDF[movingIndex];
1019 double jointPDFValue = *(jointPDFPtr);
1021 // check for non-zero bin contribution
1022 if( jointPDFValue > 1e-16 && movingImagePDFValue > 1e-16 ) {
1024 double pRatio = vcl_log(jointPDFValue / movingImagePDFValue );
1026 if( fixedImagePDFValue > 1e-16) {
1027 sum += jointPDFValue * ( pRatio - vcl_log(fixedImagePDFValue ) );
1030 if( this->m_UseExplicitPDFDerivatives ) {
1031 // move joint pdf derivative pointer to the right position
1032 JointPDFValueType * derivPtr = m_JointPDFDerivatives->GetBufferPointer()
1033 + ( fixedIndex * m_JointPDFDerivatives->GetOffsetTable()[2] )
1034 + ( movingIndex * m_JointPDFDerivatives->GetOffsetTable()[1] );
1036 for( unsigned int parameter=0; parameter < this->m_NumberOfParameters; ++parameter, derivPtr++ ) {
1038 // Ref: eqn 23 of Thevenaz & Unser paper [3]
1039 derivative[parameter] -= (*derivPtr) * pRatio;
1041 } // end for-loop over parameters
1043 this->m_PRatioArray[fixedIndex][movingIndex] = pRatio * nFactor;
1045 } // end if-block to check non-zero bin contribution
1046 } // end for-loop over moving index
1047 } // end for-loop over fixed index
1049 if( !(this->m_UseExplicitPDFDerivatives ) ) {
1050 // Second pass: This one is done for accumulating the contributions
1051 // to the derivative array.
1053 this->m_ImplicitDerivativesSecondPass = true;
1055 // MUST BE CALLED TO INITIATE PROCESSING ON SAMPLES
1056 this->GetValueAndDerivativeMultiThreadedInitiate();
1058 // CALL IF DOING THREADED POST PROCESSING
1059 this->GetValueAndDerivativeMultiThreadedPostProcessInitiate();
1061 // Consolidate the contributions from each one of the threads to the total
1063 for(unsigned int t = 0; t < this->m_NumberOfThreads-1; t++ ) {
1064 DerivativeType * source = &(this->m_ThreaderMetricDerivative[t]);
1065 for(unsigned int pp=0; pp < this->m_NumberOfParameters; pp++ ) {
1066 this->m_MetricDerivative[pp] += (*source)[pp];
1070 derivative = this->m_MetricDerivative;
1073 value = static_cast<MeasureType>( -1.0 * sum );
1078 * Get the match measure derivative
1080 template < class TFixedImage, class TMovingImage >
1082 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
1083 ::GetDerivative( const ParametersType & parameters,
1084 DerivativeType & derivative ) const
1087 // call the combined version
1088 this->GetValueAndDerivative( parameters, value, derivative );
1093 * Compute PDF derivatives contribution for each parameter
1095 template < class TFixedImage, class TMovingImage >
1097 MattesMutualInformationImageToImageMetricFor3DBLUTFFD<TFixedImage,TMovingImage>
1098 ::ComputePDFDerivatives( unsigned int threadID,
1099 unsigned int sampleNumber,
1101 const ImageDerivativesType & movingImageGradientValue,
1102 double cubicBSplineDerivativeValue ) const
1104 // Update bins in the PDF derivatives for the current intensity pair
1105 // Could pre-compute
1106 JointPDFDerivativesValueType * derivPtr;
1108 double precomputedWeight = 0.0;
1110 const int pdfFixedIndex = this->m_FixedImageSamples[sampleNumber].valueIndex;
1112 DerivativeType * derivativeHelperArray = NULL;
1114 if( this->m_UseExplicitPDFDerivatives ) {
1116 derivPtr = m_ThreaderJointPDFDerivatives[threadID-1]->GetBufferPointer()
1117 + ( pdfFixedIndex * m_JointPDFDerivatives->GetOffsetTable()[2] )
1118 + ( pdfMovingIndex * m_JointPDFDerivatives->GetOffsetTable()[1] );
1120 derivPtr = m_JointPDFDerivatives->GetBufferPointer()
1121 + ( pdfFixedIndex * m_JointPDFDerivatives->GetOffsetTable()[2] )
1122 + ( pdfMovingIndex * m_JointPDFDerivatives->GetOffsetTable()[1] );
1126 // Recover the precomputed weight for this specific PDF bin
1127 precomputedWeight = this->m_PRatioArray[pdfFixedIndex][pdfMovingIndex];
1129 derivativeHelperArray = &(this->m_ThreaderMetricDerivative[threadID-1]);
1131 derivativeHelperArray = &(this->m_MetricDerivative);
1136 if( !this->m_TransformIsBSpline ) {
1138 * Generic version which works for all transforms.
1141 // Compute the transform Jacobian.
1142 // Should pre-compute
1143 typedef typename TransformType::JacobianType JacobianType;
1145 // Need to use one of the threader transforms if we're
1148 // Use a raw pointer here to avoid the overhead of smart pointers.
1149 // For instance, Register and UnRegister have mutex locks around
1150 // the reference counts.
1151 TransformType* transform;
1154 transform = this->m_ThreaderTransform[threadID - 1];
1156 transform = this->m_Transform;
1159 #if ITK_VERSION_MAJOR >= 4
1160 JacobianType jacobian;
1161 transform->ComputeJacobianWithRespectToParameters(this->m_FixedImageSamples[sampleNumber].point, jacobian);
1163 const JacobianType& jacobian =
1164 transform->GetJacobian( this->m_FixedImageSamples[sampleNumber].point );
1167 // for ( unsigned int mu = 0; mu < this->m_NumberOfParameters; mu++ )
1169 // double innerProduct = 0.0;
1170 // for ( unsigned int dim = 0; dim < Superclass::FixedImageDimension; dim++ )
1172 // innerProduct += jacobian[dim][mu] * movingImageGradientValue[dim];
1175 // const double derivativeContribution = innerProduct * cubicBSplineDerivativeValue;
1177 // if( this->m_UseExplicitPDFDerivatives )
1179 // *(derivPtr) -= derivativeContribution;
1184 // (*derivativeHelperArray)[mu] += precomputedWeight * derivativeContribution;
1189 unsigned int mu, dim;
1190 double innerProduct,derivativeContribution;
1191 for ( mu = 0; mu < this->m_NumberOfParameters; mu+=3 ) {
1192 // JV only for 3D Space BLUT FFD: if J(0, par)=0, then J(1,par+1)=0 & ...
1193 if (jacobian[0][mu])
1194 for ( dim = 0; dim < Superclass::FixedImageDimension; dim++ ) {
1195 innerProduct = jacobian[dim][mu+dim] * movingImageGradientValue[dim];
1196 derivativeContribution = innerProduct * cubicBSplineDerivativeValue;
1198 if( this->m_UseExplicitPDFDerivatives ) {
1199 *(derivPtr) -= derivativeContribution;
1202 (*derivativeHelperArray)[mu+dim] += precomputedWeight * derivativeContribution;
1205 else if( this->m_UseExplicitPDFDerivatives ) derivPtr+=3;
1208 const WeightsValueType * weights = NULL;
1209 const IndexValueType * indices = NULL;
1212 BSplineTransformWeightsType * weightsHelper = NULL;
1213 BSplineTransformIndexArrayType * indicesHelper = NULL;
1215 if( this->m_UseCachingOfBSplineWeights ) {
1217 // If the transform is of type BSplineDeformableTransform, we can obtain
1218 // a speed up by only processing the affected parameters. Note that
1219 // these pointers are just pointing to pre-allocated rows of the caching
1220 // arrays. There is therefore, no need to free this memory.
1222 weights = this->m_BSplineTransformWeightsArray[sampleNumber];
1223 indices = this->m_BSplineTransformIndicesArray[sampleNumber];
1225 if( threadID > 0 ) {
1226 weightsHelper = &(this->m_ThreaderBSplineTransformWeights[threadID-1]);
1227 indicesHelper = &(this->m_ThreaderBSplineTransformIndices[threadID-1]);
1229 weightsHelper = &(this->m_BSplineTransformWeights);
1230 indicesHelper = &(this->m_BSplineTransformIndices);
1233 #if ITK_VERSION_MAJOR >= 4
1234 this->m_BSplineTransform->ComputeJacobianFromBSplineWeightsWithRespectToPosition(
1235 this->m_FixedImageSamples[sampleNumber].point,
1236 *weightsHelper, *indicesHelper );
1238 this->m_BSplineTransform->GetJacobian(
1239 this->m_FixedImageSamples[sampleNumber].point,
1240 *weightsHelper, *indicesHelper );
1244 for( unsigned int dim = 0; dim < Superclass::FixedImageDimension; dim++ ) {
1246 double innerProduct;
1249 for( unsigned int mu = 0; mu < this->m_NumBSplineWeights; mu++ ) {
1251 /* The array weights contains the Jacobian values in a 1-D array
1252 * (because for each parameter the Jacobian is non-zero in only 1 of the
1253 * possible dimensions) which is multiplied by the moving image
1255 if( this->m_UseCachingOfBSplineWeights ) {
1256 innerProduct = movingImageGradientValue[dim] * weights[mu];
1257 parameterIndex = indices[mu] + this->m_BSplineParametersOffset[dim];
1259 innerProduct = movingImageGradientValue[dim] * (*weightsHelper)[mu];
1260 parameterIndex = (*indicesHelper)[mu] + this->m_BSplineParametersOffset[dim];
1263 const double derivativeContribution = innerProduct * cubicBSplineDerivativeValue;
1265 if( this->m_UseExplicitPDFDerivatives ) {
1266 JointPDFValueType * ptr = derivPtr + parameterIndex;
1267 *(ptr) -= derivativeContribution;
1269 (*derivativeHelperArray)[parameterIndex] += precomputedWeight * derivativeContribution;
1273 } //end dim for loop
1275 } // end if-block transform is BSpline
1280 } // end namespace itk