ANIMA  4.0
animaLesionEvolutionDetection.cxx
Go to the documentation of this file.
2 
3 #include <itkConnectedComponentImageFilter.h>
4 #include <itkRelabelComponentImageFilter.h>
5 #include <itkImageRegionIterator.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 int main(int argc, char **argv)
14 {
15  TCLAP::CmdLine cmd("Computes from two segmentations the connected components that grew (L=3), were already there (L=1), or are new (L=2).\n INRIA / IRISA - VisAGeS/Empenn Team", ' ', ANIMA_VERSION);
16 
17  /*
18  * Computes the labels following this algorithm:
19  * - connected components in test image (newest timepoint)
20  * - For each CC, test the accordance with older time point:
21  * - Compute overlapping and non overlapping parts with previous timepoint (ref)
22  * - If non overlap below an absolute threshold or ratio non overlap / overlap < beta -> classify as L1 and go on
23  * - If overlap / ccSize < alpha -> classify as new = L2 and go on
24  * - If ratio non overlap / overlap > gamma and non overlap volume > gammaAbsolute -> classify as new = L2 and go on
25  * - Else -> classify as growing = L3 and go on
26  */
27 
28  TCLAP::ValueArg <std::string> refArg("r", "ref", "Reference binary segmentation", true, "", "reference segmentation", cmd);
29  TCLAP::ValueArg <std::string> testArg("t", "test", "Test binary segmentation", true, "", "test segmentation", cmd);
30  TCLAP::ValueArg <std::string> outArg("o", "out", "output image with labeled lesions", true, "", "output labeled lesions", cmd);
31 
32  TCLAP::ValueArg <double> alphaArg("a", "alpha", "Alpha threshold (in between 0 and 1, default: 0)", false, 0, "alpha threshold", cmd);
33  TCLAP::ValueArg <double> gammaArg("g", "gamma", "Gamma threshold (*100 %, default: 0.5)", false, 0.5, "gamma threshold", cmd);
34  TCLAP::ValueArg <double> gammaAbsoluteArg("G", "gamma-abs", "Gamma threshold (in mm3, default: 12)", false, 12, "gamma absolute threshold", cmd);
35  TCLAP::ValueArg <double> betaArg("b", "beta", "Beta threshold (in between 0 and 1, default: 0.05)", false, 0.05, "beta threshold", cmd);
36 
37  TCLAP::ValueArg <unsigned int> minVolumeArg("m", "min-vol", "Minimal volume for the component to be considered (default: 6 mm3)", false, 6, "minimal volume", cmd);
38  TCLAP::ValueArg <unsigned int> nbpArg("T","numberofthreads","Number of threads to run on (default : all cores)",false,itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(),"number of threads",cmd);
39 
40  TCLAP::SwitchArg fullConnectArg("F","full-connect","Use 26-connectivity instead of 6-connectivity",cmd,false);
41 
42  try
43  {
44  cmd.parse(argc, argv);
45  }
46  catch (TCLAP::ArgException& e)
47  {
48  std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl;
49  return EXIT_FAILURE;
50  }
51 
52  typedef itk::Image <unsigned short, 3> ImageType;
53  typedef itk::ImageRegionIterator <ImageType> ImageIteratorType;
54 
55  ImageType::Pointer refSegmentation = anima::readImage <ImageType> (refArg.getValue());
56  ImageType::Pointer testSegmentation = anima::readImage <ImageType> (testArg.getValue());
57 
58  typedef itk::ConnectedComponentImageFilter <ImageType, ImageType> CCFilterType;
59  typedef itk::RelabelComponentImageFilter <ImageType, ImageType> RelabelComponentFilterType;
60 
61  CCFilterType::Pointer refCCFilter = CCFilterType::New();
62  refCCFilter->SetInput(refSegmentation);
63  refCCFilter->SetFullyConnected(fullConnectArg.isSet());
64  refCCFilter->SetNumberOfWorkUnits(nbpArg.getValue());
65  refCCFilter->Update();
66 
67  ImageType::SpacingType spacing = refSegmentation->GetSpacing();
68  ImageType::SpacingValueType spacingTot = spacing[0];
69  for (unsigned int i = 1;i < 3;++i)
70  spacingTot *= spacing[i];
71 
72  // Compute minsize in voxels for connected component
73  unsigned int minSizeInVoxel = static_cast <unsigned int> (std::ceil(minVolumeArg.getValue() / spacingTot));
74 
75  // Remove too small reference objects
76  RelabelComponentFilterType::Pointer relabelRefFilter = RelabelComponentFilterType::New();
77  relabelRefFilter->SetInput(refCCFilter->GetOutput());
78  relabelRefFilter->SetMinimumObjectSize(minSizeInVoxel);
79  relabelRefFilter->SetNumberOfWorkUnits(nbpArg.getValue());
80  relabelRefFilter->Update();
81 
82  // Reference segmentation is now labeled per connected objects
83  refSegmentation = relabelRefFilter->GetOutput();
84  refSegmentation->DisconnectPipeline();
85 
86  CCFilterType::Pointer testCCFilter = CCFilterType::New();
87  testCCFilter->SetInput(testSegmentation);
88  testCCFilter->SetFullyConnected(fullConnectArg.isSet());
89  testCCFilter->SetNumberOfWorkUnits(nbpArg.getValue());
90  testCCFilter->Update();
91 
92  // Remove too small test objects
93  RelabelComponentFilterType::Pointer relabelTestFilter = RelabelComponentFilterType::New();
94  relabelTestFilter->SetInput(testCCFilter->GetOutput());
95  relabelTestFilter->SetMinimumObjectSize(minSizeInVoxel);
96  relabelTestFilter->SetNumberOfWorkUnits(nbpArg.getValue());
97  relabelTestFilter->Update();
98 
99  // Test segmentation is now labeled per connected objects
100  testSegmentation = relabelTestFilter->GetOutput();
101  testSegmentation->DisconnectPipeline();
102 
103  ImageIteratorType testItr(testSegmentation, testSegmentation->GetLargestPossibleRegion());
104 
105  unsigned short maxTestLabel = 0;
106  while (!testItr.IsAtEnd())
107  {
108  if (testItr.Get() > maxTestLabel)
109  maxTestLabel = testItr.Get();
110 
111  ++testItr;
112  }
113 
114  ++maxTestLabel;
115 
116  ImageIteratorType refItr(refSegmentation, refSegmentation->GetLargestPossibleRegion());
117  std::vector <unsigned int> labelsOverlap(maxTestLabel,0);
118  std::vector <unsigned int> labelsNonOverlapping(maxTestLabel,0);
119  std::vector <unsigned int> labelsSizes(maxTestLabel,0);
120 
121  ImageType::Pointer subImage = ImageType::New();
122  subImage->Initialize();
123  subImage->SetRegions(testSegmentation->GetLargestPossibleRegion());
124  subImage->SetSpacing (testSegmentation->GetSpacing());
125  subImage->SetOrigin (testSegmentation->GetOrigin());
126  subImage->SetDirection (testSegmentation->GetDirection());
127  subImage->Allocate();
128  ImageIteratorType subItr(subImage,testSegmentation->GetLargestPossibleRegion());
129 
130  testItr.GoToBegin();
131  while (!refItr.IsAtEnd())
132  {
133  if (refItr.Get() != 0)
134  labelsOverlap[testItr.Get()]++;
135  else
136  labelsNonOverlapping[testItr.Get()]++;
137 
138  labelsSizes[testItr.Get()]++;
139 
140  ++refItr;
141  ++testItr;
142  }
143 
144  std::cout << "Processing " << maxTestLabel << " lesions in second timepoint..." << std::endl;
145  for (unsigned int i = 1;i < maxTestLabel;++i)
146  {
147  testItr.GoToBegin();
148  double ratioNonOverlapOverlap = static_cast <double> (labelsNonOverlapping[i]) / labelsOverlap[i];
149  // Shrinking or not enough change -> label = maxTestLabel + 1
150  if ((labelsNonOverlapping[i] <= minSizeInVoxel) || (ratioNonOverlapOverlap <= betaArg.getValue()))
151  {
152  while (!testItr.IsAtEnd())
153  {
154  if (testItr.Get() == i)
155  testItr.Set(maxTestLabel + 1);
156 
157  ++testItr;
158  }
159 
160  continue;
161  }
162 
163  // New lesion -> put it all as new -> label = maxTestLabel + 2
164  double ratioOverlapSizes = static_cast <double> (labelsOverlap[i]) / labelsSizes[i];
165  if (ratioOverlapSizes <= alphaArg.getValue())
166  {
167  while (!testItr.IsAtEnd())
168  {
169  if (testItr.Get() == i)
170  testItr.Set(maxTestLabel + 2);
171 
172  ++testItr;
173  }
174 
175  continue;
176  }
177 
178  // Test for growing lesion too large
179  if ((ratioNonOverlapOverlap > gammaArg.getValue()) && (labelsNonOverlapping[i] * spacingTot > gammaAbsoluteArg.getValue()))
180  {
181  // New lesion -> label = maxTestLabel + 2
182  while (!testItr.IsAtEnd())
183  {
184  if (testItr.Get() == i)
185  testItr.Set(maxTestLabel + 2);
186 
187  ++testItr;
188  }
189 
190  continue;
191  }
192 
193  // We are looking at a growing lesion -> label = maxTestLabel + 3
194  subImage->FillBuffer(0);
195  refItr.GoToBegin();
196  subItr.GoToBegin();
197  while (!testItr.IsAtEnd())
198  {
199  if ((testItr.Get() == i)&&(refItr.Get() == 0))
200  subItr.Set(1);
201 
202  ++testItr;
203  ++refItr;
204  ++subItr;
205  }
206 
207  CCFilterType::Pointer tmpCCFilter = CCFilterType::New();
208  tmpCCFilter->SetInput(subImage);
209  tmpCCFilter->SetFullyConnected(fullConnectArg.isSet());
210  tmpCCFilter->SetNumberOfWorkUnits(nbpArg.getValue());
211  tmpCCFilter->Update();
212 
213  // Remove too small test objects
214  RelabelComponentFilterType::Pointer relabelTmpCCFilter = RelabelComponentFilterType::New();
215  relabelTmpCCFilter->SetInput(tmpCCFilter->GetOutput());
216  relabelTmpCCFilter->SetMinimumObjectSize(0);
217  relabelTmpCCFilter->SetNumberOfWorkUnits(nbpArg.getValue());
218  relabelTmpCCFilter->Update();
219 
220  ImageIteratorType subCCItr(relabelTmpCCFilter->GetOutput(),testSegmentation->GetLargestPossibleRegion());
221  std::vector <unsigned int> numVoxelsCCSub(1);
222  while (!subCCItr.IsAtEnd())
223  {
224  unsigned int currentSize = numVoxelsCCSub.size();
225  unsigned int value = subCCItr.Get();
226 
227  if (value >= currentSize)
228  {
229  numVoxelsCCSub.resize(value + 1);
230  for (unsigned int j = currentSize;j <= value;++j)
231  numVoxelsCCSub[j] = 0;
232  }
233 
234  numVoxelsCCSub[value]++;
235  ++subCCItr;
236  }
237 
238  unsigned int numSubLabels = numVoxelsCCSub.size() - 1;
239  double ratioOverlap = static_cast <double> (numVoxelsCCSub[numSubLabels]) / labelsOverlap[i];
240  bool okGrowing = (ratioOverlap > betaArg.getValue());
241 
242  refItr.GoToBegin();
243  testItr.GoToBegin();
244  if (okGrowing)
245  {
246  while (!refItr.IsAtEnd())
247  {
248  if (testItr.Get() == i)
249  {
250  if (refItr.Get() == 0)
251  testItr.Set(maxTestLabel + 3);
252  else
253  testItr.Set(maxTestLabel + 1);
254  }
255 
256  ++refItr;
257  ++testItr;
258  }
259  }
260  else
261  {
262  // Non growing lesion, setting to
263  while (!testItr.IsAtEnd())
264  {
265  if (testItr.Get() == i)
266  testItr.Set(maxTestLabel + 1);
267 
268  ++testItr;
269  }
270  }
271  }
272 
273  testItr.GoToBegin();
274  while (!testItr.IsAtEnd())
275  {
276  unsigned int value = testItr.Get();
277  if (value > 0)
278  testItr.Set(value - maxTestLabel);
279 
280  ++testItr;
281  }
282 
283  std::cout << "Writing output to " << outArg.getValue() << std::endl;
284 
285  anima::writeImage <ImageType> (outArg.getValue(),testSegmentation);
286  return EXIT_SUCCESS;
287 }
288 
int main(int argc, char **argv)