ANIMA  4.0
animaDetectedComponents.cxx
Go to the documentation of this file.
2 
3 #include <itkConnectedComponentImageFilter.h>
4 #include <itkRelabelComponentImageFilter.h>
5 #include <itkImageRegionConstIterator.h>
6 #include <itkMultiThreaderBase.h>
7 #include <vnl/vnl_matrix.h>
8 
9 #include <tclap/CmdLine.h>
10 #include <fstream>
11 #include <algorithm>
12 
13 struct
14 {
15  bool operator()(const std::pair <unsigned int, unsigned int> &a, const std::pair <unsigned int, unsigned int> &b) const
16  {
17  return a.second > b.second;
18  }
20 
21 int main(int argc, char * *argv)
22 {
23  TCLAP::CmdLine cmd("INRIA / IRISA - VisAGeS/Empenn Team", ' ', ANIMA_VERSION);
24 
25  TCLAP::ValueArg <std::string> refArg("r", "ref", "Reference binary segmentation", true, "", "reference segmentation", cmd);
26  TCLAP::ValueArg <std::string> testArg("t", "test", "Test binary segmentation", true, "", "test segmentation", cmd);
27  TCLAP::ValueArg <std::string> outArg("o", "out-csv", "output CSV data on lesions", true, "", "output CSV", cmd);
28 
29  TCLAP::ValueArg <double> alphaArg("a", "alpha", "Alpha threshold (in between 0 and 1, default: 0.1)", false, 0.1, "alpha threshold", cmd);
30  TCLAP::ValueArg <double> gammaArg("g", "gamma", "Gamma threshold (in between 0 and 1, default: 0.65)", false, 0.65, "gamma threshold", cmd);
31  TCLAP::ValueArg <double> betaArg("b", "beta", "Beta threshold (in between 0 and 1, default: 0.7)", false, 0.7, "beta threshold", cmd);
32 
33  TCLAP::ValueArg <unsigned int> minVolumeArg("m", "min-vol", "Minimal volume for the component to be considered (default: 3 mm3)", false, 3, "minimal volume", cmd);
34  TCLAP::ValueArg <unsigned int> nbpArg("T","numberofthreads","Number of threads to run on (default : all cores)",false,itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(),"number of threads",cmd);
35 
36  TCLAP::SwitchArg fullConnectArg("F","full-connect","Use 26-connectivity instead of 6-connectivity",cmd,false);
37 
38  try
39  {
40  cmd.parse(argc, argv);
41  }
42  catch (TCLAP::ArgException& e)
43  {
44  std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl;
45  return EXIT_FAILURE;
46  }
47 
48  typedef itk::Image <unsigned short, 3> ImageType;
49  typedef itk::ImageRegionConstIterator <ImageType> ImageIteratorType;
50 
51  ImageType::Pointer refSegmentation = anima::readImage <ImageType> (refArg.getValue());
52  ImageType::Pointer testSegmentation = anima::readImage <ImageType> (testArg.getValue());
53 
54  typedef itk::ConnectedComponentImageFilter <ImageType, ImageType> CCFilterType;
55  typedef itk::RelabelComponentImageFilter <ImageType, ImageType> RelabelComponentFilterType;
56 
57  CCFilterType::Pointer refCCFilter = CCFilterType::New();
58  refCCFilter->SetInput(refSegmentation);
59  refCCFilter->SetFullyConnected(fullConnectArg.isSet());
60  refCCFilter->SetNumberOfWorkUnits(nbpArg.getValue());
61  refCCFilter->Update();
62 
63  ImageType::SpacingType spacing = refSegmentation->GetSpacing();
64  ImageType::SpacingValueType spacingTot = spacing[0];
65  for (unsigned int i = 1;i < 3;++i)
66  spacingTot *= spacing[i];
67 
68  // Compute minsize in voxels
69  unsigned int minSizeInVoxel = (unsigned int)std::ceil(minVolumeArg.getValue() / spacingTot);
70 
71  // Remove too small reference objects
72  RelabelComponentFilterType::Pointer relabelRefFilter = RelabelComponentFilterType::New();
73  relabelRefFilter->SetInput(refCCFilter->GetOutput());
74  relabelRefFilter->SetMinimumObjectSize(minSizeInVoxel);
75  relabelRefFilter->SetNumberOfWorkUnits(nbpArg.getValue());
76  relabelRefFilter->Update();
77 
78  // Reference segmentation is now labeled per connected objects
79  refSegmentation = relabelRefFilter->GetOutput();
80  refSegmentation->DisconnectPipeline();
81 
82  CCFilterType::Pointer testCCFilter = CCFilterType::New();
83  testCCFilter->SetInput(testSegmentation);
84  testCCFilter->SetFullyConnected(fullConnectArg.isSet());
85  testCCFilter->SetNumberOfWorkUnits(nbpArg.getValue());
86  testCCFilter->Update();
87 
88  // Remove too small test objects
89  RelabelComponentFilterType::Pointer relabelTestFilter = RelabelComponentFilterType::New();
90  relabelTestFilter->SetInput(testCCFilter->GetOutput());
91  relabelTestFilter->SetMinimumObjectSize(minSizeInVoxel);
92  relabelTestFilter->SetNumberOfWorkUnits(nbpArg.getValue());
93  relabelTestFilter->Update();
94 
95  // Test segmentation is now labeled per connected objects
96  testSegmentation = relabelTestFilter->GetOutput();
97  testSegmentation->DisconnectPipeline();
98 
99  ImageIteratorType refItr(refSegmentation, refSegmentation->GetLargestPossibleRegion());
100  ImageIteratorType testItr(testSegmentation, testSegmentation->GetLargestPossibleRegion());
101 
102  unsigned int maxRefLabel = 0;
103  unsigned int maxTestLabel = 0;
104  while (!refItr.IsAtEnd())
105  {
106  if (refItr.Get() > maxRefLabel)
107  maxRefLabel = refItr.Get();
108 
109  if (testItr.Get() > maxTestLabel)
110  maxTestLabel = testItr.Get();
111 
112  ++refItr;
113  ++testItr;
114  }
115 
116  ++maxRefLabel;
117  ++maxTestLabel;
118  if (maxRefLabel <= 1)
119  return EXIT_FAILURE;
120 
121  refItr.GoToBegin();
122  testItr.GoToBegin();
123 
124  vnl_matrix <unsigned int> labelsOverlap(maxRefLabel,maxTestLabel);
125  labelsOverlap.fill(0);
126 
127  while (!refItr.IsAtEnd())
128  {
129  labelsOverlap(refItr.Get(),testItr.Get())++;
130 
131  ++refItr;
132  ++testItr;
133  }
134 
135  std::vector < std::pair <unsigned int, unsigned int> > subVectorForSort(maxTestLabel - 1);
136  std::vector < std::pair <double,unsigned int> > detectionTable(maxRefLabel-1);
137  std::vector <double> columnSums(maxTestLabel,0);
138  std::vector <double> lineSums(maxRefLabel,0);
139 
140  for (unsigned int i = 1;i < maxRefLabel;++i)
141  {
142  for (unsigned int j = 1;j < maxTestLabel;++j)
143  {
144  lineSums[i] += labelsOverlap(i,j);
145  columnSums[j] += labelsOverlap(i,j);
146  }
147  }
148 
149  for (unsigned int i = 1;i < maxRefLabel;++i)
150  {
151  double denom = labelsOverlap(i,0) + lineSums[i];
152  double Si = lineSums[i] / denom;
153 
154  if (Si <= alphaArg.getValue())
155  {
156  detectionTable[i-1] = std::make_pair(denom * spacingTot,0);
157  continue;
158  }
159 
160  for (unsigned int j = 1;j < maxTestLabel;++j)
161  subVectorForSort[j-1] = std::pair <unsigned int,unsigned int>(j,labelsOverlap(i,j));
162 
163  std::sort(subVectorForSort.begin(),subVectorForSort.end(),pair_decreasing_comparator);
164 
165  double wSum = 0;
166  unsigned int detectedObject = 1;
167  unsigned int k = 0;
168  while (wSum < gammaArg.getValue())
169  {
170  unsigned int kIndex = subVectorForSort[k].first;
171  double sumK = labelsOverlap(0,kIndex) + columnSums[kIndex];
172  double Tk = labelsOverlap(0,kIndex) / sumK;
173 
174  if (Tk > betaArg.getValue())
175  {
176  detectedObject = 0;
177  break;
178  }
179 
180  wSum += labelsOverlap(i,kIndex) / lineSums[i];
181  ++k;
182  }
183 
184  detectionTable[i-1] = std::make_pair(denom * spacingTot,detectedObject);
185  }
186 
187  unsigned int totalNumberOfDetections = 0;
188  unsigned int refCount = 0;
189  for (unsigned int i = 1;i < maxRefLabel;++i)
190  {
191  if (detectionTable[i].second > 0)
192  ++totalNumberOfDetections;
193 
194  for (unsigned int j = 0;j < maxTestLabel;++j)
195  refCount += labelsOverlap(i,j);
196  }
197 
198  std::ofstream outputFile(outArg.getValue());
199  outputFile.precision(10);
200  outputFile << "Total number of reference objects:," << maxRefLabel - 1 << std::endl;
201  outputFile << "Total number of detections:," << totalNumberOfDetections << std::endl;
202  outputFile << "Total objects volume:," << refCount * spacingTot << std::endl;
203 
204  outputFile << "Object number,Object volume,Detected flag" << std::endl;
205  for (unsigned int i = 1;i < maxRefLabel;++i)
206  outputFile << i << "," << detectionTable[i-1].first << "," << detectionTable[i-1].second << std::endl;
207 
208  outputFile.close();
209 
210  return EXIT_SUCCESS;
211 }
212 
int main(int argc, char **argv)
struct @0 pair_decreasing_comparator