ANIMA  4.0
animaMCMCorrelationImageToImageMetric.hxx
Go to the documentation of this file.
1 #pragma once
3 
4 #include <vnl/vnl_matrix_fixed.h>
5 #include <animaBaseTensorTools.h>
7 #include <itkImageRegionConstIteratorWithIndex.h>
8 
11 #include <animaNLOPTOptimizers.h>
12 #include <animaMCMConstants.h>
13 
14 namespace anima
15 {
16 
17 template < class TFixedImagePixelType, class TMovingImagePixelType, unsigned int ImageDimension >
20 {
21  m_FixedImagePoints.clear();
22  m_FixedImageValues.clear();
23 
25  mcmCreator.SetNumberOfCompartments(0);
26  mcmCreator.SetModelWithStationaryWaterComponent(true);
27  mcmCreator.SetModelWithFreeWaterComponent(false);
28 
29  m_ZeroDiffusionModel = mcmCreator.GetNewMultiCompartmentModel();
30  m_ZeroDiffusionVector = m_ZeroDiffusionModel->GetModelVector();
31 
32  m_SmallDelta = anima::DiffusionSmallDelta;
33  m_BigDelta = anima::DiffusionBigDelta;
34  m_GradientStrengths.clear();
35  m_GradientDirections.clear();
36 
37  m_ForceApproximation = false;
38 
39  m_LowerBoundGaussianSigma = 0;
40  m_UpperBoundGaussianSigma = 25;
41 }
42 
43 template < class TFixedImagePixelType, class TMovingImagePixelType, unsigned int ImageDimension >
44 bool
47 {
48  if (m_ForceApproximation)
49  return false;
50 
51  FixedImageType *fixedImage = const_cast <FixedImageType *> (this->GetFixedImage());
52  MCModelPointer val = fixedImage->GetDescriptionModel();
53  for (unsigned int i = 0;i < val->GetNumberOfCompartments();++i)
54  {
55  if (!val->GetCompartment(i)->GetTensorCompatible())
56  return false;
57  }
58 
59  MovingImageType *movingImage = const_cast <MovingImageType *> (this->GetMovingImage());
60  val = movingImage->GetDescriptionModel();
61  for (unsigned int i = 0;i < val->GetNumberOfCompartments();++i)
62  {
63  if (!val->GetCompartment(i)->GetTensorCompatible())
64  return false;
65  }
66 
67  return true;
68 }
69 
70 template < class TFixedImagePixelType, class TMovingImagePixelType, unsigned int ImageDimension >
73 ::GetValue(const TransformParametersType & parameters) const
74 {
75  FixedImageConstPointer fixedImage = this->m_FixedImage;
76 
77  if (!fixedImage)
78  itkExceptionMacro("Fixed image has not been assigned");
79 
80  if (this->m_NumberOfPixelsCounted == 0)
81  return 0;
82 
83  this->SetTransformParameters( parameters );
84 
85  PixelType movingValue;
86 
87  OutputPointType transformedPoint;
88  ContinuousIndexType transformedIndex;
89 
90  MovingImageType *movingImage = const_cast <MovingImageType *> (this->GetMovingImage());
91  bool tensorCompatibilityCondition = this->CheckTensorCompatibility();
92 
93  std::vector <MCModelPointer> movingValues(this->m_NumberOfPixelsCounted);
94 
95  for (unsigned int i = 0;i < this->m_NumberOfPixelsCounted;++i)
96  {
97  transformedPoint = this->m_Transform->TransformPoint( m_FixedImagePoints[i] );
98  this->m_Interpolator->GetInputImage()->TransformPhysicalPointToContinuousIndex(transformedPoint,transformedIndex);
99 
100  if( this->m_Interpolator->IsInsideBuffer( transformedIndex ) )
101  {
102  movingValue = this->m_Interpolator->EvaluateAtContinuousIndex( transformedIndex );
103 
104  if (!isZero(movingValue))
105  {
106  MCModelPointer currentMovingValue = movingImage->GetDescriptionModel()->Clone();
107  currentMovingValue->SetModelVector(movingValue);
108 
109  if (this->GetModelRotation() != Superclass::NONE)
110  currentMovingValue->Reorient(this->m_OrientationMatrix, (this->GetModelRotation() == Superclass::PPD));
111 
112  movingValues[i] = currentMovingValue;
113  }
114  else
115  movingValues[i] = m_ZeroDiffusionModel;
116  }
117  else
118  movingValues[i] = m_ZeroDiffusionModel;
119  }
120 
121  if (tensorCompatibilityCondition)
122  return this->ComputeTensorBasedMetric(movingValues);
123 
124  return this->ComputeNonTensorBasedMetric(movingValues);
125 }
126 
127 template < class TFixedImagePixelType, class TMovingImagePixelType, unsigned int ImageDimension >
128 double
130 ::ComputeTensorBasedMetric(const std::vector <MCModelPointer> &movingValues) const
131 {
132  typedef anima::MultiTensorSmoothingCostFunction CostFunctionType;
133  typename CostFunctionType::Pointer smootherCostFunction = CostFunctionType::New();
134 
135  smootherCostFunction->SetReferenceModels(m_FixedImageValues);
136  smootherCostFunction->SetMovingModels(movingValues);
137  smootherCostFunction->SetTensorsScale(1000.0);
138 
139  typedef anima::NLOPTOptimizers OptimizerType;
140  OptimizerType::ParametersType p(smootherCostFunction->GetNumberOfParameters());
141  OptimizerType::ParametersType lowerBounds(smootherCostFunction->GetNumberOfParameters());
142  OptimizerType::ParametersType upperBounds(smootherCostFunction->GetNumberOfParameters());
143 
144  lowerBounds[0] = m_LowerBoundGaussianSigma;
145  upperBounds[0] = m_UpperBoundGaussianSigma;
146 
147  p[0] = m_LowerBoundGaussianSigma + (m_UpperBoundGaussianSigma - m_LowerBoundGaussianSigma) / 10.0;
148 
149  typename OptimizerType::Pointer smoothingOptimizer = OptimizerType::New();
150  smoothingOptimizer->SetAlgorithm(NLOPT_LN_BOBYQA);
151  smoothingOptimizer->SetMaximize(false);
152  smoothingOptimizer->SetXTolRel(1.0e-8);
153  smoothingOptimizer->SetMaxEval(2000);
154  smoothingOptimizer->SetCostFunction(smootherCostFunction);
155 
156  smoothingOptimizer->SetLowerBoundParameters(lowerBounds);
157  smoothingOptimizer->SetUpperBoundParameters(upperBounds);
158 
159  smoothingOptimizer->SetInitialPosition(p);
160  smoothingOptimizer->StartOptimization();
161 
162  p = smoothingOptimizer->GetCurrentPosition();
163  return smootherCostFunction->GetValue(p);
164 }
165 
166 template < class TFixedImagePixelType, class TMovingImagePixelType, unsigned int ImageDimension >
167 double
169 ::ComputeNonTensorBasedMetric(const std::vector <MCModelPointer> &movingValues) const
170 {
171  typedef anima::ApproximateMCMSmoothingCostFunction CostFunctionType;
172  typename CostFunctionType::Pointer smootherCostFunction = CostFunctionType::New();
173 
174  smootherCostFunction->SetReferenceModels(m_FixedImageValues,m_GradientDirections,
175  m_SmallDelta,m_BigDelta,m_GradientStrengths);
176  smootherCostFunction->SetMovingModels(movingValues,m_GradientDirections,
177  m_SmallDelta,m_BigDelta,m_GradientStrengths);
178  smootherCostFunction->SetGradientDirections(m_GradientDirections);
179  smootherCostFunction->SetSmallDelta(m_SmallDelta);
180  smootherCostFunction->SetBigDelta(m_SmallDelta);
181  smootherCostFunction->SetGradientStrengths(m_GradientStrengths);
182  smootherCostFunction->SetBValueWeightIndexes(m_BValWeightsIndexes);
183  smootherCostFunction->SetSphereWeights(m_SphereWeights);
184  smootherCostFunction->SetParameterScale(1.0e-3);
185 
186  typedef anima::NLOPTOptimizers OptimizerType;
187  OptimizerType::ParametersType p(smootherCostFunction->GetNumberOfParameters());
188  OptimizerType::ParametersType lowerBounds(smootherCostFunction->GetNumberOfParameters());
189  OptimizerType::ParametersType upperBounds(smootherCostFunction->GetNumberOfParameters());
190 
191  lowerBounds[0] = m_LowerBoundGaussianSigma;
192  upperBounds[0] = m_UpperBoundGaussianSigma;
193 
194  p[0] = m_LowerBoundGaussianSigma + (m_UpperBoundGaussianSigma - m_LowerBoundGaussianSigma) / 10.0;
195 
196  typename OptimizerType::Pointer smoothingOptimizer = OptimizerType::New();
197  smoothingOptimizer->SetAlgorithm(NLOPT_LN_BOBYQA);
198  smoothingOptimizer->SetMaximize(false);
199  smoothingOptimizer->SetXTolRel(1.0e-8);
200  smoothingOptimizer->SetMaxEval(2000);
201  smoothingOptimizer->SetCostFunction(smootherCostFunction);
202 
203  smoothingOptimizer->SetLowerBoundParameters(lowerBounds);
204  smoothingOptimizer->SetUpperBoundParameters(upperBounds);
205 
206  smoothingOptimizer->SetInitialPosition(p);
207  smoothingOptimizer->StartOptimization();
208 
209  p = smoothingOptimizer->GetCurrentPosition();
210  return smootherCostFunction->GetValue(p);
211 }
212 
213 template < class TFixedImagePixelType, class TMovingImagePixelType, unsigned int ImageDimension >
214 void
216 ::SetSmallDelta(double val)
217 {
218  double oldVal = m_SmallDelta;
219  m_SmallDelta = val;
220 
221  if (oldVal != val)
222  this->UpdateSphereWeights();
223 }
224 
225 template < class TFixedImagePixelType, class TMovingImagePixelType, unsigned int ImageDimension >
226 void
228 ::SetBigDelta(double val)
229 {
230  double oldVal = m_BigDelta;
231  m_BigDelta = val;
232 
233  if (oldVal != val)
234  this->UpdateSphereWeights();
235 }
236 
237 template < class TFixedImagePixelType, class TMovingImagePixelType, unsigned int ImageDimension >
238 void
240 ::SetGradientStrengths(std::vector <double> &val)
241 {
242  m_GradientStrengths = val;
243 
244  if ((m_GradientDirections.size() == m_GradientStrengths.size())&&(m_GradientStrengths.size() != 0)&&(m_GradientDirections.size() != 0))
245  this->UpdateSphereWeights();
246 }
247 
248 template < class TFixedImagePixelType, class TMovingImagePixelType, unsigned int ImageDimension >
249 void
251 ::SetGradientDirections(std::vector <GradientType> &val)
252 {
253  m_GradientDirections = val;
254 
255  if ((m_GradientDirections.size() == m_GradientStrengths.size())&&(m_GradientStrengths.size() != 0)&&(m_GradientDirections.size() != 0))
256  this->UpdateSphereWeights();
257 }
258 
259 template < class TFixedImagePixelType, class TMovingImagePixelType, unsigned int ImageDimension >
260 void
263 {
264  std::vector <double> individualGradientStrengths;
265  for (unsigned int i = 0;i < m_GradientStrengths.size();++i)
266  {
267  bool alreadyIn = false;
268  for (unsigned int j = 0;j < individualGradientStrengths.size();++j)
269  {
270  if (individualGradientStrengths[j] == m_GradientStrengths[i])
271  {
272  alreadyIn = true;
273  break;
274  }
275  }
276 
277  if (!alreadyIn)
278  individualGradientStrengths.push_back(m_GradientStrengths[i]);
279  }
280 
281  std::sort(individualGradientStrengths.begin(),individualGradientStrengths.end());
282 
283  if (individualGradientStrengths[0] != 0)
284  {
285  m_GradientStrengths.push_back(0);
286  GradientType tmpVec;
287  tmpVec.fill(0);
288  m_GradientDirections.push_back(tmpVec);
289  individualGradientStrengths.insert(individualGradientStrengths.begin(),0);
290  }
291 
292  m_SphereWeights.resize(individualGradientStrengths.size());
293  m_BValWeightsIndexes.resize(m_GradientStrengths.size());
294  double lastBValue = anima::GetBValueFromAcquisitionParameters(m_SmallDelta, m_BigDelta, individualGradientStrengths[individualGradientStrengths.size() - 1]);
295 
296  for (unsigned int i = 0;i < individualGradientStrengths.size();++i)
297  {
298  unsigned int numValues = 0;
299  for (unsigned int j = 0;j < m_GradientStrengths.size();++j)
300  {
301  if (m_GradientStrengths[j] == individualGradientStrengths[i])
302  {
303  ++numValues;
304  m_BValWeightsIndexes[j] = i;
305  }
306  }
307 
308  double lowerRadius = 0;
309  double baseValue = 0;
310  double bValueCenter = anima::GetBValueFromAcquisitionParameters(m_SmallDelta, m_BigDelta, individualGradientStrengths[i]);
311 
312  if (i > 0)
313  {
314  double bValueBefore = anima::GetBValueFromAcquisitionParameters(m_SmallDelta, m_BigDelta, individualGradientStrengths[i-1]);
315  lowerRadius = (bValueCenter + bValueBefore) / 2.0;
316  baseValue = bValueBefore;
317  }
318 
319  double upperRadius = lastBValue + lowerRadius - baseValue;
320  if (i < individualGradientStrengths.size() - 1)
321  {
322  double bValueAfter = anima::GetBValueFromAcquisitionParameters(m_SmallDelta, m_BigDelta, individualGradientStrengths[i+1]);
323  upperRadius = (bValueCenter + bValueAfter) / 2.0;
324  }
325 
326  lowerRadius = std::sqrt(2.0 * lowerRadius);
327  upperRadius = std::sqrt(2.0 * upperRadius);
328 
329  m_SphereWeights[i] = 4 * M_PI * (std::pow(upperRadius,3.0) - std::pow(lowerRadius,3.0)) / (3.0 * numValues);
330  }
331 }
332 
333 template < class TFixedImagePixelType, class TMovingImagePixelType, unsigned int ImageDimension >
334 bool
336 ::isZero(PixelType &vector) const
337 {
338  unsigned int ndim = vector.GetSize();
339 
340  for (unsigned int i = 0;i < ndim;++i)
341  {
342  if (vector[i] != 0)
343  return false;
344  }
345 
346  return true;
347 }
348 
349 template < class TFixedImagePixelType, class TMovingImagePixelType, unsigned int ImageDimension >
350 void
353 {
354  if(!this->m_FixedImage)
355  itkExceptionMacro( << "Fixed image has not been assigned" );
356 
357  FixedImageType *fixedImage = const_cast <FixedImageType *> (this->GetFixedImage());
358  this->m_NumberOfPixelsCounted = this->GetFixedImageRegion().GetNumberOfPixels();
359  typedef itk::ImageRegionConstIteratorWithIndex<FixedImageType> FixedIteratorType;
360  FixedIteratorType ti(fixedImage, this->GetFixedImageRegion());
361  typename FixedImageType::IndexType index;
362 
363  m_FixedImagePoints.resize(this->m_NumberOfPixelsCounted);
364  m_FixedImageValues.resize(this->m_NumberOfPixelsCounted);
365 
366  InputPointType inputPoint;
367 
368  unsigned int pos = 0;
369  PixelType fixedValue;
370  while(!ti.IsAtEnd())
371  {
372  index = ti.GetIndex();
373  fixedImage->TransformIndexToPhysicalPoint( index, inputPoint );
374 
375  m_FixedImagePoints[pos] = inputPoint;
376  fixedValue = ti.Get();
377 
378  if (!isZero(fixedValue))
379  {
380  m_FixedImageValues[pos] = fixedImage->GetDescriptionModel()->Clone();
381  m_FixedImageValues[pos]->SetModelVector(fixedValue);
382  }
383  else
384  {
385  m_FixedImageValues[pos] = m_ZeroDiffusionModel->Clone();
386  m_FixedImageValues[pos]->SetModelVector(m_ZeroDiffusionVector);
387  }
388 
389  ++ti;
390  ++pos;
391  }
392 }
393 
394 } // end namespace anima
Implements an ITK wrapper for the NLOPT library.
double ComputeNonTensorBasedMetric(const std::vector< MCModelPointer > &movingValues) const
Superclass::TransformParametersType TransformParametersType
void UpdateSphereWeights()
Compute base integration weights for non tensor integration.
void SetGradientDirections(std::vector< GradientType > &val)
const double DiffusionBigDelta
Default big delta value (classical values)
double GetBValueFromAcquisitionParameters(double smallDelta, double bigDelta, double gradientStrength)
Given gyromagnetic ratio in rad/s/T, gradient strength in T/mm and deltas in s, computes b-value in s...
const double DiffusionSmallDelta
Default small delta value (classical values)
itk::ContinuousIndex< double, ImageDimension > ContinuousIndexType
double ComputeTensorBasedMetric(const std::vector< MCModelPointer > &movingValues) const
MeasureType GetValue(const TransformParametersType &parameters) const ITK_OVERRIDE
Really this class is some simplified factory that creates the MCM that it knows.