Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
LocalDirectionalFiberPlausibility.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("Local Directional Fiber Plausibility");
46  parser.setCategory("Fiber Tracking and Processing Methods");
47  parser.setDescription("Calculate angular error of a tractogram with respect to the input reference directions.");
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("reference", "r", mitkCommandLineParser::StringList, "Reference images:", "reference direction images", us::Any(), false);
53  parser.addArgument("out", "o", mitkCommandLineParser::OutputDirectory, "Output:", "output root", us::Any(), false);
54  parser.addArgument("mask", "m", mitkCommandLineParser::StringList, "Masks:", "mask images");
55  parser.addArgument("athresh", "a", mitkCommandLineParser::Float, "Angular threshold:", "angular threshold in degrees. closer fiber directions are regarded as one direction and clustered together.", 25, true);
56  parser.addArgument("sthresh", "s", mitkCommandLineParser::Float, "Size threshold:", "Relative peak size threshold per voxel.", 0.0, true);
57  parser.addArgument("maxdirs", "md", mitkCommandLineParser::Int, "Max. Clusters:", "Maximum number of fiber clusters.", 0, true);
58  parser.addArgument("verbose", "v", mitkCommandLineParser::Bool, "Verbose:", "output optional and intermediate calculation results");
59  parser.addArgument("ignore", "n", mitkCommandLineParser::Bool, "Ignore:", "don't increase error for missing or too many directions");
60  parser.addArgument("empty", "e", mitkCommandLineParser::Bool, "Empty Voxels:", "don't increase error for empty voxels");
61  parser.addArgument("fileID", "id", mitkCommandLineParser::String, "ID:", "optional ID field");
62 
63  map<string, us::Any> parsedArgs = parser.parseArguments(argc, argv);
64  if (parsedArgs.size()==0)
65  return EXIT_FAILURE;
66 
69  if (parsedArgs.count("mask"))
70  maskImages = us::any_cast<mitkCommandLineParser::StringContainerType>(parsedArgs["mask"]);
71 
72  string fibFile = us::any_cast<string>(parsedArgs["input"]);
73 
74  float angularThreshold = 25;
75  if (parsedArgs.count("athresh"))
76  angularThreshold = us::any_cast<float>(parsedArgs["athresh"]);
77 
78  float sizeThreshold = 0;
79  if (parsedArgs.count("sthresh"))
80  sizeThreshold = us::any_cast<float>(parsedArgs["sthresh"]);
81 
82  int maxDirs = 0;
83  if (parsedArgs.count("maxdirs"))
84  maxDirs = us::any_cast<int>(parsedArgs["maxdirs"]);
85 
86  string outRoot = us::any_cast<string>(parsedArgs["out"]);
87 
88  bool verbose = false;
89  if (parsedArgs.count("verbose"))
90  verbose = us::any_cast<bool>(parsedArgs["verbose"]);
91 
92  bool ignoreMissing = false;
93  if (parsedArgs.count("ignore"))
94  ignoreMissing = us::any_cast<bool>(parsedArgs["ignore"]);
95 
96  bool ignoreEmpty = false;
97  if (parsedArgs.count("empty"))
98  ignoreEmpty = us::any_cast<bool>(parsedArgs["empty"]);
99 
100  string fileID = "";
101  if (parsedArgs.count("fileID"))
102  fileID = us::any_cast<string>(parsedArgs["fileID"]);
103 
104 
105  try
106  {
107  typedef itk::Image<unsigned char, 3> ItkUcharImgType;
108  typedef itk::Image< itk::Vector< float, 3>, 3 > ItkDirectionImage3DType;
109  typedef itk::VectorContainer< unsigned int, ItkDirectionImage3DType::Pointer > ItkDirectionImageContainerType;
110  typedef itk::EvaluateDirectionImagesFilter< float > EvaluationFilterType;
111 
112  // load fiber bundle
113  mitk::FiberBundle::Pointer inputTractogram = dynamic_cast<mitk::FiberBundle*>(mitk::IOUtil::LoadDataNode(fibFile)->GetData());
114 
115  // load reference directions
117  for (unsigned int i=0; i<referenceImages.size(); i++)
118  {
119  try
120  {
121  mitk::Image::Pointer img = dynamic_cast<mitk::Image*>(mitk::IOUtil::LoadDataNode(referenceImages.at(i))->GetData());
124  caster->SetInput(img);
125  caster->Update();
126  ItkDirectionImage3DType::Pointer itkImg = caster->GetOutput();
127  referenceImageContainer->InsertElement(referenceImageContainer->Size(),itkImg);
128  }
129  catch(...){ std::cout << "could not load: " << referenceImages.at(i); }
130  }
131 
133  ItkDirectionImage3DType::Pointer dirImg = referenceImageContainer->GetElement(0);
134  itkMaskImage->SetSpacing( dirImg->GetSpacing() );
135  itkMaskImage->SetOrigin( dirImg->GetOrigin() );
136  itkMaskImage->SetDirection( dirImg->GetDirection() );
137  itkMaskImage->SetLargestPossibleRegion( dirImg->GetLargestPossibleRegion() );
138  itkMaskImage->SetBufferedRegion( dirImg->GetLargestPossibleRegion() );
139  itkMaskImage->SetRequestedRegion( dirImg->GetLargestPossibleRegion() );
140  itkMaskImage->Allocate();
141  itkMaskImage->FillBuffer(1);
142 
143  // extract directions from fiber bundle
145  fOdfFilter->SetFiberBundle(inputTractogram);
146  fOdfFilter->SetMaskImage(itkMaskImage);
147  fOdfFilter->SetAngularThreshold(cos(angularThreshold*M_PI/180));
148  fOdfFilter->SetNormalizeVectors(true);
149  fOdfFilter->SetUseWorkingCopy(false);
150  fOdfFilter->SetSizeThreshold(sizeThreshold);
151  fOdfFilter->SetMaxNumDirections(maxDirs);
152  fOdfFilter->Update();
153  ItkDirectionImageContainerType::Pointer directionImageContainer = fOdfFilter->GetDirectionImageContainer();
154 
155  if (verbose)
156  {
157  // write vector field
158  mitk::FiberBundle::Pointer directions = fOdfFilter->GetOutputFiberBundle();
159 
160  string outfilename = outRoot;
161  outfilename.append("_VECTOR_FIELD.fib");
162 
163  mitk::IOUtil::SaveBaseData(directions.GetPointer(), outfilename );
164 
165  // write direction images
166  for (unsigned int i=0; i<directionImageContainer->Size(); i++)
167  {
168  itk::TractsToVectorImageFilter<float>::ItkDirectionImageType::Pointer itkImg = directionImageContainer->GetElement(i);
169  typedef itk::ImageFileWriter< itk::TractsToVectorImageFilter<float>::ItkDirectionImageType > WriterType;
171 
172  string outfilename = outRoot;
173  outfilename.append("_DIRECTION_");
174  outfilename.append(boost::lexical_cast<string>(i));
175  outfilename.append(".nrrd");
176 
177  writer->SetFileName(outfilename.c_str());
178  writer->SetInput(itkImg);
179  writer->Update();
180  }
181 
182  // write num direction image
183  {
184  ItkUcharImgType::Pointer numDirImage = fOdfFilter->GetNumDirectionsImage();
185  typedef itk::ImageFileWriter< ItkUcharImgType > WriterType;
187 
188  string outfilename = outRoot;
189  outfilename.append("_NUM_DIRECTIONS.nrrd");
190 
191  writer->SetFileName(outfilename.c_str());
192  writer->SetInput(numDirImage);
193  writer->Update();
194  }
195  }
196 
197  string logFile = outRoot;
198  logFile.append("_ANGULAR_ERROR.csv");
199  ofstream file;
200  file.open (logFile.c_str());
201 
202  if (maskImages.size()>0)
203  {
204  for (unsigned int i=0; i<maskImages.size(); i++)
205  {
206  mitk::Image::Pointer mitkMaskImage = dynamic_cast<mitk::Image*>(mitk::IOUtil::LoadDataNode(maskImages.at(i))->GetData());
207  mitk::CastToItkImage(mitkMaskImage, itkMaskImage);
208 
209  // evaluate directions
211  evaluationFilter->SetImageSet(directionImageContainer);
212  evaluationFilter->SetReferenceImageSet(referenceImageContainer);
213  evaluationFilter->SetMaskImage(itkMaskImage);
214  evaluationFilter->SetIgnoreMissingDirections(ignoreMissing);
215  evaluationFilter->SetIgnoreEmptyVoxels(ignoreEmpty);
216  evaluationFilter->Update();
217 
218  if (verbose)
219  {
220  EvaluationFilterType::OutputImageType::Pointer angularErrorImage = evaluationFilter->GetOutput(0);
221  typedef itk::ImageFileWriter< EvaluationFilterType::OutputImageType > WriterType;
223 
224  string outfilename = outRoot;
225  outfilename.append("_ERROR_IMAGE.nrrd");
226 
227  writer->SetFileName(outfilename.c_str());
228  writer->SetInput(angularErrorImage);
229  writer->Update();
230  }
231 
232  string maskFileName = itksys::SystemTools::GetFilenameWithoutExtension(maskImages.at(i));
233  unsigned found = maskFileName.find_last_of("_");
234 
235  string sens = itksys::SystemTools::GetFilenameWithoutLastExtension(fibFile);
236  if (!fileID.empty())
237  sens = fileID;
238  sens.append(",");
239 
240  sens.append(maskFileName.substr(found+1));
241  sens.append(",");
242 
243  sens.append(boost::lexical_cast<string>(evaluationFilter->GetMeanAngularError()));
244  sens.append(",");
245 
246  sens.append(boost::lexical_cast<string>(evaluationFilter->GetMedianAngularError()));
247  sens.append(",");
248 
249  sens.append(boost::lexical_cast<string>(evaluationFilter->GetMaxAngularError()));
250  sens.append(",");
251 
252  sens.append(boost::lexical_cast<string>(evaluationFilter->GetMinAngularError()));
253  sens.append(",");
254 
255  sens.append(boost::lexical_cast<string>(std::sqrt(evaluationFilter->GetVarAngularError())));
256  sens.append(";\n");
257  file << sens;
258  }
259  }
260  else
261  {
262  // evaluate directions
264  evaluationFilter->SetImageSet(directionImageContainer);
265  evaluationFilter->SetReferenceImageSet(referenceImageContainer);
266  evaluationFilter->SetMaskImage(itkMaskImage);
267  evaluationFilter->SetIgnoreMissingDirections(ignoreMissing);
268  evaluationFilter->SetIgnoreEmptyVoxels(ignoreEmpty);
269  evaluationFilter->Update();
270 
271  if (verbose)
272  {
273  EvaluationFilterType::OutputImageType::Pointer angularErrorImage = evaluationFilter->GetOutput(0);
274  typedef itk::ImageFileWriter< EvaluationFilterType::OutputImageType > WriterType;
276 
277  string outfilename = outRoot;
278  outfilename.append("_ERROR_IMAGE.nrrd");
279 
280  writer->SetFileName(outfilename.c_str());
281  writer->SetInput(angularErrorImage);
282  writer->Update();
283  }
284 
285  string sens = itksys::SystemTools::GetFilenameWithoutLastExtension(fibFile);
286  if (!fileID.empty())
287  sens = fileID;
288  sens.append(",");
289 
290  sens.append(boost::lexical_cast<string>(evaluationFilter->GetMeanAngularError()));
291  sens.append(",");
292 
293  sens.append(boost::lexical_cast<string>(evaluationFilter->GetMedianAngularError()));
294  sens.append(",");
295 
296  sens.append(boost::lexical_cast<string>(evaluationFilter->GetMaxAngularError()));
297  sens.append(",");
298 
299  sens.append(boost::lexical_cast<string>(evaluationFilter->GetMinAngularError()));
300  sens.append(",");
301 
302  sens.append(boost::lexical_cast<string>(std::sqrt(evaluationFilter->GetVarAngularError())));
303  sens.append(";\n");
304  file << sens;
305  }
306  file.close();
307  }
308  catch (itk::ExceptionObject e)
309  {
310  std::cout << e;
311  return EXIT_FAILURE;
312  }
313  catch (std::exception e)
314  {
315  std::cout << e.what();
316  return EXIT_FAILURE;
317  }
318  catch (...)
319  {
320  std::cout << "ERROR!?!";
321  return EXIT_FAILURE;
322  }
323  return EXIT_SUCCESS;
324 }
int main(int argc, char *argv[])
Calculate angular error of a tractogram with respect to the input reference directions.
itk::SmartPointer< Self > Pointer
void setContributor(std::string contributor)
STL namespace.
ValueType * any_cast(Any *operand)
Definition: usAny.h:377
std::map< std::string, us::Any > parseArguments(const StringContainerType &arguments, bool *ok=nullptr)
T::Pointer GetData(const std::string &name)
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)
Evaluates the voxel-wise angular error between two sets of directions.
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;.
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)
void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer< ItkOutputImageType > &itkOutputImage)
Cast an mitk::Image to an itk::Image with a specific type.
std::vector< std::string > StringContainerType
void setTitle(std::string title)
void setDescription(std::string description)
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.