Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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.