SMILX  1.01
milxImageApp.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 <tclap/CmdLine.h> //Command line parser library
20 
21 // SMILI
22 #include <milxFile.h>
23 #include <milxImage.h>
24 
25 using namespace TCLAP;
26 
27 typedef std::vector< std::string >::iterator stringiterator;
28 
29 //Supported operations
30 enum operations {none = 0, info, convert, labelinfo, rescale, invert, relabel, smooth, bilateral, median, gradmag, laplacian, distancemap, threshold, Otsu, crop, mask, resample, match, checker, add, diff, mean, merge, cast, flip};
31 
32 //Image stuff
33 typedef unsigned char charPixelType;
34 typedef itk::Image<charPixelType, milx::imgDimension> charImageType;
35 typedef float floatPixelType;
36 typedef itk::Image<floatPixelType, milx::imgDimension> floatImageType;
37 typedef itk::VectorImage<floatPixelType, milx::imgDimension> vectorImageType;
38 
69 int main(int argc, char *argv[])
70 {
71  //---------------------------
73  milx::PrintInfo("--------------------------------------------------------");
74  milx::PrintInfo("SMILI Image Tool for Images/Volumes.");
75  milx::PrintInfo("(c) Copyright Chandra et al., 2015.");
76  milx::PrintInfo("Version: " + milx::NumberToString(milx::Version));
77  milx::PrintInfo("University of Queensland, Australia.");
78  milx::PrintInfo("Australian e-Health Research Centre, CSIRO, Australia.");
79  milx::PrintInfo("--------------------------------------------------------\n");
80 
81  //---------------------------
83  CmdLine cmd("A diagnostic tool for image/volume operations", ' ', milx::NumberToString(milx::Version));
84 
86  ValueArg<size_t> threadsArg("", "threads", "Set he number of global threads to use.", false, milx::NumberOfProcessors(), "Threads");
87  ValueArg<std::string> outputArg("o", "output", "Output Image", false, "result.nii.gz", "Output");
88  ValueArg<std::string> prefixArg("p", "prefix", "Output prefix for multiple output.", false, "img_", "Output Prefix");
89  //~ ValueArg<float> decimateArg("d", "decimate", "Decimate all the meshes provided using the Quadric Decimate algorithm", false, 0.5, "Decimate");
90  ValueArg<float> smoothArg("s", "smooth", "Smooth the images using the Gradient Anisotropic Diffusion algorithm given timestep (use negative value to auto select).", false, -1.0, "Smooth");
91  ValueArg<float> bilateralArg("", "bilateral", "Smooth the images using the Bilteral algorithm given range sigma. Use sigma argument for provide spatial sigma.", false, 0.1, "Bilateral");
92  ValueArg<size_t> medianArg("", "median", "Smooth the images using the median filtering given the number of pixels in neighbourhood.", false, 1, "Median");
93  ValueArg<size_t> mergeArg("", "merge", "Merge the labels in labelled images, 0:keep, 1:aggregate, 2:pack, 3:strict.", false, 0, "Merge");
94  ValueArg<std::string> cropArg("", "crop", "Masks and crops the images using the mask image name provided.", false, "mask.nii.gz", "Crop");
95  ValueArg<std::string> maskArg("m", "mask", "Masks the images using the mask image name provided.", false, "mask.nii.gz", "Mask");
96  ValueArg<std::string> resampleArg("", "resample", "Resample the images to the reference image provided assuming they are both already in the correct space.", false, "reference.nii.gz", "Resample");
97  ValueArg<std::string> matchArg("", "match", "Match the histograms of the images to the reference image provided.", false, "reference.nii.gz", "Match");
98  ValueArg<std::string> checkerArg("", "checkerboard", "Checkerboard all of the images to the reference image provided.", false, "reference.nii.gz", "Checkerboard");
99  //~ ValueArg<float> scaleArg("", "scale", "Scale the coordinates of the point", false, 0.9, "Scale");
100  ValueArg<float> aboveArg("", "above", "Add above value to operation (such as to thresholding).", false, 0.0, "Above");
101  ValueArg<float> belowArg("", "below", "Add below value to operation (such as to thresholding).", false, 255.0, "Below");
102  ValueArg<float> insideArg("", "inside", "Add inside value to operation (such as to thresholding).", false, 1.0, "Inside");
103  ValueArg<float> sigmaArg("", "sigma", "Sigma (Stddev) value to operation (such as to bilateral).", false, 1.0, "Sigma");
104  ValueArg<size_t> iterationsArg("i", "iterations", "Number of iterations to use in the operation (such as smoothing).", false, 5, "Labels");
105  ValueArg<size_t> labelsArg("", "labels", "Number of labels to use in the operation (such as Otsu thresholding) else print labelling info.", false, 3, "Labels");
106  ValueArg<size_t> OtsuArg("", "Otsu", "Otsu multiple threshold with the number of bins to use.", false, 128, "Otsu");
107  ValueArg<size_t> paddingArg("", "padding", "Number of pixels to pad in the operation in question (such as crop).", false, 1, "Padding");
108  ValueArg<size_t> flipArg("f", "flip", "Flip the image about origin at axis indicated (0: x-axis, 1:y-axis, 2:z-axis).", false, 0, "Flip");
111  SwitchArg infoArg("", "info", "Report the image information(s).", false);
112  SwitchArg labelInfoArg("", "labelinfo", "Report the label information for a labelled image. Same as --info --labels arguments.", false);
113  SwitchArg convertArg("c", "convert", "Convert the image from current format to the one given at output.", false);
114  SwitchArg gradMagArg("g", "gradmag", "Compute the Gradient magnitude (show edges) of the image(s).", false);
115  SwitchArg laplacianArg("l", "laplacian", "Compute the Laplacian of the image(s).", false);
116  SwitchArg distancemapArg("d", "distancemap", "Compute the Signed Distance map of the image(s).", false);
117  SwitchArg thresholdArg("t", "threshold", "Compute the Threshold of the image(s).", false);
118  SwitchArg binaryArg("b", "binary", "Compute the binary version of the operation(s).", false);
119  SwitchArg rescaleArg("r", "rescale", "Re-scale the intensities of the image to a given range (set inconjuction with the above and below arguments).", false);
120  SwitchArg invertArg("", "invert", "Invert the intensities of the images.", false);
121  SwitchArg relabelArg("", "relabel", "Relabel the labels of the labelled image consecutatively based on connectivity.", false);
122  SwitchArg addArg("", "add", "Add/Sum the provided images together (pixel-wise).", false);
123  SwitchArg diffArg("", "diff", "Difference the provided images from the first image (pixel-wise).", false);
124  SwitchArg meanArg("", "mean", "Average the provided images together (pixel-wise).", false);
125  SwitchArg castArg("", "cast", "Cast the images from 8-bit to floating-point type (or vice-versa) depending on the input type.", false);
126  //~ SwitchArg mseArg("", "mse", "Mean Squared Error of Points in models", false);
127  //~ SwitchArg rigidArg("", "rigid", "Rigid alignment of surfaces assuming points have correspondence.", false);
128  //~ SwitchArg saveTransformsArg("", "savetransforms", "Save the transformation matrix after surface alignment. For use with --icp", false);
129 
131  UnlabeledMultiArg<std::string> multinames("images", "Images to operate on (pixel type is auto detected from the first image)", true, "Images");
132 
134  cmd.add( multinames );
135  cmd.add( threadsArg );
136  cmd.add( outputArg );
137  cmd.add( prefixArg );
138  //~ cmd.add( rigidArg );
139  //~ cmd.add( saveTransformsArg );
140  cmd.add( aboveArg );
141  cmd.add( belowArg );
142  cmd.add( insideArg );
143  cmd.add( sigmaArg );
144  cmd.add( binaryArg );
145  cmd.add( iterationsArg );
146  cmd.add( labelsArg );
147  cmd.add( paddingArg );
149  std::vector<Arg*> xorlist;
150  xorlist.push_back(&infoArg);
151  xorlist.push_back(&convertArg);
152  //~ xorlist.push_back(&scaleArg);
153  //~ xorlist.push_back(&decimateArg);
154  xorlist.push_back(&smoothArg);
155  xorlist.push_back(&bilateralArg);
156  xorlist.push_back(&medianArg);
157  xorlist.push_back(&gradMagArg);
158  xorlist.push_back(&laplacianArg);
159  xorlist.push_back(&distancemapArg);
160  xorlist.push_back(&thresholdArg);
161  xorlist.push_back(&OtsuArg);
162  xorlist.push_back(&rescaleArg);
163  xorlist.push_back(&invertArg);
164  xorlist.push_back(&relabelArg);
165  xorlist.push_back(&maskArg);
166  xorlist.push_back(&resampleArg);
167  xorlist.push_back(&matchArg);
168  xorlist.push_back(&checkerArg);
169  xorlist.push_back(&addArg);
170  xorlist.push_back(&diffArg);
171  xorlist.push_back(&meanArg);
172  xorlist.push_back(&castArg);
173  xorlist.push_back(&flipArg);
174  //~ xorlist.push_back(&mseArg);
175 #if (ITK_REVIEW || ITK_VERSION_MAJOR > 3) //Review only members
176  xorlist.push_back(&labelInfoArg);
177  xorlist.push_back(&mergeArg);
178  xorlist.push_back(&cropArg);
179 #endif // ITK_REVIEW
180  cmd.xorAdd(xorlist);
181 
183  cmd.parse( argc, argv );
184 
186  //Filenames of surfaces
187  const size_t threads = threadsArg.getValue();
188  std::vector<std::string> filenames = multinames.getValue();
189  std::string outputName = outputArg.getValue();
190  const std::string prefixName = prefixArg.getValue();
191  const std::string resampleName = resampleArg.getValue();
192  const std::string matchName = matchArg.getValue();
193  const std::string checkerName = checkerArg.getValue();
194  //~ const float decimateFactor = decimateArg.getValue();
195  //~ const float scaleFactor = scaleArg.getValue();
196  const float smoothTimestep = smoothArg.getValue();
197  const float rangeSigma = bilateralArg.getValue();
198  const size_t radius = medianArg.getValue();
199  const size_t mergeType = mergeArg.getValue();
200  float aboveValue = aboveArg.getValue();
201  float belowValue = belowArg.getValue();
202  float insideValue = insideArg.getValue();
203  float sigmaValue = sigmaArg.getValue();
204  size_t iterations = iterationsArg.getValue(); //number of labels
205  size_t labelsValue = labelsArg.getValue(); //number of labels
206  size_t OtsuValue = OtsuArg.getValue(); //number of bins
207  size_t paddingValue = paddingArg.getValue(); //number of bins
208  size_t flipAxis = flipArg.getValue(); //number of bins
209 
210  std::string maskName;
211  if(cropArg.isSet())
212  maskName = cropArg.getValue();
213  else
214  maskName = maskArg.getValue();
215 
217  itk::MultiThreader::SetGlobalDefaultNumberOfThreads(threads);
218  milx::PrintInfo("Threads to use: " + milx::NumberToString(threads));
219 
221  operations operation = none;
222  bool outputRequired = false;
223  bool multiOutputRequired = true;
224  if(infoArg.isSet())
225  {
226  outputRequired = false;
227  multiOutputRequired = false;
228 
229  //Info
230  operation = info;
231  }
232  if(labelInfoArg.isSet())
233  {
234  outputRequired = false;
235  multiOutputRequired = false;
236 
237  //Info
238  operation = labelinfo;
239  }
240  if(convertArg.isSet())
241  {
242  if(!outputArg.isSet())
243  {
244  milx::PrintError("Argument Error: Use Output (-o) for conversion.");
245  milx::PrintError("Re-run with the output name set.");
246  exit(EXIT_FAILURE);
247  }
248  if(filenames.size() != 1)
249  {
250  milx::PrintError("Argument Error: Only one output is supported for conversion.");
251  milx::PrintError("Re-run with the correct number of inputs.");
252  exit(EXIT_FAILURE);
253  }
254 
255  outputRequired = true;
256  multiOutputRequired = false;
257 
258  //Info
259  operation = convert;
260  }
261  if( smoothArg.isSet() || bilateralArg.isSet() || medianArg.isSet() || gradMagArg.isSet() || laplacianArg.isSet() || distancemapArg.isSet() || cropArg.isSet() || maskArg.isSet()
262  || resampleArg.isSet() || matchArg.isSet() || checkerArg.isSet() || thresholdArg.isSet() || OtsuArg.isSet() || rescaleArg.isSet() || invertArg.isSet() || relabelArg.isSet() || addArg.isSet()
263  || diffArg.isSet() || meanArg.isSet() || mergeArg.isSet() || castArg.isSet() || flipArg.isSet() )
264  {
266  if(filenames.size() == 1 && (addArg.isSet() || diffArg.isSet() || meanArg.isSet() || mergeArg.isSet()))
267  {
268  milx::PrintError("Argument Error: Cannot use arithmetic operations on single input.");
269  milx::PrintError("Re-run with more inputs set.");
270  exit(EXIT_FAILURE);
271  }
272  else if(filenames.size() == 1 && prefixArg.isSet())
273  {
274  milx::PrintError("Argument Error: Cannot use prefix argument on single input.");
275  milx::PrintError("Re-run with output (-o) option set instead.");
276  exit(EXIT_FAILURE);
277  }
278  else if(filenames.size() == 1)
279  {
280  outputRequired = true;
281  multiOutputRequired = false;
282  }
283  else if(filenames.size() > 1 && (addArg.isSet() || diffArg.isSet() || meanArg.isSet() || mergeArg.isSet()))
284  {
285  outputRequired = true;
286  multiOutputRequired = false;
287  }
288  else if(outputArg.isSet() && filenames.size() > 1)
289  {
290  milx::PrintError("Argument Error: Use Output Prefix (-p) for multiple inputs.");
291  milx::PrintError("Re-run with the prefix name set.");
292  exit(EXIT_FAILURE);
293  }
294  else if(!prefixArg.isSet() && filenames.size() > 1)
295  {
296  milx::PrintError("Argument Error: Output Prefix (-p) must be provided for multiple inputs.");
297  milx::PrintError("Re-run with the prefix name set.");
298  exit(EXIT_FAILURE);
299  }
300 
301  //Operations allowed
302  if(smoothArg.isSet())
303  operation = smooth;
304  if(bilateralArg.isSet())
305  operation = bilateral;
306  if(medianArg.isSet())
307  operation = median;
308  if(gradMagArg.isSet())
309  operation = gradmag;
310  if(laplacianArg.isSet())
311  operation = laplacian;
312  if(distancemapArg.isSet())
313  operation = distancemap;
314  if(thresholdArg.isSet())
315  operation = threshold;
316  if(cropArg.isSet())
317  operation = crop;
318  if(maskArg.isSet())
319  operation = mask;
320  if(resampleArg.isSet())
321  operation = resample;
322  if(matchArg.isSet())
323  operation = match;
324  if(checkerArg.isSet())
325  operation = checker;
326  if(OtsuArg.isSet())
327  operation = Otsu;
328  if(rescaleArg.isSet())
329  operation = rescale;
330  if(invertArg.isSet())
331  operation = invert;
332  if(relabelArg.isSet())
333  operation = relabel;
334  if(addArg.isSet())
335  operation = add;
336  if(diffArg.isSet())
337  operation = diff;
338  if(meanArg.isSet())
339  operation = mean;
340  if(mergeArg.isSet())
341  operation = merge;
342  if(castArg.isSet())
343  operation = cast;
344  if(flipArg.isSet())
345  operation = flip;
346  }
347  if(aboveArg.isSet() || belowArg.isSet())
348  {
349  if(!thresholdArg.isSet() && !rescaleArg.isSet())
350  {
351  milx::PrintError("Argument Error: Another argument (such as threshold) must be provided.");
352  milx::PrintError("Re-run with one of these flags set.");
353  exit(EXIT_FAILURE);
354  }
355  }
356  if(labelsArg.isSet())
357  {
358  if(!OtsuArg.isSet() && infoArg.isSet())
359  {
360  #if (ITK_REVIEW || ITK_VERSION_MAJOR > 3) //Review only members
361  milx::PrintError("Argument Warning: Another argument (such as Otsu) not provided. Printing label info instead.");
362  operation = labelinfo;
363  #else
364  milx::PrintError("Argument Warning: Another argument (such as Otsu) not provided and ITK version not sufficient for printing label info. Exiting.");
365  exit(EXIT_FAILURE);
366  #endif
367  }
368  }
369  if(iterationsArg.isSet())
370  {
371  if(!smoothArg.isSet())
372  {
373  milx::PrintError("Argument Error: Another argument (such as smoothing) must be provided.");
374  milx::PrintError("Re-run with one of these flags set.");
375  exit(EXIT_FAILURE);
376  }
377  }
378  if(sigmaArg.isSet())
379  {
380  if(!bilateralArg.isSet())
381  {
382  milx::PrintError("Argument Error: Another argument (such as bilateral) must be provided.");
383  milx::PrintError("Re-run with one of these flags set.");
384  exit(EXIT_FAILURE);
385  }
386  }
387  if(paddingArg.isSet())
388  {
389  if(!cropArg.isSet())
390  {
391  milx::PrintError("Argument Error: Another argument (such as crop) must be provided.");
392  milx::PrintError("Re-run with one of these flags set.");
393  exit(EXIT_FAILURE);
394  }
395  }
396  if(flipArg.isSet())
397  {
398  if(flipAxis < 0 || flipAxis > 2)
399  {
400  milx::PrintError("Argument Error: Incorrect value provided for axis flipping.");
401  milx::PrintError("Re-run with value correctly set.");
402  exit(EXIT_FAILURE);
403  }
404  }
405  if(thresholdArg.isSet() || rescaleArg.isSet())
406  {
407  if( (!aboveArg.isSet() || !belowArg.isSet()) && rescaleArg.isSet() )
408  {
409  milx::PrintError("Argument Error: Above and below argument(s) must be provided.");
410  milx::PrintError("Re-run with one of these flags set.");
411  exit(EXIT_FAILURE);
412  }
413  else
414  {
415  if( (!aboveArg.isSet() && !belowArg.isSet()) && !binaryArg.isSet() )
416  {
417  milx::PrintError("Argument Error: Either above or below argument(s) must be provided.");
418  milx::PrintError("Re-run with one of these flags set.");
419  exit(EXIT_FAILURE);
420  }
421  if( (!aboveArg.isSet() || !belowArg.isSet() || !insideArg.isSet()) && binaryArg.isSet() )
422  {
423  milx::PrintError("Argument Error: The inside, above and below argument(s) must be provided.");
424  milx::PrintError("Re-run with one of these flags set.");
425  exit(EXIT_FAILURE);
426  }
427  }
428  }
429 
430  std::cout << "Total Images: " << filenames.size() << std::endl;
431  if(filenames.empty())
432  {
433  milx::PrintError("No image file names provided. Exiting.");
434  exit(EXIT_FAILURE);
435  }
436  for(stringiterator name = filenames.begin(); name != filenames.end(); name ++)
437  std::cout << *name << ", ";
438  std::cout << std::endl;
439 
440  //---------------------------
442  std::cerr << "Reading Header for type info etc." << std::endl;
443  std::string pixelType, componentType;
444  size_t dimensions = 3;
445  if( !milx::File::ReadImageInformation(filenames[0], pixelType, componentType, dimensions) )
446  {
447  milx::PrintError("Failed Reading First Image. Check the image type/file. Exiting.");
448  exit(EXIT_FAILURE);
449  }
450  if(infoArg.isSet() || labelInfoArg.isSet())
451  {
452  milx::PrintInfo("Pixel Type: " + pixelType);
453  milx::PrintInfo("Component Type: " + componentType);
454  milx::PrintInfo("Dimensions: " + milx::NumberToString(dimensions));
455  }
456 
457  std::cerr << "Reading Images... ";
458  std::vector< itk::SmartPointer<vectorImageType> > vectorCollection;
459  std::vector< itk::SmartPointer<charImageType> > labelledCollection;
460  std::vector< itk::SmartPointer<floatImageType> > collection;
461  bool labelledImages = false, vectorImages = false;
462  if(pixelType == "vector" || dimensions > 3)
463  {
464  milx::PrintInfo("Detected vector images.");
465  if( !milx::File::OpenImages<vectorImageType>(filenames, vectorCollection) ) //Error NOT printed inside
466  {
467  milx::PrintError("Failed Reading Vector Images. Exiting.");
468  exit(EXIT_FAILURE);
469  }
470  vectorImages = true;
471  std::cout << "Read " << vectorCollection.size() << " vector images" << std::endl;
472 
473  #if ITK_MAJOR_VERSION > 3
474  if(!infoArg.isSet() && !maskArg.isSet() && !addArg.isSet() && !diffArg.isSet() && !meanArg.isSet())
475  #else
476  if(!infoArg.isSet() && !addArg.isSet() && !diffArg.isSet() && !meanArg.isSet())
477  #endif
478  {
479  milx::PrintError("Input Error: Operation provided not supported for vector images yet.");
480  exit(EXIT_FAILURE);
481  }
482  }
483  else if(componentType == "unsigned_char" || componentType == "unsigned char")
484  {
485  milx::PrintInfo("Detected labelled images.");
486  if( !milx::File::OpenImages<charImageType>(filenames, labelledCollection) ) //Error NOT printed inside
487  {
488  milx::PrintError("Failed Reading Labelled Images. Exiting.");
489  exit(EXIT_FAILURE);
490  }
491  labelledImages = true;
492  std::cout << "Read " << labelledCollection.size() << " labels" << std::endl;
493  }
494  else
495  {
496  milx::PrintInfo("Detected floating point images.");
497  if( !milx::File::OpenImages<floatImageType>(filenames, collection) ) //Error NOT printed inside
498  {
499  milx::PrintError("Failed Reading Images. Exiting.");
500  exit(EXIT_FAILURE);
501  }
502  std::cout << "Read " << collection.size() << " images" << std::endl;
503  }
504 
505  charImageType::Pointer maskImage;
506  if(cropArg.isSet() || maskArg.isSet())
507  {
508  if(!milx::File::OpenImage<charImageType>(maskName, maskImage))
509  {
510  milx::PrintError("Failed Reading Mask. Exiting.");
511  exit(EXIT_FAILURE);
512  }
513  std::cout << "Read " << maskName << " as mask" << std::endl;
514  }
515  std::cerr << "Done" << std::endl;
516 
517  //---------------------------
519  vectorImageType::Pointer vecResult;
520  charImageType::Pointer charResult;
521  floatImageType::Pointer floatResult;
522  std::cerr << "Applying... ";
523  if(vectorImages)
524  {
525  switch(operation)
526  {
527  case info:
529  break;
530 
531  case convert:
532 
533  break;
534 
535  #if ITK_MAJOR_VERSION > 3
536  case mask:
537  milx::Image<vectorImageType>::MaskCollection<charImageType>(vectorCollection, maskImage);
538  break;
539  #endif
540 
541  case add:
542  vecResult = milx::Image<vectorImageType>::AddCollection(vectorCollection);
543  break;
544 
545  case diff:
546  vecResult = milx::Image<vectorImageType>::DifferenceCollection(vectorCollection);
547  break;
548 
549  case mean:
550  vecResult = milx::Image<vectorImageType>::AverageVectorCollection(vectorCollection, vectorCollection[0]->GetNumberOfComponentsPerPixel());
551  break;
552 
553  case none: //--------------------------------
554  break;
555 
556  default:
557  milx::PrintError("Operation not supported. Exiting");
558  exit(EXIT_FAILURE);
559  break;
560  }
561  }
562  else if(labelledImages)
563  {
564  switch(operation)
565  {
566  case info:
568  break;
569 
570  case convert:
571 
572  break;
573 
574  #if (ITK_REVIEW || ITK_VERSION_MAJOR > 3)
575  case labelinfo:
576  {
577  milx::PrintInfo("\n>> Labels present: ");
578  milx::PrintInfo("-----------------------------------------------");
579  std::vector<unsigned char> allValues;
580  for(size_t k = 0; k < labelledCollection.size(); k ++)
581  {
582  milx::PrintInfo("Image " + milx::NumberToString(k));
583  milx::PrintInfo(milx::File::ExtractFilename(filenames[k]) + ": \n");
584  std::vector<unsigned char> values = milx::Image<charImageType>::LabelValues(labelledCollection[k]);
585  for(size_t j = 0; j < values.size(); j ++)
586  {
587  allValues.push_back(values[j]);
588  std::cout << static_cast<unsigned>(values[j]) << ", ";
589  }
590  std::cout << std::endl;
591  }
592  milx::PrintInfo("\n>> All Labels present (as single list, n = " + milx::NumberToString(allValues.size()) + "): ");
593  for(size_t j = 0; j < allValues.size(); j ++)
594  std::cout << static_cast<unsigned>(allValues[j]) << ", ";
595  std::cout << std::endl;
596  break;
597  }
598  #endif
599 
600  case rescale:
601  milx::Image<charImageType>::RescaleIntensityCollection(labelledCollection, belowValue, aboveValue);
602  break;
603 
604  case invert:
606  break;
607 
608  case relabel:
610  break;
611 
612  case smooth:
613  {
614  collection = milx::Image<charImageType>::AnisotropicDiffusionCollection<floatImageType>(labelledCollection, iterations, smoothTimestep);
615  labelledImages = false;
616  break;
617  }
618 
619  case median:
620  milx::Image<charImageType>::MedianCollection(labelledCollection, radius);
621  break;
622 
623  case bilateral:
624  milx::Image<charImageType>::BilateralCollection(labelledCollection, rangeSigma, sigmaValue);
625  break;
626 
627  case gradmag:
629  break;
630 
631  case laplacian:
633  break;
634 
635  case distancemap:
636  {
637  const bool binary = false, signedDistance = true, insideDistance = false, squaredDistance = false;
638 
639  collection = milx::Image<charImageType>::DistanceMapCollection<floatImageType>(labelledCollection, binary, signedDistance, insideDistance, squaredDistance);
640 
641  labelledImages = false;
642  break;
643  }
644 
645  case threshold:
646  if(aboveArg.isSet() && !belowArg.isSet())
647  {
648  milx::PrintInfo("Thresholding Above " + milx::NumberToString(aboveValue));
649  milx::Image<charImageType>::ThresholdAboveCollection(labelledCollection, 0.0, aboveValue);
650  }
651  else if(!aboveArg.isSet() && belowArg.isSet())
652  {
653  milx::PrintInfo("Thresholding Below " + milx::NumberToString(belowValue));
654  milx::Image<charImageType>::ThresholdBelowCollection(labelledCollection, 0.0, belowValue);
655  }
656  else if(binaryArg.isSet())
657  {
658  milx::PrintInfo("Binary Thresholding Above " + milx::NumberToString(aboveValue) + " and Below " + milx::NumberToString(belowValue));
659  labelledCollection = milx::Image<charImageType>::BinaryThresholdCollection<charImageType>(labelledCollection, 0.0, insideValue, belowValue, aboveValue);
660  }
661  else
662  {
663  milx::PrintInfo("Thresholding Above " + milx::NumberToString(aboveValue) + " and Below " + milx::NumberToString(belowValue));
664  milx::Image<charImageType>::ThresholdCollection(labelledCollection, 0.0, belowValue, aboveValue);
665  }
666 
667  break;
668 
669  case Otsu:
670  milx::PrintInfo("Otsu Multiple Thresholding with " + milx::NumberToString(OtsuValue) + " and " + milx::NumberToString(labelsValue) + " labels");
671  labelledCollection = milx::Image<charImageType>::OtsuMultipleThresholdCollection<charImageType>(labelledCollection, OtsuValue, labelsValue);
672  break;
673 
674  case crop:
675  #if (ITK_REVIEW || ITK_VERSION_MAJOR > 3) //Review only members
676  milx::Image<charImageType>::MaskAndCropCollection<charImageType>(labelledCollection, maskImage, paddingValue);
677  #endif
678  break;
679 
680  case mask:
681  milx::Image<charImageType>::MaskCollection<charImageType>(labelledCollection, maskImage);
682  break;
683 
684  case resample:
685  {
686  charImageType::Pointer referenceImage;
687  if(resampleArg.isSet())
688  {
689  if(!milx::File::OpenImage<charImageType>(resampleName, referenceImage))
690  {
691  milx::PrintError("Failed Reading Reference Image. Exiting.");
692  exit(EXIT_FAILURE);
693  }
694  std::cout << "Read " << resampleName << " as the reference image for resampling" << std::endl;
695  }
696  milx::Image<charImageType>::ResampleLabelCollection<charImageType>(labelledCollection, referenceImage);
697  break;
698  }
699 
700  case match:
701  {
702  charImageType::Pointer referenceImage;
703  if(matchArg.isSet())
704  {
705  if(!milx::File::OpenImage<charImageType>(matchName, referenceImage))
706  {
707  milx::PrintError("Failed Reading Reference Image. Exiting.");
708  exit(EXIT_FAILURE);
709  }
710  std::cout << "Read " << matchName << " as the reference image for matching" << std::endl;
711  }
712  milx::Image<charImageType>::MatchHistogramCollection(labelledCollection, referenceImage);
713  break;
714  }
715 
716  case checker:
717  {
718  charImageType::Pointer referenceImage;
719  if(checkerArg.isSet())
720  {
721  if(!milx::File::OpenImage<charImageType>(checkerName, referenceImage))
722  {
723  milx::PrintError("Failed Reading Reference Image. Exiting.");
724  exit(EXIT_FAILURE);
725  }
726  std::cout << "Read " << checkerName << " as the reference image for checkerboarding" << std::endl;
727  }
728  milx::Image<charImageType>::CheckerboardCollection(labelledCollection, referenceImage);
729  break;
730  }
731 
732  case add:
733  charResult = milx::Image<charImageType>::AddCollection(labelledCollection);
734  break;
735 
736  case diff:
737  charResult = milx::Image<charImageType>::DifferenceCollection(labelledCollection);
738  break;
739 
740  case mean:
741  floatResult = milx::Image<charImageType>::AverageCollection<floatImageType>(labelledCollection);
742  labelledImages = false;
743  break;
744 
745 
746  case merge:
747  #if (ITK_REVIEW || ITK_VERSION_MAJOR > 3) //Review only members
748  charResult = milx::Image<charImageType>::MergeLabelledImages(labelledCollection, mergeType);
749  #endif
750  break;
751 
752  case cast:
753  collection = milx::Image<charImageType>::CastCollection<floatImageType>(labelledCollection);
754  labelledCollection.clear();
755  labelledImages = false;
756  break;
757 
758  case flip:
759  if(flipAxis == 0)
760  milx::Image<charImageType>::FlipCollection(labelledCollection, true, false, false, true);
761  else if(flipAxis == 1)
762  milx::Image<charImageType>::FlipCollection(labelledCollection, false, true, false, true);
763  else
764  milx::Image<charImageType>::FlipCollection(labelledCollection, false, false, true, true);
765  break;
766 
767  case none: //--------------------------------
768  break;
769 
770  default:
771  milx::PrintError("Operation not supported. Exiting");
772  exit(EXIT_FAILURE);
773  break;
774  }
775  std::cerr << "Done" << std::endl;
776  }
777  else
778  {
779  switch(operation)
780  {
781  case info:
783  break;
784 
785  case convert:
786 
787  break;
788 
789  #if (ITK_REVIEW || ITK_VERSION_MAJOR > 3)
790  case labelinfo:
791  {
792  milx::PrintWarning("Float images found and will be cast to 8-bit images for labelling info.");
793  labelledCollection = milx::Image<floatImageType>::CastCollection<charImageType>(collection);
794  collection.clear();
795 
796  milx::PrintInfo("\n>> Labels present: ");
797  milx::PrintInfo("-----------------------------------------------");
798  std::vector<unsigned char> allValues;
799  for(size_t k = 0; k < labelledCollection.size(); k ++)
800  {
801  milx::PrintInfo("Image " + milx::NumberToString(k));
802  milx::PrintInfo(milx::File::ExtractFilename(filenames[k]) + ": \n");
803  std::vector<unsigned char> values = milx::Image<charImageType>::LabelValues(labelledCollection[k]);
804  for(size_t j = 0; j < values.size(); j ++)
805  {
806  allValues.push_back(values[j]);
807  std::cout << static_cast<unsigned>(values[j]) << ", ";
808  }
809  std::cout << std::endl;
810  }
811  milx::PrintInfo("\n>> All Labels present (as single list, n = " + milx::NumberToString(allValues.size()) + "): ");
812  for(size_t j = 0; j < allValues.size(); j ++)
813  std::cout << static_cast<unsigned>(allValues[j]) << ", ";
814  std::cout << std::endl;
815  break;
816  }
817  #endif
818 
819  case rescale:
820  milx::Image<floatImageType>::RescaleIntensityCollection(collection, belowValue, aboveValue);
821  break;
822 
823  case invert:
825  break;
826 
827  case relabel:
828  milx::PrintError("Failed Relabelling. Only applicable to Labelled images. Exiting.");
829  exit(EXIT_FAILURE);
830 
831  case smooth:
832  collection = milx::Image<floatImageType>::AnisotropicDiffusionCollection<floatImageType>(collection, iterations, smoothTimestep);
833  break;
834 
835  case bilateral:
836  milx::Image<floatImageType>::BilateralCollection(collection, rangeSigma, sigmaValue);
837  break;
838 
839  case median:
841  break;
842 
843  case gradmag:
845  break;
846 
847  case laplacian:
849  break;
850 
851  case distancemap:
852  {
853  const bool binary = false, signedDistance = true, insideDistance = false, squaredDistance = false;
854 
855  collection = milx::Image<floatImageType>::DistanceMapCollection<floatImageType>(collection, binary, signedDistance, insideDistance, squaredDistance);
856 
857  break;
858  }
859 
860  case threshold:
861  if(aboveArg.isSet() && !belowArg.isSet())
862  {
863  milx::PrintInfo("Thresholding Above " + milx::NumberToString(aboveValue));
864  milx::Image<floatImageType>::ThresholdAboveCollection(collection, 0.0, aboveValue);
865  }
866  else if(!aboveArg.isSet() && belowArg.isSet())
867  {
868  milx::PrintInfo("Thresholding Below " + milx::NumberToString(belowValue));
869  milx::Image<floatImageType>::ThresholdBelowCollection(collection, 0.0, belowValue);
870  }
871  else if(binaryArg.isSet())
872  {
873  milx::PrintInfo("Binary Thresholding Above " + milx::NumberToString(aboveValue) + " and Below " + milx::NumberToString(belowValue));
874  labelledCollection = milx::Image<floatImageType>::BinaryThresholdCollection<charImageType>(collection, 0.0, insideValue, belowValue, aboveValue);
875  labelledImages = true;
876  }
877  else
878  {
879  milx::PrintInfo("Thresholding Above " + milx::NumberToString(aboveValue) + " and Below " + milx::NumberToString(belowValue));
880  milx::Image<floatImageType>::ThresholdCollection(collection, 0.0, belowValue, aboveValue);
881  }
882 
883  break;
884 
885  case Otsu:
886  milx::PrintInfo("Otsu Multiple Thresholding with " + milx::NumberToString(OtsuValue) + " and " + milx::NumberToString(labelsValue) + " labels");
887  labelledCollection = milx::Image<floatImageType>::OtsuMultipleThresholdCollection<charImageType>(collection, OtsuValue, labelsValue);
888  labelledImages = true;
889 
890  break;
891 
892  case crop:
893  #if (ITK_REVIEW || ITK_VERSION_MAJOR > 3) //Review only members
894  milx::Image<floatImageType>::MaskAndCropCollection<charImageType>(collection, maskImage, paddingValue);
895  #endif
896 
897  break;
898 
899  case mask:
900  milx::Image<floatImageType>::MaskCollection<charImageType>(collection, maskImage);
901  break;
902 
903  case resample:
904  {
905  floatImageType::Pointer referenceImage;
906  if(resampleArg.isSet())
907  {
908  if(!milx::File::OpenImage<floatImageType>(resampleName, referenceImage))
909  {
910  milx::PrintError("Failed Reading Reference Image. Exiting.");
911  exit(EXIT_FAILURE);
912  }
913  std::cout << "Read " << resampleName << " as the reference image for resampling" << std::endl;
914  }
915  milx::Image<floatImageType>::ResampleCollection<floatImageType>(collection, referenceImage);
916  break;
917  }
918 
919  case match:
920  {
921  floatImageType::Pointer referenceImage;
922  if(matchArg.isSet())
923  {
924  if(!milx::File::OpenImage<floatImageType>(matchName, referenceImage))
925  {
926  milx::PrintError("Failed Reading Reference Image. Exiting.");
927  exit(EXIT_FAILURE);
928  }
929  std::cout << "Read " << matchName << " as the reference image for matching" << std::endl;
930  }
931  milx::Image<floatImageType>::MatchHistogramCollection(collection, referenceImage);
932  break;
933  }
934 
935  case checker:
936  {
937  floatImageType::Pointer referenceImage;
938  if(checkerArg.isSet())
939  {
940  if(!milx::File::OpenImage<floatImageType>(checkerName, referenceImage))
941  {
942  milx::PrintError("Failed Reading Reference Image. Exiting.");
943  exit(EXIT_FAILURE);
944  }
945  std::cout << "Read " << checkerName << " as the reference image for checkerboarding" << std::endl;
946  }
947  milx::Image<floatImageType>::CheckerboardCollection(collection, referenceImage);
948  break;
949  }
950 
951  case add:
952  floatResult = milx::Image<floatImageType>::AddCollection(collection);
953  break;
954 
955  case diff:
956  floatResult = milx::Image<floatImageType>::DifferenceCollection(collection);
957  break;
958 
959  case mean:
960  floatResult = milx::Image<floatImageType>::AverageCollection<floatImageType>(collection);
961  break;
962 
963  case merge:
964  #if (ITK_REVIEW || ITK_VERSION_MAJOR > 3) //Review only members
965  milx::PrintWarning("Float images found and will be cast to 8-bit images for merge.");
966  labelledCollection = milx::Image<floatImageType>::CastCollection<charImageType>(collection);
967  collection.clear();
968  charResult = milx::Image<charImageType>::MergeLabelledImages(labelledCollection, mergeType);
969  labelledImages = true;
970  #endif
971  break;
972 
973  case cast:
974  labelledCollection = milx::Image<floatImageType>::CastCollection<charImageType>(collection);
975  collection.clear();
976  labelledImages = true;
977  break;
978 
979  case flip:
980  if(flipAxis == 0)
981  milx::Image<floatImageType>::FlipCollection(collection, true, false, false, true);
982  else if(flipAxis == 1)
983  milx::Image<floatImageType>::FlipCollection(collection, false, true, false, true);
984  else
985  milx::Image<floatImageType>::FlipCollection(collection, false, false, true, true);
986  break;
987 
988  case none: //--------------------------------
989  break;
990 
991  default:
992  milx::PrintError("Operation not supported. Exiting");
993  exit(EXIT_FAILURE);
994  break;
995  }
996  std::cerr << "Done" << std::endl;
997  }
998 
999  if(outputArg.isSet() && filenames.size() == 1)
1000  {
1001  if(vectorImages)
1002  vecResult = vectorCollection[0];
1003  if(labelledImages)
1004  charResult = labelledCollection[0];
1005  else
1006  floatResult = collection[0];
1007  }
1008 
1010  if(vectorImages)
1011  {
1012  if(outputRequired)
1013  {
1014  milx::PrintInfo("Writing vector result to " + outputName);
1015  milx::File::SaveImage<vectorImageType>(outputName, vecResult);
1016  }
1017  if(multiOutputRequired)
1018  {
1019  if(!vectorCollection.empty())
1020  {
1021  const int N = vectorCollection.size();
1022 
1023  milx::PrintInfo("Writing vector results with prefix " + prefixName);
1024  std::vector<std::string> names;
1025 
1026  int n = 0;
1027  for(stringiterator filename = filenames.begin(); filename != filenames.end() && n < N; filename ++, n ++)
1028  {
1029  std::string ext = milx::File::GetFileExtension(*filename);
1030  names.push_back(prefixName + milx::File::GetBaseName(*filename) + "." + ext);
1031  milx::PrintInfo("Will be writing " + prefixName + milx::File::GetBaseName(*filename) + "." + ext);
1032  }
1033 
1034  milx::File::SaveImages<vectorImageType>(names, vectorCollection);
1035  }
1036  else
1037  {
1038  milx::PrintError("Result was empty. Skipping Output.");
1039  }
1040  }
1041  }
1042  else if(labelledImages)
1043  {
1044  if(outputRequired)
1045  {
1046  milx::PrintInfo("Writing labelled result to " + outputName);
1047  milx::File::SaveImage<charImageType>(outputName, charResult);
1048  }
1049  if(multiOutputRequired)
1050  {
1051  if(!labelledCollection.empty())
1052  {
1053  const int N = labelledCollection.size();
1054 
1055  milx::PrintInfo("Writing labelled results with prefix " + prefixName);
1056  std::vector<std::string> names;
1057 
1058  int n = 0;
1059  for(stringiterator filename = filenames.begin(); filename != filenames.end() && n < N; filename ++, n ++)
1060  {
1061  std::string ext = milx::File::GetFileExtension(*filename);
1062  names.push_back(prefixName + milx::File::GetBaseName(*filename) + "." + ext);
1063  milx::PrintInfo("Will be writing " + prefixName + milx::File::GetBaseName(*filename) + "." + ext);
1064  }
1065 
1066  milx::File::SaveImages<charImageType>(names, labelledCollection);
1067  }
1068  else
1069  {
1070  milx::PrintError("Result was empty. Skipping Output.");
1071  }
1072  }
1073  }
1074  else
1075  {
1076  if(outputRequired)
1077  {
1078  milx::PrintInfo("Writing result to " + outputName);
1079  milx::File::SaveImage<floatImageType>(outputName, floatResult);
1080  }
1081  if(multiOutputRequired)
1082  {
1083  if(!collection.empty())
1084  {
1085  const int N = collection.size();
1086 
1087  milx::PrintInfo("Writing results with prefix " + prefixName);
1088  std::vector<std::string> names;
1089 
1090  int n = 0;
1091  for(stringiterator filename = filenames.begin(); filename != filenames.end() && n < N; filename ++, n ++)
1092  {
1093  std::string ext = milx::File::GetFileExtension(*filename);
1094  names.push_back(prefixName + milx::File::GetBaseName(*filename) + "." + ext);
1095  milx::PrintInfo("Will be writing " + prefixName + milx::File::GetBaseName(*filename) + "." + ext);
1096  }
1097 
1098  milx::File::SaveImages<floatImageType>(names, collection);
1099  }
1100  else
1101  {
1102  milx::PrintError("Result was empty. Skipping Output.");
1103  }
1104  }
1105  }
1106 
1107  //---------------------------
1108  milx::PrintInfo("Operation Complete");
1109  return EXIT_SUCCESS;
1110 }
static void FlipCollection(std::vector< typename itk::SmartPointer< TImage > > &images, bool xAxis=false, bool yAxis=true, bool zAxis=false, bool aboutOrigin=true)
Batch process images by flipping each image along axis provided.
Definition: milxImage.h:3139
static std::string ExtractFilename(const std::string &filename, char delimiter='/')
Returns filename (with extension) with paths removed.
Definition: milxFile.h:442
static void MedianCollection(std::vector< typename itk::SmartPointer< TImage > > &images, const int radius)
Batch process images by smoothing each image using the Median of the neighbourhood.
Definition: milxImage.h:3205
void PrintError(const std::string msg)
Displays a generic msg to standard error with carriage return.
Definition: milxGlobal.h:202
static void ThresholdAboveCollection(std::vector< typename itk::SmartPointer< TImage > > &images, float outsideValue, float aboveValue)
Batch process images by thresholding each image from above.
Definition: milxImage.h:3249
This program Animates image slices and animates surfaces in the model view for creating movies...
static void ThresholdCollection(std::vector< typename itk::SmartPointer< TImage > > &images, float outsideValue, float belowValue, float aboveValue)
Batch process images by thresholding each image within band provided.
Definition: milxImage.h:3267
static void BilateralCollection(std::vector< typename itk::SmartPointer< TImage > > &images, float sigmaRange, float sigmaSpatial)
Batch process images by bilateral smoothing each image.
Definition: milxImage.h:3180
Represents an image (i.e. an regular rectangular array with scalar values) and their common operation...
Definition: milxImage.h:174
static void ThresholdBelowCollection(std::vector< typename itk::SmartPointer< TImage > > &images, float outsideValue, float belowValue)
Batch process images by thresholding each image from below.
Definition: milxImage.h:3258
void PrintWarning(const std::string msg)
Displays a generic msg to standard output with carriage return.
Definition: milxGlobal.h:183
static void InvertIntensityCollection(std::vector< typename itk::SmartPointer< TImage > > &images)
Batch process images by inverting the intensities for each image.
Definition: milxImage.h:3127
static itk::SmartPointer< TImage > AddCollection(std::vector< typename itk::SmartPointer< TImage > > &images)
Batch process images by the adding the images together (pixel-wise).
Definition: milxImage.h:3359
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 itk::SmartPointer< TImage > AverageVectorCollection(std::vector< typename itk::SmartPointer< TImage > > &images, int numberOfComponents)
Batch process images by the averaging the vector images (pixel-wise).
Definition: milxImage.h:3395
static itk::SmartPointer< TImage > DifferenceCollection(std::vector< typename itk::SmartPointer< TImage > > &images)
Same as SubtractCollection.
Definition: milxImage.h:817
int main(int argc, char *argv[])
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 void RelabelCollection(std::vector< typename itk::SmartPointer< TImage > > &images)
Batch process images by relabelling unconnected regions consecutatively.
Definition: milxImage.h:3330
static void InformationCollection(std::vector< typename itk::SmartPointer< TImage > > &images)
Batch process images by output the image information for each image.
Definition: milxImage.h:3088
static void GradientMagnitudeCollection(std::vector< typename itk::SmartPointer< TImage > > &images)
Batch process images by hightlighting the edges of each image using Gradient magnitude information of...
Definition: milxImage.h:3214
static std::string GetFileExtension(const std::string &filename)
Returns the file extension (in lower case) of the given filename.
Definition: milxFile.h:472
unsigned NumberOfProcessors()
Number of processors or cores on the machine.
Definition: milxGlobal.h:124
static void LaplacianCollection(std::vector< typename itk::SmartPointer< TImage > > &images)
Batch process images by hightlighting the edges of each image using the Laplacian of the image...
Definition: milxImage.h:3223
static void CheckerboardCollection(std::vector< typename itk::SmartPointer< TImage > > &images, itk::SmartPointer< TImage > refImage, const int squares=10)
Batch process images by checkerboarding all of the images to the reference image provided.
Definition: milxImage.h:3109
static void MatchHistogramCollection(std::vector< typename itk::SmartPointer< TImage > > &images, itk::SmartPointer< TImage > refImage, const int bins=128)
Batch process images by matching the histograms of the images to the reference image provided...
Definition: milxImage.h:3100