ANIMA  4.0
animaSegPerfCAnalyzer.cxx
Go to the documentation of this file.
2 
3 #include <vector>
4 #include <algorithm>
5 #include <numeric>
6 #include <exception>
7 #include <itkConnectedComponentImageFilter.h>
8 #include <itkRelabelComponentImageFilter.h>
9 #include <itkImageIterator.h>
10 #include <itkMultiThreaderBase.h>
11 #include <itkImageDuplicator.h>
12 
13 namespace anima
14 {
15 
23 SegPerfCAnalyzer::SegPerfCAnalyzer(std::string &pi_pchImageTestName, std::string &pi_pchImageRefName, bool bAdvancedEvaluation)
24 {
25  m_ThreadNb = itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads();
26 
27  m_dfDetectionThresholdAlpha = 0.05;
28  m_dfDetectionThresholdBeta = 0.50;
29  m_dfDetectionThresholdGamma = 0.50;
30  m_dfMinLesionVolumeDetection = 3.0;
31 
32  m_uiNbLabels = 0;
33 
34  ImageReaderType::Pointer imageTestReader = ImageReaderType::New();
35  imageTestReader->SetFileName(pi_pchImageTestName);
36 
37  try
38  {
39  imageTestReader->Update();
40  }
41 
42  catch (itk::ExceptionObject& e)
43  {
44  std::cerr << "exception in file reader " << std::endl;
45  std::cerr << e << std::endl;
46  return;
47  }
48 
49  m_imageTest = imageTestReader->GetOutput();
50 
51  ImageReaderType::Pointer imageRefReader = ImageReaderType::New();
52  imageRefReader->SetFileName(pi_pchImageRefName);
53 
54  try
55  {
56  imageRefReader->Update();
57  }
58 
59  catch (itk::ExceptionObject& e)
60  {
61  std::cerr << "exception in file reader " << std::endl;
62  std::cerr << e << std::endl;
63  return;
64  }
65 
66  m_imageRef = imageRefReader->GetOutput();
67 
69  throw std::runtime_error("Images are incompatible");
70 
71  this->formatLabels();
72 
73  if(bAdvancedEvaluation && m_uiNbLabels > 2)
74  {
75  //TO DO : attention si pas meme nombre de labels
76  typedef itk::ImageDuplicator< ImageType > DuplicatorType;
77  DuplicatorType::Pointer duplicator = DuplicatorType::New();
78  duplicator->SetInputImage(m_imageRef);
79  duplicator->Update();
80  m_imageRefDuplicated = ImageType::New();
81  m_imageRefDuplicated = duplicator->GetOutput();
82 
83  duplicator->SetInputImage(m_imageTest);
84  duplicator->Update();
85  m_imageTestDuplicated = ImageType::New();
86  m_imageTestDuplicated = duplicator->GetOutput();
87  }
88 
89  m_bValuesComputed = false;
90  m_bContourDetected = false;
91 }
92 
98 {
99  bool bRes = true;
100 
101  unsigned int uiTestedImageDimension = m_imageTest->GetImageDimension();
102  const itk::Vector<ImageType::SpacingValueType, ImageType::ImageDimension> oVectSpacingTested = m_imageTest->GetSpacing();
103  const itk::Size<ImageType::ImageDimension> oSizeTested = m_imageTest->GetLargestPossibleRegion().GetSize();
104  const itk::Point<ImageType::IndexValueType, ImageType::ImageDimension> oPointOriginTested = m_imageTest->GetOrigin();
105 
106  unsigned int uiRefImageDimension = m_imageRef->GetImageDimension();
107  const itk::Vector<ImageType::SpacingValueType, ImageType::ImageDimension> oVectSpacingRef = m_imageRef->GetSpacing();
108  const itk::Size<ImageType::ImageDimension> oSizeRef = m_imageRef->GetLargestPossibleRegion().GetSize();
109  const itk::Point<ImageType::IndexValueType, ImageType::ImageDimension> oPointOriginRef = m_imageRef->GetOrigin();
110 
111  bRes = uiTestedImageDimension == uiRefImageDimension;
112 
113  if (bRes)
114  {
115  //Check dimensions
116  for (int i = 0; i < uiTestedImageDimension; ++i)
117  {
118  if (oSizeTested[i] != oSizeRef[i])
119  {
120  bRes = false;
121  std::cerr << "Different image dimensions " << i << " Tested size : " << oSizeTested[i] << " Reference size : " << oSizeRef[i] << std::endl;
122  }
123  }
124 
125  //Check origin
126  for (int i = 0; i < uiTestedImageDimension; ++i)
127  {
128  if (oPointOriginTested[i] != oPointOriginRef[i])
129  {
130  bRes = false;
131  std::cerr << "Different image origin " << i << " Tested origin : " << oPointOriginTested[i] << " Reference origin : " << oPointOriginRef[i] << std::endl;
132  }
133  }
134 
135  //Check direction
136  for (int i = 0; i < uiTestedImageDimension; ++i)
137  {
138  for (int j = 0; j < uiTestedImageDimension; ++j)
139  {
140  double dDiff = m_imageTest->GetDirection().GetVnlMatrix().get(i, j) - m_imageRef->GetDirection().GetVnlMatrix().get(i, j);
141 
142 
143  if ((dDiff > 0.000001) || (dDiff < -0.000001))
144  {
145  bRes = false;
146  std::cerr << "Different image direction " << i << " - " << j << std::endl;
147  std::cerr << "Diff = " << dDiff << " Tested direction : " << m_imageTest->GetDirection().GetVnlMatrix().get(i, j) << " Reference direction : " << m_imageRef->GetDirection().GetVnlMatrix().get(i, j) << std::endl;
148  }
149  }
150  }
151  }
152  else
153  {
154  std::cerr << "Different image dimensions." << std::endl << "Tested is in : " << uiTestedImageDimension << "D and Ref is in :" << uiRefImageDimension << "D" << std::endl;
155  }
156 
157  return bRes;
158 }
159 
161 {
162 }
163 
169 {
170  return this->m_uiNbLabels;
171 }
172 
177 void SegPerfCAnalyzer::selectCluster(unsigned int iCluster)
178 {
179  //cout << "select cluster : "<< iCluster<<endl;
180  if(iCluster!=0)
181  {
182  typedef itk::ImageDuplicator< ImageType > DuplicatorType;
183  DuplicatorType::Pointer duplicator = DuplicatorType::New();
184  duplicator->SetInputImage(m_imageRefDuplicated);
185  duplicator->Update();
186  m_imageRef = duplicator->GetOutput();
187 
188  duplicator->SetInputImage(m_imageTestDuplicated);
189  duplicator->Update();
190  m_imageTest = duplicator->GetOutput();
191 
192  unsigned int dimX = m_imageRef->GetLargestPossibleRegion().GetSize()[0];
193  unsigned int dimY = m_imageRef->GetLargestPossibleRegion().GetSize()[1];
194  unsigned int dimZ = m_imageRef->GetLargestPossibleRegion().GetSize()[2];
195 
196  ImageType::IndexType index;
197 
198  for(int z=0; z<dimZ; z++)
199  {
200  for(int y=0; y<dimY; y++)
201  {
202  for(int x=0; x<dimX; x++)
203  {
204  index[0]=x;
205  index[1]=y;
206  index[2]=z;
207 
208  int valueRef = m_imageRef->GetPixel(index);
209  int valueTest = m_imageTest->GetPixel(index);
210 
211  if(valueRef != iCluster)
212  m_imageRef->SetPixel(index, 0);
213  else
214  m_imageRef->SetPixel(index, 1);
215 
216  if(valueTest != iCluster)
217  m_imageTest->SetPixel(index, 0);
218  else
219  m_imageTest->SetPixel(index, 1);
220  }
221  }
222  }
223  this->m_uiNbLabels = 2;
224  }
225 
226  return;
227 }
228 
233 {
234  //cout<<"****** CONTOUR DETECTION ******"<<endl;
235  //cout<<"nbLabels = "<<this->m_uiNbLabels<<endl;
236  if(this->m_uiNbLabels == 2)
237  {
238  typedef itk::BinaryContourImageFilter< ImageType, ImageType > FilterType;
240  filter->SetInput( m_imageTest );
241  filter->SetFullyConnected( 0 );
242  filter->SetForegroundValue( 1 );
243  filter->SetBackgroundValue( 0 );
244  filter->Update();
245 
246 
247  m_imageTestContour = ImageType::New();
248  m_imageTestContour = filter->GetOutput();
249 
251  filter2->SetInput( m_imageRef );
252  filter2->SetFullyConnected( 0 );
253  filter2->SetForegroundValue( 1 );
254  filter2->SetBackgroundValue( 0 );
255  filter2->Update();
256 
257  m_imageRefContour = ImageType::New();
258  m_imageRefContour = filter2->GetOutput();
259  }
260 
261  else
262  {
263  typedef itk::LabelContourImageFilter< ImageType, ImageType > FilterType;
265  filter->SetInput( m_imageTest );
266  filter->SetFullyConnected( 0 );
267  filter->SetBackgroundValue( 0 );
268  filter->Update();
269 
270  m_imageTestContour = ImageType::New();
271  m_imageTestContour = filter->GetOutput();
272 
274  filter2->SetInput( m_imageRef );
275  filter2->SetFullyConnected( 0 );
276  filter2->SetBackgroundValue( 0 );
277  filter2->Update();
278 
279  m_imageRefContour = ImageType::New();
280  m_imageRefContour = filter2->GetOutput();
281  }
282  m_bContourDetected = true;
283 }
284 
289 {
290  m_oFilter = FilterType::New();
291 
292  m_oFilter->SetNumberOfWorkUnits(m_ThreadNb);
293  m_oFilter->SetSourceImage( m_imageTest);
294  m_oFilter->SetTargetImage( m_imageRef );
295 
296  try
297  {
298  m_oFilter->Update();
299  }
300  catch( itk::ExceptionObject& excp )
301  {
302  std::cerr << excp << std::endl;
303  return;
304  }
305 }
306 
312 {
313  return m_oFilter->getUnionOverlap();
314 }
315 
321 {
322  return m_oFilter->getMeanOverlap();
323 }
324 
330 {
331  return m_oFilter->getSensitivity();
332 }
333 
339 {
340  return m_oFilter->getSpecificity();
341 }
342 
348 {
349  return m_oFilter->getPPV();
350 }
351 
357 {
358  return m_oFilter->getNPV();
359 }
360 
366 {
367  return m_oFilter->GetDiceCoefficient();
368 }
369 
375 {
376  return m_oFilter->GetJaccardCoefficient();
377 }
378 
384 {
385  return m_oFilter->getRelativeVolumeError();
386 }
387 
391 void SegPerfCAnalyzer::checkNumberOfLabels(int iNbLabelsImageTest, int iNbLabelsImageRef)
392 {
393  if(iNbLabelsImageTest<=1)
394  {
395  m_uiNbLabels = 1;
396  std::cerr << "ERROR : Number of labels in input image is 0 !" << std::endl;
397  return;
398  }
399 
400  if(iNbLabelsImageRef<=1)
401  {
402  m_uiNbLabels = 1;
403  std::cerr << "ERROR : Number of labels in ground truth is 0 !"<< std::endl;
404  return;
405  }
406 
407  if (iNbLabelsImageTest == iNbLabelsImageRef)
408  {
409  m_uiNbLabels = iNbLabelsImageTest;
410  }
411  else
412  {
413  if(iNbLabelsImageTest > 2)
414  {
415  //binariser ImageTest
416  unsigned int dimX = m_imageTest->GetLargestPossibleRegion().GetSize()[0];
417  unsigned int dimY = m_imageTest->GetLargestPossibleRegion().GetSize()[1];
418  unsigned int dimZ = m_imageTest->GetLargestPossibleRegion().GetSize()[2];
419 
420  ImageType::IndexType index;
421 
422  for(int z=0; z<dimZ; z++)
423  {
424  for(int y=0; y<dimY; y++)
425  {
426  for(int x=0; x<dimX; x++)
427  {
428  index[0]=x;
429  index[1]=y;
430  index[2]=z;
431 
432  if( m_imageTest->GetPixel(index) != 0 )
433  m_imageTest->SetPixel(index, 1);
434  }
435  }
436  }
437 
438  m_uiNbLabels = 2;
439  std::cout << "WARNING : Segmented image have not the same number of labels as ground truth, it have been binarized for segmentation evaluation" << std::endl;
440  }
441 
442  if(iNbLabelsImageRef > 2)
443  {
444  //binariser ImageRef
445 
446  unsigned int dimX = m_imageRef->GetLargestPossibleRegion().GetSize()[0];
447  unsigned int dimY = m_imageRef->GetLargestPossibleRegion().GetSize()[1];
448  unsigned int dimZ = m_imageRef->GetLargestPossibleRegion().GetSize()[2];
449 
450  ImageType::IndexType index;
451 
452  for(int z=0; z<dimZ; z++)
453  {
454  for(int y=0; y<dimY; y++)
455  {
456  for(int x=0; x<dimX; x++)
457  {
458  index[0]=x;
459  index[1]=y;
460  index[2]=z;
461 
462  if( m_imageRef->GetPixel(index) != 0 )
463  m_imageRef->SetPixel(index, 1);
464  }
465  }
466  }
467 
468  m_uiNbLabels = 2;
469  std::cout << "WARNING : Ground truth have not the same number of labels as segmented image, both images have been binarized for segmentation evaluation" << std::endl;
470  }
471  }
472 }
473 
478 {
479  ImageIteratorType refIt (m_imageTest, m_imageTest->GetLargestPossibleRegion());
480 
481  std::vector <unsigned int> usefulLabels;
482 
483  while (!refIt.IsAtEnd())
484  {
485  bool isAlreadyIn = false;
486  for (unsigned int i = 0; i < usefulLabels.size(); ++i)
487  {
488  if (refIt.Get() == usefulLabels[i])
489  {
490  isAlreadyIn = true;
491  break;
492  }
493  }
494 
495  if (!isAlreadyIn)
496  {
497  usefulLabels.push_back(refIt.Get());
498  }
499 
500  ++refIt;
501  }
502 
503  std::sort(usefulLabels.begin(), usefulLabels.end());
504 
505  unsigned int dimX = m_imageTest->GetLargestPossibleRegion().GetSize()[0];
506  unsigned int dimY = m_imageTest->GetLargestPossibleRegion().GetSize()[1];
507  unsigned int dimZ = m_imageTest->GetLargestPossibleRegion().GetSize()[2];
508 
509  ImageType::IndexType index;
510 
511  for(int z=0; z<dimZ; z++)
512  {
513  for(int y=0; y<dimY; y++)
514  {
515  for(int x=0; x<dimX; x++)
516  {
517  index[0]=x;
518  index[1]=y;
519  index[2]=z;
520 
521  int indexvalue = std::find(usefulLabels.begin(), usefulLabels.end(), m_imageTest->GetPixel(index))-usefulLabels.begin();
522  m_imageTest->SetPixel(index, indexvalue);
523  }
524  }
525  }
526 
527 
528  //imageRef
529 
530  ImageIteratorType refItRef(m_imageRef, m_imageRef->GetLargestPossibleRegion());
531 
532  std::vector <unsigned int> usefulLabels2;
533 
534  while (!refItRef.IsAtEnd())
535  {
536  bool isAlreadyIn = false;
537  for (unsigned int i = 0; i < usefulLabels2.size(); ++i)
538  {
539  if (refItRef.Get() == usefulLabels2[i])
540  {
541  isAlreadyIn = true;
542  break;
543  }
544  }
545 
546  if (!isAlreadyIn) {
547  usefulLabels2.push_back(refItRef.Get());
548  }
549 
550  ++refItRef;
551  }
552 
553  std::sort(usefulLabels2.begin(), usefulLabels2.end());
554 
555 
556 
557  dimX = m_imageRef->GetLargestPossibleRegion().GetSize()[0];
558  dimY = m_imageRef->GetLargestPossibleRegion().GetSize()[1];
559  dimZ = m_imageRef->GetLargestPossibleRegion().GetSize()[2];
560 
561 
562  for(int z=0; z<dimZ; z++)
563  {
564  for(int y=0; y<dimY; y++)
565  {
566  for(int x=0; x<dimX; x++)
567  {
568  index[0]=x;
569  index[1]=y;
570  index[2]=z;
571 
572  int indexvalue = std::find(usefulLabels2.begin(), usefulLabels2.end(), m_imageRef->GetPixel(index))-usefulLabels2.begin();
573  m_imageRef->SetPixel(index, indexvalue);
574  }
575  }
576  }
577 
578  int iNbOfLabelsImageTest = usefulLabels.size();
579  int iNbOfLabelsImageRef = usefulLabels2.size();
580 
581  checkNumberOfLabels(iNbOfLabelsImageTest, iNbOfLabelsImageRef);
582 
583  return;
584 }
585 
591 {
592  FilterType::RealType hausdorffDistance = std::numeric_limits<double>::quiet_NaN();
593 
594  if (m_uiNbLabels>1)
595  {
596  // compute the Hausdorff distance
597  typedef itk::HausdorffDistanceImageFilter<ImageType, ImageType> FilterType;
599 
600  filter->SetInput1(m_imageTest);
601  filter->SetInput2(m_imageRef);
602 
603  try
604  {
605  filter->Update();
606  }
607  catch (itk::ExceptionObject& e)
608  {
609  std::cerr << "exception in filter " << std::endl;
610  std::cerr << e << std::endl;
611  return EXIT_FAILURE;
612  }
613 
614  hausdorffDistance = filter->GetHausdorffDistance();
615  }
616 
617  return hausdorffDistance;
618 }
619 
625 {
626  FilterType::RealType meanDistance = std::numeric_limits<double>::quiet_NaN();
627 
628  if (m_uiNbLabels > 1)
629  {
630  if (!this->m_bContourDetected)
631  this->contourDectection();
632 
633  // compute the Hausdorff distance H(image1,image2)
634  typedef itk::ContourMeanDistanceImageFilter<ImageType, ImageType> FilterType;
636 
637  filter->SetInput1(m_imageTestContour);
638  filter->SetInput2(m_imageRefContour);
639 
640  try
641  {
642  filter->Update();
643  }
644 
645  catch (itk::ExceptionObject& e)
646  {
647  std::cerr << "exception in filter " << std::endl;
648  std::cerr << e << std::endl;
649  return EXIT_FAILURE;
650  }
651 
652  meanDistance = filter->GetMeanDistance();
653  }
654 
655  return meanDistance;
656 }
657 
663 {
664  double meanDistance = std::numeric_limits<double>::quiet_NaN();
665 
666  if (m_uiNbLabels > 1)
667  {
668  if (!this->m_bContourDetected)
669  this->contourDectection();
670 
671  double sum_dist = 0;
672  double sum_size = 0;
673 
674  for (int i = 1; i < m_uiNbLabels; i++)
675  {
676  std::vector <ImageType::PointType> coordRef;
677  coordRef.clear();
678  ImageIteratorType refContourIt(m_imageRefContour, m_imageRefContour->GetLargestPossibleRegion());
679 
680  while (!refContourIt.IsAtEnd())
681  {
682  if (refContourIt.Get() == i)
683  {
684  ImageType::IndexType oIndex = refContourIt.GetIndex();
685  ImageType::PointType oPoint;
686  m_imageRefContour->TransformIndexToPhysicalPoint(oIndex, oPoint);
687  coordRef.push_back(oPoint);
688  }
689  ++refContourIt;
690  }
691 
692  std::vector <ImageType::PointType> coordTest;
693  coordTest.clear();
694 
695  ImageIteratorType testContourIt(m_imageTestContour, m_imageTestContour->GetLargestPossibleRegion());
696 
697  while (!testContourIt.IsAtEnd())
698  {
699  if (testContourIt.Get() == i)
700  {
701  ImageType::IndexType oIndex = testContourIt.GetIndex();
702  ImageType::PointType oPoint;
703  m_imageTestContour->TransformIndexToPhysicalPoint(oIndex, oPoint);
704  coordTest.push_back(oPoint);
705  }
706  ++testContourIt;
707  }
708 
709  std::vector <double> distance1, distance2;
710  double fDistanceTemp1 = 1000000, fDistanceTemp2 = 1000000;
711  double distanceValue = 0;
712  for (int m = 0; m < coordRef.size(); m++)
713  {
714  for (int n = 0; n < coordTest.size(); n++)
715  {
716  distanceValue = sqrt(pow((double)(coordRef[m][0] - coordTest[n][0]), 2) + pow((double)(coordRef[m][1] - coordTest[n][1]), 2) + pow((double)(coordRef[m][2] - coordTest[n][2]), 2));
717  if (distanceValue < fDistanceTemp1)
718  fDistanceTemp1 = distanceValue;
719  }
720 
721  distance1.push_back(fDistanceTemp1);
722  }
723 
724  for (int m = 0; m < coordTest.size(); m++)
725  {
726  for (int n = 0; n < coordRef.size(); n++)
727  {
728  distanceValue = sqrt(pow((double)(coordTest[m][0] - coordRef[n][0]), 2) + pow((double)(coordTest[m][1] - coordRef[n][1]), 2) + pow((double)(coordTest[m][2] - coordRef[n][2]), 2));
729 
730  if (distanceValue < fDistanceTemp2)
731  fDistanceTemp2 = distanceValue;
732  }
733 
734  distance2.push_back(fDistanceTemp2);
735  }
736 
737  sum_dist = sum_dist + (double)accumulate(distance1.begin(), distance1.end(), 0.0) + (double)accumulate(distance2.begin(), distance2.end(), 0.0);
738  sum_size = sum_size + (double)distance1.size() + (double)distance2.size();
739  }
740 
741  meanDistance = sum_dist / sum_size;
742  }
743  return meanDistance;
744 }
745 
755 bool SegPerfCAnalyzer::getDetectionMarks(double&po_fPPVL, double&po_fSensL, double&po_fF1)
756 {
757  bool bRes = true;
758 
759  int iNbLabelsRef = 0;
760  int iNbLabelsTest = 0;
761  int * *ppiOverlapTab = NULL;
762  int * *ppiOverlapTabTransposed = NULL;
763  int iTPLgt = 0;
764  int iTPLd = 0;
765 
766  getOverlapTab(iNbLabelsRef, iNbLabelsTest, ppiOverlapTab);
767  transposer(iNbLabelsRef, iNbLabelsTest, ppiOverlapTab, ppiOverlapTabTransposed);
768  if (iNbLabelsRef>1 && iNbLabelsTest>1)
769  {
770  iTPLgt = getTruePositiveLesions(iNbLabelsRef, iNbLabelsTest, ppiOverlapTab);
771  iTPLd = getTruePositiveLesions(iNbLabelsTest, iNbLabelsRef, ppiOverlapTabTransposed);
772 
773  //Deallocates tables allocated by getOverlapTab and transposer
774  removeOverlapTab(ppiOverlapTab, iNbLabelsRef);
775  removeOverlapTab(ppiOverlapTabTransposed, iNbLabelsTest);
776  }
777 
778  po_fPPVL = (double)((double)iTPLd / (double)(iNbLabelsTest-1)); //po_fTPLd = (double)((double)iTPLd / (double)(iNbLabelsTest-1));// The "-1" is to reject background label
779  po_fSensL = (double)((double)iTPLgt / (double)(iNbLabelsRef-1)); //po_fTPLgt = (double)((double)iTPLgt / (double)(iNbLabelsRef-1 ));// The "-1" is to reject background label
780 
781  po_fF1 = 2 * (po_fPPVL * po_fSensL) / (po_fPPVL + po_fSensL);
782 
783  return bRes;
784 }
785 
790 void SegPerfCAnalyzer::setNbThreads(int pi_iNbThreads)
791 {
792  m_ThreadNb = static_cast<itk::ThreadIdType>(pi_iNbThreads);
793 }
794 
801 void SegPerfCAnalyzer::getOverlapTab(int&po_iNbLabelsRef, int&po_iNbLabelsTest, int* *&po_ppiOverlapTab)
802 {
803  typedef itk::ImageRegionConstIterator<ImageType> imageConstIteratorType;
804  typedef itk::ConnectedComponentImageFilter<ImageType, ImageType> connectedComponentImageFilterType;
805  typedef itk::RelabelComponentImageFilter<ImageType, ImageType> relabelComponentImageFilterType;
806  typedef itk::ImageDuplicator<ImageType> imageDuplicatorFilterType;
807 
808  //Variable declaration
809  connectedComponentImageFilterType::Pointer poLesionSeparatorFilter = connectedComponentImageFilterType::New();
810  ImageType::Pointer poImageRefLesionsByLabels = ITK_NULLPTR;
811  ImageType::Pointer poImageTestLesionsByLabels = ITK_NULLPTR;
812  relabelComponentImageFilterType::Pointer poRelabelFilter = relabelComponentImageFilterType::New();
813  int&iNbLabelsRef = po_iNbLabelsRef;
814  int&iNbLabelsTest = po_iNbLabelsTest;
815  int* *&ppiOverlapTab = po_ppiOverlapTab;
816 
817  //take into account the background label
818  iNbLabelsRef = 1;
819  iNbLabelsTest = 1;
820 
821  poLesionSeparatorFilter->SetNumberOfWorkUnits(m_ThreadNb);
822 
823  //Create a label per connected component into ground truth
824  poLesionSeparatorFilter->SetInput(m_imageRef);
825 
826  //Run filter
827  poLesionSeparatorFilter->Update();
828 
829  // Compute Image Spacing, 4th dimension is not physical but temporal
830  ImageType::SpacingType spacing = m_imageRef->GetSpacing();
831  ImageType::SpacingValueType spacingTot = spacing[0];
832  unsigned int imageDim = ImageType::ImageDimension;
833  for (unsigned int i = 1; i < std::min(imageDim, (unsigned int)3); ++i)
834  spacingTot *= spacing[i];
835 
836  // Compute minsize in voxels
837  double minSizeInVoxelD = m_dfMinLesionVolumeDetection / spacingTot;
838  double minSizeInVoxelD_floor = floor(minSizeInVoxelD);
839  unsigned int minSizeInVoxel = static_cast<unsigned int>(minSizeInVoxelD_floor);
840  minSizeInVoxel++; // to have strickly superior sizes
841 
842  poRelabelFilter->SetInput( poLesionSeparatorFilter->GetOutput() );
843  poRelabelFilter->SetMinimumObjectSize(minSizeInVoxel);
844  poRelabelFilter->SetNumberOfWorkUnits(m_ThreadNb);
845  poRelabelFilter->Update();
846 
847  iNbLabelsRef += poRelabelFilter->GetNumberOfObjects();
848  //poImageRefLesionsByLabels = poRelabelFilter->GetOutput();
849  imageDuplicatorFilterType::Pointer poDuplicatorFilter = imageDuplicatorFilterType::New();
850  poDuplicatorFilter->SetInputImage(poRelabelFilter->GetOutput());
851  poDuplicatorFilter->Update();
852  poImageRefLesionsByLabels = poDuplicatorFilter->GetOutput();
853 
854  //Create a label per connected component into image to evaluate
855  poLesionSeparatorFilter->SetInput(m_imageTest);
856  poLesionSeparatorFilter->Update();
857 
858  poRelabelFilter->SetInput( poLesionSeparatorFilter->GetOutput() );
859  poRelabelFilter->SetMinimumObjectSize(minSizeInVoxel);
860  poRelabelFilter->SetNumberOfWorkUnits(m_ThreadNb);
861  poRelabelFilter->Update();
862 
863  iNbLabelsTest += poRelabelFilter->GetNumberOfObjects();
864  poImageTestLesionsByLabels = poRelabelFilter->GetOutput();
865 
866  //Create and initialize table to store overlaps
867  ppiOverlapTab = new int*[iNbLabelsRef];
868  for(int i=0; i<iNbLabelsRef; ++i)
869  {
870  ppiOverlapTab[i] = new int[iNbLabelsTest];
871  memset(ppiOverlapTab[i], 0, iNbLabelsTest*sizeof(int));
872  }
873 
874  //Iterate on both image to fill the overlap tab
875  imageConstIteratorType itRefLabels = imageConstIteratorType(poImageRefLesionsByLabels, poImageRefLesionsByLabels->GetLargestPossibleRegion());
876  imageConstIteratorType itTestLabels = imageConstIteratorType(poImageTestLesionsByLabels, poImageTestLesionsByLabels->GetLargestPossibleRegion());
877 
878  while(!itRefLabels.IsAtEnd())
879  {
880  ImageType::PixelType voxelRefVal = itRefLabels.Value();
881  ImageType::PixelType voxelTestVal = itTestLabels.Value();
882 
883  ppiOverlapTab[voxelRefVal][voxelTestVal]++;
884 
885  ++itRefLabels;
886  ++itTestLabels;
887  }
888 
889  return;
890 }
891 
896 int SegPerfCAnalyzer::getTruePositiveLesions(int pi_iNbLabelsRef, int pi_iNbLabelsTest, int * *pi_ppiOverlapTab)
897 {
898  int iNbLesionsDetected = 0;
899 
900  double dfSensibility = 0;
901  int *piTPRowSumTab = new int[pi_iNbLabelsRef]; //TODO verif si OK ou taille+1
902  int *piColumnSumTab = new int[pi_iNbLabelsTest];
903 
904  //Init sum vectors
905  memset(piTPRowSumTab, 0, pi_iNbLabelsRef*sizeof(int));
906  memset(piColumnSumTab, 0, pi_iNbLabelsTest*sizeof(int));
907  for (int i=0; i<pi_iNbLabelsRef; ++i) //TODO verif si < ou <=
908  {
909  for(int j=0; j<pi_iNbLabelsTest; ++j)
910  {
911  piColumnSumTab[j]+=pi_ppiOverlapTab[i][j];
912  if (j>0)
913  {
914  //Iteration on each detection compared to a Ref label
915  piTPRowSumTab[i] += pi_ppiOverlapTab[i][j];
916  }
917  }
918  }
919 
920  //Iteration on each Ref label
921  for(int i=1; i<pi_iNbLabelsRef; ++i)
922  {
923  int&iTP = piTPRowSumTab[i]; // True-Positive
924  int&iFN = pi_ppiOverlapTab[i][0]; // False-Negative
925 
926  //Compute sensibility for one element of the ground-truth
927  dfSensibility = ((double)iTP) / ((double)( iTP + iFN ));
928 
929  //Check if sensibility is enough
930  if (dfSensibility>m_dfDetectionThresholdAlpha)
931  {
932  //Call the second threshold test. Threshold on FalsePositive/DetectedVolume.
933  if(falsePositiveRatioTester(i, pi_iNbLabelsTest, pi_iNbLabelsRef, pi_ppiOverlapTab, piTPRowSumTab, piColumnSumTab, m_dfDetectionThresholdBeta, m_dfDetectionThresholdGamma))
934  {
935  iNbLesionsDetected++;
936  }
937  }
938  }
939 
940  return iNbLesionsDetected;
941 }
942 
943 // Pair comparison functor
945 {
946  bool operator() (const std::pair<int, int>&f, const std::pair<int, int>&s)
947  {
948  return (f.second > s.second);
949  }
950 };
951 
952 
965 bool SegPerfCAnalyzer::falsePositiveRatioTester(int pi_iLesionReference, int pi_iNbLabelsTest, int pi_iNbLabelsRef, int * *pi_ppiOverlapTab, int *pi_piTPRowSumTab, int *pi_piColumnSumTab, double pi_dfBeta, double pi_dfGamma)
966 {
967  bool bRes = false;
968 
970  // Locals variables declarations
971  bool bExit = false;
972  int k = 0;
973  int &iSumOfTPForCurentRow = pi_piTPRowSumTab[pi_iLesionReference];
974  double dfSumWeight = 0.0;
975  double dfRatioOutsideInside = 0.0; // This is the ratio of the outside part of a tested lesion on the total size od this tested lesion.
976  std::vector<std::pair<int, int> > oSortedCollumVector;
977 
979  // Construction of sorted vector for a TP column
980  for(int l=1; l<pi_iNbLabelsTest; ++l)
981  {
982  int iTmpValOfTP = pi_ppiOverlapTab[pi_iLesionReference][l];
983  oSortedCollumVector.push_back( std::pair<int, int>(l, iTmpValOfTP) );
984  }
985  std::sort(oSortedCollumVector.begin(), oSortedCollumVector.end(), pair_decreasing_comparator());
986 
988  // Test in intersection size decreasing order that the regions overlapping the tested lesion are not too much outside of this lesion
989  while(dfSumWeight<pi_dfGamma && k<pi_iNbLabelsRef && !bExit)
990  {
991  dfRatioOutsideInside = (double)(pi_ppiOverlapTab[0][oSortedCollumVector[k].first])/(double)(pi_piColumnSumTab[oSortedCollumVector[k].first]);
992  bExit = dfRatioOutsideInside>pi_dfBeta;
993  if (!bExit)
994  {
995  dfSumWeight += (double)(oSortedCollumVector[k].second)/(double)(iSumOfTPForCurentRow);
996  }
997  ++k;
998  }
999 
1001  // Lesion is detected if a sufficient percentage (gamma) of the regions overlapping the tested lesion is not too much outside of this lesion
1002  bRes = !bExit; //Almost equivalent, except Floating point imprecision, to bRes=dfSumWeight>=pi_dfGamma;
1003 
1004  return bRes;
1005 }
1006 
1014 void SegPerfCAnalyzer::transposer(int pi_iNbLabelsRef, int pi_iNbLabelsTest, int * *pi_ppiOverlapTab, int* *&po_rppiOverlapTabTransposed)
1015 {
1016  po_rppiOverlapTabTransposed = new int*[pi_iNbLabelsTest+1];
1017  for(int i=0; i<pi_iNbLabelsTest; ++i)
1018  {
1019  po_rppiOverlapTabTransposed[i] = new int[pi_iNbLabelsRef+1];
1020  }
1021 
1022  for(int i=0; i<pi_iNbLabelsRef; ++i)
1023  {
1024  for(int j=0; j<pi_iNbLabelsTest; ++j)
1025  {
1026  po_rppiOverlapTabTransposed[j][i] = pi_ppiOverlapTab[i][j];
1027  }
1028  }
1029 }
1030 
1036 void SegPerfCAnalyzer::removeOverlapTab(int * *pi_ppiOverlapTab, int pi_iNbLabelsRef)
1037 {
1038  if (pi_ppiOverlapTab)
1039  {
1040  for(int i=0; i<pi_iNbLabelsRef; ++i)
1041  {
1042  delete[] (pi_ppiOverlapTab[i]);
1043  }
1044  delete[] (pi_ppiOverlapTab);
1045  }
1046 }
1047 
1048 } // end namespace anima
double getPPV()
Getter of Positive predictive value.
double getDiceCoefficient()
Getter of Dice coefficient.
double computeAverageSurfaceDistance()
Compute average surface distance.
bool falsePositiveRatioTester(int pi_iLessionReference, int pi_iNbLabelsTest, int pi_iNbLabelsRef, int **pi_ppiOverlapTab, int *pi_piTPRowSumTab, int *pi_piColumnSumTab, double pi_dfBeta, double pi_dfGamma)
Compute if a lesion is detected or not with beta and gamma thresholds.
struct @0 pair_decreasing_comparator
double getJaccardCoefficient()
Getter of Jaccard coefficient.
void computeITKMeasures()
Compute different measures with ITK to evaluate segmentation.
double computeMeanDist()
Compute mean distance.
double getNPV()
Getter of Negative predictive value.
void selectCluster(unsigned int)
Select the cluster we want to use to compute evaluation results.
double getMeanOverlap()
Getter of Mean overlap.
bool checkImagesMatrixAndVolumes()
Check if the 2 inputs images are compatible.
void contourDectection()
Extract the contour of the image to evaluate and the ground truth.
void getOverlapTab(int &po_iNbLabelsRef, int &po_iNbLabelsTest, int **&po_ppiOverlapTab)
Compute a table of lesion overlap between 2 images.
int getNumberOfClusters()
Return the number of clusters.
void formatLabels()
Format labels values for the image to evaluate and the ground truth to obtain : background pixels val...
double getRelativeVolumeError()
Getter of Relative volume error.
void setNbThreads(int pi_iNbThreads)
Set the number of threads to use for the computation of ITK.
bool getDetectionMarks(double &po_fPPVL, double &po_fSensL, double &po_fF1)
Compute useful variables to get detection scores.
double getSensitivity()
Getter of Sensibility.
int getTruePositiveLesions(int pi_iNbLabelsRef, int pi_iNbLabelsTest, int **pi_ppiOverlapTab)
Getter of True positive lesions.
double getSpecificity()
Getter of Specificity.
itk::NumericTraits< LabelType >::RealType RealType
double getUnionOverlap()
Getter of Union overlap.
double computeHausdorffDist()
Compute Haussdorf distance.
void checkNumberOfLabels(int, int)
Check if the number of labels is the same for both input images.