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 #ifndef __clitkOptNormalizedCorrelationImageToImageMetric_txx
20 #define __clitkOptNormalizedCorrelationImageToImageMetric_txx
22 #include "clitkOptNormalizedCorrelationImageToImageMetric.h"
23 #include "itkCovariantVector.h"
24 #include "itkImageRandomConstIteratorWithIndex.h"
25 #include "itkImageRegionConstIterator.h"
26 #include "itkImageRegionIterator.h"
27 #include "itkImageIterator.h"
28 #include "vnl/vnl_math.h"
36 template < class TFixedImage, class TMovingImage >
37 NormalizedCorrelationImageToImageMetric<TFixedImage,TMovingImage>
38 ::NormalizedCorrelationImageToImageMetric()
40 this->SetComputeGradient(true);
47 m_ThreaderDerivativeF = NULL;
48 m_ThreaderDerivativeM = NULL;
49 this->m_WithinThreadPreProcess = false;
50 this->m_WithinThreadPostProcess = false;
52 // For backward compatibility, the default behavior is to use all the pixels
53 // in the fixed image.
54 this->UseAllPixelsOn();
60 template < class TFixedImage, class TMovingImage >
61 NormalizedCorrelationImageToImageMetric<TFixedImage,TMovingImage>
62 ::~NormalizedCorrelationImageToImageMetric()
64 if(m_ThreaderSFF != NULL) {
65 delete [] m_ThreaderSFF;
69 if(m_ThreaderSMM != NULL) {
70 delete [] m_ThreaderSMM;
74 if(m_ThreaderSFM != NULL) {
75 delete [] m_ThreaderSFM;
79 if(m_ThreaderSF != NULL) {
80 delete [] m_ThreaderSF;
84 if(m_ThreaderSM != NULL) {
85 delete [] m_ThreaderSM;
89 if(m_ThreaderDerivativeF != NULL) {
90 delete [] m_ThreaderDerivativeF;
92 m_ThreaderDerivativeF = NULL;
94 if(m_ThreaderDerivativeM != NULL) {
95 delete [] m_ThreaderDerivativeM;
97 m_ThreaderDerivativeM = NULL;
101 * Print out internal information about this class
103 template < class TFixedImage, class TMovingImage >
105 NormalizedCorrelationImageToImageMetric<TFixedImage,TMovingImage>
106 ::PrintSelf(std::ostream& os, itk::Indent indent) const
109 Superclass::PrintSelf(os, indent);
117 template <class TFixedImage, class TMovingImage>
119 NormalizedCorrelationImageToImageMetric<TFixedImage,TMovingImage>
120 ::Initialize(void) throw ( itk::ExceptionObject )
123 this->Superclass::Initialize();
124 this->Superclass::MultiThreadingInitialize();
128 * Allocate memory for the accumulators (set to zero in GetValue)
130 if(m_ThreaderSFF != NULL) {
131 delete [] m_ThreaderSFF;
133 m_ThreaderSFF = new double[this->m_NumberOfThreads];
136 if(m_ThreaderSMM != NULL) {
137 delete [] m_ThreaderSMM;
139 m_ThreaderSMM = new double[this->m_NumberOfThreads];
141 if(m_ThreaderSFM != NULL) {
142 delete [] m_ThreaderSFM;
144 m_ThreaderSFM = new double[this->m_NumberOfThreads];
146 if(this->m_SubtractMean) {
147 if(m_ThreaderSF != NULL) {
148 delete [] m_ThreaderSF;
150 m_ThreaderSF = new double[this->m_NumberOfThreads];
152 if(m_ThreaderSM != NULL) {
153 delete [] m_ThreaderSM;
155 m_ThreaderSM = new double[this->m_NumberOfThreads];
158 if(m_ThreaderDerivativeF != NULL) {
159 delete [] m_ThreaderDerivativeF;
161 m_ThreaderDerivativeF = new DerivativeType[this->m_NumberOfThreads];
162 for(unsigned int threadID=0; threadID<this->m_NumberOfThreads; threadID++) {
163 m_ThreaderDerivativeF[threadID].SetSize( this->m_NumberOfParameters );
166 if(m_ThreaderDerivativeM != NULL) {
167 delete [] m_ThreaderDerivativeM;
169 m_ThreaderDerivativeM = new DerivativeType[this->m_NumberOfThreads];
170 for(unsigned int threadID=0; threadID<this->m_NumberOfThreads; threadID++) {
171 m_ThreaderDerivativeM[threadID].SetSize( this->m_NumberOfParameters );
176 template < class TFixedImage, class TMovingImage >
178 NormalizedCorrelationImageToImageMetric<TFixedImage,TMovingImage>
179 ::GetValueThreadProcessSample(
180 unsigned int threadID,
181 unsigned long fixedImageSample,
182 const MovingImagePointType & itkNotUsed(mappedPoint),
183 double movingImageValue) const
185 const RealType fixedImageValue= this->m_FixedImageSamples[fixedImageSample].value;
186 m_ThreaderSFF[threadID] += fixedImageValue * fixedImageValue;
187 m_ThreaderSMM[threadID] += movingImageValue * movingImageValue;
188 m_ThreaderSFM[threadID] += fixedImageValue * movingImageValue;
189 if ( this->m_SubtractMean ) {
190 m_ThreaderSF[threadID] += fixedImageValue;
191 m_ThreaderSM[threadID] += movingImageValue;
197 template < class TFixedImage, class TMovingImage >
198 typename NormalizedCorrelationImageToImageMetric<TFixedImage,TMovingImage>
200 NormalizedCorrelationImageToImageMetric<TFixedImage,TMovingImage>
201 ::GetValue( const ParametersType & parameters ) const
203 itkDebugMacro("GetValue( " << parameters << " ) ");
205 if( !this->m_FixedImage ) {
206 itkExceptionMacro( << "Fixed image has not been assigned" );
210 //Reset the accumulators
211 memset( m_ThreaderSFF, 0, this->m_NumberOfThreads * sizeof(AccumulateType) );
212 memset( m_ThreaderSMM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) );
213 memset( m_ThreaderSFM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) );
214 if(this->m_SubtractMean) {
215 memset( m_ThreaderSF, 0, this->m_NumberOfThreads * sizeof(AccumulateType) );
216 memset( m_ThreaderSM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) );
220 // Set up the parameters in the transform
221 this->m_Transform->SetParameters( parameters );
222 #if ITK_VERSION_MAJOR < 4
223 this->m_Parameters = parameters;
226 // MUST BE CALLED TO INITIATE PROCESSING
227 this->GetValueMultiThreadedInitiate();
229 itkDebugMacro( "Ratio of voxels mapping into moving image buffer: "
230 << this->m_NumberOfPixelsCounted << " / "
231 << this->m_NumberOfFixedImageSamples
234 if( this->m_NumberOfPixelsCounted <
235 this->m_NumberOfFixedImageSamples / 4 ) {
236 itkExceptionMacro( "Too many samples map outside moving image buffer: "
237 << this->m_NumberOfPixelsCounted << " / "
238 << this->m_NumberOfFixedImageSamples
242 // Accumulate the threads
243 AccumulateType sff, smm, sfm, sf, sm;
244 sff = m_ThreaderSFF[0];
245 smm = m_ThreaderSMM[0];
246 sfm = m_ThreaderSFM[0];
247 sf = m_ThreaderSF[0];
248 sm = m_ThreaderSM[0];
250 for(unsigned int t=1; t<this->m_NumberOfThreads; t++) {
251 sff += m_ThreaderSFF[t];
252 smm += m_ThreaderSMM[t];
253 sfm += m_ThreaderSFM[t];
254 if ( this->m_SubtractMean ) {
255 sf += m_ThreaderSF[t];
256 sm += m_ThreaderSM[t];
260 if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) {
261 sff -= ( sf * sf / this->m_NumberOfPixelsCounted );
262 smm -= ( sm * sm / this->m_NumberOfPixelsCounted );
263 sfm -= ( sf * sm / this->m_NumberOfPixelsCounted );
267 const RealType denom = -1.0 * vcl_sqrt(sff * smm );
269 if( this->m_NumberOfPixelsCounted > 0 && denom != 0.0) {
270 measure = sfm / denom;
272 measure = itk::NumericTraits< MeasureType >::Zero;
280 template < class TFixedImage, class TMovingImage >
281 typename NormalizedCorrelationImageToImageMetric<TFixedImage,TMovingImage>
283 NormalizedCorrelationImageToImageMetric<TFixedImage,TMovingImage>
284 ::ComputeSums( const ParametersType & parameters ) const
286 //No checking for the fixed image, done in the caller
287 //Reset the accumulators
288 memset( m_ThreaderSFF, 0, this->m_NumberOfThreads * sizeof(AccumulateType) );
289 memset( m_ThreaderSMM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) );
290 memset( m_ThreaderSFM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) );
291 if(this->m_SubtractMean) {
292 memset( m_ThreaderSF, 0, this->m_NumberOfThreads * sizeof(AccumulateType) );
293 memset( m_ThreaderSM, 0, this->m_NumberOfThreads * sizeof(AccumulateType) );
297 // Set up the parameters in the transform
298 this->m_Transform->SetParameters( parameters );
299 #if ITK_VERSION_MAJOR < 4
300 this->m_Parameters = parameters;
303 // MUST BE CALLED TO INITIATE PROCESSING
304 this->GetValueMultiThreadedInitiate();
306 itkDebugMacro( "Ratio of voxels mapping into moving image buffer: "
307 << this->m_NumberOfPixelsCounted << " / "
308 << this->m_NumberOfFixedImageSamples
311 if( this->m_NumberOfPixelsCounted <
312 this->m_NumberOfFixedImageSamples / 4 ) {
313 itkExceptionMacro( "Too many samples map outside moving image buffer: "
314 << this->m_NumberOfPixelsCounted << " / "
315 << this->m_NumberOfFixedImageSamples
319 // Accumulate the threads
320 m_SFF = m_ThreaderSFF[0];
321 m_SMM = m_ThreaderSMM[0];
322 m_SFM = m_ThreaderSFM[0];
323 m_SF = m_ThreaderSF[0];
324 m_SM = m_ThreaderSM[0];
326 for(unsigned int t=1; t<this->m_NumberOfThreads; t++) {
327 m_SFF += m_ThreaderSFF[t];
328 m_SMM += m_ThreaderSMM[t];
329 m_SFM += m_ThreaderSFM[t];
330 if ( this->m_SubtractMean ) {
331 m_SF += m_ThreaderSF[t];
332 m_SM += m_ThreaderSM[t];
336 if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) {
337 m_SFF -= ( m_SF * m_SF / this->m_NumberOfPixelsCounted );
338 m_SMM -= ( m_SM * m_SM / this->m_NumberOfPixelsCounted );
339 m_SFM -= ( m_SF * m_SM / this->m_NumberOfPixelsCounted );
340 m_FixedMean=m_SF / this->m_NumberOfPixelsCounted;
341 m_MovingMean=m_SM / this->m_NumberOfPixelsCounted;
345 m_Denom = -1.0 * vcl_sqrt(m_SFF * m_SMM );
347 if( this->m_NumberOfPixelsCounted > 0 && m_Denom != 0.0) {
348 measure = m_SFM / m_Denom;
350 measure = itk::NumericTraits< MeasureType >::Zero;
357 template < class TFixedImage, class TMovingImage >
359 NormalizedCorrelationImageToImageMetric<TFixedImage,TMovingImage>
360 ::GetValueAndDerivativeThreadProcessSample(
361 unsigned int threadID,
362 unsigned long fixedImageSample,
363 const MovingImagePointType & itkNotUsed(mappedPoint),
364 double movingImageValue,
365 const ImageDerivativesType &
366 movingImageGradientValue
370 const RealType fixedImageValue=this->m_FixedImageSamples[fixedImageSample].value;
371 const FixedImagePointType fixedImagePoint = this->m_FixedImageSamples[fixedImageSample].point;
373 // Need to use one of the threader transforms if we're
376 // Use a raw pointer here to avoid the overhead of smart pointers.
377 // For instance, Register and UnRegister have mutex locks around
378 // the reference counts.
379 TransformType* transform;
382 transform = this->m_ThreaderTransform[threadID - 1];
384 transform = this->m_Transform;
387 // Jacobian should be evaluated at the unmapped (fixed image) point.
388 #if ITK_VERSION_MAJOR >= 4
389 TransformJacobianType jacobian;
390 transform->ComputeJacobianWithRespectToParameters(fixedImagePoint, jacobian);
392 const TransformJacobianType & jacobian = transform
393 ->GetJacobian( fixedImagePoint );
396 for(unsigned int par=0; par<this->m_NumberOfParameters; par++) {
397 RealType sumF = itk::NumericTraits< RealType >::Zero;
398 RealType sumM = itk::NumericTraits< RealType >::Zero;
399 for(unsigned int dim=0; dim<MovingImageDimension; dim++) {
400 const RealType differential = jacobian( dim, par ) * movingImageGradientValue[dim];
401 if ( this->m_SubtractMean && this->m_NumberOfPixelsCounted > 0 ) {
402 sumF += (fixedImageValue-m_FixedMean) * differential;
403 sumM += (movingImageValue-m_MovingMean) * differential;
405 sumF += differential * fixedImageValue;
406 sumM += differential * movingImageValue;
409 m_ThreaderDerivativeF[threadID][par] += sumF;
410 m_ThreaderDerivativeM[threadID][par] += sumM;
418 * Get the both Value and Derivative Measure
420 template < class TFixedImage, class TMovingImage >
422 NormalizedCorrelationImageToImageMetric<TFixedImage,TMovingImage>
423 ::GetValueAndDerivative( const ParametersType & parameters,
425 DerivativeType & derivative) const
428 if( !this->m_FixedImage ) {
429 itkExceptionMacro( << "Fixed image has not been assigned" );
432 // Set up the parameters in the transform
433 this->m_Transform->SetParameters( parameters );
434 #if ITK_VERSION_MAJOR < 4
435 this->m_Parameters = parameters;
438 //We need the sums and the value to be calculated first
439 value=this->ComputeSums(parameters);
441 //Set output values to zero
442 if(derivative.GetSize() != this->m_NumberOfParameters) {
443 derivative = DerivativeType( this->m_NumberOfParameters );
445 memset( derivative.data_block(),
447 this->m_NumberOfParameters * sizeof(typename DerivativeType::ValueType) );
449 for( unsigned int threadID = 0; threadID<this->m_NumberOfThreads; threadID++ ) {
450 memset( m_ThreaderDerivativeF[threadID].data_block(),
452 this->m_NumberOfParameters * sizeof(typename DerivativeType::ValueType) );
454 memset( m_ThreaderDerivativeM[threadID].data_block(),
456 this->m_NumberOfParameters * sizeof(typename DerivativeType::ValueType) );
459 // MUST BE CALLED TO INITIATE PROCESSING
460 this->GetValueAndDerivativeMultiThreadedInitiate();
462 // Accumulate over the threads
463 DerivativeType derivativeF(this->m_NumberOfParameters), derivativeM(this->m_NumberOfParameters);
464 for(unsigned int t=0; t<this->m_NumberOfThreads; t++) {
465 for(unsigned int parameter = 0; parameter < this->m_NumberOfParameters; parameter++) {
466 derivativeF[parameter] += m_ThreaderDerivativeF[t][parameter];
467 derivativeM[parameter] += m_ThreaderDerivativeM[t][parameter];
471 //Compute derivatives
472 if( this->m_NumberOfPixelsCounted > 0 && m_Denom != 0.0) {
473 for(unsigned int i=0; i<this->m_NumberOfParameters; i++) {
474 derivative[i] = ( derivativeF[i] - (m_SFM/m_SMM)* derivativeM[i] ) / m_Denom;
477 for(unsigned int i=0; i<this->m_NumberOfParameters; i++) {
478 derivative[i] = itk::NumericTraits< MeasureType >::Zero;
486 * Get the match measure derivative
488 template < class TFixedImage, class TMovingImage >
490 NormalizedCorrelationImageToImageMetric<TFixedImage,TMovingImage>
491 ::GetDerivative( const ParametersType & parameters,
492 DerivativeType & derivative ) const
494 if( !this->m_FixedImage ) {
495 itkExceptionMacro( << "Fixed image has not been assigned" );
499 // call the combined version
500 this->GetValueAndDerivative( parameters, value, derivative );
503 } // end namespace clitk