ANIMA  4.0
animaMCMEstimatorImageFilter.hxx
Go to the documentation of this file.
1 #pragma once
3 #include <animaMCMConstants.h>
4 
5 #include <itkImageRegionIterator.h>
6 #include <itkImageRegionConstIterator.h>
7 #include <itkSymmetricEigenAnalysis.h>
8 
9 #include <animaNLOPTOptimizers.h>
11 #include <animaNNLSOptimizer.h>
13 
16 #include <animaGaussianMCMCost.h>
18 
19 #include <animaVectorOperations.h>
20 #include <animaSphereOperations.h>
21 
22 #include <animaBaseTensorTools.h>
23 #include <animaMCMFileWriter.h>
24 
25 #include <limits>
26 
27 namespace anima
28 {
29 
30 template <class InputPixelType, class OutputPixelType>
31 void
33 ::AddGradientDirection(unsigned int i, GradientType &grad)
34 {
35  if (i == m_GradientDirections.size())
36  m_GradientDirections.push_back(grad);
37  else if (i > m_GradientDirections.size())
38  std::cerr << "Trying to add a direction not contiguous... Add directions contiguously (0,1,2,3,...)..." << std::endl;
39  else
40  m_GradientDirections[i] = grad;
41 }
42 
43 template <class InputPixelType, class OutputPixelType>
47 {
48  return new MCMCreatorType;
49 }
50 
51 template <class InputPixelType, class OutputPixelType>
52 void
55 {
56  // Override the method in itkImageSource, so we can set the vector length of
57  // the output itk::VectorImage
58 
59  this->Superclass::GenerateOutputInformation();
60 
61  OutputImageType *output = this->GetOutput();
62 
63  // Create fake MCM output to get its length
64  MCMCreatorType *tmpMCMCreator = this->GetNewMCMCreatorInstance();
65  tmpMCMCreator->SetModelWithFreeWaterComponent(m_ModelWithFreeWaterComponent);
66  tmpMCMCreator->SetModelWithStationaryWaterComponent(m_ModelWithStationaryWaterComponent);
67  tmpMCMCreator->SetModelWithRestrictedWaterComponent(m_ModelWithRestrictedWaterComponent);
68  tmpMCMCreator->SetModelWithStaniszComponent(m_ModelWithStaniszComponent);
69  tmpMCMCreator->SetCompartmentType(m_CompartmentType);
70  tmpMCMCreator->SetNumberOfCompartments(m_NumberOfCompartments);
71 
72  MCMPointer tmpMCM = tmpMCMCreator->GetNewMultiCompartmentModel();
73 
74  output->SetVectorLength(tmpMCM->GetSize());
75  output->SetDescriptionModel(tmpMCM);
76 
77  delete tmpMCMCreator;
78 }
79 
80 template <class InputPixelType, class OutputPixelType>
81 void
83 ::WriteMCMOutput(std::string fileName)
84 {
85  MCMPointer tmpMCM = m_MCMCreators[0]->GetNewMultiCompartmentModel();
86 
88  MCMFileWriterType writer;
89 
90  writer.SetInputImage(this->GetOutput());
91  writer.SetFileName(fileName);
92 
93  writer.Update();
94 }
95 
96 template <class InputPixelType, class OutputPixelType>
97 void
100 {
101  if (this->GetComputationMask())
102  return;
103 
104  typedef itk::ImageRegionConstIterator <InputImageType> B0IteratorType;
105  typedef itk::ImageRegionIterator <MaskImageType> MaskIteratorType;
106 
107  unsigned int firstB0Index = 0;
108  double bValueFirstB0Index = anima::GetBValueFromAcquisitionParameters(m_SmallDelta, m_BigDelta, m_GradientStrengths[firstB0Index]);
109  while (bValueFirstB0Index > 10)
110  {
111  ++firstB0Index;
112  bValueFirstB0Index = anima::GetBValueFromAcquisitionParameters(m_SmallDelta, m_BigDelta, m_GradientStrengths[firstB0Index]);
113  }
114 
115  B0IteratorType b0Itr(this->GetInput(firstB0Index),this->GetOutput()->GetLargestPossibleRegion());
116 
117  if (!this->GetComputationMask())
118  this->Superclass::CheckComputationMask();
119 
120  MaskIteratorType maskItr(this->GetComputationMask(),this->GetOutput()->GetLargestPossibleRegion());
121 
122  while (!b0Itr.IsAtEnd())
123  {
124  if ((maskItr.Get() != 0)&&(b0Itr.Get() <= m_B0Threshold))
125  maskItr.Set(0);
126 
127  ++b0Itr;
128  ++maskItr;
129  }
130 }
131 
132 template <class InputPixelType, class OutputPixelType>
133 void
136 {
137  if ((m_Optimizer == "levenberg")&&((m_MLEstimationStrategy == Marginal)||(m_NoiseType != Gaussian)))
138  itkExceptionMacro("Levenberg Marquardt optimizer only working with Gaussian noise and variable projection");
139 
140  if ((m_Optimizer != "bobyqa")&&(m_UseCommonDiffusivities||m_UseCommonExtraAxonalFractions||m_UseCommonConcentrations))
141  itkExceptionMacro("Derivative based optimizers not supported yet for common parameters, use Bobyqa instead");
142 
143  if ((m_NoiseType == NCC)&&(m_MLEstimationStrategy != Profile))
144  itkExceptionMacro("NCC noise is only compatible with profile estimation strategy");
145 
146  m_NumberOfImages = this->GetNumberOfIndexedInputs();
147 
148  if (m_GradientStrengths.size() != m_NumberOfImages)
149  itkExceptionMacro("There should be the same number of input images and input b-values...");
150 
151  itk::ImageRegionIterator <OutputImageType> fillOut(this->GetOutput(),this->GetOutput()->GetLargestPossibleRegion());
152  unsigned int outSize = this->GetOutput()->GetNumberOfComponentsPerPixel();
153  typename OutputImageType::PixelType emptyModelVec(outSize);
154  emptyModelVec.Fill(0);
155 
156  while (!fillOut.IsAtEnd())
157  {
158  fillOut.Set(emptyModelVec);
159  ++fillOut;
160  }
161 
162  // Create AICc volume
163  m_AICcVolume = OutputScalarImageType::New();
164  m_AICcVolume->Initialize();
165  m_AICcVolume->SetRegions(this->GetInput(0)->GetLargestPossibleRegion());
166  m_AICcVolume->SetSpacing (this->GetInput(0)->GetSpacing());
167  m_AICcVolume->SetOrigin (this->GetInput(0)->GetOrigin());
168  m_AICcVolume->SetDirection (this->GetInput(0)->GetDirection());
169  m_AICcVolume->Allocate();
170  m_AICcVolume->FillBuffer(0);
171 
172  // Create B0 volume
173  m_B0Volume = OutputScalarImageType::New();
174  m_B0Volume->Initialize();
175  m_B0Volume->SetRegions(this->GetInput(0)->GetLargestPossibleRegion());
176  m_B0Volume->SetSpacing (this->GetInput(0)->GetSpacing());
177  m_B0Volume->SetOrigin (this->GetInput(0)->GetOrigin());
178  m_B0Volume->SetDirection (this->GetInput(0)->GetDirection());
179  m_B0Volume->Allocate();
180  m_B0Volume->FillBuffer(0);
181 
182  // Create sigma volume
183  m_SigmaSquareVolume = OutputScalarImageType::New();
184  m_SigmaSquareVolume->Initialize();
185  m_SigmaSquareVolume->SetRegions(this->GetInput(0)->GetLargestPossibleRegion());
186  m_SigmaSquareVolume->SetSpacing (this->GetInput(0)->GetSpacing());
187  m_SigmaSquareVolume->SetOrigin (this->GetInput(0)->GetOrigin());
188  m_SigmaSquareVolume->SetDirection (this->GetInput(0)->GetDirection());
189  m_SigmaSquareVolume->Allocate();
190  m_SigmaSquareVolume->FillBuffer(0);
191 
192  // Create mose volume
193  if (!m_MoseVolume)
194  {
195  m_MoseVolume = MoseImageType::New();
196  m_MoseVolume->Initialize();
197  m_MoseVolume->SetRegions(this->GetInput(0)->GetLargestPossibleRegion());
198  m_MoseVolume->SetSpacing (this->GetInput(0)->GetSpacing());
199  m_MoseVolume->SetOrigin (this->GetInput(0)->GetOrigin());
200  m_MoseVolume->SetDirection (this->GetInput(0)->GetDirection());
201  m_MoseVolume->Allocate();
202  m_MoseVolume->FillBuffer(0);
203  }
204 
205  if (m_ExternalMoseVolume)
206  m_FindOptimalNumberOfCompartments = false;
207 
208  Superclass::BeforeThreadedGenerateData();
209 
210  m_MCMCreators.resize(this->GetNumberOfWorkUnits());
211  for (unsigned int i = 0;i < this->GetNumberOfWorkUnits();++i)
212  m_MCMCreators[i] = this->GetNewMCMCreatorInstance();
213 
214  std::cout << "Initial diffusivities:" << std::endl;
215  std::cout << " - Axial diffusivity: " << m_AxialDiffusivityValue << " mm2/s," << std::endl;
216  std::cout << " - Radial diffusivity 1: " << m_RadialDiffusivity1Value << " mm2/s," << std::endl;
217  std::cout << " - Radial diffusivity 2: " << m_RadialDiffusivity2Value << " mm2/s," << std::endl;
218 
219  if (m_ModelWithRestrictedWaterComponent)
220  std::cout << " - IRW diffusivity: " << m_IRWDiffusivityValue << " mm2/s," << std::endl;
221 
222  if (m_ModelWithStaniszComponent)
223  std::cout << " - Stanisz diffusivity: " << m_StaniszDiffusivityValue << " mm2/s," << std::endl;
224 
225  // Setting up creators
226  for (unsigned int i = 0;i < this->GetNumberOfWorkUnits();++i)
227  {
228  m_MCMCreators[i]->SetAxialDiffusivityValue(m_AxialDiffusivityValue);
229  m_MCMCreators[i]->SetFreeWaterDiffusivityValue(3.0e-3);
230  m_MCMCreators[i]->SetIRWDiffusivityValue(m_IRWDiffusivityValue);
231  m_MCMCreators[i]->SetStaniszDiffusivityValue(m_StaniszDiffusivityValue);
232  m_MCMCreators[i]->SetRadialDiffusivity1Value(m_RadialDiffusivity1Value);
233  m_MCMCreators[i]->SetRadialDiffusivity2Value(m_RadialDiffusivity2Value);
234  }
235 
236  // Switch over compartment types to setup coarse grid initialization
237  switch (m_CompartmentType)
238  {
239  case NODDI:
240  case DDI:
241  this->ComputeExtraAxonalAndKappaCoarseGrids();
242  break;
243 
244  case Tensor:
245  this->ComputeTensorRadialDiffsAndAzimuthCoarseGrids();
246  break;
247 
248  default:
249  // No coarse grid initialization for simple models
250  break;
251  }
252 
253  // Sparse pre-computation
254  this->InitializeDictionary();
255 }
256 
257 template <class InputPixelType, class OutputPixelType>
258 void
261 {
262  anima::GetSphereEvenSampling(m_DictionaryDirections,m_NumberOfDictionaryEntries);
263  std::vector <double> fakeIsotropicDirection(3,0);
264 
265  unsigned int countIsoComps = 0;
266  if (m_ModelWithFreeWaterComponent)
267  {
268  m_DictionaryDirections.insert(m_DictionaryDirections.begin(),fakeIsotropicDirection);
269  ++countIsoComps;
270  }
271 
272  if (m_ModelWithStationaryWaterComponent)
273  {
274  m_DictionaryDirections.insert(m_DictionaryDirections.begin(),fakeIsotropicDirection);
275  ++countIsoComps;
276  }
277 
278  if (m_ModelWithRestrictedWaterComponent)
279  {
280  m_DictionaryDirections.insert(m_DictionaryDirections.begin(),fakeIsotropicDirection);
281  ++countIsoComps;
282  }
283 
284  if (m_ModelWithStaniszComponent)
285  {
286  m_DictionaryDirections.insert(m_DictionaryDirections.begin(),fakeIsotropicDirection);
287  ++countIsoComps;
288  }
289 
290  m_SparseSticksDictionary.set_size(m_NumberOfImages,countIsoComps + m_NumberOfDictionaryEntries);
291  m_SparseSticksDictionary.fill(0.0);
292 
293  countIsoComps = 0;
294  MCMPointer mcm;
295  MCMCreatorType *mcmCreator = m_MCMCreators[0];
296  mcmCreator->SetModelWithFreeWaterComponent(false);
297  mcmCreator->SetModelWithStationaryWaterComponent(false);
298  mcmCreator->SetModelWithRestrictedWaterComponent(false);
299  mcmCreator->SetModelWithStaniszComponent(false);
300  mcmCreator->SetNumberOfCompartments(0);
301 
302  if (m_ModelWithFreeWaterComponent)
303  {
304  mcmCreator->SetModelWithFreeWaterComponent(true);
305  mcm = mcmCreator->GetNewMultiCompartmentModel();
306  mcmCreator->SetModelWithFreeWaterComponent(false);
307 
308  for (unsigned int i = 0;i < m_NumberOfImages;++i)
309  m_SparseSticksDictionary(i,countIsoComps) = mcm->GetPredictedSignal(m_SmallDelta, m_BigDelta, m_GradientStrengths[i], m_GradientDirections[i]);
310 
311  ++countIsoComps;
312  }
313 
314  if (m_ModelWithStationaryWaterComponent)
315  {
316  mcmCreator->SetModelWithStationaryWaterComponent(true);
317  mcm = mcmCreator->GetNewMultiCompartmentModel();
318  mcmCreator->SetModelWithStationaryWaterComponent(false);
319 
320  for (unsigned int i = 0;i < m_NumberOfImages;++i)
321  m_SparseSticksDictionary(i,countIsoComps) = mcm->GetPredictedSignal(m_SmallDelta, m_BigDelta, m_GradientStrengths[i], m_GradientDirections[i]);
322 
323  ++countIsoComps;
324  }
325 
326  if (m_ModelWithRestrictedWaterComponent)
327  {
328  mcmCreator->SetModelWithRestrictedWaterComponent(true);
329  mcm = mcmCreator->GetNewMultiCompartmentModel();
330  mcmCreator->SetModelWithRestrictedWaterComponent(false);
331 
332  for (unsigned int i = 0;i < m_NumberOfImages;++i)
333  m_SparseSticksDictionary(i,countIsoComps) = mcm->GetPredictedSignal(m_SmallDelta, m_BigDelta, m_GradientStrengths[i], m_GradientDirections[i]);
334 
335  ++countIsoComps;
336  }
337 
338  if (m_ModelWithStaniszComponent)
339  {
340  mcmCreator->SetModelWithStaniszComponent(true);
341  mcm = mcmCreator->GetNewMultiCompartmentModel();
342  mcmCreator->SetModelWithStaniszComponent(false);
343 
344  for (unsigned int i = 0;i < m_NumberOfImages;++i)
345  m_SparseSticksDictionary(i,countIsoComps) = mcm->GetPredictedSignal(m_SmallDelta, m_BigDelta, m_GradientStrengths[i], m_GradientDirections[i]);
346 
347  ++countIsoComps;
348  }
349 
350  mcmCreator->SetNumberOfCompartments(1);
351  mcmCreator->SetCompartmentType(Stick);
352 
353  mcm = mcmCreator->GetNewMultiCompartmentModel();
354 
355  for (unsigned int i = 0;i < m_NumberOfDictionaryEntries;++i)
356  {
357  anima::TransformCartesianToSphericalCoordinates(m_DictionaryDirections[i + countIsoComps],
358  m_DictionaryDirections[i + countIsoComps]);
359 
360  mcm->GetCompartment(0)->SetOrientationTheta(m_DictionaryDirections[i + countIsoComps][0]);
361  mcm->GetCompartment(0)->SetOrientationPhi(m_DictionaryDirections[i + countIsoComps][1]);
362 
363  anima::TransformSphericalToCartesianCoordinates(m_DictionaryDirections[i + countIsoComps],
364  m_DictionaryDirections[i + countIsoComps]);
365 
366  for (unsigned int j = 0;j < m_NumberOfImages;++j)
367  m_SparseSticksDictionary(j,countIsoComps + i) = mcm->GetPredictedSignal(m_SmallDelta, m_BigDelta, m_GradientStrengths[j], m_GradientDirections[j]);
368  }
369 }
370 
371 template <class InputPixelType, class OutputPixelType>
372 void
375 {
376  // m_ValuesCoarseGrid[0] -> extra axonal fractions, m_ValuesCoarseGrid[1] -> kappa
377  m_ValuesCoarseGrid.resize(2);
378 
379  unsigned int coarseGridSize = 8;
380  m_ValuesCoarseGrid[0].resize(coarseGridSize);
381  m_ValuesCoarseGrid[1].resize(coarseGridSize);
382 
383  // The anisotropy index suggested by NODDI signal expression is not the ODI
384  // index defined in Zhang et al., 2012, Neuroimage. Instead, it amounts to
385  // (3 * tau1(k) - 1) / 2 \in [0,1] which is not analytically invertible.
386  // However it is well approximated by k^power / (halfLife + k^power) where
387  // power and halfLife are given by:
388 
389  double power = 1.385829;
390  double halfLife = 5.312364;
391  for (unsigned int i = 0;i < coarseGridSize;++i)
392  {
393  double tmpVal = (i + 1.0) / (coarseGridSize + 1.0);
394  m_ValuesCoarseGrid[0][i] = tmpVal;
395  m_ValuesCoarseGrid[1][i] = std::pow(tmpVal * halfLife / (1.0 - tmpVal), 1.0 / power);
396  }
397 }
398 
399 template <class InputPixelType, class OutputPixelType>
400 void
403 {
404  // m_ValuesCoarseGrid[0] -> azimuth
405  // m_ValuesCoarseGrid[1] -> radial diff 1 vs axial diff weight
406  // m_ValuesCoarseGrid[2] -> radial diff 2 vs radial diff 1 weight
407  m_ValuesCoarseGrid.resize(3);
408 
409  unsigned int coarseGridSize = 8;
410  for (unsigned int i = 0;i < 3;++i)
411  m_ValuesCoarseGrid[i].resize(coarseGridSize);
412 
413  for (unsigned int i = 0;i < coarseGridSize;++i)
414  {
415  m_ValuesCoarseGrid[0][i] = 2.0 * (i + 1) * M_PI / (coarseGridSize + 1.0);
416  m_ValuesCoarseGrid[1][i] = i / (coarseGridSize - 1.0);
417  m_ValuesCoarseGrid[2][i] = i / (coarseGridSize - 1.0);
418  }
419 }
420 
421 template <class InputPixelType, class OutputPixelType>
422 void
425 {
426  typedef itk::ImageRegionConstIterator <InputImageType> ConstImageIteratorType;
427 
428  std::vector <ConstImageIteratorType> inIterators(m_NumberOfImages);
429  for (unsigned int i = 0;i < m_NumberOfImages;++i)
430  inIterators[i] = ConstImageIteratorType(this->GetInput(i),outputRegionForThread);
431 
432  typedef itk::ImageRegionIterator <OutputImageType> OutImageIteratorType;
433  OutImageIteratorType outIterator(this->GetOutput(),outputRegionForThread);
434 
435  typedef itk::ImageRegionIterator <MaskImageType> MaskIteratorType;
436  MaskIteratorType maskItr(this->GetComputationMask(),outputRegionForThread);
437 
438  typedef itk::ImageRegionIterator <OutputScalarImageType> ImageIteratorType;
439  ImageIteratorType aiccIterator(m_AICcVolume, outputRegionForThread);
440  ImageIteratorType b0Iterator(m_B0Volume, outputRegionForThread);
441  ImageIteratorType sigmaIterator(m_SigmaSquareVolume, outputRegionForThread);
442 
443  typedef itk::ImageRegionIterator <MoseImageType> MoseIteratorType;
444  MoseIteratorType moseIterator(m_MoseVolume, outputRegionForThread);
445 
446  std::vector <double> observedSignals(m_NumberOfImages,0);
447 
448  typename OutputImageType::PixelType resVec(this->GetOutput()->GetNumberOfComponentsPerPixel());
449 
450  MCMPointer mcmData = nullptr;
451  MCMPointer outputMCMData = this->GetOutput()->GetDescriptionModel()->Clone();
452  MCMType::ListType outputWeights(outputMCMData->GetNumberOfCompartments(),0);
453 
454  double aiccValue, b0Value, sigmaSqValue;
455 
456  unsigned int threadId = this->GetSafeThreadId();
457 
458  while (!outIterator.IsAtEnd())
459  {
460  resVec.Fill(0.0);
461 
462  if (maskItr.Get() == 0)
463  {
464  outIterator.Set(resVec);
465 
466  for (unsigned int i = 0;i < m_NumberOfImages;++i)
467  ++inIterators[i];
468 
469  ++outIterator;
470  ++maskItr;
471  ++aiccIterator;
472  ++b0Iterator;
473  ++sigmaIterator;
474  ++moseIterator;
475 
476  continue;
477  }
478 
479  // Load DWI
480  for (unsigned int i = 0;i < m_NumberOfImages;++i)
481  observedSignals[i] = inIterators[i].Get();
482 
483  int moseValue = -1;
484  bool estimateNonIsoCompartments = false;
485  if (m_ExternalMoseVolume)
486  {
487  moseValue = moseIterator.Get();
488  if (moseValue > 0)
489  estimateNonIsoCompartments = true;
490  }
491  else if (m_NumberOfCompartments > 0)
492  estimateNonIsoCompartments = true;
493 
494  bool hasIsoCompartment = m_ModelWithFreeWaterComponent || m_ModelWithRestrictedWaterComponent || m_ModelWithStationaryWaterComponent || m_ModelWithStaniszComponent;
495  if (estimateNonIsoCompartments)
496  {
497  // If model selection, handle it here
498  unsigned int minimalNumberOfCompartments = m_NumberOfCompartments;
499  unsigned int maximalNumberOfCompartments = m_NumberOfCompartments;
500  if (m_FindOptimalNumberOfCompartments)
501  {
502  minimalNumberOfCompartments = 1;
503  moseValue = 0;
504 
505  if (hasIsoCompartment)
506  this->EstimateFreeWaterModel(mcmData,observedSignals,threadId,aiccValue,b0Value,sigmaSqValue);
507  }
508  else if (moseValue != -1)
509  {
510  int numMoseCompartments = std::min(moseValue,(int)m_NumberOfCompartments);
511  minimalNumberOfCompartments = numMoseCompartments;
512  maximalNumberOfCompartments = numMoseCompartments;
513  }
514 
515  for (unsigned int i = minimalNumberOfCompartments;i <= maximalNumberOfCompartments;++i)
516  {
517  double tmpB0Value = 0;
518  double tmpSigmaSqValue = 0;
519  double tmpAiccValue = 0;
520  MCMPointer mcmValue;
521 
522  this->OptimizeNonIsotropicCompartments(mcmValue,i,observedSignals,threadId,tmpAiccValue,tmpB0Value,tmpSigmaSqValue);
523 
524  if ((tmpAiccValue < aiccValue)||(!m_FindOptimalNumberOfCompartments))
525  {
526  aiccValue = tmpAiccValue;
527  if (m_FindOptimalNumberOfCompartments)
528  moseValue = i;
529  b0Value = tmpB0Value;
530  sigmaSqValue = tmpSigmaSqValue;
531  mcmData = mcmValue;
532  }
533  }
534  }
535  else if (hasIsoCompartment)
536  this->EstimateFreeWaterModel(mcmData,observedSignals,threadId,aiccValue,b0Value,sigmaSqValue);
537  else
538  itkExceptionMacro("Nothing to estimate...");
539 
540  if (outputMCMData->GetNumberOfCompartments() != mcmData->GetNumberOfCompartments())
541  {
542  // If we are selecting the number of compartments, create some empty ones here
543  std::fill(outputWeights.begin(),outputWeights.end(),0.0);
544  for (unsigned int i = 0;i < mcmData->GetNumberOfCompartments();++i)
545  {
546  outputMCMData->GetCompartment(i)->CopyFromOther(mcmData->GetCompartment(i));
547  outputWeights[i] = mcmData->GetCompartmentWeight(i);
548  }
549 
550  outputMCMData->SetCompartmentWeights(outputWeights);
551  resVec = outputMCMData->GetModelVector();
552  }
553  else
554  resVec = mcmData->GetModelVector();
555 
556  outIterator.Set(resVec);
557  aiccIterator.Set(aiccValue);
558  b0Iterator.Set(b0Value);
559  sigmaIterator.Set(sigmaSqValue);
560  moseIterator.Set(mcmData->GetNumberOfCompartments() - mcmData->GetNumberOfIsotropicCompartments());
561 
562  for (unsigned int i = 0;i < m_NumberOfImages;++i)
563  ++inIterators[i];
564 
565  this->IncrementNumberOfProcessedPoints();
566  ++outIterator;
567  ++maskItr;
568  ++aiccIterator;
569  ++b0Iterator;
570  ++sigmaIterator;
571  ++moseIterator;
572  }
573 
574  this->SafeReleaseThreadId(threadId);
575 }
576 
577 template <class InputPixelType, class OutputPixelType>
578 void
580 ::OptimizeNonIsotropicCompartments(MCMPointer &mcmValue, unsigned int currentNumberOfCompartments,
581  std::vector <double> &observedSignals, itk::ThreadIdType threadId,
582  double &aiccValue, double &b0Value, double &sigmaSqValue)
583 {
584  b0Value = 0;
585  sigmaSqValue = 1;
586  aiccValue = -1;
587 
588  this->InitialOrientationsEstimation(mcmValue,false,currentNumberOfCompartments,observedSignals,threadId,
589  aiccValue,b0Value,sigmaSqValue);
590 
591  this->ModelEstimation(mcmValue,false,observedSignals,threadId,aiccValue,b0Value,sigmaSqValue);
592 
593  if (b0Value == 0.0)
594  {
595  this->InitialOrientationsEstimation(mcmValue,true,currentNumberOfCompartments,observedSignals,threadId,
596  aiccValue,b0Value,sigmaSqValue);
597 
598  this->ModelEstimation(mcmValue,true,observedSignals,threadId,aiccValue,b0Value,sigmaSqValue);
599  }
600 
601  if (b0Value != 0.0)
602  {
603  MCMType::ListType outputWeights = mcmValue->GetCompartmentWeights();
604 
605  for (unsigned int i = 0;i < outputWeights.size();++i)
606  outputWeights[i] /= b0Value;
607 
608  mcmValue->SetCompartmentWeights(outputWeights);
609  }
610 }
611 
612 template <class InputPixelType, class OutputPixelType>
613 void
615 ::EstimateFreeWaterModel(MCMPointer &mcmValue, std::vector <double> &observedSignals, itk::ThreadIdType threadId,
616  double &aiccValue, double &b0Value, double &sigmaSqValue)
617 {
618  // Declarations for optimization
619  MCMCreatorType *mcmCreator = m_MCMCreators[threadId];
620  mcmCreator->SetModelWithFreeWaterComponent(m_ModelWithFreeWaterComponent);
621  mcmCreator->SetModelWithStationaryWaterComponent(m_ModelWithStationaryWaterComponent);
622  mcmCreator->SetModelWithRestrictedWaterComponent(m_ModelWithRestrictedWaterComponent);
623  mcmCreator->SetModelWithStaniszComponent(m_ModelWithStaniszComponent);
624  mcmCreator->SetNumberOfCompartments(0);
625  mcmCreator->SetVariableProjectionEstimationMode(m_MLEstimationStrategy == VariableProjection);
626  mcmCreator->SetUseConstrainedFreeWaterDiffusivity(m_UseConstrainedFreeWaterDiffusivity);
627  mcmCreator->SetUseConstrainedIRWDiffusivity(m_UseConstrainedIRWDiffusivity);
628  mcmCreator->SetUseConstrainedStaniszDiffusivity(m_UseConstrainedStaniszDiffusivity);
629  mcmCreator->SetUseConstrainedStaniszRadius(m_UseConstrainedStaniszRadius);
630 
631  mcmValue = mcmCreator->GetNewMultiCompartmentModel();
632 
633  b0Value = 0;
634  sigmaSqValue = 1;
635 
636  CostFunctionBasePointer cost = this->CreateCostFunction(observedSignals,mcmValue);
637 
638  unsigned int dimension = mcmValue->GetNumberOfParameters();
639  ParametersType p(dimension);
640  MCMType::ListType workVec(dimension);
641 
642  workVec = mcmValue->GetParametersAsVector();
643  for (unsigned int i = 0;i < dimension;++i)
644  p[i] = workVec[i];
645 
646  double costValue = this->GetCostValue(cost,p);
647  itk::Array<double> lowerBounds(dimension), upperBounds(dimension);
648 
649  if (dimension > 0)
650  {
651  workVec = mcmValue->GetParameterLowerBounds();
652  for (unsigned int i = 0;i < dimension;++i)
653  lowerBounds[i] = workVec[i];
654 
655  workVec = mcmValue->GetParameterUpperBounds();
656  for (unsigned int i = 0;i < dimension;++i)
657  upperBounds[i] = workVec[i];
658 
659  costValue = this->PerformSingleOptimization(p,cost,lowerBounds,upperBounds);
660 
661  // - Get estimated DTI and B0
662  for (unsigned int i = 0;i < dimension;++i)
663  workVec[i] = p[i];
664 
665  mcmValue->SetParametersFromVector(workVec);
666  }
667 
668  this->GetProfiledInformation(cost,mcmValue,b0Value,sigmaSqValue);
669  aiccValue = this->ComputeAICcValue(mcmValue,costValue);
670 
671  if (b0Value == 0.0)
672  {
673  // Try with negative bounds
674  mcmValue->SetNegativeWeightBounds(true);
675  costValue = this->GetCostValue(cost,p);
676 
677  if (dimension > 0)
678  {
679  workVec = mcmValue->GetParameterLowerBounds();
680  for (unsigned int i = 0;i < dimension;++i)
681  lowerBounds[i] = workVec[i];
682 
683  workVec = mcmValue->GetParameterUpperBounds();
684  for (unsigned int i = 0;i < dimension;++i)
685  upperBounds[i] = workVec[i];
686 
687  workVec = mcmValue->GetParametersAsVector();
688  for (unsigned int i = 0;i < dimension;++i)
689  p[i] = workVec[i];
690 
691  costValue = this->PerformSingleOptimization(p,cost,lowerBounds,upperBounds);
692 
693  // - Get estimated DTI and B0
694  for (unsigned int i = 0;i < dimension;++i)
695  workVec[i] = p[i];
696 
697  mcmValue->SetParametersFromVector(workVec);
698  }
699 
700  this->GetProfiledInformation(cost,mcmValue,b0Value,sigmaSqValue);
701  aiccValue = this->ComputeAICcValue(mcmValue,costValue);
702  }
703 
704  if (b0Value != 0.0)
705  {
706  MCMType::ListType outputWeights = mcmValue->GetCompartmentWeights();
707 
708  for (unsigned int i = 0;i < outputWeights.size();++i)
709  outputWeights[i] /= b0Value;
710 
711  mcmValue->SetCompartmentWeights(outputWeights);
712  }
713 }
714 
715 template <class InputPixelType, class OutputPixelType>
718 {
719  CostFunctionBasePointer returnCost;
720 
721  if (m_NoiseType != Gaussian)
722  itkExceptionMacro("Cost function type not supported");
723 
725  if (m_MLEstimationStrategy != VariableProjection)
726  {
728  internalCost->SetMarginalEstimation(m_MLEstimationStrategy == Marginal);
729  baseCost = internalCost;
730  }
731  else
732  {
734  baseCost = internalCost;
735  }
736 
737  baseCost->SetObservedSignals(observedSignals);
738  baseCost->SetGradients(m_GradientDirections);
739  baseCost->SetSmallDelta(m_SmallDelta);
740  baseCost->SetBigDelta(m_BigDelta);
741  baseCost->SetGradientStrengths(m_GradientStrengths);
742  baseCost->SetMCMStructure(mcmModel);
743 
744  if (m_Optimizer == "levenberg")
745  {
748 
749  tmpCost->SetInternalCost(baseCost);
750 
751  returnCost = tmpCost;
752  }
753  else
754  {
757 
758  tmpCost->SetInternalCost(baseCost);
759 
760  returnCost = tmpCost;
761  }
762 
763  return returnCost;
764 }
765 
766 template <class InputPixelType, class OutputPixelType>
767 void
769 ::InitialOrientationsEstimation(MCMPointer &mcmValue, bool authorizedNegativeB0Value, unsigned int currentNumberOfCompartments,
770  std::vector <double> &observedSignals, itk::ThreadIdType threadId,
771  double &aiccValue, double &b0Value, double &sigmaSqValue)
772 {
773  b0Value = 0;
774  sigmaSqValue = 1;
775  aiccValue = -1;
776 
777  // Single DTI is already estimated, get it to use in next step
778  // - First create output object
779  MCMCreatorType *mcmCreator = m_MCMCreators[threadId];
780 
781  // - First create model
782  mcmCreator->SetModelWithFreeWaterComponent(m_ModelWithFreeWaterComponent);
783  mcmCreator->SetModelWithStationaryWaterComponent(m_ModelWithStationaryWaterComponent);
784  mcmCreator->SetModelWithRestrictedWaterComponent(m_ModelWithRestrictedWaterComponent);
785  mcmCreator->SetModelWithStaniszComponent(m_ModelWithStaniszComponent);
786  mcmCreator->SetCompartmentType(Stick);
787  mcmCreator->SetNumberOfCompartments(currentNumberOfCompartments);
788  mcmCreator->SetVariableProjectionEstimationMode(m_MLEstimationStrategy == VariableProjection);
789  mcmCreator->SetUseConstrainedDiffusivity(true);
790  mcmCreator->SetUseConstrainedFreeWaterDiffusivity(m_UseConstrainedFreeWaterDiffusivity);
791  mcmCreator->SetUseConstrainedIRWDiffusivity(m_UseConstrainedIRWDiffusivity);
792  mcmCreator->SetUseConstrainedStaniszDiffusivity(m_UseConstrainedStaniszDiffusivity);
793  mcmCreator->SetUseConstrainedStaniszRadius(m_UseConstrainedStaniszRadius);
794  mcmCreator->SetUseCommonDiffusivities(m_UseCommonDiffusivities);
795 
796  MCMPointer mcmUpdateValue = mcmCreator->GetNewMultiCompartmentModel();
797  mcmUpdateValue->SetNegativeWeightBounds(authorizedNegativeB0Value);
798 
799  // - Now initialize sticks from dictionary
800  this->SparseInitializeSticks(mcmUpdateValue,authorizedNegativeB0Value,observedSignals,threadId);
801 
802  unsigned int dimension = mcmUpdateValue->GetNumberOfParameters();
803  ParametersType p(dimension);
804  MCMType::ListType workVec(dimension);
805  itk::Array<double> lowerBounds(dimension), upperBounds(dimension);
806 
807  workVec = mcmUpdateValue->GetParameterLowerBounds();
808  for (unsigned int j = 0;j < dimension;++j)
809  lowerBounds[j] = workVec[j];
810 
811  workVec = mcmUpdateValue->GetParameterUpperBounds();
812  for (unsigned int j = 0;j < dimension;++j)
813  upperBounds[j] = workVec[j];
814 
815  CostFunctionBasePointer cost = this->CreateCostFunction(observedSignals,mcmUpdateValue);
816 
817  // - Update ball and stick model against observed signals
818  workVec = mcmUpdateValue->GetParametersAsVector();
819  for (unsigned int j = 0;j < dimension;++j)
820  p[j] = workVec[j];
821 
822  double costValue = this->PerformSingleOptimization(p,cost,lowerBounds,upperBounds);
823 
824  // - Get estimated data
825  for (unsigned int j = 0;j < dimension;++j)
826  workVec[j] = p[j];
827 
828  mcmUpdateValue->SetParametersFromVector(workVec);
829 
830  this->GetProfiledInformation(cost,mcmUpdateValue,b0Value,sigmaSqValue);
831 
832  aiccValue = this->ComputeAICcValue(mcmUpdateValue,costValue);
833  mcmValue = mcmUpdateValue;
834 }
835 
836 template <class InputPixelType, class OutputPixelType>
837 void
839 ::ModelEstimation(MCMPointer &mcmValue, bool authorizedNegativeB0Value, std::vector <double> &observedSignals, itk::ThreadIdType threadId,
840  double &aiccValue, double &b0Value, double &sigmaSqValue)
841 {
842  unsigned int optimalNumberOfCompartments = mcmValue->GetNumberOfCompartments() - mcmValue->GetNumberOfIsotropicCompartments();
843 
844  //Already done in initial orientations estimation
845  if ((m_CompartmentType == Stick)&&(m_UseConstrainedDiffusivity))
846  return;
847 
848  // - First create model
849  MCMCreatorType *mcmCreator = m_MCMCreators[threadId];
850  mcmCreator->SetCompartmentType(Stick);
851  mcmCreator->SetNumberOfCompartments(optimalNumberOfCompartments);
852  mcmCreator->SetUseConstrainedDiffusivity(m_UseConstrainedDiffusivity);
853 
854  MCMPointer mcmUpdateValue = mcmCreator->GetNewMultiCompartmentModel();
855  mcmUpdateValue->SetNegativeWeightBounds(authorizedNegativeB0Value);
856 
857  // - Now the tricky part: initialize from previous model, handled somewhere else
858  this->InitializeModelFromSimplifiedOne(mcmValue,mcmUpdateValue);
859 
860  CostFunctionBasePointer cost = this->CreateCostFunction(observedSignals,mcmUpdateValue);
861 
862  unsigned int dimension = mcmUpdateValue->GetNumberOfParameters();
863  ParametersType p(dimension);
864  MCMType::ListType workVec(dimension);
865  itk::Array<double> lowerBounds(dimension), upperBounds(dimension);
866 
867  workVec = mcmUpdateValue->GetParameterLowerBounds();
868  for (unsigned int i = 0;i < dimension;++i)
869  lowerBounds[i] = workVec[i];
870 
871  workVec = mcmUpdateValue->GetParameterUpperBounds();
872  for (unsigned int i = 0;i < dimension;++i)
873  upperBounds[i] = workVec[i];
874 
875  workVec = mcmUpdateValue->GetParametersAsVector();
876  for (unsigned int i = 0;i < dimension;++i)
877  p[i] = workVec[i];
878 
879  double costValue = this->PerformSingleOptimization(p,cost,lowerBounds,upperBounds);
880  this->GetProfiledInformation(cost,mcmUpdateValue,b0Value,sigmaSqValue);
881 
882  for (unsigned int i = 0;i < dimension;++i)
883  workVec[i] = p[i];
884 
885  mcmUpdateValue->SetParametersFromVector(workVec);
886 
887  mcmValue = mcmUpdateValue;
888  aiccValue = this->ComputeAICcValue(mcmValue,costValue);
889 
890  if ((m_CompartmentType == Stick)||(b0Value == 0.0))
891  return;
892 
893  // We're done with ball and stick, next up is ball and zeppelin
894  // - First create model
895  mcmCreator->SetCompartmentType(Zeppelin);
896  mcmUpdateValue = mcmCreator->GetNewMultiCompartmentModel();
897  mcmUpdateValue->SetNegativeWeightBounds(authorizedNegativeB0Value);
898 
899  // - Now the tricky part: initialize from previous model, handled somewhere else
900  this->InitializeModelFromSimplifiedOne(mcmValue,mcmUpdateValue);
901 
902  // - Update ball and zeppelin model against observed signals
903  cost = this->CreateCostFunction(observedSignals,mcmUpdateValue);
904  dimension = mcmUpdateValue->GetNumberOfParameters();
905  p.SetSize(dimension);
906  lowerBounds.SetSize(dimension);
907  upperBounds.SetSize(dimension);
908 
909  workVec = mcmUpdateValue->GetParameterLowerBounds();
910  for (unsigned int i = 0;i < dimension;++i)
911  lowerBounds[i] = workVec[i];
912 
913  workVec = mcmUpdateValue->GetParameterUpperBounds();
914  for (unsigned int i = 0;i < dimension;++i)
915  upperBounds[i] = workVec[i];
916 
917  workVec = mcmUpdateValue->GetParametersAsVector();
918  for (unsigned int i = 0;i < dimension;++i)
919  p[i] = workVec[i];
920 
921  costValue = this->PerformSingleOptimization(p,cost,lowerBounds,upperBounds);
922  this->GetProfiledInformation(cost,mcmUpdateValue,b0Value,sigmaSqValue);
923 
924  for (unsigned int i = 0;i < dimension;++i)
925  workVec[i] = p[i];
926 
927  mcmUpdateValue->SetParametersFromVector(workVec);
928 
929  mcmValue = mcmUpdateValue;
930  aiccValue = this->ComputeAICcValue(mcmValue,costValue);
931 
932  if ((m_CompartmentType == Zeppelin)||(b0Value == 0.0))
933  return;
934 
935  // Finally, we're done with ball and zeppelin, an example of what's next up with multi-tensor
936  // - First create model
937  mcmCreator->SetCompartmentType(m_CompartmentType);
938  mcmCreator->SetUseConstrainedExtraAxonalFraction(m_UseConstrainedExtraAxonalFraction);
939  mcmCreator->SetUseConstrainedOrientationConcentration(m_UseConstrainedOrientationConcentration);
940  mcmCreator->SetUseCommonConcentrations(m_UseCommonConcentrations);
941  mcmCreator->SetUseCommonExtraAxonalFractions(m_UseCommonExtraAxonalFractions);
942 
943  mcmUpdateValue = mcmCreator->GetNewMultiCompartmentModel();
944  mcmUpdateValue->SetNegativeWeightBounds(authorizedNegativeB0Value);
945 
946  // - Now the tricky part: initialize from previous model, handled somewhere else
947  this->InitializeModelFromSimplifiedOne(mcmValue,mcmUpdateValue);
948 
949  // - Update complex model against observed signals
950  cost = this->CreateCostFunction(observedSignals,mcmUpdateValue);
951  dimension = mcmUpdateValue->GetNumberOfParameters();
952  p.SetSize(dimension);
953  lowerBounds.SetSize(dimension);
954  upperBounds.SetSize(dimension);
955 
956  workVec = mcmUpdateValue->GetParameterLowerBounds();
957  for (unsigned int i = 0;i < dimension;++i)
958  lowerBounds[i] = workVec[i];
959 
960  workVec = mcmUpdateValue->GetParameterUpperBounds();
961  for (unsigned int i = 0;i < dimension;++i)
962  upperBounds[i] = workVec[i];
963 
964  switch (m_CompartmentType)
965  {
966  case NODDI:
967  case DDI:
968  this->ExtraAxonalAndKappaCoarseGridInitialization(mcmUpdateValue, cost, workVec, p);
969  break;
970 
971  case Tensor:
972  this->TensorCoarseGridInitialization(mcmUpdateValue, cost, workVec, p);
973  break;
974 
975  default:
976  itkExceptionMacro("No coarse grid initialization for simple models, shouldn't be here");
977  break;
978  }
979 
980  workVec = mcmUpdateValue->GetParametersAsVector();
981  for (unsigned int i = 0;i < dimension;++i)
982  p[i] = workVec[i];
983 
984  costValue = this->PerformSingleOptimization(p,cost,lowerBounds,upperBounds);
985  this->GetProfiledInformation(cost,mcmUpdateValue,b0Value,sigmaSqValue);
986 
987  for (unsigned int i = 0;i < dimension;++i)
988  workVec[i] = p[i];
989 
990  mcmUpdateValue->SetParametersFromVector(workVec);
991 
992  mcmValue = mcmUpdateValue;
993  aiccValue = this->ComputeAICcValue(mcmValue,costValue);
994 }
995 
996 template <class InputPixelType, class OutputPixelType>
997 void
1000  MCMType::ListType &workVec,ParametersType &p)
1001 {
1002  unsigned int dimension = p.GetSize();
1003  double optNu = 0;
1004  double optKappa = 0;
1005  double optValue = -1.0;
1006 
1007  unsigned int numIsoCompartments = mcmUpdateValue->GetNumberOfIsotropicCompartments();
1008  unsigned int numCompartments = mcmUpdateValue->GetNumberOfCompartments();
1009 
1010  for (unsigned int j = 0;j < m_ValuesCoarseGrid[1].size();++j)
1011  {
1012  double tmpKappa = m_ValuesCoarseGrid[1][j];
1013 
1014  for (unsigned int k = 0;k < m_ValuesCoarseGrid[0].size();++k)
1015  {
1016  double tmpNu = m_ValuesCoarseGrid[0][k];
1017 
1018  for (unsigned int i = numIsoCompartments;i < numCompartments;++i)
1019  {
1020  mcmUpdateValue->GetCompartment(i)->SetOrientationConcentration(tmpKappa);
1021  mcmUpdateValue->GetCompartment(i)->SetExtraAxonalFraction(tmpNu);
1022  }
1023 
1024  workVec = mcmUpdateValue->GetParametersAsVector();
1025  for (unsigned int i = 0;i < dimension;++i)
1026  p[i] = workVec[i];
1027 
1028  double tmpValue = this->GetCostValue(cost,p);
1029 
1030  if ((tmpValue < optValue) || (optValue < 0))
1031  {
1032  optValue = tmpValue;
1033  optKappa = tmpKappa;
1034  optNu = tmpNu;
1035  }
1036  }
1037  }
1038 
1039  for (unsigned int i = numIsoCompartments;i < numCompartments;++i)
1040  {
1041  mcmUpdateValue->GetCompartment(i)->SetOrientationConcentration(optKappa);
1042  mcmUpdateValue->GetCompartment(i)->SetExtraAxonalFraction(optNu);
1043  }
1044 }
1045 
1046 template <class InputPixelType, class OutputPixelType>
1047 void
1050  MCMType::ListType &workVec,ParametersType &p)
1051 {
1052  unsigned int dimension = p.GetSize();
1053  unsigned int numIsoCompartments = mcmUpdateValue->GetNumberOfIsotropicCompartments();
1054  unsigned int numCompartments = mcmUpdateValue->GetNumberOfCompartments();
1055 
1056  double optRadialDiff1 = 0.0;
1057  double optRadialDiff2 = 0.0;
1058  double optAzimuth = 0.0;
1059  double optValue = - 1.0;
1060 
1061  double meanAxialDiff = 0.0;
1062  double meanRadialDiff = 0.0;
1063  unsigned int numUsefulCompartments = 0;
1064 
1065  for (unsigned int i = numIsoCompartments;i < numCompartments;++i)
1066  {
1067  if (mcmUpdateValue->GetCompartmentWeight(i) > 0)
1068  {
1069  meanAxialDiff += mcmUpdateValue->GetCompartment(i)->GetAxialDiffusivity();
1070  meanRadialDiff += mcmUpdateValue->GetCompartment(i)->GetRadialDiffusivity1();
1071 
1072  ++numUsefulCompartments;
1073  }
1074  }
1075 
1076  if (numUsefulCompartments > 0)
1077  {
1078  meanAxialDiff /= numUsefulCompartments;
1079  meanRadialDiff /= numUsefulCompartments;
1080  }
1081  else
1082  {
1083  meanAxialDiff = m_AxialDiffusivityValue;
1084  meanRadialDiff = (m_RadialDiffusivity1Value + m_RadialDiffusivity2Value) / 2.0;
1085  }
1086 
1087  if (meanAxialDiff - meanRadialDiff < anima::MCMAxialDiffusivityAddonLowerBound)
1088  meanAxialDiff = meanRadialDiff + anima::MCMAxialDiffusivityAddonLowerBound;
1089 
1090  for (unsigned int i = numIsoCompartments;i < numCompartments;++i)
1091  mcmUpdateValue->GetCompartment(i)->SetAxialDiffusivity(meanAxialDiff);
1092 
1093  for (unsigned int l = 0;l < m_ValuesCoarseGrid[2].size();++l)
1094  {
1095  double tmpWeight2 = m_ValuesCoarseGrid[2][l];
1096 
1097  // In between meanRD and (meanRD + MCMDiffusivityLowerBound) / 2
1098  double tmpRadialDiffusivity2 = 0.5 * ((1.0 + tmpWeight2) * meanRadialDiff + (1.0 - tmpWeight2) * anima::MCMDiffusivityLowerBound);
1099 
1100  for (unsigned int k = 0;k < m_ValuesCoarseGrid[1].size();++k)
1101  {
1102  double tmpWeight1 = m_ValuesCoarseGrid[1][k];
1103 
1104  // In between meanRD and (meanRD + meanAD) / 2
1105  double tmpRadialDiffusivity1 = 0.5 * ((1.0 + tmpWeight1) * meanRadialDiff + (1.0 - tmpWeight1) * meanAxialDiff);
1106  if (meanAxialDiff - tmpRadialDiffusivity1 < anima::MCMAxialDiffusivityAddonLowerBound)
1107  continue;
1108 
1109  for (unsigned int j = 0;j < m_ValuesCoarseGrid[0].size();++j)
1110  {
1111  double tmpAzimuth = m_ValuesCoarseGrid[0][j];
1112 
1113  for (unsigned int i = numIsoCompartments;i < numCompartments;++i)
1114  {
1115  mcmUpdateValue->GetCompartment(i)->SetPerpendicularAngle(tmpAzimuth);
1116  mcmUpdateValue->GetCompartment(i)->SetRadialDiffusivity1(tmpRadialDiffusivity1);
1117  mcmUpdateValue->GetCompartment(i)->SetRadialDiffusivity2(tmpRadialDiffusivity2);
1118  }
1119 
1120  workVec = mcmUpdateValue->GetParametersAsVector();
1121  for (unsigned int i = 0;i < dimension;++i)
1122  p[i] = workVec[i];
1123 
1124  double tmpValue = this->GetCostValue(cost,p);
1125 
1126  if ((tmpValue < optValue) || (optValue < 0))
1127  {
1128  optValue = tmpValue;
1129  optRadialDiff1 = tmpRadialDiffusivity1;
1130  optRadialDiff2 = tmpRadialDiffusivity2;
1131  optAzimuth = tmpAzimuth;
1132  }
1133  }
1134  }
1135  }
1136 
1137  for (unsigned int i = numIsoCompartments;i < numCompartments;++i)
1138  {
1139  mcmUpdateValue->GetCompartment(i)->SetPerpendicularAngle(optAzimuth);
1140  mcmUpdateValue->GetCompartment(i)->SetRadialDiffusivity1(optRadialDiff1);
1141  mcmUpdateValue->GetCompartment(i)->SetRadialDiffusivity2(optRadialDiff2);
1142  }
1143 }
1144 
1145 template <class InputPixelType, class OutputPixelType>
1148 ::CreateOptimizer(CostFunctionBasePointer &cost, itk::Array<double> &lowerBounds, itk::Array<double> &upperBounds)
1149 {
1150  OptimizerPointer returnOpt;
1151  double xTol = m_XTolerance;
1152  bool defaultTol = false;
1153  if (m_XTolerance == 0)
1154  {
1155  defaultTol = true;
1156  xTol = 1.0e-4;
1157  }
1158 
1159  unsigned int maxEvals = m_MaxEval;
1160  if (m_MaxEval == 0)
1161  maxEvals = 400 * lowerBounds.GetSize();
1162 
1163  if (m_Optimizer != "levenberg")
1164  {
1166 
1167  if (m_Optimizer == "bobyqa")
1168  {
1169  tmpOpt->SetAlgorithm(NLOPT_LN_BOBYQA);
1170  if (defaultTol)
1171  xTol = 1.0e-7;
1172  }
1173  else if (m_Optimizer == "ccsaq")
1174  tmpOpt->SetAlgorithm(NLOPT_LD_CCSAQ);
1175  else if (m_Optimizer == "bfgs")
1176  tmpOpt->SetAlgorithm(NLOPT_LD_LBFGS);
1177 
1178  double fTol = m_FTolerance;
1179  if (m_FTolerance == 0)
1180  fTol = 1.0e-2 * xTol;
1181 
1183  dynamic_cast <anima::MCMSingleValuedCostFunction *> (cost.GetPointer());
1184  tmpOpt->SetCostFunction(costCast);
1185 
1186  tmpOpt->SetMaximize(false);
1187  tmpOpt->SetXTolRel(xTol);
1188  tmpOpt->SetFTolRel(fTol);
1189  tmpOpt->SetMaxEval(maxEvals);
1190  tmpOpt->SetVectorStorageSize(2000);
1191 
1192  tmpOpt->SetLowerBoundParameters(lowerBounds);
1193  tmpOpt->SetUpperBoundParameters(upperBounds);
1194 
1195  returnOpt = tmpOpt;
1196  }
1197  else
1198  {
1199  if (m_MLEstimationStrategy == Marginal)
1200  itkExceptionMacro("Levenberg Marquardt optimizer not supported with marginal optimization");
1201 
1202  typedef anima::BoundedLevenbergMarquardtOptimizer LevenbergMarquardtOptimizerType;
1203  LevenbergMarquardtOptimizerType::Pointer tmpOpt = LevenbergMarquardtOptimizerType::New();
1204 
1206  dynamic_cast <anima::MCMMultipleValuedCostFunction *> (cost.GetPointer());
1207 
1208  double fTol = m_FTolerance;
1209  if (m_FTolerance == 0)
1210  fTol = 1.0e-2 * xTol;
1211 
1212  tmpOpt->SetCostFunction(costCast);
1213  tmpOpt->SetCostTolerance(fTol);
1214  tmpOpt->SetNumberOfIterations(maxEvals);
1215  tmpOpt->SetValueTolerance(xTol);
1216 
1217  tmpOpt->SetLowerBounds(lowerBounds);
1218  tmpOpt->SetUpperBounds(upperBounds);
1219 
1220  returnOpt = tmpOpt;
1221  }
1222 
1223  return returnOpt;
1224 }
1225 
1226 template <class InputPixelType, class OutputPixelType>
1227 double
1229 ::PerformSingleOptimization(ParametersType &p, CostFunctionBasePointer &cost, itk::Array<double> &lowerBounds, itk::Array<double> &upperBounds)
1230 {
1231  double costValue = this->GetCostValue(cost,p);
1232 
1233  OptimizerPointer optimizer = this->CreateOptimizer(cost,lowerBounds,upperBounds);
1234 
1235  optimizer->SetInitialPosition(p);
1236  optimizer->StartOptimization();
1237 
1238  p = optimizer->GetCurrentPosition();
1239 
1240  // Takes care of round-off errors resulting
1241  // in parameters sometimes slightly off bounds
1242  for (unsigned int i = 0;i < p.GetSize();++i)
1243  {
1244  if (p[i] < lowerBounds[i])
1245  p[i] = lowerBounds[i];
1246 
1247  if (p[i] > upperBounds[i])
1248  p[i] = upperBounds[i];
1249  }
1250 
1251  costValue = this->GetCostValue(cost,p);
1252 
1253  return costValue;
1254 }
1255 
1256 template <class InputPixelType, class OutputPixelType>
1257 double
1260 {
1261  if (m_Optimizer == "levenberg")
1262  {
1264  dynamic_cast <anima::MCMMultipleValuedCostFunction *> (cost.GetPointer());
1265 
1266  costCast->GetInternalCost()->GetValues(p);
1267  return costCast->GetInternalCost()->GetCurrentCostValue();
1268  }
1269 
1271  dynamic_cast <anima::MCMSingleValuedCostFunction *> (cost.GetPointer());
1272 
1273  return costCast->GetValue(p);
1274 }
1275 
1276 template <class InputPixelType, class OutputPixelType>
1277 void
1279 ::GetProfiledInformation(CostFunctionBasePointer &cost, MCMPointer &mcm, double &b0Value, double &sigmaSqValue)
1280 {
1281  unsigned int numCompartments = mcm->GetNumberOfCompartments();
1282  MCMType::ListType tmpWeights;
1283 
1284  if (m_Optimizer == "levenberg")
1285  {
1287  dynamic_cast <anima::MCMMultipleValuedCostFunction *> (cost.GetPointer());
1288 
1289  sigmaSqValue = costCast->GetSigmaSquare();
1290  tmpWeights = costCast->GetMCMStructure()->GetCompartmentWeights();
1291  }
1292  else
1293  {
1295  dynamic_cast <anima::MCMSingleValuedCostFunction *> (cost.GetPointer());
1296 
1297  sigmaSqValue = costCast->GetSigmaSquare();
1298  tmpWeights = costCast->GetMCMStructure()->GetCompartmentWeights();
1299  }
1300 
1301  b0Value = 0.0;
1302  for (unsigned int i = 0;i < numCompartments;++i)
1303  b0Value += tmpWeights[i];
1304 
1305  if (m_MLEstimationStrategy == VariableProjection)
1306  mcm->SetCompartmentWeights(tmpWeights);
1307 }
1308 
1309 template <class InputPixelType, class OutputPixelType>
1310 double
1312 ::ComputeAICcValue(MCMPointer &mcmValue, double costValue)
1313 {
1314  // Compute AICu (improvement over AICc)
1315  // nbparams is the number of parameters of the model plus the variance value
1316  double nbparams = mcmValue->GetNumberOfParameters() + 1.0;
1317 
1318  // We assume the cost value is returned as - 2 * log-likelihood
1319  double AICc = costValue + 2.0 * nbparams + 2.0 * nbparams * (nbparams + 1.0) / (m_NumberOfImages - nbparams - 1.0)
1320  + m_NumberOfImages * std::log(m_NumberOfImages / (m_NumberOfImages - nbparams - 1.0));
1321 
1322  return AICc;
1323 }
1324 
1325 template <class InputPixelType, class OutputPixelType>
1326 void
1328 ::SparseInitializeSticks(MCMPointer &complexModel, bool authorizeNegativeB0Value, std::vector<double> &observedSignals,
1329  itk::ThreadIdType threadId)
1330 {
1331  unsigned int numIsotropicComponents = complexModel->GetNumberOfIsotropicCompartments();
1332  unsigned int numNonIsotropicComponents = complexModel->GetNumberOfCompartments() - numIsotropicComponents;
1333  unsigned int numCompartments = complexModel->GetNumberOfCompartments();
1334 
1335  //First compute sparse solution as NNLS optmization
1337  sparseOptimizer->SetDataMatrix(m_SparseSticksDictionary);
1338 
1339  unsigned int dictionarySize = m_SparseSticksDictionary.cols();
1340  unsigned int numSignals = observedSignals.size();
1341  ParametersType rightHandValues(numSignals);
1342  for (unsigned int i = 0;i < numSignals;++i)
1343  rightHandValues[i] = observedSignals[i];
1344 
1345  if (authorizeNegativeB0Value)
1346  rightHandValues *= -1;
1347 
1348  sparseOptimizer->SetPoints(rightHandValues);
1349  sparseOptimizer->SetSquaredProblem(false);
1350  sparseOptimizer->StartOptimization();
1351 
1352  // Get atom weights and determine the number of non null weighted components, first quartile of their weights
1353  ParametersType dictionaryWeights = sparseOptimizer->GetCurrentPosition();
1354  std::vector <unsigned int> nonNullAtomIndexes;
1355 
1356  double totalWeightsSum = 0.0;
1357  double sumWeights = 0.0;
1358  for (unsigned int i = 0;i < numIsotropicComponents;++i)
1359  {
1360  totalWeightsSum += dictionaryWeights[i];
1361  sumWeights += dictionaryWeights[i];
1362  dictionaryWeights[i] = 0;
1363  }
1364 
1365  std::sort(dictionaryWeights.begin(),dictionaryWeights.end());
1366 
1367  unsigned int numRealAnisotropicCompartments = dictionarySize;
1368  for (int i = dictionarySize - 1;i >= 0;--i)
1369  {
1370  if (dictionaryWeights[i] == 0.0)
1371  {
1372  numRealAnisotropicCompartments = dictionarySize - 1 - i;
1373  break;
1374  }
1375 
1376  totalWeightsSum += dictionaryWeights[i];
1377  }
1378 
1379  unsigned int thrIndex = dictionarySize - std::max(numNonIsotropicComponents,static_cast <unsigned int>(std::floor(numRealAnisotropicCompartments * 0.75)));
1380  if (thrIndex > 0)
1381  --thrIndex;
1382 
1383  double thrWeight = dictionaryWeights[thrIndex];
1384  double maxDictionaryWeight = dictionaryWeights[dictionarySize - 1];
1385  dictionaryWeights = sparseOptimizer->GetCurrentPosition();
1386 
1387  for (unsigned int i = numIsotropicComponents;i < dictionarySize;++i)
1388  {
1389  if ((dictionaryWeights[i] > thrWeight) && (dictionaryWeights[i] > maxDictionaryWeight / 10.0))
1390  {
1391  nonNullAtomIndexes.push_back(i);
1392  sumWeights += dictionaryWeights[i];
1393  }
1394  }
1395 
1396  numRealAnisotropicCompartments = nonNullAtomIndexes.size();
1397  MCMType::ListType sparseWeights(numCompartments,0.0);
1398  for (unsigned int i = 0;i < numIsotropicComponents;++i)
1399  {
1400  if (!authorizeNegativeB0Value)
1401  sparseWeights[i] = dictionaryWeights[i];
1402  else
1403  sparseWeights[i] = - dictionaryWeights[i];
1404  }
1405 
1406  std::vector <double> tmpDirection(3,0.0);
1407  if (numRealAnisotropicCompartments <= numNonIsotropicComponents)
1408  {
1409  // Not enough atoms detected, keep what we get and return
1410  MCMCreatorType *mcmCreator = m_MCMCreators[threadId];
1411  mcmCreator->SetNumberOfCompartments(numRealAnisotropicCompartments);
1412  complexModel = mcmCreator->GetNewMultiCompartmentModel();
1413  numCompartments = complexModel->GetNumberOfCompartments();
1414  sparseWeights.resize(numCompartments);
1415 
1416  for (unsigned int i = numIsotropicComponents;i < numCompartments;++i)
1417  {
1418  unsigned int iIndex = i - numIsotropicComponents;
1419 
1420  if (!authorizeNegativeB0Value)
1421  sparseWeights[i] = dictionaryWeights[nonNullAtomIndexes[iIndex]];
1422  else
1423  sparseWeights[i] = - dictionaryWeights[nonNullAtomIndexes[iIndex]];
1424 
1425  anima::BaseCompartment *currentCompartment = complexModel->GetCompartment(i);
1426 
1427  anima::TransformCartesianToSphericalCoordinates(m_DictionaryDirections[nonNullAtomIndexes[iIndex]],tmpDirection);
1428  currentCompartment->SetOrientationTheta(tmpDirection[0]);
1429  currentCompartment->SetOrientationPhi(tmpDirection[1]);
1430  }
1431 
1432  complexModel->SetCompartmentWeights(sparseWeights);
1433  return;
1434  }
1435 
1436  // There are more atoms selected than the regular number, cluster them
1437  vnl_matrix <double> directionsDistanceMatrix(numRealAnisotropicCompartments, numRealAnisotropicCompartments);
1438  for (unsigned int i = 0;i < numRealAnisotropicCompartments;++i)
1439  {
1440  directionsDistanceMatrix(i,i) = 0;
1441  for (unsigned int j = i + 1;j < numRealAnisotropicCompartments;++j)
1442  {
1443  double dotProduct = std::abs(anima::ComputeScalarProduct(m_DictionaryDirections[nonNullAtomIndexes[i]],m_DictionaryDirections[nonNullAtomIndexes[j]]));
1444  if (dotProduct > 1.0)
1445  dotProduct = 1.0;
1446 
1447  double acosValue = std::acos(dotProduct);
1448  directionsDistanceMatrix(i,j) = acosValue * acosValue;
1449  directionsDistanceMatrix(j,i) = directionsDistanceMatrix(i,j);
1450  }
1451  }
1452 
1453  typedef anima::SpectralClusteringFilter <double> SpectralClusterFilterType;
1454  SpectralClusterFilterType spectralClustering;
1455  spectralClustering.SetInputData(directionsDistanceMatrix);
1456  spectralClustering.SetNbClass(numNonIsotropicComponents);
1457  spectralClustering.SetMaxIterations(200);
1458  spectralClustering.SetVerbose(false);
1459  spectralClustering.InitializeSigmaFromDistances();
1460  spectralClustering.SetCMeansAverageType(SpectralClusterFilterType::CMeansFilterType::Euclidean);
1461 
1462  spectralClustering.Update();
1463 
1464  // Compute cluster direction for each cluster
1465  std::vector <double> classMemberships;
1466  vnl_matrix <double> dcmMatrix(3,3);
1467  typedef vnl_matrix <double> EigenMatrixType;
1468  typedef vnl_vector_fixed <double,3> EigenVectorType;
1469  typedef itk::SymmetricEigenAnalysis <EigenMatrixType,EigenVectorType,EigenMatrixType> EigenAnalysisType;
1470  EigenAnalysisType eigSystem(3);
1471  EigenMatrixType eigVecs(3,3);
1472  EigenVectorType eigVals;
1473  eigSystem.SetOrderEigenValues(true);
1474 
1475  // Set output directions and compute their absolute weights (includes B0)
1476  for (unsigned int i = 0;i < numNonIsotropicComponents;++i)
1477  {
1478  dcmMatrix.fill(0.0);
1479  double weightComponent = 0.0;
1480  for (unsigned int j = 0;j < numRealAnisotropicCompartments;++j)
1481  {
1482  classMemberships = spectralClustering.GetClassesMembership(j);
1483  unsigned int atomIndex = nonNullAtomIndexes[j];
1484  double membershipWeight = classMemberships[i];
1485  weightComponent += membershipWeight * dictionaryWeights[atomIndex];
1486  for (unsigned int k = 0;k < 3;++k)
1487  {
1488  dcmMatrix(k,k) += membershipWeight * dictionaryWeights[atomIndex] * m_DictionaryDirections[atomIndex][k] * m_DictionaryDirections[atomIndex][k];
1489 
1490  for (unsigned int l = k + 1;l < 3;++l)
1491  {
1492  double tmpValue = membershipWeight * dictionaryWeights[atomIndex] * m_DictionaryDirections[atomIndex][k] * m_DictionaryDirections[atomIndex][l];
1493  dcmMatrix(k,l) += tmpValue;
1494  dcmMatrix(l,k) += tmpValue;
1495  }
1496  }
1497  }
1498 
1499  eigSystem.ComputeEigenValuesAndVectors(dcmMatrix, eigVals, eigVecs);
1500  for (unsigned int j = 0;j < 3;++j)
1501  tmpDirection[j] = eigVecs(2,j);
1502 
1503  anima::TransformCartesianToSphericalCoordinates(tmpDirection,tmpDirection);
1504 
1505  anima::BaseCompartment *currentCompartment = complexModel->GetCompartment(i + numIsotropicComponents);
1506  currentCompartment->SetOrientationTheta(tmpDirection[0]);
1507  currentCompartment->SetOrientationPhi(tmpDirection[1]);
1508 
1509  if (!authorizeNegativeB0Value)
1510  sparseWeights[i + numIsotropicComponents] = weightComponent;
1511  else
1512  sparseWeights[i + numIsotropicComponents] = - weightComponent;
1513  }
1514 
1515  // Rearrange weights to account for left out atoms (ensure isotropic weights don't get inflated
1516  for (unsigned int i = numIsotropicComponents;i < numCompartments;++i)
1517  {
1518  if (!authorizeNegativeB0Value)
1519  sparseWeights[i] += (totalWeightsSum - sumWeights) / numNonIsotropicComponents;
1520  else
1521  sparseWeights[i] += (sumWeights - totalWeightsSum) / numNonIsotropicComponents;
1522  }
1523 
1524  complexModel->SetCompartmentWeights(sparseWeights);
1525 }
1526 
1527 template <class InputPixelType, class OutputPixelType>
1528 void
1531 {
1532  // copy everything
1533  unsigned int numCompartments = complexModel->GetNumberOfCompartments();
1534  if (numCompartments != simplifiedModel->GetNumberOfCompartments())
1535  itkExceptionMacro("Simplified and complex model should have the same number of components.");
1536 
1537  for (unsigned int i = 0;i < numCompartments;++i)
1538  complexModel->GetCompartment(i)->CopyFromOther(simplifiedModel->GetCompartment(i));
1539 
1540  // Now we're talking about weights
1541  complexModel->SetCompartmentWeights(simplifiedModel->GetCompartmentWeights());
1542 }
1543 
1544 } // end namespace anima
itk::SmartPointer< Self > Pointer
void TensorCoarseGridInitialization(MCMPointer &mcmUpdateValue, CostFunctionBasePointer &cost, MCMType::ListType &workVec, ParametersType &p)
Coarse grid initialization of tensor model.
OptimizerType::ParametersType ParametersType
InternalCostType::MCMPointer & GetMCMStructure()
void TransformSphericalToCartesianCoordinates(const VectorType &v, VectorType &resVec)
itk::SmartPointer< Self > Pointer
virtual void InitializeModelFromSimplifiedOne(MCMPointer &simplifiedModel, MCMPointer &complexModel)
Performs initialization from simplified model with the same number of compartments.
void EstimateFreeWaterModel(MCMPointer &mcmValue, std::vector< double > &observedSignals, itk::ThreadIdType threadId, double &aiccValue, double &b0Value, double &sigmaSqValue)
Specific method for N=0 compartments estimation (only free water)
double PerformSingleOptimization(ParametersType &p, CostFunctionBasePointer &cost, itk::Array< double > &lowerBounds, itk::Array< double > &upperBounds)
Performs an optimization of the supplied cost function and parameters using the specified optimizer(s...
Levenberg-Marquardt optimizer with lower and upper bounds on parameters Implementation of the origina...
static Pointer New()
virtual void SparseInitializeSticks(MCMPointer &complexModel, bool authorizeNegativeB0Value, std::vector< double > &observedSignals, itk::ThreadIdType threadId)
Performs initialization from single DTI.
BaseCompartment::ListType ListType
void ComputeExtraAxonalAndKappaCoarseGrids()
Computes extra axonal and kappa coarse grids (used for NODDI and DDI initialization) ...
void SetInputData(MatrixType &data)
Input data: matrix of squared distances.
void SetInputImage(InputImageType *input)
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...
void AddGradientDirection(unsigned int i, GradientType &grad)
virtual const InternalCostPointer & GetInternalCost() const
CostFunctionBaseType::Pointer CostFunctionBasePointer
double ComputeAICcValue(MCMPointer &mcmValue, double costValue)
Compute AICc value from a cost function value and model.
void GetProfiledInformation(CostFunctionBasePointer &cost, MCMPointer &mcm, double &b0Value, double &sigmaSqValue)
Utility function to get profiled data from the cost function into an MCM model.
void ModelEstimation(MCMPointer &mcmValue, bool authorizedNegativeB0Value, std::vector< double > &observedSignals, itk::ThreadIdType threadId, double &aiccValue, double &b0Value, double &sigmaSqValue)
Doing estimation, calling initialization procedure until ball and zeppelin, returns AICc value...
virtual void SetOrientationTheta(double num)
void OptimizeNonIsotropicCompartments(MCMPointer &mcmValue, unsigned int currentNumberOfCompartments, std::vector< double > &observedSignals, itk::ThreadIdType threadId, double &aiccValue, double &b0Value, double &sigmaSqValue)
Doing estimation of non isotropic compartments (for a given number of anisotropic compartments) ...
void ComputeTensorRadialDiffsAndAzimuthCoarseGrids()
Computes extra axonal and kappa coarse grids (used for tensor final initaialization) ...
static Pointer New()
itk::SmartPointer< Self > Pointer
Superclass::OutputImageRegionType OutputImageRegionType
void GetSphereEvenSampling(std::vector< std::vector< ScalarType > > &spherePoints, unsigned int numSamples)
void ExtraAxonalAndKappaCoarseGridInitialization(MCMPointer &mcmUpdateValue, CostFunctionBasePointer &cost, MCMType::ListType &workVec, ParametersType &p)
Coarse grid initialization of NODDI and DDI models.
virtual MeasureType GetValue(const ParametersType &parameters) const ITK_OVERRIDE
vnl_vector_fixed< double, 3 > GradientType
void SetDescriptionModel(MCMPointer &mcm)
OptimizerPointer CreateOptimizer(CostFunctionBasePointer &cost, itk::Array< double > &lowerBounds, itk::Array< double > &upperBounds)
Create an optimizer following the optimizer type and estimation mode.
void InitialOrientationsEstimation(MCMPointer &mcmValue, bool authorizedNegativeB0Value, unsigned int currentNumberOfCompartments, std::vector< double > &observedSignals, itk::ThreadIdType threadId, double &aiccValue, double &b0Value, double &sigmaSqValue)
Doing estimation only of multiple orientations.
double ComputeScalarProduct(const VectorType &v1, const VectorType &v2, const unsigned int NDimension)
virtual void SetOrientationPhi(double num)
const double MCMDiffusivityLowerBound
Diffusivity lower bound for estimation.
static Pointer New()
virtual MCMCreatorType * GetNewMCMCreatorInstance()
double GetCostValue(CostFunctionBasePointer &cost, ParametersType &p)
Utility function to get a value from a cost function.
virtual void BeforeThreadedGenerateData() ITK_OVERRIDE
void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) ITK_OVERRIDE
void TransformCartesianToSphericalCoordinates(const VectorType &v, VectorType &resVec)
itk::SmartPointer< Self > Pointer
const double MCMAxialDiffusivityAddonLowerBound
Axial diffusivity add on to lower bound (used to ensure a minimal anisotropy to the anisotropic compa...
Really this class is some simplified factory that creates the MCM that it knows.
virtual CostFunctionBasePointer CreateCostFunction(std::vector< double > &observedSignals, MCMPointer &mcmModel)
Create a cost function following the noise type and estimation mode.