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
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.