ANIMA  4.0
animaODFEstimatorImageFilter.hxx
Go to the documentation of this file.
1 #pragma once
2 #include <cmath>
3 
6 #include <itkImageRegionConstIterator.h>
7 #include <itkImageRegionIterator.h>
8 #include <boost/math/special_functions/legendre.hpp>
9 #include <fstream>
10 
11 #include <animaVectorOperations.h>
12 
13 namespace anima
14 {
15 
16 template <typename TInputPixelType, typename TOutputPixelType>
17 void
19 ::AddGradientDirection(unsigned int i, std::vector <double> &grad)
20 {
21  if (isZero(grad))
22  {
23  m_B0Indexes.push_back(i);
24  return;
25  }
26 
27  m_GradientIndexes.push_back(i);
28 
29  std::vector <double> sphericalCoords;
31  m_GradientDirections.push_back(sphericalCoords);
32 }
33 
34 template <typename TInputPixelType, typename TOutputPixelType>
35 void
38 {
39  // Override the method in itkImageSource, so we can set the vector length of
40  // the output itk::VectorImage
41 
42  this->Superclass::GenerateOutputInformation();
43 
44  unsigned int vectorLength = (m_LOrder + 1)*(m_LOrder + 2)/2;
45  TOutputImage *output = this->GetOutput();
46  output->SetVectorLength(vectorLength);
47 }
48 
49 template <typename TInputPixelType, typename TOutputPixelType>
50 void
53 {
54  unsigned int vectorLength = (m_LOrder + 1)*(m_LOrder + 2)/2;
55  unsigned int numGrads = m_GradientDirections.size();
56 
57  if ((m_GradientIndexes.size() + m_B0Indexes.size()) != this->GetNumberOfIndexedInputs())
58  throw itk::ExceptionObject(__FILE__, __LINE__,"Number of gradient directions different from number of inputs",ITK_LOCATION);
59 
60  if (m_UseAganjEstimation)
61  {
62  m_Normalize = false;
63  m_Sharpen = false;
64  }
65 
66  if (m_BValueShellSelected < 0)
67  m_BValueShellSelected = m_BValuesList[m_GradientIndexes[0]];
68 
69  std::vector <unsigned int> bvalKeptIndexes;
70  std::vector < std::vector <double> > keptGradients;
71  // First filter out unwanted b-values (keeping only b = m_BValueShellSelected)
72  for (unsigned int i = 0;i < numGrads;++i)
73  {
74  if ((m_BValuesList[m_GradientIndexes[i]] >= m_BValueShellSelected - m_BValueShellTolerance) &&
75  (m_BValuesList[m_GradientIndexes[i]] <= m_BValueShellSelected + m_BValueShellTolerance))
76  {
77  bvalKeptIndexes.push_back(m_GradientIndexes[i]);
78  keptGradients.push_back(m_GradientDirections[i]);
79  }
80  }
81 
82  m_GradientIndexes = bvalKeptIndexes;
83  m_GradientDirections = keptGradients;
84  numGrads = m_GradientIndexes.size();
85 
86  std::cout << "Running ODF estimation using " << m_B0Indexes.size() << " B0 images and " << numGrads << " gradient images with b-value at " << m_BValueShellSelected << "s.mm^-2" << std::endl;
87 
88  // Compute TMatrix as expressed in Descoteaux MRM 2007
89 
90  unsigned int posValue = 0;
91  vnl_matrix <double> BMatrix(numGrads,vectorLength);
92 
93  anima::ODFSphericalHarmonicBasis tmpBasis(m_LOrder);
94 
95  for (unsigned int i = 0;i < numGrads;++i)
96  {
97  posValue = 0;
98  for (int k = 0;k <= (int)m_LOrder;k += 2)
99  for (int m = -k;m <= k;++m)
100  {
101  BMatrix(i,posValue) = tmpBasis.getNthSHValueAtPosition(k,m,m_GradientDirections[i][0],m_GradientDirections[i][1]);
102  ++posValue;
103  }
104  }
105 
106  std::vector <double> LVector(vectorLength,0);
107  m_PVector.resize(vectorLength);
108 
109  posValue = 0;
110  for (unsigned int k = 0;k <= m_LOrder;k += 2)
111  {
112  double ljVal = k*k*(k + 1)*(k + 1);
113  double pjValNum = 1, pjValDenom = 1, pjVal;
114 
115  for (unsigned int l = 2;l <= k; l += 2)
116  {
117  pjValNum *= (l-1);
118  pjValDenom *= l;
119  }
120 
121  pjVal = pjValNum/pjValDenom;
122  if (k/2 % 2 != 0)
123  pjVal *= -1;
124 
125  for (int m = -k;m <= (int)k;++m)
126  {
127  LVector[posValue] = ljVal;
128 
129  if (!m_UseAganjEstimation)
130  m_PVector[posValue] = 2*M_PI*pjVal;
131  else
132  m_PVector[posValue] = k*(k+1.0)*pjVal/(-8.0*M_PI);
133 
134  ++posValue;
135  }
136  }
137 
138  vnl_matrix <double> tmpMat = BMatrix.transpose() * BMatrix;
139  for (unsigned int i = 0;i < vectorLength;++i)
140  tmpMat(i,i) += m_Lambda*LVector[i];
141 
142  vnl_matrix_inverse <double> tmpInv(tmpMat);
143 
144  m_TMatrix = tmpInv.inverse() * BMatrix.transpose();
145 
146  if (m_Sharpen)
147  {
148  m_DeconvolutionVector.clear();
149 
150  for (unsigned int k = 0;k <= m_LOrder;k += 2)
151  {
152  double Zfactor = 0;
153  double lambdaL = 0;
154  double delta = 0.001;
155 
156  for (double t = -1;t <= 1;t += delta)
157  {
158  double tmpVal = sqrt(1.0/((m_SharpnessRatio - 1)*t*t + 1));
159  if ((t == -1) || (t == 1))
160  {
161  lambdaL += tmpVal*boost::math::legendre_p(k,t);
162  Zfactor += tmpVal;
163  }
164  else
165  {
166  lambdaL += 2*tmpVal*boost::math::legendre_p(k,t);
167  Zfactor += 2*tmpVal;
168  }
169  }
170 
171  for (int m = -k;m <= (int)k;++m)
172  m_DeconvolutionVector.push_back(lambdaL*2*M_PI/Zfactor);
173  }
174 
175  for (unsigned int i = 0;i < vectorLength;++i)
176  for (unsigned int j = 0;j < numGrads;++j)
177  m_TMatrix(i,j) *= m_PVector[i]/m_DeconvolutionVector[i];
178  }
179  else
180  {
181  for (unsigned int i = 0;i < vectorLength;++i)
182  for (unsigned int j = 0;j < numGrads;++j)
183  m_TMatrix(i,j) *= m_PVector[i];
184  }
185 
186  if ((m_Normalize)&&(m_FileNameSphereTesselation != ""))
187  {
188  std::ifstream sphereIn(m_FileNameSphereTesselation.c_str());
189 
190  m_SphereSHSampling.clear();
191 
192  std::vector <double> dirTmp(3,0);
193  std::vector <double> sphericalCoords;
194  std::vector <double> shData;
195 
196  while (!sphereIn.eof())
197  {
198  char tmpStr[2048];
199  sphereIn.getline(tmpStr,2048);
200 
201  if (strcmp(tmpStr,"") == 0)
202  continue;
203 
204  std::stringstream tmpStrStream(tmpStr);
205  tmpStrStream >> dirTmp[0] >> dirTmp[1] >> dirTmp[2];
206 
207  anima::TransformCartesianToSphericalCoordinates(dirTmp,sphericalCoords);
208  shData.clear();
209 
210  for (int k = 0;k <= (int)m_LOrder;k += 2)
211  for (int m = -k;m <= k;++m)
212  shData.push_back(tmpBasis.getNthSHValueAtPosition(k,m,sphericalCoords[0],sphericalCoords[1]));
213 
214  m_SphereSHSampling.push_back(shData);
215  }
216  sphereIn.close();
217  }
218  else
219  m_Normalize = false;
220 }
221 
222 template <typename TInputPixelType, typename TOutputPixelType>
223 void
226 {
227  typedef itk::ImageRegionConstIterator <TInputImage> InputIteratorType;
228  typedef itk::ImageRegionIterator <TOutputImage> OutputIteratorType;
229 
230  unsigned int vectorLength = (m_LOrder + 1)*(m_LOrder + 2)/2;
231  unsigned int numGrads = m_GradientIndexes.size();
232  unsigned int numB0 = m_B0Indexes.size();
233 
234  OutputIteratorType resIt(this->GetOutput(),outputRegionForThread);
235 
236  std::vector<InputIteratorType> diffusionIts(numGrads);
237  std::vector<InputIteratorType> b0Its(numGrads);
238  for (unsigned int i = 0;i < numGrads;++i)
239  diffusionIts[i] = InputIteratorType(this->GetInput(m_GradientIndexes[i]),outputRegionForThread);
240  for (unsigned int i = 0;i < numB0;++i)
241  b0Its[i] = InputIteratorType(this->GetInput(m_B0Indexes[i]),outputRegionForThread);
242 
243  InputIteratorType refB0Itr;
244  if (m_ReferenceB0Image.IsNotNull())
245  refB0Itr = InputIteratorType(m_ReferenceB0Image, outputRegionForThread);
246 
247  itk::VariableLengthVector <TOutputPixelType> outputData(vectorLength);
248  std::vector <double> tmpData(numGrads,0);
249  while (!diffusionIts[0].IsAtEnd())
250  {
251  double b0Value = 0;
252  if (m_ReferenceB0Image.IsNotNull())
253  {
254  b0Value = refB0Itr.Get();
255  ++refB0Itr;
256  }
257  else
258  {
259  for (unsigned int i = 0;i < numB0;++i)
260  {
261  b0Value += b0Its[i].Get();
262  ++b0Its[i];
263  }
264 
265  b0Value /= numB0;
266  }
267 
268  for (unsigned int i = 0;i < numGrads;++i)
269  {
270  tmpData[i] = diffusionIts[i].Get();
271  ++diffusionIts[i];
272  }
273 
274  for (unsigned int i = 0;i < vectorLength;++i)
275  outputData[i] = 0;
276 
277  if ((isZero(tmpData))||(b0Value <= 0))
278  {
279  resIt.Set(outputData);
280  ++resIt;
281  continue;
282  }
283 
284  if (m_UseAganjEstimation)
285  {
286  for (unsigned int i = 0;i < numGrads;++i)
287  {
288  double e = tmpData[i] / b0Value;
289 
290  if (e < 0)
291  tmpData[i] = m_DeltaAganjRegularization / 2.0;
292  else if (e < m_DeltaAganjRegularization)
293  tmpData[i] = m_DeltaAganjRegularization / 2.0 + e * e / (2.0 * m_DeltaAganjRegularization);
294  else if (e < 1.0 - m_DeltaAganjRegularization)
295  tmpData[i] = e;
296  else if (e < 1)
297  tmpData[i] = 1.0 - m_DeltaAganjRegularization / 2.0 - (1.0 - e) * (1.0 - e) / (2.0 * m_DeltaAganjRegularization);
298  else
299  tmpData[i] = 1.0 - m_DeltaAganjRegularization / 2.0;
300 
301  tmpData[i] = std::log(-std::log(tmpData[i]));
302  }
303  }
304 
305  if (b0Value > 0)
306  {
307  for (unsigned int i = 0;i < vectorLength;++i)
308  for (unsigned int j = 0;j < numGrads;++j)
309  outputData[i] += m_TMatrix(i,j)*tmpData[j];
310 
311  if (!m_UseAganjEstimation)
312  {
313  for (unsigned int i = 0;i < vectorLength;++i)
314  outputData[i] /= b0Value;
315  }
316  else
317  outputData[0] = 1/(2*sqrt(M_PI));
318  }
319 
320  if (m_Normalize)
321  {
322  long double integralODF = 0;
323  for (unsigned int i = 0;i < m_SphereSHSampling.size();++i)
324  for (unsigned int j = 0;j < vectorLength;++j)
325  integralODF += m_SphereSHSampling[i][j]*outputData[j];
326 
327  for (unsigned int i = 0;i < vectorLength;++i)
328  outputData[i] /= integralODF;
329  }
330 
331  resIt.Set(outputData);
332  ++resIt;
333  }
334 }
335 
336 } // end of namespace anima
itk::VectorImage< TOutputPixelType, 3 > TOutputImage
void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) ITK_OVERRIDE
Superclass::OutputImageRegionType OutputImageRegionType
double getNthSHValueAtPosition(int k, int m, double theta, double phi)
void AddGradientDirection(unsigned int i, std::vector< double > &grad)
void TransformCartesianToSphericalCoordinates(const VectorType &v, VectorType &resVec)