Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkCorrelationCalculator.txx
Go to the documentation of this file.
1 
2 /*===================================================================
3 
4 The Medical Imaging Interaction Toolkit (MITK)
5 
6 Copyright (c) German Cancer Research Center,
7 Division of Medical and Biological Informatics.
8 All rights reserved.
9 
10 This software is distributed WITHOUT ANY WARRANTY; without
11 even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 A PARTICULAR PURPOSE.
13 
14 See LICENSE.txt or http://www.mitk.org for details.
15 
16 ===================================================================*/
17 
18 #ifndef _MITK_CORRELATIONCALCULATOR_TXX
19 #define _MITK_CORRELATIONCALCULATOR_TXX
20 
21 #include <algorithm>
22 #include <numeric>
23 #include <mitkLogMacros.h>
24 #include <mitkLog.h>
25 #include <mitkImageCast.h>
26 #include <mitkImageAccessByItk.h>
27 #include <mitkImagePixelReadAccessor.h>
28 
29 #include <itkImageRegionConstIteratorWithIndex.h>
30 
31 template< class T >
32 mitk::CorrelationCalculator<T>::CorrelationCalculator()
33  : m_InvalidTimeSeries( true )
34  , m_ParcellationImage( nullptr )
35  , m_TimeSeriesImage( nullptr )
36 {
37 }
38 
39 template< class T >
40 mitk::CorrelationCalculator<T>::~CorrelationCalculator()
41 {
42 }
43 
44 template< class T >
45 double mitk::CorrelationCalculator<T>::CalculatePearsonCorrelationCoefficient( const std::vector<T>& one, const std::vector<T>& two )
46 {
47  double standardDevOne( std::sqrt(CalculateCovariance( one, one )) );
48  double standardDevTwo( std::sqrt(CalculateCovariance( two, two )) );
49 
50  return CalculateCovariance(one, two) / (standardDevOne * standardDevTwo);
51 
52 }
53 
54 template< class T >
55 double mitk::CorrelationCalculator<T>::CalculateCovariance( const std::vector<T>& one, const std::vector<T>& two )
56 {
57  if( one.size() != two.size() )
58  {
59  MITK_WARN << "Expect two same length vectors for covariance.";
60  return 0;
61  }
62 
63  double meanOne( std::accumulate( one.begin(), one.end(), 0.0 ) / double(one.size()) );
64  double meanTwo( std::accumulate( two.begin(), two.end(), 0.0 ) / double(two.size()) );
65 
66  double sum( 0.0 );
67 
68  for(unsigned int index(0); index < one.size(); ++index)
69  {
70  sum += (one[index] - meanOne ) * (two[index] - meanTwo);
71  }
72 
73  double n( one.size() );
74 
75  return sum/n;
76 }
77 
78 template< class T >
79 std::vector< double > mitk::CorrelationCalculator<T>::ExtractAverageTimeSeriesOfParcel( int parcelValue )
80 {
81  if( this->m_InvalidTimeSeries)
82  {
83  this->ExtractAllAverageTimeSeries();
84  this->m_InvalidTimeSeries = false;
85  }
86 
87  std::vector< double > result;
88 
89  if( m_AverageTimeSeries.count(parcelValue) > 0 )
90  {
91  result = m_AverageTimeSeries.find(parcelValue)->second;
92  }
93 
94  return result;
95 }
96 
97 template< class T >
98 void mitk::CorrelationCalculator<T>::DoWholeCorrelation( )
99 {
100  if(this->m_TimeSeriesImage->GetDimension() != 4 )
101  {
102  MITK_ERROR << "Expect 3D+t timeseries image.";
103  return;
104  }
105  // calculate numbe of voxels
106  unsigned int numberOfVoxels(1);
107  unsigned int dim[3];
108  for(unsigned int loop(0); loop < 3; ++loop)
109  {
110  numberOfVoxels *= this->m_TimeSeriesImage->GetDimension( loop );
111  dim[loop] = this->m_TimeSeriesImage->GetDimension( loop );
112  }
113  m_CorrelationMatrix = vnl_matrix<double>(numberOfVoxels, numberOfVoxels);
114  unsigned int numberOfSteps( this->m_TimeSeriesImage->GetDimension( 3 ));
115 
116  //iterate over all voxels and calculate correlation
117  for( unsigned int i( 0 ); i < numberOfVoxels; ++i )
118  {
119  for( unsigned int j(0); j < i; ++j)
120  {
121  std::vector< T > one, two;
122  one.resize(numberOfSteps);
123  two.resize(numberOfSteps);
124 
125  mitk::ImagePixelReadAccessor<T, 4> readAccess(m_TimeSeriesImage);
126 
127  itk::Index<4> idx_i;
128  itk::Index<4> idx_j;
129 
130  idx_i[2] = (i / (dim[0] * dim[1])) % dim[2];
131  idx_i[1] = (i / dim[0]) % dim[1];
132  idx_i[0] = i % dim[0];
133 
134  idx_j[2] = (j / (dim[0] * dim[1])) % dim[2];
135  idx_j[1] = (j / dim[0]) % dim[1];
136  idx_j[0] = j % dim[0];
137 
138  for(unsigned int timestep(0); timestep < numberOfSteps; ++timestep)
139  {
140  idx_i[3] = timestep;
141  idx_j[3] = timestep;
142  one[timestep] = readAccess.GetPixelByIndex(idx_i);
143  two[timestep] = readAccess.GetPixelByIndex(idx_j);
144  }
145 
146  m_CorrelationMatrix[i][j] = mitk::CorrelationCalculator<T>::CalculatePearsonCorrelationCoefficient( one, two );
147  m_CorrelationMatrix[j][i] = m_CorrelationMatrix[i][j];
148  }
149  m_CorrelationMatrix[i][i] = 1;
150  }
151 }
152 
153 template< class T >
154 void mitk::CorrelationCalculator<T>::DoParcelCorrelation( typename mitk::CorrelationCalculator<T>::ParcelMode mode )
155 {
156  this->ExtractCenterOfMassForParcels();
157 
158  m_CorrelationMatrix = vnl_matrix<double>(m_ParcelCenterOfMass.size(), m_ParcelCenterOfMass.size());
159 
160 
161  AccessFixedDimensionByItk(this->m_TimeSeriesImage, ExtractAllAverageTimeSeries, 4);
162 
163  // fill matrix
164  unsigned int i(0);
165  for( std::map< int, std::vector<double> >::const_iterator it(m_AverageTimeSeries.begin()); it != m_AverageTimeSeries.end(); ++it, ++i)
166  {
167  unsigned int j(0);
168  for(std::map< int, std::vector<double> >::const_iterator inner_it(m_AverageTimeSeries.begin()); inner_it != it; ++inner_it, ++j)
169  {
170  switch( mode )
171  {
172  case UseAverageTimeSeries :
173  m_CorrelationMatrix[i][j] = mitk::CorrelationCalculator<double>::CalculatePearsonCorrelationCoefficient( it->second, inner_it->second );
174  break;
175  case UseMaximumCorrelation :
176  m_CorrelationMatrix[i][j] = mitk::CorrelationCalculator<T>::GetParcelCorrelation(it->first, inner_it->first, mode);
177  break;
178  case UseAverageCorrelation :
179  m_CorrelationMatrix[i][j] = mitk::CorrelationCalculator<T>::GetParcelCorrelation(it->first, inner_it->first, mode);
180  break;
181  default:
182  mitkThrow() << "No valid parcel correlation mode selected";
183  }
184  m_CorrelationMatrix[j][i] = m_CorrelationMatrix[i][j];
185  }
186 
187  m_CorrelationMatrix[i][i] = 1;
188  m_LabelOrderMap.insert(std::pair< unsigned int, int >(i, it->first));
189  }
190 }
191 
192 template< class T >
193 double mitk::CorrelationCalculator<T>::GetParcelCorrelation(const int& parcelA, const int& parcelB, typename mitk::CorrelationCalculator<T>::ParcelMode& mode) const
194 {
195  double result(0.0);
196 
197  if(m_ParcelTimeSeries.count(parcelA) < 1 || m_ParcelTimeSeries.count(parcelB) < 1)
198  {
199  MITK_ERROR << "Tried to calculate correlation between at least one non-existent parcel " << parcelA << " and " << parcelB;
200  return result;
201  }
202  std::vector< std::vector< T > > parcelATimeSeriesVector = m_ParcelTimeSeries.find( parcelA )->second;
203  std::vector< std::vector< T > > parcelBTimeSeriesVector = m_ParcelTimeSeries.find( parcelB )->second;
204 
205  switch( mode )
206  {
207  case UseAverageTimeSeries :
208  {
209  mitkThrow() << "Wrong function called for this mode.";
210  break;
211  }
212  case UseMaximumCorrelation :
213  {
214  for(unsigned int i(0); i < parcelATimeSeriesVector.size(); ++i)
215  {
216  for(unsigned int j(0); j < parcelBTimeSeriesVector.size(); ++j)
217  {
218  double corr =
219  mitk::CorrelationCalculator<T>::CalculatePearsonCorrelationCoefficient(
220  parcelATimeSeriesVector[i], parcelBTimeSeriesVector[j]
221  );
222  if( corr > result )
223  {
224  result = corr;
225  }
226  }
227  }
228  break;
229  }
230  case UseAverageCorrelation :
231  {
232  double corr(0.0);
233  for(unsigned int i(0); i < parcelATimeSeriesVector.size(); ++i)
234  {
235  for(unsigned int j(0); j < parcelBTimeSeriesVector.size(); ++j)
236  {
237  corr +=
238  mitk::CorrelationCalculator<T>::CalculatePearsonCorrelationCoefficient(
239  parcelATimeSeriesVector[i], parcelBTimeSeriesVector[j]
240  );
241  }
242  }
243  result = corr / double( parcelATimeSeriesVector.size() * parcelBTimeSeriesVector.size() );
244  break;
245  }
246  default:
247  {
248  mitkThrow() << "No valid parcel correlation mode selected";
249  }
250  }
251 
252  return result;
253 }
254 
255 template< class T >
256 void mitk::CorrelationCalculator<T>::Modified( ) const
257 {
258  Superclass::Modified();
259 }
260 
261 template< class T >
262 void mitk::CorrelationCalculator<T>::Modified( )
263 {
264  Superclass::Modified();
265  this->m_InvalidTimeSeries = true;
266 }
267 
268 template< class T>
269 template< typename TPixel, unsigned int VImageDimension >
270 void mitk::CorrelationCalculator<T>::ExtractAllAverageTimeSeries( itk::Image<TPixel, VImageDimension>* itkTimeSeriesImage )
271 {
272  // storage contains for each parcel value a vector of length time steps,
273  // which manages the pixel value vectors for each time step
274  std::map< int, std::vector< std::vector< T > > > storage;
275 
276  itk::ImageRegionConstIteratorWithIndex< itk::Image<TPixel, VImageDimension> > it_itkTimeSeriesImage(
277  itkTimeSeriesImage, itkTimeSeriesImage->GetLargestPossibleRegion() );
278 
279  unsigned int timesteps( itkTimeSeriesImage->GetLargestPossibleRegion().GetSize()[3] );
280 
281  for( it_itkTimeSeriesImage.GoToBegin(); !it_itkTimeSeriesImage.IsAtEnd(); ++it_itkTimeSeriesImage )
282  {
283  itk::Index< 4 > itkIndex = it_itkTimeSeriesImage.GetIndex();
284  itk::Index< 3 > itkIndex3D;
285  for(unsigned int axis( 0 ); axis < 3; ++axis )
286  {
287  itkIndex3D.SetElement( axis, itkIndex.GetElement(axis) );
288  }
289 
290  mitk::Point3D tempPoint;
291 
292  m_TimeSeriesImage->GetGeometry()->IndexToWorld( itkIndex3D, tempPoint );
293  mitk::ImagePixelReadAccessor<int, 3> readAccess(m_ParcellationImage);
294  int value( std::floor( readAccess.GetPixelByWorldCoordinates( tempPoint ) + 0.5 ) );
295 
296  if(storage.count(value) == 0)
297  {
298  std::vector< std::vector< T > > temp;
299  temp.resize( timesteps );
300  storage.insert(std::pair< int, std::vector< std::vector< T > > >(value, temp));
301  }
302 
303  storage[value][ itkIndex.GetElement( 3 ) ].push_back( it_itkTimeSeriesImage.Value() );
304  }
305 
306  // store means
307  for( typename std::map< int, std::vector< std::vector< T > > >::iterator it = storage.begin(); it != storage.end(); ++it )
308  {
309  std::vector< double > means;
310  means.resize( timesteps );
311  for(unsigned int loop(0); loop < timesteps; ++loop)
312  {
313  means[loop] = std::accumulate( it->second[loop].begin(), it->second[loop].end(), 0.0 ) / double(it->second[loop].size());
314  }
315  m_AverageTimeSeries.insert( std::pair< int, std::vector< double > >(it->first, means) );
316 
317  // reorder the information in storage for m_ParcelTimeSeries
318  // time series of each voxel by their parcellation value
319  std::vector< std::vector< T > > vectorOfTimeSeriesVectors;
320  vectorOfTimeSeriesVectors.resize(it->second[0].size());
321  for(unsigned int loop(0); loop < vectorOfTimeSeriesVectors.size(); ++loop)
322  {
323  vectorOfTimeSeriesVectors[loop].resize(timesteps);
324  }
325 
326  for(unsigned int step(0); step < timesteps; ++step)
327  {
328  for(unsigned int number(0); number < vectorOfTimeSeriesVectors.size(); ++number)
329  {
330  vectorOfTimeSeriesVectors[number][step] = it->second[step][number];
331  }
332  }
333  m_ParcelTimeSeries.insert(std::pair< int, std::vector< std::vector< T > > >(it->first, vectorOfTimeSeriesVectors));
334  }
335 }
336 
337 template< class T >
338 void mitk::CorrelationCalculator<T>::ExtractCenterOfMassForParcels()
339 {
340  itk::Image< int, 3 >::Pointer itkParcelImage;
341  mitk::CastToItkImage( m_ParcellationImage, itkParcelImage );
342 
343  itk::ImageRegionConstIteratorWithIndex< itk::Image<int, 3> > it_itkParcelImage(
344  itkParcelImage, itkParcelImage->GetLargestPossibleRegion() );
345 
346  std::map< int, std::vector< mitk::Point3D > > storage;
347 
348  for( it_itkParcelImage.GoToBegin(); !it_itkParcelImage.IsAtEnd(); ++it_itkParcelImage )
349  {
350  mitk::Point3D tempPoint;
351 
352  m_ParcellationImage->GetGeometry()->IndexToWorld( it_itkParcelImage.GetIndex(), tempPoint );
353 
354  if( storage.count( it_itkParcelImage.Value() ) == 1 )
355  {
356  storage[ it_itkParcelImage.Value() ].push_back( tempPoint );
357  }
358  else
359  {
360  storage.insert( std::pair< int, std::vector< mitk::Point3D > >(
361  it_itkParcelImage.Value(), std::vector< mitk::Point3D >( 1, tempPoint ) ));
362  }
363  }
364 
365  for( std::map< int, std::vector< mitk::Point3D > >::iterator it( storage.begin() ); it != storage.end(); ++it )
366  {
367  itk::Vector< int, 3 > tempVector( 0 );
368 
369  for( unsigned int loop(0); loop < it->second.size(); ++loop)
370  {
371  for(unsigned int index(0); index < 3; ++index)
372  {
373  tempVector[index] = tempVector[index] + it->second[loop][index];
374  }
375  }
376 
377  mitk::Point3D tempPoint;
378 
379  for( unsigned int loop(0); loop < 3; ++loop )
380  {
381  tempPoint.SetElement(loop, tempVector.GetElement(loop) / it->second.size());
382  }
383 
384  m_ParcelCenterOfMass.insert( std::pair< int, mitk::Point3D >(it->first, tempPoint) );
385  }
386 }
387 
388 template< class T >
389 const vnl_matrix< double >* mitk::CorrelationCalculator<T>::GetCorrelationMatrix() const
390 {
391  return &m_CorrelationMatrix;
392 }
393 
394 template< class T >
395 const std::map< unsigned int, int >* mitk::CorrelationCalculator<T>::GetLabelOrderMap() const
396 {
397  return &m_LabelOrderMap;
398 }
399 
400 template< class T >
401 mitk::ConnectomicsNetwork::Pointer mitk::CorrelationCalculator<T>::GetConnectomicsNetwork()
402 {
403  mitk::ConnectomicsNetwork::NetworkType* boostGraph = new mitk::ConnectomicsNetwork::NetworkType;
404 
405  std::map< int, mitk::ConnectomicsNetwork::VertexDescriptorType > idToVertexMap;
406 
407  // fill nodes
408  unsigned int numberOfNodes( m_CorrelationMatrix.columns() );
409 
410  for(unsigned int loop(0); loop < numberOfNodes; ++loop )
411  {
412  idToVertexMap.insert( std::pair< int, mitk::ConnectomicsNetwork::VertexDescriptorType >(loop, boost::add_vertex( *boostGraph )) );
413  }
414 
415  if( m_ParcelCenterOfMass.size() > 0)
416  {
417  std::map< int, mitk::Point3D >::const_iterator it = m_ParcelCenterOfMass.begin();
418  for(unsigned int loop(0); (loop < numberOfNodes) && (it != m_ParcelCenterOfMass.end()); ++loop, ++it )
419  {
420  (*boostGraph)[ idToVertexMap[loop] ].id = idToVertexMap[loop];
421  (*boostGraph)[ idToVertexMap[loop] ].label = std::to_string( it->first );
422  (*boostGraph)[ idToVertexMap[loop] ].coordinates.resize(3);
423  for(unsigned int dimension(0); dimension < 3; ++dimension)
424  {
425  (*boostGraph)[ idToVertexMap[loop] ].coordinates[dimension] = it->second[dimension];
426  }
427  }
428  }
429 
430  //fill edges
431  for( unsigned int i(0); i < numberOfNodes; ++i)
432  {
433  for(unsigned int j(0); j < i; ++j)
434  {
435  mitk::ConnectomicsNetwork::VertexDescriptorType vertexA = idToVertexMap[i];
436  mitk::ConnectomicsNetwork::VertexDescriptorType vertexB = idToVertexMap[j];
437  boost::add_edge( vertexA, vertexB, *boostGraph );
438  (*boostGraph)[ boost::edge(vertexA, vertexB, *boostGraph ).first ].sourceId = (*boostGraph)[vertexA].id;
439  (*boostGraph)[ boost::edge(vertexA, vertexB, *boostGraph ).first ].targetId = (*boostGraph)[vertexB].id;
440  (*boostGraph)[ boost::edge(vertexA, vertexB, *boostGraph ).first ].weight = 1;
441  (*boostGraph)[ boost::edge(vertexA, vertexB, *boostGraph ).first ].edge_weight = m_CorrelationMatrix[i][j];
442  }
443  }
444 
445  // create data node
446  mitk::ConnectomicsNetwork::Pointer network = mitk::ConnectomicsNetwork::New();
447  network->SetBoostGraph( boostGraph );
448  network->SetGeometry( dynamic_cast<mitk::BaseGeometry*>(m_TimeSeriesImage->GetGeometry()->Clone().GetPointer()) );
449  network->UpdateBounds();
450  network->SetIsModified( true );
451  return network;
452 }
453 
454 #endif /* _MITK_CORRELATIONCALCULATOR_TXX */