SMILX  1.01
milxDICOMApp.cxx
Go to the documentation of this file.
1 /*=========================================================================
2  The Software is copyright (c) Commonwealth Scientific and Industrial Research Organisation (CSIRO)
3  ABN 41 687 119 230.
4  All rights reserved.
5 
6  Licensed under the CSIRO BSD 3-Clause License
7  You may not use this file except in compliance with the License.
8  You may obtain a copy of the License in the file LICENSE.md or at
9 
10  https://stash.csiro.au/projects/SMILI/repos/smili/browse/license.txt
11 
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 =========================================================================*/
18 #include <vector>
19 #include <locale> //isspace
20 #include <algorithm> //remove_if
21 #include <tclap/CmdLine.h> //Command line parser library
22 
23 // SMILI
24 #include <milxFile.h>
25 #include <milxImage.h>
26 
27 using namespace TCLAP;
28 
29 typedef std::vector< std::string >::iterator stringiterator;
30 typedef std::vector< std::vector< std::string > >::iterator listiterator;
31 typedef std::vector< std::pair<std::string, std::string> >::iterator tagiterator;
32 
33 //Supported operations
34 enum operations {none = 0, info, convert, print, csv};
35 
36 //Image typedefs
37 typedef unsigned char charPixelType;
38 typedef itk::Image<charPixelType, milx::imgDimension> charImageType;
39 typedef short shortPixelType;
40 typedef itk::Image<shortPixelType, milx::imgDimension> shortImageType;
41 typedef unsigned short ushortPixelType;
42 typedef itk::Image<ushortPixelType, milx::imgDimension> ushortImageType;
43 typedef int intPixelType;
44 typedef itk::Image<intPixelType, milx::imgDimension> intImageType;
45 typedef unsigned int uintPixelType;
46 typedef itk::Image<intPixelType, milx::imgDimension> uintImageType;
47 typedef float floatPixelType;
48 typedef itk::Image<floatPixelType, milx::imgDimension> floatImageType;
49 typedef itk::VectorImage<floatPixelType, milx::imgDimension> vectorImageType;
50 
58 int main(int argc, char *argv[])
59 {
60  //---------------------------
62  milx::PrintInfo("--------------------------------------------------------");
63  milx::PrintInfo("SMILI DICOM Image Tool for Images/Volumes.");
64  milx::PrintInfo("(c) Copyright Chandra et al., 2015.");
65  milx::PrintInfo("Version: " + milx::NumberToString(milx::Version));
66  milx::PrintInfo("Supported Pixel/Voxel Types: Float, Short, UShort, Int, UInt, UChar, Vector.");
67  milx::PrintInfo("Use suffix argument to set output format for conversions. Default is Nifti (.nii.gz)");
68  milx::PrintInfo("University of Queensland, Australia.");
69  milx::PrintInfo("Australian e-Health Research Centre, CSIRO, Australia.");
70  milx::PrintInfo("--------------------------------------------------------\n");
71 
72  //---------------------------
74  CmdLine cmd("A diagnostic tool for DICOM series operations", ' ', milx::NumberToString(milx::Version));
75 
77  ValueArg<std::string> outputArg("o", "output", "Output Image", false, "result.nii.gz", "Output");
78  ValueArg<std::string> prefixArg("p", "prefix", "Output prefix for multiple output.", false, "img_", "Output Prefix");
79  ValueArg<std::string> suffixArg("s", "suffix", "Output suffix for output format and additional text.", false, ".nii.gz", "Output Suffix");
80  ValueArg<std::string> exportArg("e", "export-tags", "Output DICOM tags as text pairs to file.", false, "tags.csv", "Export Tags");
81  //~ ValueArg<float> decimateArg("d", "decimate", "Decimate all the meshes provided using the Quadric Decimate algorithm", false, 0.5, "Decimate");
82  //ValueArg<size_t> mergeArg("", "merge", "Merge the labels in labelled images, 0:keep, 1:aggregate, 2:pack, 3:strict.", false, 0, "Merge");
85  SwitchArg infoArg("", "info", "Report the image information(s) for each image in the DICOM series.", false);
86  SwitchArg convertArg("c", "convert", "Convert the DICOM series to image volumes given at output.", false);
87  SwitchArg printArg("t", "print-tags", "Output DICOM tags as text pairs in terminal.", false);
88  SwitchArg recursiveArg("r", "recursive", "Recursively parse series directory provided.", false);
89  SwitchArg instanceArg("", "instance", "Add Instance ID to filename if found.", false);
90  SwitchArg echoArg("", "echo", "Add Echo ID to filename if found.", false);
91  SwitchArg acquisitionArg("", "acquisition", "Add Acquisition ID to filename if found.", true);
92  SwitchArg nocaseIDArg("", "nocase", "Ignore case ID in filename etc.", false);
93 
95  UnlabeledMultiArg<std::string> multinames("series", "DICOM Image series to operate on", true, "Series");
96 
98  cmd.add( multinames );
99  cmd.add( outputArg );
100  cmd.add( prefixArg );
101  cmd.add( suffixArg );
102  cmd.add( recursiveArg );
103  cmd.add( instanceArg );
104  cmd.add( echoArg );
105  cmd.add( acquisitionArg );
106  cmd.add( nocaseIDArg );
108  std::vector<Arg*> xorlist;
109  xorlist.push_back(&infoArg);
110  xorlist.push_back(&convertArg);
111  xorlist.push_back(&printArg);
112  xorlist.push_back(&exportArg);
113  //~ xorlist.push_back(&mseArg);
114 #if (ITK_REVIEW || ITK_VERSION_MAJOR > 3) //Review only members
115 
116 #endif // ITK_REVIEW
117  cmd.xorAdd(xorlist);
118 
120  cmd.parse( argc, argv );
121 
123  //Filenames of surfaces
124  std::vector<std::string> filenames = multinames.getValue();
125  std::string outputName = outputArg.getValue();
126  const std::string prefixName = prefixArg.getValue();
127  const std::string suffixName = suffixArg.getValue();
128  const std::string exportName = exportArg.getValue();
129  const bool recurse = recursiveArg.isSet();
130 
132  operations operation = none;
133  bool outputRequired = false;
134  bool multiOutputRequired = true;
135  if(infoArg.isSet())
136  {
137  outputRequired = false;
138  multiOutputRequired = false;
139 
140  //Info
141  operation = info;
142  }
143  if(convertArg.isSet())
144  {
145  if(outputArg.isSet())
146  {
147  milx::PrintError("Argument Error: Use Prefix (-p) for conversion.");
148  milx::PrintError("Re-run with the prefix name set.");
149  exit(EXIT_FAILURE);
150  }
151 
152  outputRequired = true;
153  multiOutputRequired = false;
154 
155  //Convert
156  operation = convert;
157  }
158  if(exportArg.isSet())
159  {
160  //Export Tags
161  operation = csv;
162  }
163  if(printArg.isSet())
164  {
165  //Print Tags
166  operation = print;
167  }
168 
169  std::cout << "Total Folders: " << filenames.size() << std::endl;
170  if(filenames.empty())
171  {
172  milx::PrintError("No folders provided. Exiting.");
173  exit(EXIT_FAILURE);
174  }
175  for(stringiterator name = filenames.begin(); name != filenames.end(); name ++)
176  std::cout << *name << ", ";
177  std::cout << std::endl;
178 
179  //---------------------------
181  std::cerr << "Reading series UIDs" << std::endl;
182  std::vector< std::vector<std::string> > UIDList;
183  std::vector<std::string> validFilenames;
184  for (stringiterator name = filenames.begin(); name != filenames.end(); name++)
185  {
186  std::vector<std::string> UIDs = milx::File::GetDICOMSeriesUIDs(*name, recurse);
187  milx::PrintInfo("Found " + milx::NumberToString(UIDs.size()) + " in " + *name);
188  if (!UIDs.empty())
189  {
190  UIDList.push_back(UIDs);
191  validFilenames.push_back(*name);
192  }
193  }
194  std::cerr << "Done" << std::endl;
195 
196  if (UIDList.empty())
197  {
198  milx::PrintError("Found no series found in input directory. You may need to provide internal directory paths explicitly.");
199  return EXIT_FAILURE;
200  }
201 
202  stringiterator dir;
203  listiterator list;
204  for (dir = validFilenames.begin(), list = UIDList.begin(); list != UIDList.end(); list++, dir++)
205  {
206  std::cerr << "Applying operation to " << *dir << std::endl;
207  for (stringiterator name = list->begin(); name != list->end(); name++)
208  {
209  std::cerr << "Processing UID: " << *name << std::endl;
210  const std::vector<std::string> seriesFilenames = milx::File::GetDICOMSeriesFilenames(*dir, *name, recurse);
211 
212  //Read Header
213  size_t dimensions = 3;
214  std::string pixelType, componentType;
215  if (!milx::File::ReadImageInformation(seriesFilenames.front(), pixelType, componentType, dimensions))
216  {
217  milx::PrintError("Failed Reading First Image. Check the image type/file. Exiting.");
218  exit(EXIT_FAILURE);
219  }
220  if (infoArg.isSet())
221  {
222  milx::PrintInfo("UID: " + *name);
223  milx::PrintInfo("Pixel Type: " + pixelType);
224  milx::PrintInfo("Component Type: " + componentType);
225  milx::PrintInfo("Dimensions: " + milx::NumberToString(dimensions));
226  }
227 
228  //Open image using relevant type
229  std::string caseID;
230  std::string echoID = "";
231  std::string seriesID = "";
232  std::string acqID = "";
233  std::string instanceID = "";
234  itk::SmartPointer<vectorImageType> vectorImage;
235  itk::SmartPointer<charImageType> labelledImage;
236  itk::SmartPointer<shortImageType> shortImage;
237  itk::SmartPointer<ushortImageType> ushortImage;
238  itk::SmartPointer<intImageType> intImage;
239  itk::SmartPointer<uintImageType> uintImage;
240  itk::SmartPointer<floatImageType> floatImage;
241  std::vector< std::pair<std::string, std::string> > tags;
242  bool labelledImages = false, shortImages = false, ushortImages = false, integerImages = false, uintegerImages = false, vectorImages = false;
243  if (pixelType == "vector" || dimensions > 3)
244  {
245  milx::PrintInfo("Detected vector images.");
246  if (!milx::File::OpenDICOMSeriesAndTags<vectorImageType>(*dir, vectorImage, tags, *name, caseID, echoID, seriesID, acqID, instanceID, recurse)) //Error NOT printed inside
247  {
248  milx::PrintError("Failed Reading Vector Images. Exiting.");
249  exit(EXIT_FAILURE);
250  }
251  vectorImages = true;
252  }
253  else if (componentType == "unsigned_char" || componentType == "unsigned char")
254  {
255  milx::PrintInfo("Detected labelled images.");
256  if (!milx::File::OpenDICOMSeriesAndTags<charImageType>(*dir, labelledImage, tags, *name, caseID, echoID, seriesID, acqID, instanceID, recurse)) //Error NOT printed inside
257  {
258  milx::PrintError("Failed Reading Labelled Images. Exiting.");
259  exit(EXIT_FAILURE);
260  }
261  labelledImages = true;
262  }
263  else if (componentType == "short" || componentType == "int16")
264  {
265  milx::PrintInfo("Detected short images.");
266  if (!milx::File::OpenDICOMSeriesAndTags<shortImageType>(*dir, shortImage, tags, *name, caseID, echoID, seriesID, acqID, instanceID, recurse)) //Error NOT printed inside
267  {
268  milx::PrintError("Failed Reading Short Images. Exiting.");
269  exit(EXIT_FAILURE);
270  }
271  shortImages = true;
272  }
273  else if (componentType == "unsigned_short" || componentType == "unsigned short")
274  {
275  milx::PrintInfo("Detected unsigned short images.");
276  if (!milx::File::OpenDICOMSeriesAndTags<ushortImageType>(*dir, ushortImage, tags, *name, caseID, echoID, seriesID, acqID, instanceID, recurse)) //Error NOT printed inside
277  {
278  milx::PrintError("Failed Reading Unsigned Short Images. Exiting.");
279  exit(EXIT_FAILURE);
280  }
281  ushortImages = true;
282  }
283  else if (componentType == "int" || componentType == "signed" || componentType == "int32" || componentType == "int64")
284  {
285  milx::PrintInfo("Detected integer images.");
286  if (!milx::File::OpenDICOMSeriesAndTags<intImageType>(*dir, intImage, tags, *name, caseID, echoID, seriesID, acqID, instanceID, recurse)) //Error NOT printed inside
287  {
288  milx::PrintError("Failed Reading Integer Images. Exiting.");
289  exit(EXIT_FAILURE);
290  }
291  integerImages = true;
292  }
293  else if (componentType == "unsigned_int" || componentType == "unsigned int" || componentType == "unsigned")
294  {
295  milx::PrintInfo("Detected unsigned int images.");
296  if (!milx::File::OpenDICOMSeriesAndTags<uintImageType>(*dir, uintImage, tags, *name, caseID, echoID, seriesID, acqID, instanceID, recurse)) //Error NOT printed inside
297  {
298  milx::PrintError("Failed Reading Unsigned Integer Images. Exiting.");
299  exit(EXIT_FAILURE);
300  }
301  uintegerImages = true;
302  }
303  else
304  {
305  milx::PrintInfo("Detected floating point images.");
306  if (!milx::File::OpenDICOMSeriesAndTags<floatImageType>(*dir, floatImage, tags, *name, caseID, echoID, seriesID, acqID, instanceID, recurse)) //Error NOT printed inside
307  {
308  milx::PrintError("Failed Reading Images. Exiting.");
309  exit(EXIT_FAILURE);
310  }
311  }
312 
313  //Remove illegal characters from names
314  name->erase(std::remove(name->begin(),name->end(),' '),name->end());
315  caseID.erase(std::remove(caseID.begin(),caseID.end(),' '),caseID.end());
316  if(!echoID.empty())
317  echoID.erase(std::remove(echoID.begin(),echoID.end(),' '),echoID.end());
318  if(!seriesID.empty())
319  seriesID.erase(std::remove(seriesID.begin(),seriesID.end(),' '),seriesID.end());
320  if(!acqID.empty())
321  acqID.erase(std::remove(acqID.begin(),acqID.end(),' '),acqID.end());
322  if(!instanceID.empty())
323  instanceID.erase(std::remove(instanceID.begin(),instanceID.end(),' '),instanceID.end());
324 
325  //Create directories
326  std::string path = caseID + "/" + *name;
327  if(nocaseIDArg.isSet())
328  path = *name;
329  if(!echoID.empty())
330  path += "_" + echoID;
331  if (prefixArg.isSet())
332  path = prefixName + path;
333  milx::PrintDebug("Making Directory " + path);
335 
336  //create filename if needed
337  std::string filename = path + "/" + caseID + "_" + *name;
338  if(nocaseIDArg.isSet())
339  filename = path + "/" + *name;
340  if(!echoID.empty() && echoArg.isSet())
341  filename += "_" + echoID;
342  if(!acqID.empty() && acquisitionArg.isSet())
343  filename += "_Acq_" + acqID;
344  if(!seriesID.empty())
345  filename += "_Series_" + seriesID;
346  if(!instanceID.empty() && instanceArg.isSet())
347  filename += "_Instance_" + instanceID;
348  filename += suffixName;
349  milx::PrintDebug("Output Filename would be " + filename);
350 
351  if(operation == print) //independent operation from pixel type
352  {
353  std::cout << "Tag \t| Value" << std::endl;
354  for (tagiterator tag = tags.begin(); tag != tags.end(); tag++)
355  std::cout << tag->first << "\t| " << tag->second << std::endl;
356  }
357  else if(operation == csv) //independent operation from pixel type
358  {
359  ofstream outFile(exportName.c_str(), ios::app);
360  //outFile << "Tag,Value" << std::endl;
361  for (tagiterator tag = tags.begin(); tag != tags.end(); tag++)
362  outFile << tag->first << "," << tag->second << std::endl;
363  outFile.close();
364  }
365 
366  //std::cout << "Series: " << *name << std::endl;
367  //std::cout << "Case ID: " << caseID << std::endl;
368  if (vectorImages)
369  {
370  switch (operation)
371  {
372  case info:
374  break;
375 
376  case convert:
377  milx::File::SaveImage<vectorImageType>(filename, vectorImage);
378  break;
379 
380  case print:
381  break;
382  case csv:
383  break;
384 
385  case none: //--------------------------------
386  break;
387 
388  default:
389  milx::PrintError("Operation not supported. Exiting");
390  exit(EXIT_FAILURE);
391  break;
392  }
393  }
394  else if (labelledImages)
395  {
396  switch (operation)
397  {
398  case info:
400  break;
401 
402  case convert:
403  milx::File::SaveImage<charImageType>(filename, labelledImage);
404  break;
405 
406  case print:
407  break;
408  case csv:
409  break;
410 
411  case none: //--------------------------------
412  break;
413 
414  default:
415  milx::PrintError("Operation not supported. Exiting");
416  exit(EXIT_FAILURE);
417  break;
418  }
419  }
420  else if (shortImages)
421  {
422  switch (operation)
423  {
424  case info:
426  break;
427 
428  case convert:
429  milx::File::SaveImage<shortImageType>(filename, shortImage);
430  break;
431 
432  case print:
433  break;
434  case csv:
435  break;
436 
437  case none: //--------------------------------
438  break;
439 
440  default:
441  milx::PrintError("Operation not supported. Exiting");
442  exit(EXIT_FAILURE);
443  break;
444  }
445  }
446  else if (ushortImages)
447  {
448  switch (operation)
449  {
450  case info:
452  break;
453 
454  case convert:
455  milx::File::SaveImage<ushortImageType>(filename, ushortImage);
456  break;
457 
458  case print:
459  break;
460  case csv:
461  break;
462 
463  case none: //--------------------------------
464  break;
465 
466  default:
467  milx::PrintError("Operation not supported. Exiting");
468  exit(EXIT_FAILURE);
469  break;
470  }
471  }
472  else if (integerImages)
473  {
474  switch (operation)
475  {
476  case info:
478  break;
479 
480  case convert:
481  milx::File::SaveImage<intImageType>(filename, intImage);
482  break;
483 
484  case print:
485  break;
486  case csv:
487  break;
488 
489  case none: //--------------------------------
490  break;
491 
492  default:
493  milx::PrintError("Operation not supported. Exiting");
494  exit(EXIT_FAILURE);
495  break;
496  }
497  }
498  else if (uintegerImages)
499  {
500  switch (operation)
501  {
502  case info:
504  break;
505 
506  case convert:
507  milx::File::SaveImage<uintImageType>(filename, uintImage);
508  break;
509 
510  case print:
511  break;
512  case csv:
513  break;
514 
515  case none: //--------------------------------
516  break;
517 
518  default:
519  milx::PrintError("Operation not supported. Exiting");
520  exit(EXIT_FAILURE);
521  break;
522  }
523  }
524  else
525  {
526  switch (operation)
527  {
528  case info:
530  break;
531 
532  case convert:
533  milx::File::SaveImage<floatImageType>(filename, floatImage);
534  break;
535 
536  case print:
537  break;
538  case csv:
539  break;
540 
541  case none: //--------------------------------
542  break;
543 
544  default:
545  milx::PrintError("Operation not supported. Exiting");
546  exit(EXIT_FAILURE);
547  break;
548  }
549  }
550  std::cerr << "Done" << std::endl;
551  }
552  std::cerr << "Done" << std::endl;
553  }
554 
555  //---------------------------
556  milx::PrintInfo("Operation Complete");
557  return EXIT_SUCCESS;
558 }
void PrintDebug(const std::string msg)
Displays a generic msg to standard error with carriage return if in Debug mode.
Definition: milxGlobal.h:192
static std::vector< std::string > GetDICOMSeriesFilenames(const std::string directoryPath, const std::string seriesName, bool recursive=false)
Returns the filenames for a given UID/Series name for a given directory.
Definition: milxFile.cxx:151
void PrintError(const std::string msg)
Displays a generic msg to standard error with carriage return.
Definition: milxGlobal.h:202
int main(int argc, char *argv[])
This program Animates image slices and animates surfaces in the model view for creating movies...
void PrintInfo(const std::string msg)
Displays a generic msg to standard output with carriage return.
Definition: milxGlobal.h:174
std::string NumberToString(double num, unsigned zeroPad=0)
Number to string converter.
Definition: milxGlobal.h:112
static void Information(itk::SmartPointer< TImage > img)
Prints the information of the image to standard output.
Definition: milxImage.h:2067
static bool ReadImageInformation(const std::string filename, std::string &pixeltype, std::string &componentType, size_t &dimensions)
Reads just the header of an image file (without reading the image data), and writes the info into the...
Definition: milxFile.cxx:69
static std::vector< std::string > GetDICOMSeriesUIDs(const std::string directoryPath, bool recursive=false)
DICOM Related.
Definition: milxFile.cxx:115
static void MakeDirectory(std::string name)
Creates directory (using ITK) if none exists.
Definition: milxFile.h:495