Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
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.