Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
TractometerMetrics.cpp
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
17 #include <mitkBaseData.h>
18 #include <mitkImageCast.h>
19 #include <mitkImageToItk.h>
21 #include <metaCommand.h>
22 #include "mitkCommandLineParser.h"
24 #include <usAny.h>
25 #include <itkImageFileWriter.h>
26 #include <mitkIOUtil.h>
27 #include <boost/lexical_cast.hpp>
28 #include <iostream>
29 #include <fstream>
30 #include <itksys/SystemTools.hxx>
31 #include <mitkCoreObjectFactory.h>
32 
33 #define _USE_MATH_DEFINES
34 #include <math.h>
35 
36 using namespace std;
37 
41 int main(int argc, char* argv[])
42 {
43  mitkCommandLineParser parser;
44 
45  parser.setTitle("Tractometer Metrics");
46  parser.setCategory("Fiber Tracking and Processing Methods");
47  parser.setDescription("Calculates the Tractometer evaluation metrics for tractograms (http://www.tractometer.org/)");
48  parser.setContributor("MBI");
49 
50  parser.setArgumentPrefix("--", "-");
51  parser.addArgument("input", "i", mitkCommandLineParser::InputFile, "Input:", "input tractogram (.fib, vtk ascii file format)", us::Any(), false);
52  parser.addArgument("out", "o", mitkCommandLineParser::OutputDirectory, "Output:", "output root", us::Any(), false);
53  parser.addArgument("labels", "l", mitkCommandLineParser::StringList, "Label pairs:", "label pairs", false);
54  parser.addArgument("labelimage", "li", mitkCommandLineParser::String, "Label image:", "label image", false);
55  parser.addArgument("verbose", "v", mitkCommandLineParser::Bool, "Verbose:", "output valid, invalid and no connections as fiber bundles");
56  parser.addArgument("fileID", "id", mitkCommandLineParser::String, "ID:", "optional ID field");
57 
58  map<string, us::Any> parsedArgs = parser.parseArguments(argc, argv);
59  if (parsedArgs.size()==0)
60  return EXIT_FAILURE;
61 
63 
64  string fibFile = us::any_cast<string>(parsedArgs["input"]);
65  string labelImageFile = us::any_cast<string>(parsedArgs["labelimage"]);
66 
67  string outRoot = us::any_cast<string>(parsedArgs["out"]);
68 
69  string fileID = "";
70  if (parsedArgs.count("fileID"))
71  fileID = us::any_cast<string>(parsedArgs["fileID"]);
72 
73  bool verbose = false;
74  if (parsedArgs.count("verbose"))
75  verbose = us::any_cast<bool>(parsedArgs["verbose"]);
76 
77  try
78  {
79  typedef itk::Image<short, 3> ItkShortImgType;
80  typedef itk::Image<unsigned char, 3> ItkUcharImgType;
81 
82  // load fiber bundle
83  mitk::FiberBundle::Pointer inputTractogram = dynamic_cast<mitk::FiberBundle*>(mitk::IOUtil::LoadDataNode(fibFile)->GetData());
84 
85  mitk::Image::Pointer img = dynamic_cast<mitk::Image*>(mitk::IOUtil::LoadDataNode(labelImageFile)->GetData());
86  typedef mitk::ImageToItk< ItkShortImgType > CasterType;
88  caster->SetInput(img);
89  caster->Update();
90  ItkShortImgType::Pointer labelImage = caster->GetOutput();
91 
92  string path = itksys::SystemTools::GetFilenamePath(labelImageFile);
93 
94  std::vector< bool > detected;
95  std::vector< std::pair< int, int > > labelsvector;
96  std::vector< ItkUcharImgType::Pointer > bundleMasks;
97  std::vector< ItkUcharImgType::Pointer > bundleMasksCoverage;
98  short max = 0;
99  for (unsigned int i=0; i<labelpairs.size()-1; i+=2)
100  {
101  std::pair< short, short > l;
102  l.first = boost::lexical_cast<short>(labelpairs.at(i));
103  l.second = boost::lexical_cast<short>(labelpairs.at(i+1));
104  std::cout << labelpairs.at(i);
105  std::cout << labelpairs.at(i+1);
106  if (l.first>max)
107  max=l.first;
108  if (l.second>max)
109  max=l.second;
110 
111  labelsvector.push_back(l);
112  detected.push_back(false);
113 
114  {
115  mitk::Image::Pointer img = dynamic_cast<mitk::Image*>(mitk::IOUtil::LoadDataNode(path+"/Bundle"+boost::lexical_cast<string>(labelsvector.size())+"_MASK.nrrd")->GetData());
116  typedef mitk::ImageToItk< ItkUcharImgType > CasterType;
118  caster->SetInput(img);
119  caster->Update();
120  ItkUcharImgType::Pointer bundle = caster->GetOutput();
121  bundleMasks.push_back(bundle);
122  }
123 
124  {
125  mitk::Image::Pointer img = dynamic_cast<mitk::Image*>(mitk::IOUtil::LoadDataNode(path+"/Bundle"+boost::lexical_cast<string>(labelsvector.size())+"_MASK_COVERAGE.nrrd")->GetData());
126  typedef mitk::ImageToItk< ItkUcharImgType > CasterType;
128  caster->SetInput(img);
129  caster->Update();
130  ItkUcharImgType::Pointer bundle = caster->GetOutput();
131  bundleMasksCoverage.push_back(bundle);
132  }
133  }
134  vnl_matrix< unsigned char > matrix; matrix.set_size(max, max); matrix.fill(0);
135 
136  vtkSmartPointer<vtkPolyData> polyData = inputTractogram->GetFiberPolyData();
137 
138  int validConnections = 0;
139  int noConnection = 0;
140  int validBundles = 0;
141  int invalidBundles = 0;
142  int invalidConnections = 0;
143 
145  coverage->SetSpacing(labelImage->GetSpacing());
146  coverage->SetOrigin(labelImage->GetOrigin());
147  coverage->SetDirection(labelImage->GetDirection());
148  coverage->SetLargestPossibleRegion(labelImage->GetLargestPossibleRegion());
149  coverage->SetBufferedRegion( labelImage->GetLargestPossibleRegion() );
150  coverage->SetRequestedRegion( labelImage->GetLargestPossibleRegion() );
151  coverage->Allocate();
152  coverage->FillBuffer(0);
153 
154  vtkSmartPointer<vtkPoints> noConnPoints = vtkSmartPointer<vtkPoints>::New();
155  vtkSmartPointer<vtkCellArray> noConnCells = vtkSmartPointer<vtkCellArray>::New();
156 
157  vtkSmartPointer<vtkPoints> invalidPoints = vtkSmartPointer<vtkPoints>::New();
158  vtkSmartPointer<vtkCellArray> invalidCells = vtkSmartPointer<vtkCellArray>::New();
159 
160  vtkSmartPointer<vtkPoints> validPoints = vtkSmartPointer<vtkPoints>::New();
161  vtkSmartPointer<vtkCellArray> validCells = vtkSmartPointer<vtkCellArray>::New();
162 
163  boost::progress_display disp(inputTractogram->GetNumFibers());
164  for (int i=0; i<inputTractogram->GetNumFibers(); i++)
165  {
166  ++disp;
167 
168  vtkCell* cell = polyData->GetCell(i);
169  int numPoints = cell->GetNumberOfPoints();
170  vtkPoints* points = cell->GetPoints();
171 
172 
173  if (numPoints>1)
174  {
175  double* start = points->GetPoint(0);
176  itk::Point<float, 3> itkStart;
177  itkStart[0] = start[0]; itkStart[1] = start[1]; itkStart[2] = start[2];
178  itk::Index<3> idxStart;
179  labelImage->TransformPhysicalPointToIndex(itkStart, idxStart);
180 
181  double* end = points->GetPoint(numPoints-1);
182  itk::Point<float, 3> itkEnd;
183  itkEnd[0] = end[0]; itkEnd[1] = end[1]; itkEnd[2] = end[2];
184  itk::Index<3> idxEnd;
185  labelImage->TransformPhysicalPointToIndex(itkEnd, idxEnd);
186 
187 
188  if ( labelImage->GetPixel(idxStart)==0 || labelImage->GetPixel(idxEnd)==0 )
189  {
190  noConnection++;
191 
192  if (verbose)
193  {
194  vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New();
195  for (int j=0; j<numPoints; j++)
196  {
197  double* p = points->GetPoint(j);
198  vtkIdType id = noConnPoints->InsertNextPoint(p);
199  container->GetPointIds()->InsertNextId(id);
200  }
201  noConnCells->InsertNextCell(container);
202  }
203  }
204  else
205  {
206  bool invalid = true;
207  for (unsigned int i=0; i<labelsvector.size(); i++)
208  {
209  bool outside = false;
210  ItkUcharImgType::Pointer bundle = bundleMasks.at(i);
211  std::pair< short, short > l = labelsvector.at(i);
212  if ( (labelImage->GetPixel(idxStart)==l.first && labelImage->GetPixel(idxEnd)==l.second) ||
213  (labelImage->GetPixel(idxStart)==l.second && labelImage->GetPixel(idxEnd)==l.first) )
214  {
215  for (int j=0; j<numPoints; j++)
216  {
217  double* p = points->GetPoint(j);
218 
219  itk::Point<float, 3> itkP;
220  itkP[0] = p[0]; itkP[1] = p[1]; itkP[2] = p[2];
221  itk::Index<3> idx;
222  bundle->TransformPhysicalPointToIndex(itkP, idx);
223 
224  if ( !bundle->GetPixel(idx)>0 && bundle->GetLargestPossibleRegion().IsInside(idx) )
225  {
226  outside=true;
227  }
228  }
229 
230  if (!outside)
231  {
232  validConnections++;
233  if (detected.at(i)==false)
234  validBundles++;
235  detected.at(i) = true;
236  invalid = false;
237 
238 
239  vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New();
240  for (int j=0; j<numPoints; j++)
241  {
242  double* p = points->GetPoint(j);
243  vtkIdType id = validPoints->InsertNextPoint(p);
244  container->GetPointIds()->InsertNextId(id);
245 
246  itk::Point<float, 3> itkP;
247  itkP[0] = p[0]; itkP[1] = p[1]; itkP[2] = p[2];
248  itk::Index<3> idx;
249  coverage->TransformPhysicalPointToIndex(itkP, idx);
250  if ( coverage->GetLargestPossibleRegion().IsInside(idx) )
251  coverage->SetPixel(idx, 1);
252  }
253  validCells->InsertNextCell(container);
254  }
255  break;
256  }
257  }
258  if (invalid==true)
259  {
260  invalidConnections++;
261  int x = labelImage->GetPixel(idxStart)-1;
262  int y = labelImage->GetPixel(idxEnd)-1;
263  if (x>=0 && y>0 && x<matrix.cols() && y<matrix.rows() && (matrix[x][y]==0 || matrix[y][x]==0) )
264  {
265  invalidBundles++;
266  matrix[x][y]=1;
267  matrix[y][x]=1;
268  }
269 
270  if (verbose)
271  {
272  vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New();
273  for (int j=0; j<numPoints; j++)
274  {
275  double* p = points->GetPoint(j);
276  vtkIdType id = invalidPoints->InsertNextPoint(p);
277  container->GetPointIds()->InsertNextId(id);
278  }
279  invalidCells->InsertNextCell(container);
280  }
281  }
282  }
283  }
284  }
285 
286  if (verbose)
287  {
289  vtkSmartPointer<vtkPolyData> noConnPolyData = vtkSmartPointer<vtkPolyData>::New();
290  noConnPolyData->SetPoints(noConnPoints);
291  noConnPolyData->SetLines(noConnCells);
292  mitk::FiberBundle::Pointer noConnFib = mitk::FiberBundle::New(noConnPolyData);
293 
294  string ncfilename = outRoot;
295  ncfilename.append("_NC.fib");
296 
297  mitk::IOUtil::SaveBaseData(noConnFib.GetPointer(), ncfilename );
298 
299  vtkSmartPointer<vtkPolyData> invalidPolyData = vtkSmartPointer<vtkPolyData>::New();
300  invalidPolyData->SetPoints(invalidPoints);
301  invalidPolyData->SetLines(invalidCells);
302  mitk::FiberBundle::Pointer invalidFib = mitk::FiberBundle::New(invalidPolyData);
303 
304  string icfilename = outRoot;
305  icfilename.append("_IC.fib");
306 
307  mitk::IOUtil::SaveBaseData(invalidFib.GetPointer(), icfilename );
308 
309  vtkSmartPointer<vtkPolyData> validPolyData = vtkSmartPointer<vtkPolyData>::New();
310  validPolyData->SetPoints(validPoints);
311  validPolyData->SetLines(validCells);
312  mitk::FiberBundle::Pointer validFib = mitk::FiberBundle::New(validPolyData);
313 
314  string vcfilename = outRoot;
315  vcfilename.append("_VC.fib");
316 
317  mitk::IOUtil::SaveBaseData(validFib.GetPointer(), vcfilename );
318 
319  {
320  typedef itk::ImageFileWriter< ItkUcharImgType > WriterType;
322  writer->SetFileName(outRoot+"_ABC.nrrd");
323  writer->SetInput(coverage);
324  writer->Update();
325  }
326  }
327 
328  // calculate coverage
329  int wmVoxels = 0;
330  int coveredVoxels = 0;
331  itk::ImageRegionIterator<ItkUcharImgType> it (coverage, coverage->GetLargestPossibleRegion());
332  while(!it.IsAtEnd())
333  {
334  bool wm = false;
335  for (unsigned int i=0; i<bundleMasksCoverage.size(); i++)
336  {
337  ItkUcharImgType::Pointer bundle = bundleMasksCoverage.at(i);
338  if (bundle->GetPixel(it.GetIndex())>0)
339  {
340  wm = true;
341  wmVoxels++;
342  break;
343  }
344  }
345  if (wm && it.Get()>0)
346  coveredVoxels++;
347  ++it;
348  }
349 
350  int numFibers = inputTractogram->GetNumFibers();
351  double nc = (double)noConnection/numFibers;
352  double vc = (double)validConnections/numFibers;
353  double ic = (double)invalidConnections/numFibers;
354  if (numFibers==0)
355  {
356  nc = 0.0;
357  vc = 0.0;
358  ic = 0.0;
359  }
360  int vb = validBundles;
361  int ib = invalidBundles;
362  double abc = (double)coveredVoxels/wmVoxels;
363 
364  std::cout << "NC: " << nc;
365  std::cout << "VC: " << vc;
366  std::cout << "IC: " << ic;
367  std::cout << "VB: " << vb;
368  std::cout << "IB: " << ib;
369  std::cout << "ABC: " << abc;
370 
371  string logFile = outRoot;
372  logFile.append("_TRACTOMETER.csv");
373  ofstream file;
374  file.open (logFile.c_str());
375  {
376  string sens = itksys::SystemTools::GetFilenameWithoutLastExtension(fibFile);
377  if (!fileID.empty())
378  sens = fileID;
379  sens.append(",");
380 
381  sens.append(boost::lexical_cast<string>(nc));
382  sens.append(",");
383 
384  sens.append(boost::lexical_cast<string>(vc));
385  sens.append(",");
386 
387  sens.append(boost::lexical_cast<string>(ic));
388  sens.append(",");
389 
390  sens.append(boost::lexical_cast<string>(validBundles));
391  sens.append(",");
392 
393  sens.append(boost::lexical_cast<string>(invalidBundles));
394  sens.append(",");
395 
396  sens.append(boost::lexical_cast<string>(abc));
397  sens.append(";\n");
398  file << sens;
399  }
400  file.close();
401  }
402  catch (itk::ExceptionObject e)
403  {
404  std::cout << e;
405  return EXIT_FAILURE;
406  }
407  catch (std::exception e)
408  {
409  std::cout << e.what();
410  return EXIT_FAILURE;
411  }
412  catch (...)
413  {
414  std::cout << "ERROR!?!";
415  return EXIT_FAILURE;
416  }
417  return EXIT_SUCCESS;
418 }
itk::SmartPointer< Self > Pointer
void setContributor(std::string contributor)
STL namespace.
ValueType * any_cast(Any *operand)
Definition: usAny.h:377
int main(int argc, char *argv[])
Calculates the Tractometer evaluation metrics for tractograms (http://www.tractometer.org/)
std::map< std::string, us::Any > parseArguments(const StringContainerType &arguments, bool *ok=nullptr)
static bool SaveBaseData(mitk::BaseData *data, const std::string &path)
SaveBaseData Convenience method to save arbitrary baseData.
Definition: mitkIOUtil.cpp:888
void addArgument(const std::string &longarg, const std::string &shortarg, Type type, const std::string &argLabel, const std::string &argHelp=std::string(), const us::Any &defaultValue=us::Any(), bool optional=true, bool ignoreRest=false, bool deprecated=false)
Image class for storing images.
Definition: mitkImage.h:76
static std::ofstream * logFile
Definition: mitkLog.cpp:30
Definition: usAny.h:163
Base Class for Fiber Bundles;.
static T max(T x, T y)
Definition: svm.cpp:70
void setCategory(std::string category)
static mitk::DataNode::Pointer LoadDataNode(const std::string &path)
LoadDataNode Method to load an arbitrary DataNode.
Definition: mitkIOUtil.cpp:586
void setArgumentPrefix(const std::string &longPrefix, const std::string &shortPrefix)
std::vector< std::string > StringContainerType
void setTitle(std::string title)
void setDescription(std::string description)
std::list< mitk::FileWriterWithInformation::Pointer > FileWriterList
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.