CBICA Toolkit  1.0.0
cbicaITKSafeImageIO.h
Go to the documentation of this file.
1 /**
2 \file cbicaITKSafeImageIO.h
3 
4 \brief Defines safe input and output of itk::Images
5 
6 Read and Write itk::Image data in a safe manner. Header-only
7 
8 https://www.cbica.upenn.edu/sbia/software/ <br>
9 software@cbica.upenn.edu
10 
11 Copyright (c) 2018 University of Pennsylvania. All rights reserved. <br>
12 See COPYING file or https://www.cbica.upenn.edu/sbia/software/license.html
13 
14 */
15 #pragma once
16 
17 #include "itkImage.h"
18 #include "itkImageFileReader.h"
19 #include "itkImageSeriesReader.h"
20 #include "itkImageSeriesWriter.h"
21 #include "itkCastImageFilter.h"
22 #include "itkImageFileWriter.h"
23 #include "itkImageIOBase.h"
24 #include "itkImageIOFactory.h"
25 #include "itkNiftiImageIO.h"
26 #include "itkGDCMImageIO.h"
27 #include "itkGDCMSeriesFileNames.h"
28 //#include "itkDCMTKImageIO.h"
29 //#include "itkDCMTKSeriesFileNames.h"
30 #include "itkNumericSeriesFileNames.h"
31 #include "itkOrientImageFilter.h"
32 #include "itkChangeInformationImageFilter.h"
33 
34 #if ITK_VERSION_MAJOR >= 4
35 #include "gdcmUIDGenerator.h"
36 #else
37 #include "gdcm/src/gdcmFile.h"
38 #include "gdcm/src/gdcmUtil.h"
39 #endif
40 
41 #include "cbicaUtilities.h"
42 #include "cbicaITKImageInfo.h"
43 #include "cbicaITKUtilities.h"
44 #include "DicomIOManager.h"
45 
46 using ImageTypeFloat3D = itk::Image< float, 3 >;
47 using TImageType = ImageTypeFloat3D;
48 using MaskType = itk::Image<unsigned int, 3>;
49 
50 namespace cbica
51 {
52  /**
53  \brief Get the itk::ImageFileReader from input file name. This is useful for scenarios where reader meta information is needed for later writing step(s).
54 
55  Usage:
56  \verbatim
57  typedef itk::Image< float, 3 > ExpectedImageType;
58  std::string inputFileName = parser.getParameterValue("inputImage");
59  ExpectedImageType::Pointer inputImage_1 = GetImageReader< ExpectedImageType >(inputFileName)->GetOutput();
60  ExpectedImageType::Pointer inputImage_2 = GetImageReader< ExpectedImageType >(inputFileName, ".nii.gz,.img")->GetOutput();
61  DoAwesomeStuffWithImage( inputImage );
62  \endverbatim
63 
64  \param fName name of the image
65  \param supportedExtensions Supported extensions, defaults to ".nii.gz,.nii"
66  \return itk::ImageFileReader::Pointer templated over the same as requested by user
67  */
68  template <class TImageType = ImageTypeFloat3D >
69  typename itk::ImageFileReader< TImageType >::Pointer GetImageReader(const std::string &fName, const std::string &supportedExtensions = ".nii.gz,.nii,.dcm", const std::string &delimitor = ",")
70  {
71  //// check read access
72  //if (((_access(fName.c_str(), 4)) == -1) || ((_access(fName.c_str(), 6)) == -1))
73  //{
74  // ShowErrorMessage("You don't have read access in selected location. Please check.");
75  // exit(EXIT_FAILURE);
76  //}
77 
78  std::string fName_wrap = cbica::normPath(fName);
79 
80  std::string fileExtension = cbica::getFilenameExtension(fName_wrap);
81  std::transform(fileExtension.begin(), fileExtension.end(), fileExtension.begin(), ::tolower);
82 
83  auto reader = /*typename*/ itk::ImageFileReader< TImageType >::New();
84 
85  if (fileExtension == ".dcm")
86  {
87  auto filesInDir = cbica::filesInDirectory(cbica::getFilenamePath(fName_wrap));
88  if (filesInDir.size() > 1)
89  {
90  std::cerr << "Trying to read DICOM file. Please use DICOMImageReader.\n";
91  return reader;
92  }
93  }
94 
95  if (supportedExtensions != "")
96  {
97  std::vector< std::string > extensions = cbica::stringSplit(supportedExtensions, delimitor);
98 
99  bool supportedExtensionFound = false;
100  for (size_t i = 0; i < extensions.size(); i++)
101  {
102  if (extensions[i] == fileExtension)
103  {
104  supportedExtensionFound = true;
105  }
106  }
107 
108  if (!supportedExtensionFound)
109  {
110  std::cerr << "Supplied file name '" << fName_wrap << "' doesn't have a supported extension. \nSupported Extensions: " << supportedExtensions << "\n";
111  return reader;
112  }
113  }
114 
115  // ensure that the requested image dimensions and read image dimensions match up
116  auto imageInfo = cbica::ImageInfo(fName_wrap);
117 
118  // perform basic sanity check
119  if ((imageInfo.GetImageDimensions() != TImageType::ImageDimension) &&
120  !((TImageType::ImageDimension == 2) && (imageInfo.GetImageSize()[2] == 1))) // this to check for a 2D DICOM
121  {
122  std::cerr << "Image Dimension mismatch. Return image is expected to be '" << TImageType::ImageDimension <<
123  "'D and doesn't match the image dimension read from the input file, which is '" << imageInfo.GetImageDimensions() << "'.\n";
124  return reader;
125  }
126 
127  reader->SetFileName(fName_wrap);
128 
129  auto supportedExtsVector = cbica::stringSplit(supportedExtensions, ",");
130 
131  if (std::find(supportedExtsVector.begin(), supportedExtsVector.end(), fileExtension) == supportedExtsVector.end())
132  {
133  std::cerr << "Extension of file doesn't match the supported extensions; can't read.\n";
134  return reader;
135  }
136 
137  // set image IO type
138  if ((fileExtension == ".dcm") || (fileExtension == ".dicom"))
139  {
140  //auto ioType = itk::GDCMKImageIO::New();
141  //ioType->SetFileName(fName_wrap);
142  //reader->SetImageIO(ioType);
143  }
144  else if ((fileExtension == ".nii") || (fileExtension == ".nii.gz"))
145  {
146  auto ioType = itk::NiftiImageIO::New();
147  ioType->SetFileName(fName_wrap);
148  reader->SetImageIO(ioType);
149  }
150 
151  try
152  {
153  reader->Update();
154  }
155  catch (itk::ExceptionObject& e)
156  {
157  std::cerr << "Exception caught while reading the image '" << fName_wrap << "': " << e.what() << "\n";
158  return reader;
159  }
160 
161  return reader;
162  }
163 
164  /**
165  \brief Returns the unique series IDs in the specified directory
166 
167  The check is only done on the DICOM tag provided, so if there are series with the same UID information (but are indeed different images),
168  this function will not able to handle it.
169 
170  \param dirName The directory in question
171  \param tagToCheck The tag on the basis of which the test is done; defaults to "0x0020|0x00E"
172  \return Vector of Series UIDs and fileName collection pairs, with each fileName collection corresponding to a UID
173  */
174  //inline std::vector< std::pair< std::string , std::vector< std::string > > > GetDICOMSeriesAndFilesInDir(const std::string &dirName,
175  // const std::string tagToCheck = "0x0020|0x00E")
176  //{
177  // std::vector<
178  // std::pair<
179  // std::string, // this is the series UID information
180  // std::vector< std::string > > // these are the fileNames corresponding to each UID
181  // > returnVector;
182 
183  // auto dirName_wrap = cbica::normPath(dirName);
184  // auto allFilesInDir = cbica::filesInDirectory(dirName_wrap);
185  //
186  // // initialize the returnVector with the first series UID and fileName
187  // returnVector.push_back(
188  // std::make_pair(cbica::GetDICOMTagValue(allFilesInDir[0], tagToCheck), // get the first series UID
189  // std::vector< std::string >({ allFilesInDir[0] }) // construct a initial vector
190  // ));
191 
192  // std::vector< std::string > volumeSeries;
193  // const std::string volumeSeriesTag = "0x0018|0x1030";
194  // volumeSeries.push_back(cbica::GetDICOMTagValue(allFilesInDir[0], volumeSeriesTag));
195 
196  // // looping through all the found files
197  // for (size_t i = 1; i < allFilesInDir.size(); i++)
198  // {
199  // auto temp = cbica::GetDICOMTagValue(allFilesInDir[i], tagToCheck);
200  // auto temp_volSeries = cbica::GetDICOMTagValue(allFilesInDir[i], volumeSeriesTag);
201 
202  // bool newUIDFound = true;
203  // for (size_t j = 0; j < returnVector.size(); j++)
204  // {
205  // if (returnVector[j].first == temp)
206  // {
207  // bool newVolSeriesFound = true;
208  // for (size_t k = 0; k < volumeSeries.size(); k++)
209  // {
210  // if (volumeSeries[k] == temp_volSeries)
211  // {
212  // newVolSeriesFound = false;
213  // }
214  // }
215  // if (!newVolSeriesFound)
216  // {
217  // returnVector[j].second.push_back(allFilesInDir[i]);
218  // newUIDFound = false;
219  // break;
220  // }
221  // else
222  // {
223  // volumeSeries.push_back(temp_volSeries); // the new volume has same series UID information so nothing changes there
224  // }
225  // }
226  // }
227  // if (newUIDFound)
228  // {
229  // // add a new seriesUID-fileNames pair
230  // returnVector.push_back(
231  // std::make_pair(temp, // this is the UID
232  // std::vector< std::string >({ allFilesInDir[i] }) // first filename corresponding to the UID
233  // ));
234  // }
235  // }
236 
237  // return returnVector;
238 
239  // //// this implementation takes a *lot* of time
240  // //auto dicomIO = itk::DCMTKImageIO::New();
241  // //auto inputNames = itk::DCMTKSeriesFileNames::New();
242  // //inputNames->SetInputDirectory(dirName_wrap);
243  // //inputNames->SetLoadPrivateTags(true);
244  // //auto UIDs = inputNames->GetSeriesUIDs(); // this is the primary bottle-neck, I think because it does checks on multiple different things
245 
246  // //return cbica::GetUniqueElements< std::string >(UIDs);
247  //}
248 
249  /**
250  \brief Get the Dicom image reader (not the image, the READER). This is useful for scenarios where reader meta information is needed for later writing step(s).
251 
252  Usage:
253  \verbatim
254  typedef itk::Image< float, 3 > ExpectedImageType;
255  std::string inputDirName = parser.getParameterValue("inputDirName");
256  auto inputImageReader = GetDicomImageReader< ExpectedImageType >(inputDirName); // reads *all* DICOM images
257  auto inputImage = inputImageReader->GetOutput();
258  DoAwesomeStuffWithImage( inputImage );
259  \endverbatim
260 
261  \param dirName This is the directory name of the DICOM image which needs to be loaded - if this is an image, the underlying path of the image is considered
262  */
263  template <class TImageType = ImageTypeFloat3D >
264  typename itk::ImageSeriesReader< TImageType >::Pointer GetDicomImageReader(const std::string &dirName)
265  {
266  std::string dirName_wrap = cbica::normPath(dirName);
267  if (!cbica::isDir(dirName_wrap))
268  {
269  dirName_wrap = cbica::getFilenamePath(dirName);
270  }
271  if (dirName_wrap[dirName_wrap.length() - 1] == '/')
272  dirName_wrap.pop_back(); // this is done to ensure the last "/" isn't taken into account for file name generation
273 
274  //// check read access
275  //if (((_access(dirName_wrap.c_str(), 4)) == -1) || ((_access(dirName_wrap.c_str(), 6)) == -1))
276  //{
277  // ShowErrorMessage("You don't have read access in selected location. Please check.");
278  // exit(EXIT_FAILURE);
279  //}
280 
281  auto dicomIO = itk::GDCMImageIO::New();
282  auto inputNames = itk::GDCMSeriesFileNames::New();
283  inputNames->SetInputDirectory(dirName_wrap);
284  inputNames->SetLoadPrivateTags(true);
285  auto UIDs = inputNames->GetSeriesUIDs();
286 
287  auto UIDs_unique = cbica::GetUniqueElements(UIDs);
288 
289  if (UIDs_unique.size() > 1)
290  {
291  std::cout << "Multiple DICOM series detected.\n";
292  }
293 
294  inputNames->SetInputDirectory(dirName_wrap);
295  //inputNames->SetLoadPrivateTags(true);
296 
297  auto filenames = inputNames->GetInputFileNames();
298 
299  auto seriesReader = /*typename*/ itk::ImageSeriesReader< TImageType >::New();
300  seriesReader->SetImageIO(dicomIO);
301  seriesReader->SetFileNames(filenames);
302 
303  try
304  {
305  seriesReader->Update();
306  }
307  catch (itk::ExceptionObject & err)
308  {
309  std::cerr << "Error while loading DICOM images: " << err.what() << "\n";
310  }
311 
312  return seriesReader;
313  }
314 
315  /**
316  \brief Get the itk::Image from input dir name
317 
318  Usage:
319  \verbatim
320  typedef itk::Image< float, 3 > ExpectedImageType;
321  std::string inputDirName = parser.getParameterValue("inputDirName");
322  ExpectedImageType::Pointer inputImage_1 = ReadDicomImage< ExpectedImageType >(inputFileName); // reads MRI and perfusion data by default tags "0008|0021,0020|0012"
323  ExpectedImageType::Pointer inputImage_2 = ReadDicomImage< ExpectedImageType >(inputDirName, "0008|0021")->GetOutput(); // only reads images with tag "0008|0021"
324  DoAwesomeStuffWithImage( inputImage );
325  \endverbatim
326 
327  \param fName name of the image
328  \param supportedExtensions Supported extensions
329  \return itk::ImageFileReader::Pointer templated over the same as requested by user
330  */
331  //template <class TImageType = ImageTypeFloat3D >
332  //typename TImageType::Pointer ReadDicomImage(const std::string &dirName)
333  //{
334  // auto dicomReader = /*typename*/ itk::ImageSeriesReader< TImageType >::New();
335  // if (cbica::isFile(dirName))
336  // {
337  // auto reader =GetImageReader< TImageType >(dirName);
338  // return reader->GetOutput();
339  // }
340  // else
341  // {
342  // //dicomReader = GetDicomImageReader< TImageType >(dirName);
343  // }
344 
345  // // the code below is to ensure that whatever ITK reads aligns with DICOM header information
346  // auto inputDict = (*(dicomReader->GetMetaDataDictionaryArray()))[0];
347  // std::string origin, pixelSpacing, direction, sliceSpacing_1, sliceSpacing_2;
348  // typename TImageType::SpacingType outputSpacing;
349  // typename TImageType::PointType outputOrigin, outputDirection;
350 
351  // itk::ExposeMetaData<std::string>(*inputDict, "0020|0032", origin);
352  // itk::ExposeMetaData<std::string>(*inputDict, "0028|0030", pixelSpacing);
353  // //itk::ExposeMetaData<std::string>(*inputDict, "0020|0037", direction);
354 
355  // if (TImageType::ImageDimension > 2)
356  // {
357  // itk::ExposeMetaData<std::string>(*inputDict, "0018|0050", sliceSpacing_1);
358  // itk::ExposeMetaData<std::string>(*inputDict, "0018|0088", sliceSpacing_2);
359  // if (sliceSpacing_1 == sliceSpacing_2)
360  // {
361  // outputSpacing[2] = static_cast< typename TImageType::PixelType >(std::atof(sliceSpacing_1.c_str()));
362  // }
363  // }
364 
365  // if (!pixelSpacing.empty())
366  // {
367  // auto temp = cbica::stringSplit(pixelSpacing, "\\");
368  // outputSpacing[0] = std::atof(temp[0].c_str());
369  // outputSpacing[1] = std::atof(temp[1].c_str());
370  // }
371 
372  // if (!origin.empty())
373  // {
374  // auto temp = cbica::stringSplit(origin, "\\");
375  // for (unsigned int i = 0; i < TImageType::ImageDimension; i++)
376  // {
377  // outputOrigin[i] = std::atof(temp[i].c_str());
378  // }
379  // }
380 
381  // //if (!direction.empty())
382  // //{
383  // // auto temp = cbica::stringSplit(direction, "\\");
384  // // for (auto i = 0; i < TImageType::ImageDimension; i++)
385  // // {
386  // // outputOrigin[i] = std::atof(temp[i].c_str());
387  // // }
388  // //}
389 
390  // auto infoChangeFilter = itk::ChangeInformationImageFilter< TImageType >::New();
391  // infoChangeFilter->SetInput(dicomReader->GetOutput());
392  // infoChangeFilter->SetChangeOrigin(true);
393  // infoChangeFilter->SetChangeSpacing(true);
394  // infoChangeFilter->SetOutputOrigin(outputOrigin);
395  // infoChangeFilter->SetOutputSpacing(outputSpacing);
396  // infoChangeFilter->Update();
397 
398  // return infoChangeFilter->GetOutput();
399  //}
400 
401  /**
402  \brief Get the itk::Image from input dir name
403 
404  Usage:
405  \verbatim
406  typedef itk::Image< float, 3 > ExpectedImageType;
407  std::string inputDirName = parser.getParameterValue("inputDirName");
408  ExpectedImageType::Pointer inputImage_1 = ReadDicomImage< ExpectedImageType >(inputFileName); // reads MRI and perfusion data by default tags "0008|0021,0020|0012"
409  ExpectedImageType::Pointer inputImage_2 = ReadDicomImage< ExpectedImageType >(inputDirName, "0008|0021")->GetOutput(); // only reads images with tag "0008|0021"
410  DoAwesomeStuffWithImage( inputImage );
411  \endverbatim
412 
413  This function calls ReadDicomImage<> internally
414 
415  \param fName name of the image
416  \param supportedExtensions Supported extensions
417  \return itk::ImageFileReader::Pointer templated over the same as requested by user
418  */
419  //template <class TImageType = ImageTypeFloat3D >
420  //typename TImageType::Pointer GetDicomImage(const std::string &dirName)
421  //{
422  // return ReadDicomImage< TImageType >(dirName);
423  //}
424 
425 
426  /**
427  \brief Write the itk::Image to the file name
428 
429  Usage:
430  \verbatim
431  typedef itk::Image< float, 3 > ComputedImageType;
432  typedef itk::Image< unsigned char, 3 > WrittenImageType;
433  ComputedImageType::Pointer imageToWrite = ComputedImageType::New();
434  imageToWrite = GetImageSomehow();
435  WriteImage< ComputedImageType >(imageToWrite, fileNameToWriteImage); // casts imageToWrite to WrittenImageType
436  WriteImage< ComputedImageType, WrittenImageType >(imageToWrite, fileNameToWriteImage); // writes imageToWrite as ComputedImageType
437  // at this point, the image has already been written
438  \endverbatim
439 
440  \param inputImage Pointer to processed image data which is to be written
441  \param fileName File containing the image
442  \return itk::Image of specified pixel and dimension type
443  */
444  template <typename ComputedImageType = ImageTypeFloat3D, typename ExpectedImageType = ComputedImageType>
445  void WriteImage(typename ComputedImageType::Pointer imageToWrite, const std::string &fileName)
446  {
447  //// check write access
448  //if (((_access(fileName.c_str(), 2)) == -1) || ((_access(fileName.c_str(), 6)) == -1))
449  //{
450  // ShowErrorMessage("You don't have write access in selected location. Please check.");
451  // return;
452  //}
453 
454  auto filter = /*typename*/ itk::CastImageFilter<ComputedImageType, ExpectedImageType>::New();
455  filter->SetInput(imageToWrite);
456  filter->Update();
457 
458  auto writer = /*typename*/ itk::ImageFileWriter< ExpectedImageType >::New();
459 
460  auto ext = cbica::getFilenameExtension(fileName, false);
461  if ((ext == ".nii") || (ext == ".nii.gz"))
462  {
463  writer->SetImageIO(itk::NiftiImageIO::New());
464  }
465 
466  writer->SetInput(filter->GetOutput());
467  writer->SetFileName(fileName);
468 
469  try
470  {
471  writer->Write();
472  }
473  catch (itk::ExceptionObject &e)
474  {
475  std::cerr << "Error occurred while trying to write the image '" << fileName << "': " << e.what() << "\n";
476  //exit(EXIT_FAILURE);//TBD all exit(EXIT_FAILURE) should be removed
477  }
478 
479  return;
480  }
481 
482  /*
483  \brief Write itk::ImageReader as DICOM to specified directory
484 
485  This uses default dictionary created by GDCM::ImageIO
486 
487  Usage:
488  \verbatim
489  typedef itk::Image< float, 3 > ComputedImageType;
490  typedef itk::Image< unsigned char, 3 > WrittenImageType;
491  itk::ImageSeriesReader< ComputedImageType >::Pointer inputImageReader = GetDicomImageReader< ComputedImageType >(inputDirName);
492  ComputedImageType::Pointer imageToWrite = GetImageAfterProcessing( inputImageReader->GetOutput() );
493  WriteImage< ComputedImageType, WrittenImageType >(imageToWrite, dirNameToWriteImage); // casts imageToWrite to WrittenImageType
494  WriteImage< ComputedImageType >(imageToWrite, dirNameToWriteImage); // writes imageToWrite as ComputedImageType
495  // at this point, the image has already been written
496  \endverbatim
497 
498  \param imageToWrite Pointer to processed image data which is to be written
499  \param dirName File containing the image
500  \return itk::Image of specified pixel and dimension type
501  */
502  template <typename ComputedImageType = ImageTypeFloat3D>
503  void WriteDicomImage(const typename ComputedImageType::Pointer imageToWrite, const std::string &dirName)
504  {
505  auto reader = typename itk::ImageSeriesReader< ComputedImageType >::New();
506  WriteDicomImage< ComputedImageType >(reader, imageToWrite, dirName);
507  }
508 
509  /**
510  \brief Write the itk::Image as a DICOM to the specified directory
511 
512  Usage:
513  \verbatim
514  typedef itk::Image< float, 3 > ComputedImageType;
515  typedef itk::Image< unsigned char, 3 > WrittenImageType;
516  itk::ImageSeriesReader< ComputedImageType >::Pointer inputImageReader = GetDicomImageReader< ComputedImageType >(inputDirName);
517  ComputedImageType::Pointer imageToWrite = GetImageAfterProcessing( inputImageReader->GetOutput() );
518  WriteImage< ComputedImageType, WrittenImageType >(inputImageReader, imageToWrite, dirNameToWriteImage); // casts imageToWrite to WrittenImageType
519  WriteImage< ComputedImageType >(inputImageReader, imageToWrite, dirNameToWriteImage); // writes imageToWrite as ComputedImageType
520  // at this point, the image has already been written
521  \endverbatim
522 
523  \param inputImageReader The image reader for DICOM - this is necessary to populate the DICOM dictionary properly
524  \param imageToWrite Pointer to processed image data which is to be written
525  \param dirName File containing the image
526  \return itk::Image of specified pixel and dimension type
527  */
528  template <typename ComputedImageType = ImageTypeFloat3D>
529  void WriteDicomImage(const typename itk::ImageSeriesReader< ComputedImageType >::Pointer inputImageReader, const typename ComputedImageType::Pointer imageToWrite, const std::string &dirName)
530  {
531  if (!cbica::isDir(dirName))
532  {
533  std::cout << "Specified directory wasn't found, creating...\n";
534  cbica::createDir(dirName);
535  }
536 
537  // check write access
538  //if (((_access(dirName.c_str(), 2)) == -1) || ((_access(dirName.c_str(), 6)) == -1))
539  //{
540  // ShowErrorMessage("You don't have write access in selected location. Please check.");
541  // return;
542  //}
543 
544  using ExpectedImageType = itk::Image< short, ComputedImageType::ImageDimension >; // this is needed because DICOM currently only supports short/int
545  typedef itk::CastImageFilter<ComputedImageType, ExpectedImageType> CastFilterType;
546  typename CastFilterType::Pointer castFilter = CastFilterType::New();
547  castFilter->SetInput(imageToWrite);
548  castFilter->Update();
549 
550  // typedef typename ExpectedImageType::PixelType DicomPixelType;
551 
552  auto dicomIO = itk::GDCMImageIO::New();
553  //auto dicomIO = MyGDCMImageIO::New();
554  dicomIO->SetComponentType(itk::ImageIOBase::IOComponentType::SHORT);
555 
556  auto seriesWriter = itk::ImageSeriesWriter< ExpectedImageType, itk::Image<typename ExpectedImageType::PixelType, 2> >::New();
557 
558  auto namesGenerator = itk::NumericSeriesFileNames::New();
559  //namesGenerator->SetUseSeriesDetails(false);
560  auto start = imageToWrite->GetLargestPossibleRegion().GetIndex();
561  auto size = imageToWrite->GetLargestPossibleRegion().GetSize();
562  namesGenerator->SetSeriesFormat((dirName + "/image%03d.dcm").c_str());
563  namesGenerator->SetStartIndex(start[2]);
564  namesGenerator->SetEndIndex(start[2] + size[2] - 1);
565  namesGenerator->SetIncrementIndex(1);
566 
567  seriesWriter->SetInput(castFilter->GetOutput());
568  seriesWriter->SetImageIO(dicomIO);
569  seriesWriter->SetFileNames(namesGenerator->GetFileNames());
570 
571  typename itk::ImageSeriesReader< ComputedImageType >::DictionaryArrayType outputArray;
572  if (inputImageReader.IsNull() || (inputImageReader->GetImageIO() == NULL))
573  {
574  //dicomIO->SetOrigin(0, imageToWrite->GetOrigin()[0]);
575  //dicomIO->SetOrigin(1, imageToWrite->GetOrigin()[1]);
576  //dicomIO->SetOrigin(2, imageToWrite->GetOrigin()[2]);
577  //dicomIO->SetSpacing(0, imageToWrite->GetSpacing()[0]);
578  //dicomIO->SetSpacing(1, imageToWrite->GetSpacing()[1]);
579  //dicomIO->SetSpacing(2, imageToWrite->GetSpacing()[2]);
580  //dicomIO->SetDimensions(0, imageToWrite->GetLargestPossibleRegion().GetSize()[0]);
581  //dicomIO->SetDimensions(1, imageToWrite->GetLargestPossibleRegion().GetSize()[1]);
582  //dicomIO->SetDimensions(2, imageToWrite->GetLargestPossibleRegion().GetSize()[2]);
583 
584  typename ExpectedImageType::IndexType index;
585  index[0] = 0;
586  index[1] = 0;
587  for (size_t i = 0; i < imageToWrite->GetLargestPossibleRegion().GetSize()[2]; i++)
588  {
589  auto dict = new typename itk::ImageSeriesReader< ComputedImageType >::DictionaryType;
590  typename ExpectedImageType::PointType position;
591  index[2] = i;
592  imageToWrite->TransformIndexToPhysicalPoint(index, position);
593  itk::EncapsulateMetaData<std::string>(*dict, "0020|0032", std::to_string(position[0]) + "\\" + std::to_string(position[1]) + "\\" + std::to_string(position[2])); // patient position
594  itk::EncapsulateMetaData<std::string>(*dict, "0020|1041", std::to_string(position[0]) + "\\" + std::to_string(position[1]) + "\\" + std::to_string(position[2])); // slice location
595  //itk::EncapsulateMetaData<std::string>(*dict, "0020|0011", std::to_string(1));
596  //itk::EncapsulateMetaData<std::string>(*dict, "0020|0013", std::to_string(i));
597  //itk::EncapsulateMetaData<std::string>(*dict, "0018|5100", std::to_string(position[0]) + "\\" + std::to_string(position[1]) + "\\" + std::to_string(position[2]));
598  //itk::EncapsulateMetaData<std::string>(*dict, "2020|0010", std::to_string(position[0]) + "\\" + std::to_string(position[1]) + "\\" + std::to_string(position[2]));
599  //itk::EncapsulateMetaData<std::string>(*dict, "0018|5101", std::to_string(position[0]) + "\\" + std::to_string(position[1]) + "\\" + std::to_string(position[2]));
600  // direction
601  //if (ComputedImageType::ImageDimension == 2)
602  //{
603  // itk::EncapsulateMetaData<std::string>(*dict, "0020|0037", std::to_string(*imageToWrite->GetDirection()[0]) + "\\" + std::to_string(*imageToWrite->GetDirection()[1]) + "\\0\\" + std::to_string(*imageToWrite->GetDirection()[2]) + "\\" + std::to_string(*imageToWrite->GetDirection()[3]) + "\\0"); // orientation
604  //}
605  //else if (ComputedImageType::ImageDimension == 3)
606  //{
607  // itk::EncapsulateMetaData<std::string>(*dict, "0020|0037",
608  // std::to_string(*imageToWrite->GetDirection()[0]) + "\\" + std::to_string(*imageToWrite->GetDirection()[1]) + "\\" + std::to_string(*imageToWrite->GetDirection()[2]) + "\\" +
609  // std::to_string(*imageToWrite->GetDirection()[3]) + "\\" + std::to_string(*imageToWrite->GetDirection()[4]) + "\\" + std::to_string(*imageToWrite->GetDirection()[5]) + "\\" +
610  // std::to_string(*imageToWrite->GetDirection()[6]) + "\\" + std::to_string(*imageToWrite->GetDirection()[7]) + "\\" + std::to_string(*imageToWrite->GetDirection()[8])
611  // ); // orientation
612  //}
613  itk::EncapsulateMetaData<std::string>(*dict, "0018|0050", std::to_string(imageToWrite->GetSpacing()[2])); // Slice Thickness
614  itk::EncapsulateMetaData<std::string>(*dict, "0018|0088", std::to_string(imageToWrite->GetSpacing()[2])); // Spacing Between Slices
615  itk::EncapsulateMetaData<std::string>(*dict, "0028|0030", std::to_string(imageToWrite->GetSpacing()[0]) + "\\" + std::to_string(imageToWrite->GetSpacing()[1]));
616  //itk::EncapsulateMetaData<std::string>(*dict, "0008|0008", "DERIVED\\SECONDARY"); // Image Type
617  //itk::EncapsulateMetaData<std::string>(*dict, "0008|0064", "DV"); // Conversion Type
618  //itk::EncapsulateMetaData<std::string>(*dict, "0008|0060", "MR"); // Modality - can never gurantee MR
619  //itk::EncapsulateMetaData<std::string>(*dict, "0018|0088", std::to_string(imageToWrite->GetSpacing()[2]));
620 
621  outputArray.push_back(dict);
622  }
623 
624  seriesWriter->SetMetaDataDictionaryArray(&outputArray);
625  }
626  else
627  {
628  dicomIO->SetMetaDataDictionary(inputImageReader->GetMetaDataDictionary());
629  seriesWriter->SetMetaDataDictionaryArray(inputImageReader->GetMetaDataDictionaryArray()); // no dictionary information present without seriesReader
630  }
631 
632  try
633  {
634  seriesWriter->Write();
635  }
636  catch (itk::ExceptionObject &e)
637  {
638  std::cerr << "Error occurred while trying to write the image '" << dirName << "': " << e.what() << "\n";
639  exit(EXIT_FAILURE);
640  }
641 
642  }
643 
644 
645  /**
646  \brief Get the itk::Image from input file name
647 
648  Usage:
649  \verbatim
650  typedef itk::Image< float, 3 > ExpectedImageType;
651  std::string inputFileName = parser.getParameterValue("inputImage");
652  ExpectedImageType::Pointer inputImage_1 = ReadImage< ExpectedImageType >(inputFileName);
653  ExpectedImageType::Pointer inputImage_2 = ReadImage< ExpectedImageType >(inputFileName, ".nii.gz,.img");
654  DoAwesomeStuffWithImage( inputImage );
655  \endverbatim
656 
657  \param fName name of the image
658  \param supportedExtensions Supported extensions, defaults to ".nii.gz,.nii"
659  \return itk::ImageFileReader::Pointer templated over the same as requested by user
660  */
661  template <class TImageType = ImageTypeFloat3D >
662  typename TImageType::Pointer ReadImage(const std::string &fName, const std::string &supportedExtensions = ".nii.gz,.nii,.dcm", const std::string &delimitor = ",")
663  {
664  if (cbica::IsDicom(fName))
665  {
666  DicomIOManager< TImageType > dcmSeriesReader;
667  dcmSeriesReader.SetDirectoryPath(fName);
668  bool loadstatus = dcmSeriesReader.LoadDicom();
669  if (!loadstatus)
670  {
671  //QMessageBox::critical(this, "Dicom Loading", "Dicom Load Failed");
672  return nullptr;
673  }
674  return dcmSeriesReader.GetITKImage();
675  }
676  else
677  {
678  return GetImageReader< TImageType >(fName, supportedExtensions, delimitor)->GetOutput();
679  }
680  }
681 
682  /**
683  \brief This is an inline function used to correct the orientation for correct visualization
684 
685  \param inputImage The input image
686  \return TImageType::Pointer templated over the same as requested by user
687  */
688  template< class TImageType >
689  inline typename TImageType::Pointer GetImageWithOrientFix(const typename TImageType::Pointer inputImage)
690  {
691  auto orienter = itk::OrientImageFilter<TImageType, TImageType>::New();
692  orienter->UseImageDirectionOn();
693  orienter->SetDesiredCoordinateOrientation(itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAI);
694  orienter->SetInput(inputImage);
695  orienter->Update();
696 
697  return orienter->GetOutput();
698  }
699 
700  /**
701  \brief The reads the image according to the appropriate extension and outputs the result in ITK's RAI orientation for visualization
702 
703  Usage:
704  \verbatim
705  using ExpectedImageType = itk::Image< float, 3 >;
706  std::string inputFileName = parser.getParameterValue("inputImage");
707  auto inputImage_1 = ReadImageWithOrientFix< ExpectedImageType >(inputFileName);
708  auto inputImage_2 = ReadImageWithOrientFix< ExpectedImageType >(inputFileName, ".nii.gz,.img");
709  DoAwesomeStuffWithImage( inputImage );
710  \endverbatim
711 
712  \param fName File name of the image
713  \param supportedExtensions Supported extensions, defaults to ".nii.gz,.nii"
714  \return TImageType::Pointer templated over the same as requested by user
715  */
716  template< class TImageType >
717  typename TImageType::Pointer ReadImageWithOrientFix(const std::string &fName, const std::string &supportedExtensions = ".nii.gz,.nii", const std::string &delimitor = ",")
718  {
719  std::string extension = cbica::getFilenameExtension(fName);
720  std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
721  /* if (cbica::isDir(fName) || (extension == ".dcm") || (extension == ".dicom"))
722  {
723  return GetImageWithOrientFix<TImageType>(GetDicomImage< TImageType >(fName));
724  }
725  else
726  {*/
727  return GetImageWithOrientFix<TImageType>(GetImageReader< TImageType >(fName, supportedExtensions, delimitor)->GetOutput());
728  // }
729  }
730 
731  /**
732  \brief Get the itk::Image from input file name
733 
734  Usage:
735  \verbatim
736  using ExpectedImageType = itk::Image< float, 3 >;
737  std::string inputFileName = parser.getParameterValue("inputImage");
738  auto inputImage_1 = cbica::ReadImage< ExpectedImageType >(inputFileName);
739  auto inputImage_2 = cbica::ReadImage< ExpectedImageType >(inputFileName, ".nii.gz,.img");
740  DoAwesomeStuffWithImage( inputImage );
741  \endverbatim
742 
743  \param fName File name of the image
744  \param supportedExtensions Supported extensions, defaults to ".nii.gz,.nii"
745  \return itk::ImageFileReader::Pointer templated over the same as requested by user
746  */
747  template <class TImageType = ImageTypeFloat3D >
748  typename TImageType::Pointer GetImage(const std::string &fName, const std::string &supportedExtensions = ".nii.gz,.nii", const std::string &delimitor = ",")
749  {
750  return ReadImage< TImageType >(fName, supportedExtensions, delimitor);
751  }
752 
753 }
bool isDir(const std::string &path)
Return True if path is an existing directory.
void SetDirectoryPath(std::string path)
set the input directory containing dicom series
Definition: DicomIOManager.hxx:18
TImageType::Pointer ReadImageWithOrientFix(const std::string &fName, const std::string &supportedExtensions=".nii.gz,.nii", const std::string &delimitor=",")
The reads the image according to the appropriate extension and outputs the result in ITK's RAI orient...
Definition: cbicaITKSafeImageIO.h:717
void WriteImage(typename ComputedImageType::Pointer imageToWrite, const std::string &fileName)
Get the itk::Image from input dir name.
Definition: cbicaITKSafeImageIO.h:445
std::vector< std::string > stringSplit(const std::string &str, const std::string &delim)
Splits the string.
TImageType::Pointer GetImageWithOrientFix(const typename TImageType::Pointer inputImage)
This is an inline function used to correct the orientation for correct visualization.
Definition: cbicaITKSafeImageIO.h:689
Definition: DicomIOManager.h:24
T::Pointer GetITKImage()
get the read dicom data as 3D float ITK image
Definition: DicomIOManager.hxx:308
Reads any image from file name and generates relevant data.
Definition: cbicaITKImageInfo.h:36
TImageType::Pointer ReadImage(const std::string &fName, const std::string &supportedExtensions=".nii.gz,.nii,.dcm", const std::string &delimitor=",")
Get the itk::Image from input file name.
Definition: cbicaITKSafeImageIO.h:662
Some basic utility functions.
bool LoadDicom()
load dicom data
Definition: DicomIOManager.hxx:23
std::vector< std::string > filesInDirectory(const std::string &dirName, bool returnFullPath=true)
Find all files inside a directory.
std::string normPath(const std::string &path)
Normalize a pathname by collapsing redundant separators and up-level references.
std::string getFilenameExtension(const std::string &filename, bool checkFile=true)
Gets the extension of the supplied file name using splitFileName()
Some basic utility functions.
itk::ImageSeriesReader< TImageType >::Pointer GetDicomImageReader(const std::string &dirName)
Returns the unique series IDs in the specified directory.
Definition: cbicaITKSafeImageIO.h:264
itk::ImageFileReader< TImageType >::Pointer GetImageReader(const std::string &fName, const std::string &supportedExtensions=".nii.gz,.nii,.dcm", const std::string &delimitor=",")
Get the itk::ImageFileReader from input file name.
Definition: cbicaITKSafeImageIO.h:69
std::string getFilenamePath(const std::string &filename, bool checkFile=true)
Gets the path of the supplied file name using splitFileName()
Declaration of the ImageInfo class.
TImageType::Pointer GetImage(const std::string &fName, const std::string &supportedExtensions=".nii.gz,.nii", const std::string &delimitor=",")
Get the itk::Image from input file name.
Definition: cbicaITKSafeImageIO.h:748
bool createDir(const std::string &dir_name)
Create a directory.
std::vector< TDataType > GetUniqueElements(const std::vector< TDataType > &inputVector)
Find the unique elements in a vector.
Definition: cbicaUtilities.h:711