ANIMA  4.0
animaSegPerfApp.cxx
Go to the documentation of this file.
1 #include "animaSegPerfApp.h"
2 
3 #include <tclap/CmdLine.h>
4 #include <iostream>
5 #include <cmath>
6 #include <limits>
7 
8 namespace anima
9 {
10 
12 {
14  // Output way
15  m_bTxt = true;
16  m_bXml = false;
17  m_bScreen = false;
18 
20  // Group of metrics enable
21  m_bSegmentationEvaluation = true;
22  m_bAdvancedEvaluation = false;
23  m_bSurfaceEvaluation = true;
24  m_bLesionsDetectionEvaluation = true;
25 
27  // Lesions specific metrics
28  m_fSensitivity = std::numeric_limits<double>::quiet_NaN();
29  m_fSpecificity = std::numeric_limits<double>::quiet_NaN();
30  m_fPPV = std::numeric_limits<double>::quiet_NaN();
31  m_fNPV = std::numeric_limits<double>::quiet_NaN();
32  m_fDice = std::numeric_limits<double>::quiet_NaN();
33  m_fJaccard = std::numeric_limits<double>::quiet_NaN();
34  m_fRVE = std::numeric_limits<double>::quiet_NaN();
35 
37  // Distances metrics
38  m_fHausdorffDist = std::numeric_limits<double>::quiet_NaN();
39  m_fMeanDist = std::numeric_limits<double>::quiet_NaN();
40  m_fAverageDist = std::numeric_limits<double>::quiet_NaN();
41 
43  // Detection scores
44  m_fPPVL = std::numeric_limits<double>::quiet_NaN();
45  m_fSensL = std::numeric_limits<double>::quiet_NaN();
46  m_fF1 = std::numeric_limits<double>::quiet_NaN();
47 
49  // general informations
50  m_iNbThreads = 0; /*<! number of thread used by processing. */
51  m_pchOutBase = ""; /*<! base name used for output file. */
52 
54  // Detection scores parameters
55  m_fDetectionLesionMinVolume = 3.0;
56  m_fTPLMinOverlapRatio = 0.1;
57  m_fTPLMaxFalsePositiveRatio = 0.7;
58  m_fTPLMaxFalsePositiveRatioModerator = 0.65;
59 }
60 
62 {
63 }
64 
71 bool SegPerfApp::init(int argc, char *argv[])
72 {
73  // Define the command line object.
74  TCLAP::CmdLine cmd("Tools to analyze segmentation performances by comparison", ' ', ANIMA_VERSION);
75 
76  // Define a value argument and add it to the command line.
77  TCLAP::ValueArg<std::string> oArgInputImg("i", "input", "Input image.", true, "", "string", cmd);
78 
79  // Define a value argument and add it to the command line.
80  TCLAP::ValueArg<std::string> oArgRefImg("r", "ref", "Reference image to compare input image.", true, "", "string", cmd);
81 
82  // Define a value argument and add it to the command line.
83  TCLAP::ValueArg<std::string> oArgBaseOutputName("o", "outputBase", "Base name for output files", true, "", "string", cmd);
84 
85  // Define a value argument and add it to the command line.
86  TCLAP::ValueArg<int> oArgNbThreads("t", "threads", "Number of threads", false, 0, "int", cmd);
87 
88  // Define a value argument and add it to the command line.
89  TCLAP::SwitchArg oArgAbout("A", "About", "Details on output metrics", cmd, false);
90 
91  // Define a switch and add it to the command line.
92  TCLAP::SwitchArg oArgSwitchText("T", "text", "Stores results into a text file.", cmd, false);
93 
94  // Define a switch and add it to the command line.
95  TCLAP::SwitchArg oArgSwitchXml("X", "xml", "Stores results into a xml file.", cmd, false);
96 
97  // Define a switch and add it to the command line.
98  TCLAP::SwitchArg oArgSwitchScreen("S", "screen", "Print results on the screen when program ended.", cmd, false);
99 
100  // Define a switch and add it to the command line.
101  TCLAP::SwitchArg oArgSegEval("s", "SegmentationEvaluationMetrics", "Compute metrics to evaluate a segmentation.", cmd, false);
102 
103  // Define a switch and add it to the command line.
104  TCLAP::SwitchArg oArgSwitchAdvancedEvaluation("a", "advancedEvaluation", "Compute results for each cluster (intra-lesion results)", cmd, false);
105 
106  // Define a switch and add it to the command line.
107  TCLAP::SwitchArg oArgSwitchDetectionEval("l", "LesionDetectionMetrics", "Compute metrics to evaluate the detection of lesions along to a segmentation.", cmd, false);
108 
109  // Define a switch and add it to the command line.
110  TCLAP::SwitchArg oArgSwitchSurfaceDist("d", "SurfaceEvaluation", "Surface distances evaluation.", cmd, false);
111 
112  // Define a switch and add it to the command line.
113  TCLAP::ValueArg<double> oArgSwitchDetectionLesionMinVolume("v", "MinLesionVolume", "Min volume of lesion for \"Lesions detection metrics\" in mm^3 (default 3mm^3).", false, 3.00, "double", cmd);
114 
115  // Define a switch and add it to the command line.
116  TCLAP::ValueArg<double> oArgSwitchTPLMinOverlapRatio("x", "MinOverlapRatio", "Minimum overlap ratio to say if a lesion of the GT is detected. (default 0.10)", false, 0.10, "double", cmd);
117 
118  // Define a switch and add it to the command line.
119  TCLAP::ValueArg<double> oArgSwitchTPLMaxFalsePositiveRatio("y", "MaxFalsePositiveRatio", "Maximum of false positive ratio to limit the detection of a lesion in GT if a lesion in the image is too big. (default 0.7)", false, 0.70, "double", cmd);
120 
121  // Define a switch and add it to the command line.
122  TCLAP::ValueArg<double> oArgSwitchTPLMaxFalsePositiveRatioModerator("z", "MaxFalsePositiveRatioModerator", "Percentage of the regions overlapping the tested lesion is not too much outside of this lesion. (default 0.65)", false, 0.65, "double", cmd);
123 
124  // Parse the args.
125  cmd.parse( argc, argv );
126 
127  if (oArgAbout.isSet())
128  {
129  about();
130  return false;
131  }
132 
133  m_oStrInImage = oArgInputImg.getValue();
134  m_oStrRefImage = oArgRefImg.getValue();
135  m_oStrBaseOut = oArgBaseOutputName.getValue();
136 
137  m_bTxt = oArgSwitchText.getValue();
138  m_bXml = oArgSwitchXml.getValue();
139  m_bScreen = oArgSwitchScreen.getValue();
140 
141  m_iNbThreads = oArgNbThreads.getValue();
142 
143  m_bSegmentationEvaluation = oArgSegEval.getValue();
144  m_bAdvancedEvaluation = oArgSwitchAdvancedEvaluation.getValue();
145  m_bSurfaceEvaluation = oArgSwitchSurfaceDist.getValue();
146  m_bLesionsDetectionEvaluation = oArgSwitchDetectionEval.getValue();
147 
148  m_fDetectionLesionMinVolume = oArgSwitchDetectionLesionMinVolume.getValue();
149  m_fTPLMinOverlapRatio = oArgSwitchTPLMinOverlapRatio.getValue();
150  m_fTPLMaxFalsePositiveRatio = oArgSwitchTPLMaxFalsePositiveRatio.getValue();
151  m_fTPLMaxFalsePositiveRatioModerator = oArgSwitchTPLMaxFalsePositiveRatioModerator.getValue();
152 
153  return true;
154 }
155 
161 {
162  bool bFault = false;
163 
164  if(!(m_bSegmentationEvaluation || m_bAdvancedEvaluation || m_bSurfaceEvaluation || m_bLesionsDetectionEvaluation))
165  {
166  m_bSegmentationEvaluation = true;
167  }
168 
169  if (m_bAdvancedEvaluation && !(m_bSegmentationEvaluation || m_bSurfaceEvaluation))
170  {
171  std::cout << "Switch \"advancedEvaluation\" need \"SegmentationEvaluationMetrics\" or/and SurfaceEvaluation switches." << std::endl;
172  std::cout << "!!!\"SegmentationEvaluationMetrics\" has been enabled by default!!!" << std::endl;
173  m_bSegmentationEvaluation = true;
174  }
175 
176  if (m_fDetectionLesionMinVolume<0)
177  {
178  std::cout << "!!!!! Error on DetectionLesionMinVolume!!!!!" << std::endl;
179  bFault = true;
180  }
181 
182  if (m_fTPLMinOverlapRatio<=0 || m_fTPLMinOverlapRatio>1)
183  {
184  std::cout << "!!!!! Error on TPLMinOverlapRatio!!!!!" << std::endl;
185  bFault = true;
186  }
187 
188  if (m_fTPLMaxFalsePositiveRatio<=0 || m_fTPLMaxFalsePositiveRatio>1)
189  {
190  std::cout << "!!!!! Error on TPLMaxFalsePositiveRatio!!!!!" << std::endl;
191  bFault = true;
192  }
193 
194  if (m_fTPLMaxFalsePositiveRatioModerator<=0 || m_fTPLMaxFalsePositiveRatioModerator>1)
195  {
196  std::cout << "!!!!! Error on TPLMaxFalsePositiveRatioModerator!!!!!" << std::endl;
197  bFault = true;
198  }
199 
200  if (bFault)
201  {
202  std::cout << "*** 0 < DetectionLesionMinVolume ***" << std::endl;
203  std::cout << "*** 0 < TPLMinOverlapRatio <= 1 ***" << std::endl;
204  std::cout << "*** 0 < TPLMaxFalsePositiveRatio <= 1 ***" << std::endl;
205  std::cout << "*** 0 < TPLMaxFalsePositiveRatioModerator <= 1 ***" << std::endl;
206  }
207 
208  return !bFault;
209 }
210 
216 {
217  if (!(m_bTxt || m_bXml || m_bScreen))
218  {
219  m_bTxt = true;
220  }
221 }
222 
227 {
228  if (m_oStrBaseOut.empty())
229  {
230  int iDotPos = m_oStrInImage.rfind('.');
231  int iFolderPos = -1;
232  int iSlashPos = m_oStrInImage.rfind('/');
233  int iBkSlashPos = m_oStrInImage.rfind('\\');
234 
235  if (iSlashPos != std::string::npos && iBkSlashPos != std::string::npos)
236  {
237  iFolderPos = iSlashPos>iBkSlashPos ? iSlashPos : iBkSlashPos;
238  }
239  else if (iSlashPos != std::string::npos)
240  {
241  iFolderPos = iSlashPos;
242  }
243  else if (iBkSlashPos != std::string::npos)
244  {
245  iFolderPos = iBkSlashPos;
246  }
247 
248  ++iFolderPos;
249 
250  m_pchOutBase = "";
251  m_pchOutBase.append(m_oStrInImage.begin() + iFolderPos, m_oStrInImage.end());
252 
253  if(iDotPos != std::string::npos)
254  m_pchOutBase[iDotPos-iFolderPos] = 0;
255  }
256  else
257  m_pchOutBase = m_oStrBaseOut;
258 }
259 
264 {
265  long lRes = 0;
266 
267  anima::SegPerfCAnalyzer oAnalyzer(m_oStrInImage, m_oStrRefImage, m_bAdvancedEvaluation);
268 
269  if(!oAnalyzer.checkImagesMatrixAndVolumes())
270  throw itk::ExceptionObject(__FILE__, __LINE__, "Orientation matrices and volumes do not match");
271 
272  if (m_iNbThreads>0)
273  oAnalyzer.setNbThreads(m_iNbThreads);
274 
275  int nbLabels = oAnalyzer.getNumberOfClusters();
276 
277  std::string sOutBase = m_pchOutBase;
278  std::string sOut = sOutBase;
279 
280  int i = 0;
281 
283  // First step of loop is for global, if next steps exist it's for each layer
284  do
285  {
286  sOut = sOutBase;
287  std::stringstream outBaseTemp;
288 
289  if(i == 0)
290  outBaseTemp << sOut << "_global";
291  else
292  outBaseTemp << sOut << "_cluster" << i;
293 
294  sOut = outBaseTemp.str();
295 
296  SegPerfResults oRes(sOut);
297 
298  processAnalyze(oAnalyzer, i);
299  storeMetricsAndMarks(oRes);
300  lRes = writeStoredMetricsAndMarks(oRes);
301 
302  i++;
303  } while (i < nbLabels && m_bAdvancedEvaluation);
304 }
305 
312 void SegPerfApp::processAnalyze(SegPerfCAnalyzer &pi_roAnalyzer, int pi_iIndex)
313 {
314  pi_roAnalyzer.selectCluster(pi_iIndex);
315 
316  // Segmentation evaluation
317  if(m_bSegmentationEvaluation || m_bSurfaceEvaluation)
318  {
319  pi_roAnalyzer.computeITKMeasures();
320  m_fSensitivity = pi_roAnalyzer.getSensitivity();
321  m_fSpecificity = pi_roAnalyzer.getSpecificity();
322  m_fPPV = pi_roAnalyzer.getPPV();
323  m_fNPV = pi_roAnalyzer.getNPV();
324  m_fDice = pi_roAnalyzer.getDiceCoefficient();
325  m_fJaccard = pi_roAnalyzer.getJaccardCoefficient();
326  m_fRVE = pi_roAnalyzer.getRelativeVolumeError();
327 
328  //Surfaces distances computing (Haussdorf, meanDist, Average ...)
329  if(m_bSurfaceEvaluation)
330  {
331  m_fHausdorffDist = pi_roAnalyzer.computeHausdorffDist();
332  m_fMeanDist = pi_roAnalyzer.computeMeanDist();
333  m_fAverageDist = pi_roAnalyzer.computeAverageSurfaceDistance();
334  }
335  }
336 
337  // Detection lesions
338  if(m_bLesionsDetectionEvaluation)
339  {
340  pi_roAnalyzer.setDetectionThresholdAlpha(m_fTPLMinOverlapRatio);
341  pi_roAnalyzer.setDetectionThresholdBeta(m_fTPLMaxFalsePositiveRatio);
342  pi_roAnalyzer.setDetectionThresholdGamma(m_fTPLMaxFalsePositiveRatioModerator);
343  pi_roAnalyzer.setMinLesionVolumeDetection(m_fDetectionLesionMinVolume);
344  pi_roAnalyzer.getDetectionMarks(m_fPPVL, m_fSensL, m_fF1);
345  }
346 }
347 
354 {
357  // Following code put out results
359  pi_roRes.setTxt(m_bTxt);
360  pi_roRes.setXml(m_bXml);
361  pi_roRes.setScreen(m_bScreen);
362 
363  //Segmentation
364  if(m_bSegmentationEvaluation)
365  {
367  pi_roRes.setDice(m_fDice);
369  pi_roRes.setJaccard(m_fJaccard);
371  pi_roRes.setSensibility(m_fSensitivity);
373  pi_roRes.setSpecificity(m_fSpecificity);
375  pi_roRes.setNPV(m_fNPV);
377  pi_roRes.setPPV(m_fPPV);
379  pi_roRes.setRVE(m_fRVE*100);
380  }
381 
382  //Surfaces distances
383  if(m_bSurfaceEvaluation)
384  {
386  pi_roRes.setHausdorffDist(m_fHausdorffDist);
388  pi_roRes.setContourMeanDist(m_fMeanDist);
390  pi_roRes.setAverageSurfaceDist(m_fAverageDist);
391  }
392 
393  //Lesion detection
394  if(m_bLesionsDetectionEvaluation)
395  {
397  pi_roRes.setPPVL(m_fPPVL);
399  pi_roRes.setSensL(m_fSensL);
401  pi_roRes.setF1test(m_fF1);
402  }
403 }
404 
411 {
412  long lRes = (long) !pi_roRes.save();
413  return lRes;
414 }
415 
420 {
421  std::cout << std::endl;
422  std::cout << "********************************************************************************" << std::endl;
423  std::cout << "********************************************************************************" << std::endl;
424  std::cout << "SegPerfAnalyser (Segmentation Performance Analyzer) provides different" << std::endl;
425  std::cout << "marks, metrics and scores for segmentation evaluation." << std::endl;
426  std::cout << std::endl;
427  std::cout << "3 categories are available:" << std::endl;
428  std::cout << " - SEGMENTATION EVALUATION:" << std::endl;
429  std::cout << " Dice, the mean overlap" << std::endl;
430  std::cout << " Jaccard, the union overlap" << std::endl;
431  std::cout << " Sensitivity" << std::endl;
432  std::cout << " Specificity" << std::endl;
433  std::cout << " NPV (Negative Predictive Value)" << std::endl;
434  std::cout << " PPV (Positive Predictive Value)" << std::endl;
435  std::cout << " RVE (Relative Volume Error) in percentage" << std::endl;
436  std::cout << " - SURFACE DISTANCE EVALUATION:" << std::endl;
437  std::cout << " Hausdorff distance" << std::endl;
438  std::cout << " Contour mean distance" << std::endl;
439  std::cout << " Average surface distance" << std::endl;
440  std::cout << " - DETECTION LESIONS EVALUATION:" << std::endl;
441  std::cout << " PPVL (Positive Predictive Value for Lesions)" << std::endl;
442  std::cout << " SensL, Lesion detection sensitivity" << std::endl;
443  std::cout << " F1 Score, a F1 Score between PPVL and SensL" << std::endl;
444  std::cout << std::endl;
445 
446 
447  std::cout << "Results are provided as follows: " << std::endl;
448  char const*const*const ppchNameTab = SegPerfResults::getMeasureNameTable();
449  for (int i=0;i < SegPerfResults::eMesureLast;++i)
450  std::cout << ppchNameTab[i]<<";\t";
451 
452  std::cout << std::endl;
453  std::cout << "********************************************************************************" << std::endl;
454  std::cout << "********************************************************************************" << std::endl;
455 }
456 
457 } // end namespace anima
double getPPV()
Getter of Positive predictive value.
double getDiceCoefficient()
Getter of Dice coefficient.
bool init(int argc, char *argv[])
This method set the application behavior thanks to command line arguments parsing.
void setScreen(bool pi_bEnable=true)
Enable or disable on screen results.
void setAverageSurfaceDist(double pi_fVal)
Set the result value of average surface distance measure.
double computeAverageSurfaceDistance()
Compute average surface distance.
void setDetectionThresholdBeta(double pi_fVal)
static void about()
This method display information about SegPerfAnalyzer results.
void setSensL(double pi_fVal)
Set the result value of SensL measure.
double getJaccardCoefficient()
Getter of Jaccard coefficient.
bool checkParamsCoherence()
This method check if command line arguments are coherent between us.
void setF1test(double pi_fVal)
Set the result value of F1 score of F-test.
bool save()
It saves results on text file or xml file in function of class default settings.
void computeITKMeasures()
Compute different measures with ITK to evaluate segmentation.
double computeMeanDist()
Compute mean distance.
double getNPV()
Getter of Negative predictive value.
void play()
This method execute images filter to obtain desired measures, marks and scores.
void selectCluster(unsigned int)
Select the cluster we want to use to compute evaluation results.
static char const *const *const getMeasureNameTable()
Get the list of all Measures available.
bool checkImagesMatrixAndVolumes()
Check if the 2 inputs images are compatible.
void setDice(double pi_fVal)
Set the result value of Dice measure.
void setRVE(double pi_fVal)
Set the result value of Relative volume error.
long writeStoredMetricsAndMarks(SegPerfResults &pi_roRes)
This method flush SegPerfResults class instance.
int getNumberOfClusters()
Return the number of clusters.
double getRelativeVolumeError()
Getter of Relative volume error.
void setSensibility(double pi_fVal)
Set the result value of Sensibility measure.
void setNbThreads(int pi_iNbThreads)
Set the number of threads to use for the computation of ITK.
void processAnalyze(SegPerfCAnalyzer &pi_oAnalyzer, int pi_iIndex)
This method provides computing metrics, marks and scores for desired label.
bool getDetectionMarks(double &po_fPPVL, double &po_fSensL, double &po_fF1)
Compute useful variables to get detection scores.
void setXml(bool pi_bEnable=true)
Enable or disable XML file results.
void storeMetricsAndMarks(SegPerfResults &pi_roRes)
This method store results into SegPerfResults class instance.
Class to compute various metrics to evaluate segmentation results.
void checkOutputCoherence()
This method define an output way if none are defined by command line.
void setDetectionThresholdAlpha(double pi_fVal)
double getSensitivity()
Getter of Sensibility.
void setJaccard(double pi_fVal)
Set the result value of Jaccard measure.
double getSpecificity()
Getter of Specificity.
void setMinLesionVolumeDetection(double pi_fVal)
void setPPVL(double pi_fVal)
Set the result value of PPVL measure.
double computeHausdorffDist()
Compute Haussdorf distance.
void setNPV(double pi_fVal)
Set the result value of NPV (Negative Predictive Value) measure.
void setHausdorffDist(double pi_fVal)
Set the result value of DistHausdorff measure.
void setContourMeanDist(double pi_fVal)
Set the result value of contour mean distance measure.
void prepareOutput()
This method define the base file name for output way.
Class to format and saves results.
bool activeMeasurementOutput(eMesureName pi_eVal)
It active the saving of one specific measure. If it set twice time the effect is inverted.
void setSpecificity(double pi_fVal)
Set the result value of Specificity measure.
void setPPV(double pi_fVal)
Set the result value of PPV (Positive Predictive Value) measure.
void setDetectionThresholdGamma(double pi_fVal)
void setTxt(bool pi_bEnable=true)
Enable or disable text file results.