Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkCollectionStatistic.cpp
Go to the documentation of this file.
2 #include <mitkDataCollection.h>
3 
4 // DataCollection Stuff
6 //stl stuff
7 #include <sstream>
8 
9 #include <itkShapedNeighborhoodIterator.h>
10 #include <itkSignedDanielssonDistanceMapImageFilter.h>
11 
12 
14  : m_GroundTruthValueToIndexMapper(NULL)
15  , m_TestValueToIndexMapper(NULL)
16 {
17 
18 }
19 
21 {
22  m_GroundTruthValueToIndexMapper = NULL;
23  m_TestValueToIndexMapper = NULL;
24 }
25 
26 
28 {
29  m_Collection = collection;
30 }
31 
33 {
34  return m_Collection;
35 }
36 
38 {
39  m_ClassCount = count;
40 }
41 
43 {
44  return m_ClassCount;
45 }
46 
48 {
49  m_GroundTruthName = name;
50 }
51 
53 {
54  return m_GroundTruthName;
55 }
56 
58 {
59  m_TestName = name;
60 }
61 
63 {
64  return m_TestName;
65 }
66 
68 {
69  m_GroundTruthValueToIndexMapper = mapper;
70 }
71 
73 {
74  return m_GroundTruthValueToIndexMapper;
75 }
76 
78 {
79  m_TestValueToIndexMapper = mapper;
80 }
81 
83 {
84  return m_TestValueToIndexMapper;
85 }
86 
87 int mitk::CollectionStatistic::IsInSameVirtualClass(unsigned char gold, unsigned char test)
88 {
89  int resultClass = -1;
90  for (int i = 0; i < m_ConnectionGold.size(); ++i)
91  {
92  if (m_ConnectionGold[i] == gold && m_ConnectionTest[i] == test)
93  {
94  resultClass = m_ConnectionClass[i];
95  break;
96  }
97  }
98  return resultClass;
99 }
100 
102 {
103  if (m_GroundTruthValueToIndexMapper == NULL)
104  {
105  MITK_ERROR << "m_GroundTruthValueToIndexMapper is NULL";
106  return false;
107  }
108 
109  if (m_TestValueToIndexMapper == NULL)
110  {
111  MITK_ERROR << "m_TestValueToIndexMapper is NULL";
112  return false;
113  }
114 
115  typedef itk::Image<unsigned char, 3> ImageType;
116  DataCollectionImageIterator<unsigned char, 3> goldIter(m_Collection, m_GroundTruthName);
117  DataCollectionImageIterator<unsigned char, 3> testIter(m_Collection, m_TestName);
118  DataCollectionImageIterator<unsigned char, 3> maskIter(m_Collection, m_MaskName);
119 
120  int index = 0;
121 
122  while (!goldIter.IsAtEnd())
123  {
124  std::size_t imageIndex = goldIter.GetImageIndex();
125  if (m_ImageClassStatistic.size() <= imageIndex)
126  {
127  MITK_INFO << "New Image: " << goldIter.GetFilePrefix();
128  m_ImageNames.push_back(goldIter.GetFilePrefix());
129  StatisticData statData;
130  m_ImageStatistic.push_back(statData);
131  DataVector data;
132  for (int i = 0; i < m_ClassCount; ++i)
133  {
134  StatisticData stat;
135  data.push_back(stat);
136  }
137  m_ImageClassStatistic.push_back(data);
138  }
139 
140  if (maskIter.GetVoxel() <= 0)
141  {
142  ++goldIter;
143  ++testIter;
144  ++maskIter;
145  continue;
146  }
147 
148  ++index;
149  unsigned char goldClass = m_GroundTruthValueToIndexMapper->operator()(goldIter.GetVoxel());
150  unsigned char testClass = m_TestValueToIndexMapper->operator()(testIter.GetVoxel());
151  if (goldClass == testClass) // True Positive
152  {
153  m_ImageStatistic[imageIndex].m_TruePositive += 1;
154  for (int i = 0; i < m_ClassCount; ++i)
155  {
156  if (goldClass == i) // For the detected class it is a true positive
157  {
158  m_ImageClassStatistic[imageIndex][i].m_TruePositive += 1;
159  } else // for all other classes than the detected it is a true negative
160  {
161  m_ImageClassStatistic[imageIndex][i].m_TrueNegative += 1;
162  }
163  }
164  } else // No True Positive
165  {
166  m_ImageStatistic[imageIndex].m_FalseNegative += 1;
167  m_ImageStatistic[imageIndex].m_FalsePositive += 1;
168  for (int i = 0; i < m_ClassCount; ++i)
169  {
170  if (goldClass == i) // For the class in Goldstandard it is a false negative
171  {
172  m_ImageClassStatistic[imageIndex][i].m_FalseNegative += 1;
173  } else if ( testClass == i) // For the test class it is a false positive
174  {
175  m_ImageClassStatistic[imageIndex][i].m_FalsePositive += 1;
176  } else // For all other it is a true negative
177  {
178  m_ImageClassStatistic[imageIndex][i].m_TrueNegative += 1;
179  }
180  }
181  }
182 
183  ++goldIter;
184  ++testIter;
185  ++maskIter;
186  }
187  MITK_INFO << "Evaluated " << index << " points";
188  return true;
189 }
190 
191 void mitk::CollectionStatistic::Print(std::ostream& out, std::ostream& sout, bool withHeader, std::string label)
192 {
193  assert(m_ImageClassStatistic.size() > 0);
194  assert(m_ImageClassStatistic[0].size() == m_ClassCount);
195 
196  if (withHeader)
197  {
198  sout << "Label;ImageName;";
199  for (int i = 0; i < m_ClassCount; ++i)
200  {
201  sout << "DICE-Class-"<< i << ";";
202  sout << "Jaccard-Class-"<< i << ";";
203  sout << "Sensitivity-Class-"<< i << ";";
204  sout << "Specificity-Class-"<< i << ";";
205  sout << "TP-Class-"<< i << ";";
206  sout << "TN-Class-"<< i << ";";
207  sout << "FP-Class-"<< i << ";";
208  sout << "FN-Class-"<< i << ";";
209  }
210  sout << "DICE-MEAN"<< ";";
211  sout << "Jaccard-MEAN"<< ";";
212  sout << "Sensitivity-MEAN"<< ";";
213  sout << "Specificity-MEAN"<< ";";
214  sout << "TP-MEAN"<< ";";
215  sout << "TN-MEAN"<< ";";
216  sout << "FP-MEAN"<< ";";
217  sout << "FN-MEAN"<< ";";
218  sout << "DICE-WMEAN"<< ";";
219  sout << "Jaccard-WMEAN"<< ";";
220  sout << "Sensitivity-WMEAN"<< ";";
221  sout << "Specificity-WMEAN"<< ";";
222  sout << "TP-WMEAN"<< ";";
223  sout << "TN-WMEAN"<< ";";
224  sout << "FP-WMEAN"<< ";";
225  sout << "FN-WMEAN"<< ";";
226  sout << "COMPLETE-TRUE/FALSE"<< ";";
227  sout << "COMPLETE-TRUES"<< ";";
228  sout << "COMPLETE_FALSE"<< ";";
229  sout << std::endl;
230  }
231  out << std::setprecision(5);
232 
233 
234  MITK_INFO << "m_ImageClassStatistic.size(): " << m_ImageClassStatistic.size();
235 
236  for (int i = 0; i < m_ImageClassStatistic.size(); ++i)
237  {
238  sout << label << ";"<< m_ImageNames[i]<<";";
239  StatisticData meanStat;
240  StatisticData wMeanStat;
241  double pointsSum = 0;
242 
243  out << "======================================================== Image " << std::setw(3) << i << " ========================================================" << std::endl;
244  out << " Image ID : " << m_ImageNames[i] <<std::endl;
245 
246  out << "|--------|-------------|-------------|-------------|-------------|-------------|-------------|-------------|--------------|" << std::endl;
247  out << "| Class | DICE | Jaccard | Sensitivity | Specificity | TP | TN | FP | FN |" << std::endl;
248  out << "|--------|-------------|-------------|-------------|-------------|-------------|-------------|-------------|--------------|" << std::endl;
249 
250  for (int j =0; j < m_ImageClassStatistic[i].size(); ++j)
251  {
252  StatisticData& stat = m_ImageClassStatistic[i][j];
253  stat.m_DICE = std::max(0.0,(2.0 * stat.m_TruePositive) / (2.0 * stat.m_TruePositive + stat.m_FalseNegative + stat.m_FalsePositive));
254  stat.m_Jaccard = std::max(0.0,(1.0 * stat.m_TruePositive) / (1.0 * stat.m_TruePositive + stat.m_FalseNegative + stat.m_FalsePositive));
255  stat.m_Sensitivity = std::max(0.0,(1.0 * stat.m_TruePositive) / (stat.m_TruePositive + stat.m_FalseNegative));
256  stat.m_Specificity = std::max(0.0,(1.0 * stat.m_TrueNegative) / ( stat.m_FalsePositive + stat.m_TrueNegative));
257 
258  meanStat.m_DICE += std::max(stat.m_DICE,0.0);
259  meanStat.m_Jaccard += std::max(stat.m_Jaccard,0.0);
260  meanStat.m_Sensitivity += std::max(stat.m_Sensitivity,0.0);
261  meanStat.m_Specificity += std::max(stat.m_Specificity,0.0);
262  meanStat.m_TruePositive += stat.m_TruePositive;
263  meanStat.m_TrueNegative += stat.m_TrueNegative;
264  meanStat.m_FalsePositive += stat.m_FalsePositive;
265  meanStat.m_FalseNegative += stat.m_FalseNegative;
266 
267  double points = stat.m_TruePositive + stat.m_FalseNegative;
268  pointsSum += points;
269 
270  wMeanStat.m_DICE += std::max(stat.m_DICE,0.0) * points;
271  wMeanStat.m_Jaccard += std::max(stat.m_Jaccard,0.0) * points;
272  wMeanStat.m_Sensitivity += std::max(stat.m_Sensitivity,0.0) * points;
273  wMeanStat.m_Specificity += std::max(stat.m_Specificity,0.0) * points;
274  wMeanStat.m_TruePositive += stat.m_TruePositive;
275  wMeanStat.m_TrueNegative += stat.m_TrueNegative;
276  wMeanStat.m_FalsePositive += stat.m_FalsePositive;
277  wMeanStat.m_FalseNegative += stat.m_FalseNegative;
278 
279  out<< "|" << std::setw(7) << j << " | ";
280  out << std::setw(11) << stat.m_DICE << " | ";
281  out << std::setw(11) << stat.m_Jaccard << " | ";
282  out << std::setw(11) << stat.m_Sensitivity << " | ";
283  out << std::setw(11) << stat.m_Specificity << " | ";
284  out << std::setw(11) << stat.m_TruePositive << " | ";
285  out << std::setw(11) << stat.m_TrueNegative<< " | ";
286  out << std::setw(11) << stat.m_FalsePositive<< " | ";
287  out << std::setw(11) << stat.m_FalseNegative << " |"<< std::endl;
288  sout << stat.m_DICE << ";";
289  sout << stat.m_Jaccard << ";";
290  sout << stat.m_Sensitivity << ";";
291  sout << stat.m_Specificity << ";";
292  sout << stat.m_TruePositive << ";";
293  sout << stat.m_TrueNegative<< ";";
294  sout << stat.m_FalsePositive<< ";";
295  sout << stat.m_FalseNegative << ";";
296  sout << stat.m_RMSD << ";";
297  }
298 
299  meanStat.m_DICE /= m_ImageClassStatistic[i].size();
300  meanStat.m_Jaccard /= m_ImageClassStatistic[i].size();
301  meanStat.m_Sensitivity /= m_ImageClassStatistic[i].size();
302  meanStat.m_Specificity /= m_ImageClassStatistic[i].size();
303  meanStat.m_TruePositive /= m_ImageClassStatistic[i].size();
304  meanStat.m_TrueNegative /= m_ImageClassStatistic[i].size();
305  meanStat.m_FalsePositive /= m_ImageClassStatistic[i].size();
306  meanStat.m_FalseNegative /= m_ImageClassStatistic[i].size();
307 
308  out << "|--------|-------------|-------------|-------------|-------------|-------------|-------------|-------------|--------------|" << std::endl;
309  out << std::setw(7) << "| Mean "<< " | ";
310  out << std::setw(11) << meanStat.m_DICE << " | ";
311  out << std::setw(11) << meanStat.m_Jaccard << " | ";
312  out << std::setw(11) << meanStat.m_Sensitivity << " | ";
313  out << std::setw(11) << meanStat.m_Specificity << " | ";
314  out << std::setw(11) << meanStat.m_TruePositive << " | ";
315  out << std::setw(11) << meanStat.m_TrueNegative<< " | ";
316  out << std::setw(11) << meanStat.m_FalsePositive<< " | ";
317  out << std::setw(11) << meanStat.m_FalseNegative << " |"<< std::endl;
318  sout << meanStat.m_DICE << ";";
319  sout << meanStat.m_Jaccard << ";";
320  sout << meanStat.m_Sensitivity << ";";
321  sout << meanStat.m_Specificity << ";";
322  sout << meanStat.m_TruePositive << ";";
323  sout << meanStat.m_TrueNegative<< ";";
324  sout << meanStat.m_FalsePositive<< ";";
325  sout << meanStat.m_FalseNegative << ";";
326 
327  wMeanStat.m_DICE /= pointsSum;
328  wMeanStat.m_Jaccard /= pointsSum;
329  wMeanStat.m_Sensitivity /= pointsSum;
330  wMeanStat.m_Specificity /= pointsSum;
331  wMeanStat.m_TruePositive /= pointsSum;
332  wMeanStat.m_TrueNegative /= pointsSum;
333  wMeanStat.m_FalsePositive /= pointsSum;
334  wMeanStat.m_FalseNegative /= pointsSum;
335 
336  out << std::setw(7) << "| W-Mean"<< " | ";
337  out << std::setw(11) << wMeanStat.m_DICE << " | ";
338  out << std::setw(11) << wMeanStat.m_Jaccard << " | ";
339  out << std::setw(11) << wMeanStat.m_Sensitivity << " | ";
340  out << std::setw(11) << wMeanStat.m_Specificity << " | ";
341  out << std::setw(11) << wMeanStat.m_TruePositive << " | ";
342  out << std::setw(11) << wMeanStat.m_TrueNegative<< " | ";
343  out << std::setw(11) << wMeanStat.m_FalsePositive<< " | ";
344  out << std::setw(11) << wMeanStat.m_FalseNegative << " |"<< std::endl;
345  sout << wMeanStat.m_DICE << ";";
346  sout << wMeanStat.m_Jaccard << ";";
347  sout << wMeanStat.m_Sensitivity << ";";
348  sout << wMeanStat.m_Specificity << ";";
349  sout << wMeanStat.m_TruePositive << ";";
350  sout << wMeanStat.m_TrueNegative<< ";";
351  sout << wMeanStat.m_FalsePositive<< ";";
352  sout << wMeanStat.m_FalseNegative << ";";
353 
354  m_ImageStatistic[i].m_Sensitivity = std::max(0.0,(1.0 * m_ImageStatistic[i].m_TruePositive) / (m_ImageStatistic[i].m_TruePositive + m_ImageStatistic[i].m_FalseNegative));
355  m_ImageStatistic[i].m_Specificity = std::max(0.0,(1.0 * m_ImageStatistic[i].m_TrueNegative) / ( m_ImageStatistic[i].m_FalsePositive + m_ImageStatistic[i].m_TrueNegative));
356 
357  out << std::setw(7) << "| Compl."<< " | ";
358  out << std::setw(11) << " x " << " | ";
359  out << std::setw(11) << " x " << " | ";
360  out << std::setw(11) << m_ImageStatistic[i].m_Sensitivity << " | ";
361  out << std::setw(11) << " x " << " | ";
362  out << std::setw(11) << m_ImageStatistic[i].m_TruePositive << " | ";
363  out << std::setw(11) << m_ImageStatistic[i].m_TrueNegative<< " | ";
364  out << std::setw(11) << m_ImageStatistic[i].m_FalsePositive<< " | ";
365  out << std::setw(11) << m_ImageStatistic[i].m_FalseNegative << " |"<< std::endl;
366  out << "|--------|-------------|-------------|-------------|-------------|-------------|-------------|-------------|--------------|" << std::endl;
367 
368  sout << m_ImageStatistic[i].m_Sensitivity << ";";
369  sout << m_ImageStatistic[i].m_TruePositive << ";";
370  sout << m_ImageStatistic[i].m_FalsePositive<< std::endl;
371  out << std::endl;
372  }
373 }
374 
375 std::vector<mitk::StatisticData> mitk::CollectionStatistic::GetStatisticData(unsigned char c) const
376 {
377  std::vector<StatisticData> statistics;
378 
379  for (size_t i = 0; i < m_ImageClassStatistic.size(); i++)
380  {
381  statistics.push_back(m_ImageClassStatistic[i][c]);
382  }
383 
384  return statistics;
385 }
386 
388 {
389  assert(m_ClassCount == 2);
390  assert(m_GroundTruthValueToIndexMapper != NULL);
391  assert(m_TestValueToIndexMapper != NULL);
392 
393  DataCollectionImageIterator<unsigned char, 3> groundTruthIter(m_Collection, m_GroundTruthName);
394  DataCollectionImageIterator<unsigned char, 3> testIter(m_Collection, m_TestName);
395  DataCollectionImageIterator<unsigned char, 3> maskIter(m_Collection, m_MaskName);
396 
397  typedef itk::Image<unsigned char, 3> LabelImage;
398  typedef itk::Image<double, 3> ImageType;
399  typedef itk::SignedDanielssonDistanceMapImageFilter<LabelImage, ImageType, ImageType> DistanceMapFilterType;
400  typedef itk::ConstantBoundaryCondition<LabelImage> BoundaryConditionType;
401  typedef itk::ConstShapedNeighborhoodIterator<LabelImage, BoundaryConditionType> ConstNeighborhoodIteratorType;
402 
403  // Build up 6-neighborhood. Diagonal voxel are maybe not considered for distance map computation.
404  // So 6-neighborhood avoids inconsistencies.
405  ConstNeighborhoodIteratorType::OffsetType offset0 = {{ 0, 0, -1}};
406  ConstNeighborhoodIteratorType::OffsetType offset1 = {{ 0, 0, 1}};
407  ConstNeighborhoodIteratorType::OffsetType offset2 = {{ 0, -1, 0}};
408  ConstNeighborhoodIteratorType::OffsetType offset3 = {{ 0, 1, 0}};
409  ConstNeighborhoodIteratorType::OffsetType offset4 = {{-1, 0, 0}};
410  ConstNeighborhoodIteratorType::OffsetType offset5 = {{ 1, 0, 0}};
411 
412  const int outsideVal = 17;
413  itk::NeighborhoodIterator<LabelImage>::RadiusType radius;
414  radius.Fill(1);
415 
416  BoundaryConditionType bc;
417  // if a neighborhood voxel is outside of the image region a default value is returned
418  bc.SetConstant(outsideVal);
419 
420  ConstNeighborhoodIteratorType neighborhoodIter;
421 
422  ImageType::Pointer distanceImage;
423  std::vector<mitk::StatisticData>* currentImageStatistics = NULL;
424 
425  unsigned int distanceBorderSamples = 0;
426  double totalBorderRMSDistance = 0;
427 
428  int previousImageIndex = -1;
429 
430  while (!testIter.IsAtEnd())
431  {
432  int currentImageIndex = testIter.GetImageIndex();
433 
434  // prepare data for next image
435  if (previousImageIndex != currentImageIndex)
436  {
437  previousImageIndex = currentImageIndex;
438 
439  currentImageStatistics = &(m_ImageClassStatistic.at(currentImageIndex));
440 
441  distanceBorderSamples = 0;
442  totalBorderRMSDistance = 0;
443 
444  // generate distance map from gold standard image
446  distanceMapFilter->SetInput(groundTruthIter.GetImageIterator().GetImage());
447  distanceMapFilter->SetUseImageSpacing(true);
448  distanceMapFilter->Update();
449  distanceImage = distanceMapFilter->GetOutput();
450 
451  neighborhoodIter = ConstNeighborhoodIteratorType(radius, testIter.GetImageIterator().GetImage(), testIter.GetImageIterator().GetImage()->GetRequestedRegion());
452  neighborhoodIter.OverrideBoundaryCondition(&bc);
453 
454  // configure 6-neighborhood
455  neighborhoodIter.ActivateOffset(offset0);
456  neighborhoodIter.ActivateOffset(offset1);
457  neighborhoodIter.ActivateOffset(offset2);
458  neighborhoodIter.ActivateOffset(offset3);
459  neighborhoodIter.ActivateOffset(offset4);
460  neighborhoodIter.ActivateOffset(offset5);
461  }
462 
463  unsigned char testClass = m_TestValueToIndexMapper->operator()(testIter.GetVoxel());
464 
465  if ( maskIter.GetVoxel() > 0 && testClass != 0)
466  {
467  // check if it is a border voxel
468  neighborhoodIter.SetLocation(testIter.GetImageIterator().GetIndex());
469  bool border = false;
470 
471  ConstNeighborhoodIteratorType::ConstIterator iter;
472  for (iter = neighborhoodIter.Begin(); !iter.IsAtEnd(); iter++)
473  {
474  if (iter.Get() == outsideVal)
475  {
476  continue;
477  }
478 
479  if (m_TestValueToIndexMapper->operator()(iter.Get()) != 1 )
480  {
481  border = true;
482  break;
483  }
484  }
485 
486  if (border)
487  {
488  double currentDistance = distanceImage->GetPixel(testIter.GetImageIterator().GetIndex());
489  totalBorderRMSDistance += currentDistance * currentDistance;
490  ++distanceBorderSamples;
491 
492  // update immediately, so the final value is set after the iterator of the last image has reached the end
493  double rmsd = std::sqrt(totalBorderRMSDistance / (double) distanceBorderSamples);
494  currentImageStatistics->at(1).m_RMSD = rmsd;
495  }
496  }
497  ++groundTruthIter;
498  ++testIter;
499  ++maskIter;
500  }
501 }
itk::SmartPointer< Self > Pointer
#define MITK_INFO
Definition: mitkLogMacros.h:22
void Print(std::ostream &out, std::ostream &sout=std::cout, bool withHeader=false, std::string label="None")
void SetTestName(std::string name)
#define MITK_ERROR
Definition: mitkLogMacros.h:24
std::vector< StatisticData > GetStatisticData(unsigned char c) const
mitk::CollectionStatistic::GetStatisticData
Follow Up Storage - Class to facilitate loading/accessing structured follow-up data.
Definition: testcase.h:32
void SetTestValueToIndexMapper(const ValueToIndexMapper *mapper)
void SetGroundTruthValueToIndexMapper(const ValueToIndexMapper *mapper)
DataCollection::Pointer GetCollection()
map::core::discrete::Elements< 3 >::InternalImageType ImageType
void SetGoldName(std::string name)
void ComputeRMSD()
Computes root-mean-square distance of two binary images.
void SetCollection(DataCollection::Pointer collection)
void SetClassCount(vcl_size_t count)
static T max(T x, T y)
Definition: svm.cpp:70
const ValueToIndexMapper * GetTestValueToIndexMapper(void) const
const ValueToIndexMapper * GetGroundTruthValueToIndexMapper(void) const
std::vector< StatisticData > DataVector
itk::SmartPointer< Self > Pointer
Definition: mitkBaseData.h:42
int IsInSameVirtualClass(unsigned char gold, unsigned char test)
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.