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
Registration.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 
18 // CTK
19 #include "mitkCommandLineParser.h"
20 
21 #include <mitkIOUtil.h>
23 #include <mitkImage.h>
24 #include <mitkImageCast.h>
25 #include <mitkITKImageImport.h>
26 #include <mitkImageTimeSelector.h>
27 #include <itkImageFileWriter.h>
28 // ITK
29 #include <itksys/SystemTools.hxx>
30 #include <itkDirectory.h>
31 #include "itkLinearInterpolateImageFunction.h"
32 #include "itkWindowedSincInterpolateImageFunction.h"
33 #include "itkIdentityTransform.h"
34 #include "itkResampleImageFilter.h"
35 #include "itkNrrdImageIO.h"
36 
37 using namespace std;
38 
39 typedef std::vector<std::string> FileListType;
40 typedef itk::Image<double, 3> InputImageType;
41 
42 static mitk::Image::Pointer ExtractFirstTS(mitk::Image* image, std::string fileType)
43 {
44  if (fileType == ".dwi")
45  return image;
47  selector->SetInput(image);
48  selector->SetTimeNr(0);
49  selector->UpdateLargestPossibleRegion();
50  mitk::Image::Pointer img =selector->GetOutput()->Clone();
51  return img;
52 }
53 
54 
55 static std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems)
56 {
57  std::stringstream ss(s);
58  std::string item;
59  while (std::getline(ss, item, delim))
60  {
61  elems.push_back(item);
62  }
63  return elems;
64 }
65 
66 static std::vector<std::string> split(const std::string &s, char delim)
67 {
68  std::vector < std::string > elems;
69  return split(s, delim, elems);
70 }
71 
73 static FileListType CreateFileList(std::string folder , std::string postfix)
74 {
76  FileListType fileList;
77 
78  if( dir->Load(folder.c_str() ) )
79  {
80  int n = dir->GetNumberOfFiles();
81  for(int r=0;r<n;r++)
82  {
83  std::string filename = dir->GetFile( r );
84  if (filename == "." || filename == "..")
85  continue;
86  filename = folder + filename;
87  if (!itksys::SystemTools::FileExists( filename.c_str()))
88  continue;
89  if (filename.length() <= postfix.length() )
90  continue;
91  if (filename.substr(filename.length() -postfix.length() ) == postfix)
92  fileList.push_back(filename);
93  }
94  }
95  return fileList;
96 }
97 
98 static std::string GetSavePath(std::string outputFolder, std::string fileName)
99 {
100  std::string fileType = itksys::SystemTools::GetFilenameExtension(fileName);
101  std::string fileStem = itksys::SystemTools::GetFilenameWithoutExtension(fileName);
102 
103  std::string savePathAndFileName = outputFolder +fileStem + fileType;
104 
105  return savePathAndFileName;
106 }
107 
108 
109 static mitk::Image::Pointer ResampleBySpacing(mitk::Image *input, float *spacing, bool useLinInt = false)
110 {
112  CastToItkImage(input,itkImage);
113 
118  // Identity transform.
119  // We don't want any transform on our image except rescaling which is not
120  // specified by a transform but by the input/output spacing as we will see
121  // later.
122  // So no transform will be specified.
123  typedef itk::IdentityTransform<double, 3> T_Transform;
124 
125  // The resampler type itself.
126  typedef itk::ResampleImageFilter<InputImageType, InputImageType> T_ResampleFilter;
127 
128  // Prepare the resampler.
129  // Instantiate the transform and specify it should be the id transform.
130  T_Transform::Pointer _pTransform = T_Transform::New();
131  _pTransform->SetIdentity();
132 
133  // Instantiate the resampler. Wire in the transform and the interpolator.
135  _pResizeFilter->SetTransform(_pTransform);
136 
137  // Set the output origin.
138  _pResizeFilter->SetOutputOrigin(itkImage->GetOrigin());
139 
140  // Compute the size of the output.
141  // The size (# of pixels) in the output is recomputed using
142  // the ratio of the input and output sizes.
143  InputImageType::SpacingType inputSpacing = itkImage->GetSpacing();
144  InputImageType::SpacingType outputSpacing;
145  const InputImageType::RegionType& inputSize = itkImage->GetLargestPossibleRegion();
146 
147  InputImageType::SizeType outputSize;
148  typedef InputImageType::SizeType::SizeValueType SizeValueType;
149 
150  // Set the output spacing.
151  outputSpacing[0] = spacing[0];
152  outputSpacing[1] = spacing[1];
153  outputSpacing[2] = spacing[2];
154 
155  outputSize[0] = static_cast<SizeValueType>(inputSize.GetSize()[0] * inputSpacing[0] / outputSpacing[0] + .5);
156  outputSize[1] = static_cast<SizeValueType>(inputSize.GetSize()[1] * inputSpacing[1] / outputSpacing[1] + .5);
157  outputSize[2] = static_cast<SizeValueType>(inputSize.GetSize()[2] * inputSpacing[2] / outputSpacing[2] + .5);
158 
159  _pResizeFilter->SetOutputSpacing(outputSpacing);
160  _pResizeFilter->SetSize(outputSize);
161 
162 
163  typedef itk::LinearInterpolateImageFunction< InputImageType > LinearInterpolatorType;
165 
166  typedef itk::Function::WelchWindowFunction<4> WelchWindowFunction;
167  typedef itk::WindowedSincInterpolateImageFunction< InputImageType, 4,WelchWindowFunction> WindowedSincInterpolatorType;
169 
170  if (useLinInt)
171  _pResizeFilter->SetInterpolator(lin_interpolator);
172  else
173  _pResizeFilter->SetInterpolator(sinc_interpolator);
174 
175  // Specify the input.
176  _pResizeFilter->SetInput(itkImage);
177  _pResizeFilter->Update();
178 
180  image->InitializeByItk(_pResizeFilter->GetOutput());
181  mitk::GrabItkImageMemory( _pResizeFilter->GetOutput(), image);
182 
183  return image;
184 }
185 
186 
187 
189 static FileListType CreateDerivedFileList(std::string baseFN, std::string baseSuffix, std::vector<std::string> derivedPatterns)
190 {
191  FileListType files;
192  for (unsigned int i=0; i < derivedPatterns.size(); i++)
193  {
194  std::string derResourceSuffix = derivedPatterns.at(i);
195  std::string derivedResourceFilename = baseFN.substr(0,baseFN.length() -baseSuffix.length()) + derResourceSuffix;
196  MITK_INFO <<" Looking for file: " << derivedResourceFilename;
197 
198  if (!itksys::SystemTools::FileExists(derivedResourceFilename.c_str()))
199  {
200  MITK_INFO << "CreateDerivedFileList: File does not exit. Skipping entry.";
201  continue;
202  }
203  files.push_back(derivedResourceFilename);
204  }
205  return files;
206 }
207 
209 static void SaveImage(std::string fileName, mitk::Image* image, std::string fileType )
210 {
211  MITK_INFO << "----Save to " << fileName;
212 
213  mitk::IOUtil::Save(image, fileName);
214 }
215 
217 static void CopyResources(FileListType fileList, std::string outputPath)
218 {
219  for (unsigned int j=0; j < fileList.size(); j++)
220  {
221  std::string derivedResourceFilename = fileList.at(j);
222  std::string fileType = itksys::SystemTools::GetFilenameExtension(derivedResourceFilename);
223  std::string fileStem = itksys::SystemTools::GetFilenameWithoutExtension(derivedResourceFilename);
224  std::string savePathAndFileName = outputPath +fileStem + "." + fileType;
225  MITK_INFO << "Copy resource " << savePathAndFileName;
226  mitk::Image::Pointer resImage = ExtractFirstTS(mitk::IOUtil::LoadImage(derivedResourceFilename), fileType);
227  mitk::IOUtil::SaveImage(resImage, savePathAndFileName);
228  }
229 }
230 
231 int main( int argc, char* argv[] )
232 {
233  mitkCommandLineParser parser;
234  parser.setArgumentPrefix("--","-");
235 
236  parser.setTitle("Folder Registration");
237  parser.setCategory("Preprocessing Tools");
238  parser.setDescription("For detail description see https://docs.mitk.org/nightly/DiffusionMiniApps.html");
239  parser.setContributor("MBI");
240 
241  // Add command line argument names
242  parser.addArgument("help", "h",mitkCommandLineParser::Bool, "Help", "Show this help text");
243  //parser.addArgument("usemask", "u", QVariant::Bool, "Use segmentations (derived resources) to exclude areas from registration metrics");
244  parser.addArgument("input", "i", mitkCommandLineParser::InputDirectory, "Input:", "Input folder",us::Any(),false);
245  parser.addArgument("output", "o", mitkCommandLineParser::OutputDirectory, "Output:", "Output folder (ending with /)",us::Any(),false);
246  parser.addArgument("fixed", "f", mitkCommandLineParser::String, "Fixed images:", "Suffix for fixed image (if none is supplied first file matching moving pattern is chosen)",us::Any(),true);
247  parser.addArgument("moving", "m", mitkCommandLineParser::String, "Moving images:", "Suffix for moving images",us::Any(),false);
248  parser.addArgument("derived", "d", mitkCommandLineParser::String, "Derived resources:", "Derived resources suffixes (replaces suffix for moving images); comma separated",us::Any(),true);
249  parser.addArgument("silent", "s", mitkCommandLineParser::Bool, "Silent:", "No xml progress output.");
250  parser.addArgument("resample", "r", mitkCommandLineParser::String, "Resample (x,y,z)mm:", "Resample provide x,y,z spacing in mm (e.g. -r 1,1,3), is not applied to tensor data",us::Any());
251  parser.addArgument("binary", "b", mitkCommandLineParser::Bool, "Binary:", "Speficies that derived resource are binary (interpolation using nearest neighbor)",us::Any());
252  parser.addArgument("correct-origin", "c", mitkCommandLineParser::Bool, "Origin correction:", "Correct for large origin displacement. Use switch when you reveive: Joint PDF summed to zero ",us::Any());
253  parser.addArgument("sinc-int", "s", mitkCommandLineParser::Bool, "Windowed-sinc interpolation:", "Use windowed-sinc interpolation (3) instead of linear interpolation ",us::Any());
254 
255 
256  map<string, us::Any> parsedArgs = parser.parseArguments(argc, argv);
257 
258  // Handle special arguments
259  bool silent = false;
260  bool isBinary = false;
261  bool alignOrigin = false;
262  bool useLinearInterpol = true;
263  {
264  if (parsedArgs.size() == 0)
265  {
266  return EXIT_FAILURE;
267  }
268 
269  if (parsedArgs.count("sinc-int"))
270  useLinearInterpol = false;
271 
272  if (parsedArgs.count("silent"))
273  silent = true;
274 
275  if (parsedArgs.count("binary"))
276  isBinary = true;
277 
278  if (parsedArgs.count("correct-origin"))
279  alignOrigin = true;
280 
281  // Show a help message
282  if ( parsedArgs.count("help") || parsedArgs.count("h"))
283  {
284  std::cout << parser.helpText();
285  return EXIT_SUCCESS;
286  }
287  }
288  std::string refPattern = "";
289  bool useFirstMoving = false;
290  std::string movingImgPattern = us::any_cast<string>(parsedArgs["moving"]);
291 
292  if (parsedArgs.count("fixed"))
293  {
294  refPattern = us::any_cast<string>(parsedArgs["fixed"]);
295  }
296  else
297  {
298  useFirstMoving = true;
299  refPattern = movingImgPattern;
300  }
301 
302  std::string outputPath = us::any_cast<string>(parsedArgs["output"]);
303 
304  std::string inputPath = us::any_cast<string>(parsedArgs["input"]);
305  //QString resampleReference = parsedArgs["resample"].toString();
306  //bool maskTumor = parsedArgs["usemask"].toBool();
307 
308  // if derived sources pattern is provided, populate QStringList with possible filename postfixes
309  std::vector<std::string> derPatterns;
310 
311  if (parsedArgs.count("derived") || parsedArgs.count("d") )
312  {
313  std::string arg = us::any_cast<string>(parsedArgs["derived"]);
314  derPatterns = split(arg ,',');
315  }
316 
317 
318  std::vector<std::string> spacings;
319  float spacing[3];
320  bool doResampling = false;
321  if (parsedArgs.count("resample") || parsedArgs.count("d") )
322  {
323  std::string arg = us::any_cast<string>(parsedArgs["resample"]);
324  spacings = split(arg ,',');
325  spacing[0] = atoi(spacings.at(0).c_str());
326  spacing[1] = atoi(spacings.at(1).c_str());
327  spacing[2] = atoi(spacings.at(2).c_str());
328  doResampling = true;
329  }
330 
331  MITK_INFO << "Input Folder : " << inputPath;
332  MITK_INFO << "Looking for reference image ...";
333  FileListType referenceFileList = CreateFileList(inputPath,refPattern);
334 
335  if ((!useFirstMoving && referenceFileList.size() != 1) || (useFirstMoving && referenceFileList.size() == 0))
336  {
337  MITK_ERROR << "None or more than one possible reference images (" << refPattern <<") found. Exiting." << referenceFileList.size();
338  MITK_INFO << "Choose a fixed arguement that is unique in the given folder!";
339  return EXIT_FAILURE;
340  }
341 
342  std::string referenceFileName = referenceFileList.at(0);
343 
344  MITK_INFO << "Loading Reference (fixed) image: " << referenceFileName;
345  std::string fileType = itksys::SystemTools::GetFilenameExtension(referenceFileName);
346  mitk::Image::Pointer refImage = ExtractFirstTS(mitk::IOUtil::LoadImage(referenceFileName), fileType);
347  mitk::Image::Pointer resampleReference = NULL;
348  if (doResampling)
349  {
350  refImage = ResampleBySpacing(refImage,spacing);
351  resampleReference = refImage;
352  }
353 
354  if (refImage.IsNull())
355  MITK_ERROR << "Loaded fixed image is NULL";
356 
357  // Copy reference image to destination
358  std::string savePathAndFileName = GetSavePath(outputPath, referenceFileName);
359 
360  mitk::IOUtil::SaveImage(refImage, savePathAndFileName);
361 
362  // Copy all derived resources also to output folder, adding _reg suffix
363  referenceFileList = CreateDerivedFileList(referenceFileName, movingImgPattern,derPatterns);
364  CopyResources(referenceFileList, outputPath);
365 
366  std::string derivedResourceFilename;
367  mitk::Image::Pointer referenceMask = NULL; // union of all segmentations
368 
369  if (!silent)
370  {
371  // XML Output to report progress
372  std::cout << "<filter-start>";
373  std::cout << "<filter-name>Batched Registration</filter-name>";
374  std::cout << "<filter-comment>Starting registration ... </filter-comment>";
375  std::cout << "</filter-start>";
376  }
377 
378  // Now iterate over all files and register them to the reference image,
379  // also register derived resources based on file patterns
380  // ------------------------------------------------------------------------------
381 
382  // Create File list
383 
384  FileListType movingImagesList = CreateFileList(inputPath, movingImgPattern);
385 
386 
387  // TODO Reactivate Resampling Feature
388  // mitk::Image::Pointer resampleImage = NULL;
389  // if (QFileInfo(resampleReference).isFile())
390  // {
391  // resampleImage = mitk::IOUtil::LoadImage(resampleReference.toStdString());
392  // }
393  for (unsigned int i =0; i < movingImagesList.size(); i++)
394  {
395  std::string fileMorphName = movingImagesList.at(i);
396  if (fileMorphName == referenceFileName)
397  {
398 
399  // do not process reference image again
400  continue;
401  }
402  MITK_INFO << "Processing image " << fileMorphName;
403 
404  // 1 Register morphological file to reference image
405 
406  if (!itksys::SystemTools::FileExists(fileMorphName.c_str()))
407  {
408  MITK_WARN << "File does not exit. Skipping entry.";
409  continue;
410  }
411  // Origin of images is cancelled
412  // TODO make this optional!!
413  double transf[6];
414  double offset[3];
415  {
416  std::string fileType = itksys::SystemTools::GetFilenameExtension(fileMorphName);
417  mitk::Image::Pointer movingImage = ExtractFirstTS(mitk::IOUtil::LoadImage(fileMorphName), fileType);
418 
419  if (movingImage.IsNull())
420  MITK_ERROR << "Loaded moving image is NULL";
421 
422  // Store transformation, apply it to morph file
423  MITK_INFO << "----------Registering moving image to reference----------";
424 
425  mitk::RegistrationWrapper::GetTransformation(refImage, movingImage, transf, offset, alignOrigin, referenceMask);
426  mitk::RegistrationWrapper::ApplyTransformationToImage(movingImage, transf,offset, resampleReference); // , resampleImage
427 
428  savePathAndFileName = GetSavePath(outputPath, fileMorphName);
429  if (fileType == ".dwi")
430  fileType = "dwi";
431 
432  if (movingImage->GetData() == nullptr)
433  MITK_INFO <<"POST DATA is null";
434 
435 
436 
437 
438  mitk::IOUtil::Save(movingImage, savePathAndFileName);
439  }
440 
441  if (!silent)
442  {
443  std::cout << "<filter-progress-text progress=\"" <<
444  (float)i / (float)movingImagesList.size()
445  << "\" >.</filter-progress-text>";
446  }
447 
448  // Now parse all derived resource and apply the above calculated transformation to them
449  // ------------------------------------------------------------------------------------
450 
451  FileListType fList = CreateDerivedFileList(fileMorphName, movingImgPattern,derPatterns);
452  if (fList.size() > 0)
453  MITK_INFO << "----------DERIVED RESOURCES ---------";
454  for (unsigned int j=0; j < fList.size(); j++)
455  {
456  derivedResourceFilename = fList.at(j);
457  MITK_INFO << "----Processing derived resorce " << derivedResourceFilename << " ...";
458  std::string fileType = itksys::SystemTools::GetFilenameExtension(derivedResourceFilename);
459  mitk::Image::Pointer derivedMovingResource = ExtractFirstTS(mitk::IOUtil::LoadImage(derivedResourceFilename), fileType);
460  // Apply transformation to derived resource, treat derived resource as binary
461  mitk::RegistrationWrapper::ApplyTransformationToImage(derivedMovingResource, transf,offset, resampleReference,isBinary);
462 
463  savePathAndFileName = GetSavePath(outputPath, derivedResourceFilename);
464  mitk::IOUtil::SaveImage(derivedMovingResource, savePathAndFileName);
465  }
466  }
467 
468  if (!silent)
469  std::cout << "<filter-end/>";
470  return EXIT_SUCCESS;
471 }
static void Save(const mitk::BaseData *data, const std::string &path)
Save a mitk::BaseData instance.
Definition: mitkIOUtil.cpp:824
static void CopyResources(FileListType fileList, std::string outputPath)
Copy derived resources from first time step. Append _reg tag, but leave data untouched.
itk::SmartPointer< Self > Pointer
#define MITK_INFO
Definition: mitkLogMacros.h:22
#define MITK_ERROR
Definition: mitkLogMacros.h:24
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)
static void GetTransformation(Image::Pointer fixedImage, Image::Pointer movingImage, RidgidTransformType transformation, double *offset, bool useSameOrigin=true, mitk::Image *mask=NULL)
GetTransformation Registeres the moving to the fixed image and returns the according transformation...
Image::Pointer GrabItkImageMemory(itk::SmartPointer< ItkOutputImageType > &itkimage, mitk::Image *mitkImage=nullptr, const BaseGeometry *geometry=nullptr, bool update=true)
Grabs the memory of an itk::Image (with a specific type) and puts it into an mitk::Image.The memory is managed by the mitk::Image after calling this function. The itk::Image remains valid until the mitk::Image decides to free the memory.
static FileListType CreateFileList(std::string folder, std::string postfix)
Create list of all files in provided folder ending with same postfix.
static Vector3D offset
static bool SaveImage(mitk::Image::Pointer image, const std::string &path)
SaveImage Convenience method to save an arbitrary mitkImage.
Definition: mitkIOUtil.cpp:870
static void ApplyTransformationToImage(mitk::Image::Pointer img, const RidgidTransformType &transformation, double *offset, mitk::Image *resampleReference=NULL, bool binary=false)
ApplyTransformationToImage Applies transformation from GetTransformation to provided image...
#define MITK_WARN
Definition: mitkLogMacros.h:23
std::vector< std::string > FileListType
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)
static const std::string filename
Image class for storing images.
Definition: mitkImage.h:76
Definition: usAny.h:163
itk::Image< double, 3 > InputImageType
void setCategory(std::string category)
static Pointer New()
void setArgumentPrefix(const std::string &longPrefix, const std::string &shortPrefix)
static FileListType CreateDerivedFileList(std::string baseFN, std::string baseSuffix, std::vector< std::string > derivedPatterns)
Build a derived file name from moving images e.g. xxx_T2.nrrd becomes xxx_GTV.nrrd.
static void SaveImage(std::string fileName, mitk::Image *image, std::string fileType)
Save images according to file type.
static mitk::Image::Pointer ResampleBySpacing(mitk::Image *input, float *spacing, bool useLinInt=false)
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::string helpText() const
static std::vector< std::string > & split(const std::string &s, char delim, std::vector< std::string > &elems)
static std::string GetSavePath(std::string outputFolder, std::string fileName)
static mitk::Image::Pointer ExtractFirstTS(mitk::Image *image, std::string fileType)
void setTitle(std::string title)
void setDescription(std::string description)
static mitk::Image::Pointer LoadImage(const std::string &path)
LoadImage Convenience method to load an arbitrary mitkImage.
Definition: mitkIOUtil.cpp:597
static Pointer New()
int main(int argc, char *argv[])
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.