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
TumorProgressionMapping.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 // MITK
17 #include <mitkIOUtil.h>
18 #include <mitkImageAccessByItk.h>
19 #include <mitkImageCast.h>
22 #include <mitkImageToItk.h>
23 #include <mitkImageWriter.h>
24 #include <mitkPixelType.h>
25 #include <mitkPlaneGeometry.h>
26 
27 // ITK
28 #include "itkImageDuplicator.h"
29 #include "itkPermuteAxesImageFilter.h"
30 #include "itkTileImageFilter.h"
31 #include <itkExtractImageFilter.h>
32 #include <itkImageFileWriter.h>
33 #include <itkRGBPixel.h>
34 #include <itkResampleImageFilter.h>
35 #include <itkVTKImageImport.h>
36 
37 #include <itkDirectory.h>
38 #include <itksys/SystemTools.hxx>
39 // VTK
40 #include <vtkCellArray.h>
41 #include <vtkFreeTypeStringToImage.h>
42 #include <vtkImageData.h>
43 #include <vtkTextProperty.h>
44 #include <vtkUnicodeString.h>
45 
46 // MITK
47 #include <mitkCommandLineParser.h>
48 
49 #include "itkRescaleIntensityImageFilter.h"
50 
51 #include <typeExtension.h>
52 
53 using namespace std;
54 
55 typedef unsigned short PixelType;
56 typedef double InputPixelType;
57 typedef itk::Image<itk::RGBPixel<PixelType>, 2> RGB2DImage;
58 typedef itk::RGBPixel<PixelType> RGBPixelType;
59 
60 typedef std::vector<std::string> FileList;
61 typedef std::vector<mitk::Image::Pointer> ImageList;
62 
64 static FileList CreateFileList(std::string folder, std::string postfix)
65 {
67  FileList fileList;
68 
69  if (dir->Load(folder.c_str()))
70  {
71  int n = dir->GetNumberOfFiles();
72  for (int r = 0; r < n; r++)
73  {
74  std::string filename = dir->GetFile(r);
75  if (filename == "." || filename == "..")
76  continue;
77  filename = folder + filename;
78  if (!itksys::SystemTools::FileExists(filename.c_str()))
79  continue;
80 
81  if (postfix.compare(filename.substr(filename.length() - postfix.length())) == 0)
82  fileList.push_back(filename);
83  }
84  }
85  std::sort(fileList.begin(), fileList.end());
86  return fileList;
87 }
88 
89 class mitkProgressionVisualization
90 {
91 public:
92  mitkProgressionVisualization() {}
98  mitk::Image::Pointer ConvertToRGBImage(mitk::Image::Pointer grayImage)
99  {
101  unsigned int *dim = grayImage->GetDimensions();
102  rgbImage->Initialize(mitk::MakePixelType<PixelType, RGBPixelType, 3>(), 3, dim);
103  rgbImage->SetGeometry(grayImage->GetGeometry());
104 
106  mitk::CastToItkImage(grayImage, itkGrayImage);
107 
109 
110  typedef itk::RescaleIntensityImageFilter<itk::Image<InputPixelType, 3>, itk::Image<PixelType, 3>> RescaleFilterType;
112  rescaleFilter->SetInput(itkGrayImage);
113  rescaleFilter->SetOutputMinimum(0);
114  rescaleFilter->SetOutputMaximum(255 * 255);
115  rescaleFilter->Update();
116 
117  itk::Index<3> idx;
118  RGBPixelType value;
119 
120  // Fill rgb image with gray values
121  for (idx[2] = 0; (unsigned int)idx[2] < dim[2]; idx[2]++)
122  {
123  for (idx[1] = 0; (unsigned int)idx[1] < dim[1]; idx[1]++)
124  {
125  for (idx[0] = 0; (unsigned int)idx[0] < dim[0]; idx[0]++)
126  {
127  value.Fill(rescaleFilter->GetOutput()->GetPixel(idx));
128  writeAcc.SetPixelByIndex(idx, value);
129  }
130  }
131  }
132  return rgbImage;
133  }
134 
135  RGB2DImage::Pointer TextAsImage(std::string text)
136  {
137  vtkSmartPointer<vtkImageData> textImage = vtkSmartPointer<vtkImageData>::New();
138  vtkSmartPointer<vtkFreeTypeStringToImage> freetype = vtkSmartPointer<vtkFreeTypeStringToImage>::New();
139  vtkSmartPointer<vtkTextProperty> prop = vtkSmartPointer<vtkTextProperty>::New();
140  float color[3] = {1, 1, 1};
141  float opacity = 1.0;
142  prop->SetColor(color[0], color[1], color[2]);
143  prop->SetFontSize(40);
144  prop->SetOpacity(opacity);
145  textImage->AllocateScalars(VTK_UNSIGNED_SHORT, 3);
146  freetype->RenderString(prop, vtkUnicodeString::from_utf8(text.c_str()), 72, textImage);
147  textImage->Modified();
148 
149  int *extent = textImage->GetExtent();
150 
152 
153  RGB2DImage::IndexType start;
154  start.Fill(0);
155  RGB2DImage::SizeType size;
156  size[0] = extent[1];
157  size[1] = extent[3];
158  size[2] = 0;
159 
160  RGB2DImage::RegionType region(start, size);
161  itkImage->SetRegions(region);
162  itkImage->Allocate();
163  RGB2DImage::IndexType idx;
164 
165  for (unsigned int y = 0; y < size[1]; y++)
166  {
167  for (unsigned int x = 0; x < size[0]; x++)
168  {
169  PixelType *pixel = static_cast<PixelType *>(textImage->GetScalarPointer(x, y, 0));
170  RGBPixelType values;
171  values.Fill(0);
172  values[0] = pixel[1];
173  values[1] = pixel[1];
174  values[2] = pixel[1];
175  idx.Fill(0);
176  idx[0] = x;
177  idx[1] = y;
178 
179  itkImage->SetPixel(idx, values);
180  }
181  }
182 
183  typedef itk::PermuteAxesImageFilter<RGB2DImage> PermuteAxesImageFilterType;
184 
185  itk::FixedArray<unsigned int, 2> order;
186  order[0] = 1;
187  order[1] = 0;
188 
190  permuteAxesFilter->SetInput(itkImage);
191  permuteAxesFilter->SetOrder(order);
192  permuteAxesFilter->Update();
193 
194  RGB2DImage::Pointer itkResultImage = RGB2DImage::New();
195  itkResultImage->SetRegions(region);
196  itkResultImage->Allocate();
197 
198  itkResultImage->Graft(permuteAxesFilter->GetOutput());
199 
200  return itkResultImage;
201  }
202 
213  void AddColouredOverlay(mitk::Image::Pointer rgbImage, mitk::Image::Pointer overlayImage, mitk::Color color)
214  {
215  unsigned int *dim = rgbImage->GetDimensions();
217  mitk::CastToItkImage(overlayImage.GetPointer(), itkOverlayImage);
219 
220  itk::Index<3> idx;
221  itk::RGBPixel<PixelType> value;
222  unsigned short overlayVal = 0;
223  // Fill rgb image with gray values
224  for (idx[2] = 0; (unsigned int)idx[2] < dim[2]; idx[2]++)
225  {
226  for (idx[1] = 0; (unsigned int)idx[1] < dim[1]; idx[1]++)
227  {
228  for (idx[0] = 0; (unsigned int)idx[0] < dim[0]; idx[0]++)
229  {
230  overlayVal = 255 * itkOverlayImage->GetPixel(idx);
231  value = writeAcc.GetPixelByIndex(idx);
232  value[0] = std::min((int)(value[0] + overlayVal * color[0]), 254 * 255);
233  value[1] = std::min((int)(value[1] + overlayVal * color[1]), 254 * 255);
234  value[2] = std::min((int)(value[2] + overlayVal * color[2]), 254 * 255);
235  writeAcc.SetPixelByIndex(idx, value);
236  }
237  }
238  }
239  }
240 
241  itk::Image<itk::RGBPixel<PixelType>, 2>::Pointer ExtractSlice(
242  itk::Image<itk::RGBPixel<PixelType>, 3>::Pointer itkImage, unsigned int sliceNo)
243  {
244  typedef itk::Image<RGBPixelType, 3> InputImageType;
245  typedef itk::Image<RGBPixelType, 2> OutputImageType;
246 
247  int dim[3];
248  dim[0] = itkImage->GetLargestPossibleRegion().GetSize()[0];
249  dim[1] = itkImage->GetLargestPossibleRegion().GetSize()[1];
250  dim[2] = itkImage->GetLargestPossibleRegion().GetSize()[2];
251 
252  itk::Index<3> desiredStart;
253  itk::Size<3> desiredSize;
254 
255  // case AXIAL: // 3rd dimension fixed
256  desiredStart.Fill(0);
257  desiredStart[2] = sliceNo;
258  desiredSize.Fill(0);
259  desiredSize[0] = dim[0];
260  desiredSize[1] = dim[1];
261  desiredSize[2] = 0;
262 
263  itk::ImageRegion<3> desiredRegion(desiredStart, desiredSize);
264 
265  // Extract slice
268  extractSlice->SetInput(itkImage);
269  extractSlice->SetExtractionRegion(desiredRegion);
270  extractSlice->SetDirectionCollapseToIdentity();
271  extractSlice->Update();
272 
273  return extractSlice->GetOutput();
274  }
275 };
276 
277 static std::string GetName(std::string fileName, std::string suffix = "_T2.nrrd")
278 {
279  fileName = itksys::SystemTools::GetFilenameName(fileName);
280  return fileName.substr(0, fileName.length() - suffix.length() - 11); // 10 = date length
281 }
282 
283 static std::string GetDate(std::string fileName, std::string suffix = "_T2.nrrd")
284 {
285  fileName = itksys::SystemTools::GetFilenameName(fileName);
286  fileName = fileName.substr(fileName.length() - suffix.length() - 10, 10); // 10 = date length
287  return fileName;
288 }
289 
291 {
293  for (unsigned int i = 0; i < files.size(); ++i)
294  {
295  images.push_back(mitk::IOUtil::LoadImage(files.at(i)));
296  }
297  return images;
298 }
299 
300 int main(int argc, char *argv[])
301 {
302  // Parse Command-Line Arguments
303  mitkCommandLineParser parser;
304  parser.setArgumentPrefix("--", "-");
305 
306  parser.setTitle("Tumor Progression Mapping");
307  parser.setCategory("Preprocessing Tools");
308  parser.setContributor("MBI");
309  parser.setDescription("Convert a set of co-registered and resampled follow-up images into a 2D png overview (and "
310  "optionally in a 4D NRRD Volume).\nNaming convecntion of files is "
311  "IDENTIFIER_YYYY-MM-DD_MODALITY.nrrd");
312 
313  parser.setArgumentPrefix("--", "-");
314  parser.addArgument("input", "i", mitkCommandLineParser::InputDirectory, "Input folder containing all follow ups");
315  parser.addArgument("output", "o", mitkCommandLineParser::OutputFile, "Output file (PNG)");
316  parser.addArgument("blanked", "b", mitkCommandLineParser::Bool, "Only Display Morphology");
317  parser.addArgument("morphology", "m", mitkCommandLineParser::String, "Morphology postfix.", "_T2.nrrd");
318  parser.addArgument(
319  "segmentation", "s", mitkCommandLineParser::String, "Segmentation postfix. Default: _GTV.nrrd", "_GTV.nrrd");
320  parser.addArgument("4dVolume", "v", mitkCommandLineParser::OutputFile, "Filepath to merged 4d NRRD Volume.");
321  parser.addArgument(
322  "skip", "k", mitkCommandLineParser::Int, "Number of slices to be skipped from top and from button (Default 0)");
323  parser.addArgument(
324  "interval", "n", mitkCommandLineParser::Int, "1 - for all slices, 2 - every second, 3 - every third ...");
325  parser.addArgument("opacity", "c", mitkCommandLineParser::Float, "Opacity of overlay [0,1] invisible -> visible");
326 
327  map<string, us::Any> parsedArgs = parser.parseArguments(argc, argv);
328 
329  if (parsedArgs.size() == 0)
330  return EXIT_SUCCESS;
331 
332  // Show a help message
333  if (parsedArgs.count("help") || parsedArgs.count("h"))
334  {
335  std::cout << parser.helpText();
336  return EXIT_SUCCESS;
337  }
338 
339  std::string outputFile;
340  std::string inputFolder;
341 
342  if (parsedArgs.count("input") || parsedArgs.count("i"))
343  {
344  inputFolder = us::any_cast<string>(parsedArgs["input"]) + "/";
345  }
346 
347  if (parsedArgs.count("output") || parsedArgs.count("o"))
348  {
349  outputFile = us::any_cast<string>(parsedArgs["output"]);
350  }
351 
352  int skip = 0;
353  int interval = 1;
354  float opacity = .3;
355 
356  if (parsedArgs.count("skip") || parsedArgs.count("k"))
357  {
358  skip = us::any_cast<int>(parsedArgs["skip"]);
359  }
360 
361  if (parsedArgs.count("interval") || parsedArgs.count("n"))
362  {
363  interval = us::any_cast<int>(parsedArgs["interval"]);
364  }
365 
366  if (parsedArgs.count("opacity") || parsedArgs.count("c"))
367  {
368  opacity = us::any_cast<float>(parsedArgs["opacity"]);
369  }
370 
371  FileList morphFiles;
372  FileList segFiles;
373 
374  std::string refPattern;
375  std::string segPattern;
376 
377  if (parsedArgs.count("morphology") || parsedArgs.count("m"))
378  {
379  refPattern = us::any_cast<std::string>(parsedArgs["morphology"]);
380  }
381  else
382  return EXIT_FAILURE;
383 
384  if (parsedArgs.count("segmentation") || parsedArgs.count("s"))
385  {
386  segPattern = us::any_cast<std::string>(parsedArgs["segmentation"]);
387  }
388 
389  bool blank = false;
390  if (parsedArgs.count("blanked") || parsedArgs.count("b"))
391  {
392  blank = true;
393  }
395  typedef itk::Image<RGBPixelType, 2> OutputImageType;
396  typedef itk::Image<RGBPixelType, 3> InputImageType;
397 
398  mitkProgressionVisualization progressVis;
399 
400  morphFiles = CreateFileList(inputFolder, refPattern);
401  segFiles = CreateFileList(inputFolder, segPattern);
402 
403  ImageList morphImages = LoadPreprocessedFiles(morphFiles);
404  ImageList segImages;
405  if (segFiles.size() > 0 && blank == false)
406  segImages = LoadPreprocessedFiles(segFiles);
407 
408  mitk::Image::Pointer rgbImage;
409 
410  // define color for overlay image
411  mitk::Color color;
412  color.Fill(0);
413  color[0] = 200 * opacity;
414  color[1] = 0;
415 
416  // Set up itk time image filter to contain 0..N-1 images
419  itk::FixedArray<unsigned int, 2> layout;
420  unsigned int noOfTimeSteps = morphImages.size();
421  layout[0] = noOfTimeSteps;
422  layout[1] = 0; // automatic number of neccessary rows
423  tileFilter->SetLayout(layout);
424 
425  // Get Reference image (all images are expected to have exact same dimensions!)
426 
427  std::string fileName = morphFiles.at(0);
428  mitk::Image *referenceImg = morphImages.at(0);
429 
430  mitk::Image::Pointer merged4D;
431  std::string volumeFile;
432 
433  if (parsedArgs.count("4dVolume") || parsedArgs.count("v"))
434  {
435  volumeFile = us::any_cast<string>(parsedArgs["4dVolume"]);
436  if (volumeFile != "")
437  {
438  unsigned int *dims = new unsigned int[4];
439  dims[0] = referenceImg->GetDimensions()[0];
440  dims[1] = referenceImg->GetDimensions()[1];
441  dims[2] = referenceImg->GetDimensions()[2];
442  dims[3] = morphImages.size();
443  merged4D = mitk::Image::New();
444  merged4D->Initialize(referenceImg->GetPixelType(), 4, dims);
445  merged4D->GetTimeGeometry()->Expand(noOfTimeSteps);
446  }
447  }
448 
449  unsigned int *dim = referenceImg->GetDimensions();
450  unsigned int sliceMaxAxial = dim[2];
451 
452  // Now iterate over all data sets, extract overlay and add it to reference image
453  mitk::Image *morphImage;
454  mitk::Image *segmentationImage = NULL;
455 
456  for (unsigned int i = 0; i < noOfTimeSteps; i++)
457  {
458  MITK_INFO << "File : " << i << " of /" << noOfTimeSteps;
459  int currentSliceNo = 0;
460 
461  // Retrieve images of current time step
462  fileName = morphFiles.at(i);
463  morphImage = morphImages.at(i);
464 
465  // Create 4D Volume image
466  if (volumeFile != "")
467  {
469 
470  merged4D->GetGeometry(i)->SetSpacing(referenceImg->GetGeometry()->GetSpacing());
471  merged4D->GetGeometry(i)->SetOrigin(referenceImg->GetGeometry()->GetOrigin());
472  merged4D->GetGeometry(i)->SetIndexToWorldTransform(referenceImg->GetGeometry()->GetIndexToWorldTransform());
473  merged4D->SetVolume(readAc.GetData(), i);
474  }
475 
476  MITK_INFO << "-- Convert to RGB";
477  rgbImage = progressVis.ConvertToRGBImage(morphImage);
478 
479  // Add current seg in red
480  color.Fill(0);
481  color[0] = 200 * opacity;
482 
483  if (!blank)
484  {
485  segmentationImage = segImages.at(i);
486  if (segmentationImage != NULL)
487  {
488  MITK_INFO << "-- Add Overlay";
489  progressVis.AddColouredOverlay(rgbImage, segmentationImage, color);
490  }
491  }
492  // Add Segmentation of next time step in red
493  if (i == 0)
494  {
495  MITK_INFO << "Skipping retro view in first time step";
496  }
497  else
498  {
499  color.Fill(0);
500  // Add previous in green
501  color[1] = 200 * opacity;
502  if (!blank)
503  {
504  mitk::Image *nextSeg = segImages.at(i - 1);
505  MITK_INFO << "-- Add Overlay of next Step";
506  progressVis.AddColouredOverlay(rgbImage, nextSeg, color);
507  }
508  }
509 
510  // Now save all slices from overlay in output folder
511  MITK_INFO << "-- Extract Slices";
512  for (int slice = sliceMaxAxial - skip - 1; slice > skip; slice -= interval) // sliceMaxAxial/40.0f))
513  {
515  InputImageType::Pointer itkImage2;
516 
517  mitk::CastToItkImage(rgbImage, itkImage);
518  typedef itk::ImageDuplicator<InputImageType> DuplicatorType;
520  duplicator->SetInputImage(itkImage);
521  duplicator->Update();
522  itkImage2 = duplicator->GetOutput();
523 
525  extractedSlice->Graft(progressVis.ExtractSlice(itkImage2, slice));
526  tileFilter->SetInput(((currentSliceNo + 1) * (noOfTimeSteps) + i), extractedSlice);
527 
528  tileFilter->SetInput(i, progressVis.TextAsImage(GetDate(fileName)));
529 
530  currentSliceNo++;
531  }
532  }
533 
534  MITK_INFO << "Tile Filter Update";
535  tileFilter->Update();
536 
537  // Write the output image
538  typedef itk::ImageFileWriter<OutputImageType> WriterType;
540  writer->SetInput(tileFilter->GetOutput());
541  std::string patientName;
542 
543  patientName = GetName(fileName);
544 
545  if (blank)
546  writer->SetFileName(outputFile);
547  else
548  writer->SetFileName(outputFile);
549 
550  writer->Update();
551  if (volumeFile != "")
552  mitk::IOUtil::Save(merged4D, volumeFile);
553 
554  return EXIT_SUCCESS;
555 }
const Point3D GetOrigin() const
Get the origin, e.g. the upper-left corner of the plane.
Gives locked and index-based write access for a particular image part. The class provides several set...
static void Save(const mitk::BaseData *data, const std::string &path)
Save a mitk::BaseData instance.
Definition: mitkIOUtil.cpp:824
itk::SmartPointer< Self > Pointer
Gives locked and index-based read access for a particular image part. The class provides several set-...
#define MITK_INFO
Definition: mitkLogMacros.h:22
static std::string GetName(std::string fileName, std::string suffix="_T2.nrrd")
void setContributor(std::string contributor)
std::vector< mitk::Image::Pointer > ImageList
int main(int argc, char *argv[])
STL namespace.
ValueType * any_cast(Any *operand)
Definition: usAny.h:377
const mitk::Vector3D GetSpacing() const
Get the spacing (size of a pixel).
std::vector< std::string > FileList
std::map< std::string, us::Any > parseArguments(const StringContainerType &arguments, bool *ok=nullptr)
static std::string GetDate(std::string fileName, std::string suffix="_T2.nrrd")
class ITK_EXPORT Image
unsigned int * GetDimensions() const
Get the sizes of all dimensions as an integer-array.
Definition: mitkImage.cpp:1308
itk::Image< double, 3 > InputImageType
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
static ImageList LoadPreprocessedFiles(FileList files)
const mitk::PixelType GetPixelType(int n=0) const
Returns the PixelType of channel n.
Definition: mitkImage.cpp:105
void setCategory(std::string category)
const TPixel * GetData() const
Gives const access to the data.
static FileList CreateFileList(std::string folder, std::string postfix)
Create list of all files in provided folder ending with same postfix.
static Pointer New()
itk::RGBPixel< float > Color
Color Standard RGB color typedef (float)
void setArgumentPrefix(const std::string &longPrefix, const std::string &shortPrefix)
static T min(T x, T y)
Definition: svm.cpp:67
itk::Image< itk::RGBPixel< PixelType >, 2 > RGB2DImage
void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer< ItkOutputImageType > &itkOutputImage)
Cast an mitk::Image to an itk::Image with a specific type.
itk::RGBPixel< PixelType > RGBPixelType
std::string helpText() const
unsigned short PixelType
void setTitle(std::string title)
double InputPixelType
mitk::BaseGeometry * GetGeometry(int t=0) const
Return the geometry, which is a TimeGeometry, of the data as non-const pointer.
Definition: mitkBaseData.h:129
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
section MAP_FRAME_Mapper_Settings Mapper settings For the mapping of corrected images
mitk::AffineTransform3D * GetIndexToWorldTransform()
Get the transformation used to convert from index to world coordinates.
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.