Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 */