ANIMA  4.0
animaMCML2DistanceComputer.cxx
Go to the documentation of this file.
3 #include <animaBaseTensorTools.h>
4 #include <animaMCMConstants.h>
5 
6 namespace anima
7 {
8 
10 {
11  m_LowPassGaussianSigma = 2000;
12  m_ForceApproximation = false;
13  m_SquaredDistance = true;
14 
15  m_SmallDelta = anima::DiffusionSmallDelta;
16  m_BigDelta = anima::DiffusionBigDelta;
17 }
18 
20 {
21  double oldVal = m_SmallDelta;
22  m_SmallDelta = val;
23 
24  if (oldVal != val)
25  this->UpdateSphereWeights();
26 }
27 
29 {
30  double oldVal = m_BigDelta;
31  m_BigDelta = val;
32 
33  if (oldVal != val)
34  this->UpdateSphereWeights();
35 }
36 
37 void MCML2DistanceComputer::SetGradientStrengths(const std::vector <double> &val)
38 {
39  m_GradientStrengths = val;
40 
41  if ((m_GradientDirections.size() == m_GradientStrengths.size())&&(m_GradientStrengths.size() != 0)&&(m_GradientDirections.size() != 0))
42  this->UpdateSphereWeights();
43 }
44 
45 void MCML2DistanceComputer::SetGradientDirections(const std::vector <GradientType> &val)
46 {
47  m_GradientDirections = val;
48 
49  if ((m_GradientDirections.size() == m_GradientStrengths.size())&&(m_GradientStrengths.size() != 0)&&(m_GradientDirections.size() != 0))
50  this->UpdateSphereWeights();
51 }
52 
54 {
55  std::vector <double> individualGradientStrengths;
56  for (unsigned int i = 0;i < m_GradientStrengths.size();++i)
57  {
58  bool alreadyIn = false;
59  for (unsigned int j = 0;j < individualGradientStrengths.size();++j)
60  {
61  if (individualGradientStrengths[j] == m_GradientStrengths[i])
62  {
63  alreadyIn = true;
64  break;
65  }
66  }
67 
68  if (!alreadyIn)
69  individualGradientStrengths.push_back(m_GradientStrengths[i]);
70  }
71 
72  std::sort(individualGradientStrengths.begin(),individualGradientStrengths.end());
73 
74  if (individualGradientStrengths[0] != 0)
75  {
76  m_GradientStrengths.push_back(0);
77  GradientType tmpVec;
78  tmpVec.fill(0);
79  m_GradientDirections.push_back(tmpVec);
80  individualGradientStrengths.insert(individualGradientStrengths.begin(),0);
81  }
82 
83  m_SphereWeights.resize(individualGradientStrengths.size());
84  m_BValWeightsIndexes.resize(m_GradientStrengths.size());
85  double lastBValue = anima::GetBValueFromAcquisitionParameters(m_SmallDelta, m_BigDelta, individualGradientStrengths[individualGradientStrengths.size() - 1]);
86 
87  for (unsigned int i = 0;i < individualGradientStrengths.size();++i)
88  {
89  unsigned int numValues = 0;
90  for (unsigned int j = 0;j < m_GradientStrengths.size();++j)
91  {
92  if (m_GradientStrengths[j] == individualGradientStrengths[i])
93  {
94  ++numValues;
95  m_BValWeightsIndexes[j] = i;
96  }
97  }
98 
99  double lowerRadius = 0;
100  double baseValue = 0;
101  double bValueCenter = anima::GetBValueFromAcquisitionParameters(m_SmallDelta, m_BigDelta, individualGradientStrengths[i]);
102 
103  if (i > 0)
104  {
105  double bValueBefore = anima::GetBValueFromAcquisitionParameters(m_SmallDelta, m_BigDelta, individualGradientStrengths[i-1]);
106  lowerRadius = (bValueCenter + bValueBefore) / 2.0;
107  baseValue = bValueBefore;
108  }
109 
110  double upperRadius = lastBValue + lowerRadius - baseValue;
111  if (i < individualGradientStrengths.size() - 1)
112  {
113  double bValueAfter = anima::GetBValueFromAcquisitionParameters(m_SmallDelta, m_BigDelta, individualGradientStrengths[i+1]);
114  upperRadius = (bValueCenter + bValueAfter) / 2.0;
115  }
116 
117  lowerRadius = std::sqrt(2.0 * lowerRadius);
118  upperRadius = std::sqrt(2.0 * upperRadius);
119 
120  m_SphereWeights[i] = 4 * M_PI * (std::pow(upperRadius,3.0) - std::pow(lowerRadius,3.0)) / (3.0 * numValues);
121  }
122 }
123 
124 double MCML2DistanceComputer::ComputeDistance(const MCMPointer &firstModel, const MCMPointer &secondModel) const
125 {
126  bool tensorCompatibility = this->CheckTensorCompatibility(firstModel,secondModel);
127 
128  if (tensorCompatibility)
129  return this->ComputeTensorDistance(firstModel,secondModel);
130 
131  return this->ComputeApproximateDistance(firstModel,secondModel);
132 }
133 
134 bool MCML2DistanceComputer::CheckTensorCompatibility(const MCMPointer &firstModel, const MCMPointer &secondModel) const
135 {
136  if (m_ForceApproximation)
137  return false;
138 
139  for (unsigned int i = 0;i < firstModel->GetNumberOfCompartments();++i)
140  {
141  if (!firstModel->GetCompartment(i)->GetTensorCompatible())
142  return false;
143  }
144 
145  for (unsigned int i = 0;i < secondModel->GetNumberOfCompartments();++i)
146  {
147  if (!secondModel->GetCompartment(i)->GetTensorCompatible())
148  return false;
149  }
150 
151  return true;
152 }
153 
154 double MCML2DistanceComputer::ComputeTensorDistance(const MCMPointer &firstModel, const MCMPointer &secondModel) const
155 {
156  unsigned int fixedNumCompartments = firstModel->GetNumberOfCompartments();
157  unsigned int movingNumCompartments = secondModel->GetNumberOfCompartments();
158 
159  std::vector < vnl_matrix_fixed <double, 3, 3> > firstModelMatrices(fixedNumCompartments);
160  std::vector < vnl_matrix_fixed <double, 3, 3> > secondModelMatrices(movingNumCompartments);
161  std::vector <double> firstModelWeights(fixedNumCompartments);
162  std::vector <double> secondModelWeights(movingNumCompartments);
163 
164  unsigned int pos = 0;
165  for (unsigned int i = 0;i < fixedNumCompartments;++i)
166  {
167  if (firstModel->GetCompartmentWeight(i) == 0)
168  continue;
169 
170  firstModelMatrices[pos] = firstModel->GetCompartment(i)->GetDiffusionTensor().GetVnlMatrix();
171  firstModelWeights[pos] = firstModel->GetCompartmentWeight(i);
172  ++pos;
173  }
174 
175  fixedNumCompartments = pos;
176 
177  if (fixedNumCompartments == 0)
178  return 0;
179 
180  firstModelMatrices.resize(fixedNumCompartments);
181  firstModelWeights.resize(fixedNumCompartments);
182 
183  pos = 0;
184  for (unsigned int i = 0;i < movingNumCompartments;++i)
185  {
186  if (secondModel->GetCompartmentWeight(i) == 0)
187  continue;
188 
189  secondModelMatrices[pos] = secondModel->GetCompartment(i)->GetDiffusionTensor().GetVnlMatrix();
190  secondModelWeights[pos] = secondModel->GetCompartmentWeight(i);
191  ++pos;
192  }
193 
194  movingNumCompartments = pos;
195 
196  if (movingNumCompartments == 0)
197  return 0;
198 
199  secondModelMatrices.resize(movingNumCompartments);
200  secondModelWeights.resize(movingNumCompartments);
201 
202  double metricValue = 0;
203  double pi23half = std::pow(2.0 * M_PI,1.5);
204 
205  vnl_matrix <double> workMatrix(3,3);
206  for (unsigned int i = 0;i < fixedNumCompartments;++i)
207  {
208  double currentFixedWeight = firstModelWeights[i];
209  for (unsigned int j = 0;j < 3;++j)
210  {
211  workMatrix(j,j) = 2.0 * firstModelMatrices[i](j,j) + 1.0 / m_LowPassGaussianSigma;
212  for (unsigned int k = j+1;k < 3;++k)
213  {
214  workMatrix(j,k) = 2.0 * firstModelMatrices[i](j,k);
215  workMatrix(k,j) = workMatrix(j,k);
216  }
217  }
218 
219  metricValue += currentFixedWeight * currentFixedWeight * pi23half / std::sqrt(vnl_determinant <double> (workMatrix));
220 
221  for (unsigned int j = i+1;j < fixedNumCompartments;++j)
222  {
223  double secondFixedWeight = firstModelWeights[j];
224  for (unsigned int k = 0;k < 3;++k)
225  {
226  workMatrix(k,k) = firstModelMatrices[i](k,k) + firstModelMatrices[j](k,k) + 1.0 / m_LowPassGaussianSigma;
227  for (unsigned int l = k+1;l < 3;++l)
228  {
229  workMatrix(k,l) = firstModelMatrices[i](k,l) + firstModelMatrices[j](k,l);
230  workMatrix(l,k) = workMatrix(k,l);
231  }
232  }
233 
234  metricValue += 2.0 * currentFixedWeight * secondFixedWeight * pi23half / std::sqrt(vnl_determinant <double> (workMatrix));
235  }
236 
237  for (unsigned int j = 0;j < movingNumCompartments;++j)
238  {
239  double currentMovingWeight = secondModelWeights[j];
240  for (unsigned int k = 0;k < 3;++k)
241  {
242  workMatrix(k,k) = firstModelMatrices[i](k,k) + secondModelMatrices[j](k,k) + 1.0 / m_LowPassGaussianSigma;
243  for (unsigned int l = k+1;l < 3;++l)
244  {
245  workMatrix(k,l) = firstModelMatrices[i](k,l) + secondModelMatrices[j](k,l);
246  workMatrix(l,k) = workMatrix(k,l);
247  }
248  }
249 
250  metricValue -= 2.0 * currentFixedWeight * currentMovingWeight * pi23half / std::sqrt(vnl_determinant <double> (workMatrix));
251  }
252  }
253 
254  for (unsigned int i = 0;i < movingNumCompartments;++i)
255  {
256  double currentMovingWeight = secondModelWeights[i];
257  for (unsigned int j = 0;j < 3;++j)
258  {
259  workMatrix(j,j) = 2.0 * secondModelMatrices[i](j,j) + 1.0 / m_LowPassGaussianSigma;
260  for (unsigned int k = j+1;k < 3;++k)
261  {
262  workMatrix(j,k) = 2.0 * secondModelMatrices[i](j,k);
263  workMatrix(k,j) = workMatrix(j,k);
264  }
265  }
266 
267  metricValue += currentMovingWeight * currentMovingWeight * pi23half / std::sqrt(vnl_determinant <double> (workMatrix));
268 
269  for (unsigned int j = i+1;j < movingNumCompartments;++j)
270  {
271  double secondMovingWeight = secondModelWeights[j];
272  for (unsigned int k = 0;k < 3;++k)
273  {
274  workMatrix(k,k) = secondModelMatrices[i](k,k) + secondModelMatrices[j](k,k) + 1.0 / m_LowPassGaussianSigma;
275  for (unsigned int l = k+1;l < 3;++l)
276  {
277  workMatrix(k,l) = secondModelMatrices[i](k,l) + secondModelMatrices[j](k,l);
278  workMatrix(l,k) = workMatrix(k,l);
279  }
280  }
281 
282  metricValue += 2.0 * currentMovingWeight * secondMovingWeight * pi23half / std::sqrt(vnl_determinant <double> (workMatrix));
283  }
284  }
285 
286  if (metricValue < 0)
287  metricValue = 0;
288 
289  if (m_SquaredDistance)
290  return metricValue;
291 
292  return std::sqrt(metricValue);
293 }
294 
295 double MCML2DistanceComputer::ComputeApproximateDistance(const MCMPointer &firstModel, const MCMPointer &secondModel) const
296 {
297  if ((m_GradientStrengths.size() == 0)||(m_GradientDirections.size() == 0)||(m_GradientStrengths.size() != m_GradientDirections.size()))
298  itkExceptionMacro("Problem in metric: b-values and gradient directions not correctly set");
299 
300  double metricValue = 0;
301  for (unsigned int i = 0;i < m_GradientStrengths.size();++i)
302  {
303  double firstCFValue = firstModel->GetPredictedSignal(m_SmallDelta, m_BigDelta, m_GradientStrengths[i], m_GradientDirections[i]);
304  double secondCFValue = secondModel->GetPredictedSignal(m_SmallDelta, m_BigDelta, m_GradientStrengths[i], m_GradientDirections[i]);
305 
306  // We should weight these squared differences by their local volume
307  metricValue += m_SphereWeights[m_BValWeightsIndexes[i]] * (firstCFValue - secondCFValue) * (firstCFValue - secondCFValue);
308  }
309 
310  if (metricValue < 0)
311  metricValue = 0;
312 
313  if (m_SquaredDistance)
314  return metricValue;
315 
316  return std::sqrt(metricValue);
317 }
318 
319 } // end namespace anima
void SetGradientDirections(const std::vector< GradientType > &val)
double ComputeTensorDistance(const MCMPointer &firstModel, const MCMPointer &secondModel) const
bool CheckTensorCompatibility(const MCMPointer &firstModel, const MCMPointer &secondModel) const
double ComputeApproximateDistance(const MCMPointer &firstModel, const MCMPointer &secondModel) const
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)
void SetGradientStrengths(const std::vector< double > &val)
double ComputeDistance(const MCMPointer &firstModel, const MCMPointer &secondModel) const