ANIMA  4.0
animaNoiseGenerator.cxx
Go to the documentation of this file.
1 #include <tclap/CmdLine.h>
5 #include <animaKummerFunctions.h>
6 
7 struct arguments
8 {
9  std::string input, output;
10  double sigma;
11  unsigned int nreplicates, nthreads;
13 };
14 
15 template <class ComponentType, unsigned int InputDim>
16 void
17 addNoise(itk::ImageIOBase::Pointer imageIO, const arguments &args)
18 {
19  typedef itk::Image<ComponentType,InputDim> InputImageType;
21  typedef typename NoiseFilterType::OutputImageType OutputImageType;
22 
23  typename NoiseFilterType::Pointer mainFilter = NoiseFilterType::New();
24  mainFilter->SetNumberOfWorkUnits(args.nthreads);
25  mainFilter->SetInput(anima::readImage<InputImageType>(args.input));
26  mainFilter->SetNoiseSigma(args.sigma);
27  mainFilter->SetUseGaussianDistribution(args.gaussianNoise);
28  mainFilter->SetNumberOfReplicates(args.nreplicates);
29  mainFilter->Update();
30 
31  if (args.nreplicates == 1)
32  anima::writeImage<OutputImageType>(args.output, mainFilter->GetOutput(0));
33  else
34  {
35  std::size_t pointLocation = args.output.find_last_of(".");
36  std::string filePrefix = args.output.substr(0, pointLocation);
37  std::string fileExtension = args.output.substr(pointLocation);
38 
39  if (fileExtension == ".gz")
40  {
41  pointLocation = filePrefix.find_last_of(".");
42  filePrefix = filePrefix.substr(0, pointLocation);
43  fileExtension = filePrefix.substr(pointLocation) + ".gz";
44  }
45 
46  for (unsigned int i = 0;i < args.nreplicates;++i)
47  {
48  std::stringstream ss;
49  ss << std::setw(3) << std::setfill('0') << (i + 1);
50  std::string tmpStr = ss.str();
51  anima::writeImage<OutputImageType>(filePrefix + tmpStr + fileExtension, mainFilter->GetOutput(i));
52  }
53  }
54 }
55 
56 template <class ComponentType >
57 void
58 retrieveNbDimensions(itk::ImageIOBase::Pointer imageIO, const arguments &args)
59 {
60  ANIMA_RETRIEVE_NUMBER_OF_DIMENSIONS(imageIO, ComponentType, addNoise, imageIO, args)
61 }
62 
63 int main(int argc, char **argv)
64 {
65  TCLAP::CmdLine cmd("INRIA / IRISA - VisAGeS/Empenn Team", ' ',ANIMA_VERSION);
66 
67  TCLAP::ValueArg<std::string> inArg("i","inputfile","Input image on which to add noise.",true,"","input image",cmd);
68  TCLAP::ValueArg<std::string> outArg("o","outputfile","Output image. If number of replicates is 1, it is used as provided for saving the noisy dataset with the corresponding file name. If number of replicates > 1, the extension is extracted as desired output file format and the file name without extension is used as base file name for all replicates.",true,"","output file prefix",cmd);
69  TCLAP::ValueArg<std::string> refArg("r","reference","Masked reference image over which the average SNR is defined.",true,"","reference image",cmd);
70 
71  TCLAP::ValueArg<double> snrArg("s","snr","Average SNR in dB over the reference image (default: 25dB).",false,25.0,"mean snr",cmd);
72  TCLAP::ValueArg<unsigned int> repArg("n","num-replicates","Number of independent noisy datasets to generate (default: 1).",false,1,"number of replicates",cmd);
73 
74  TCLAP::SwitchArg gaussArg("G","gauss-noise","Adds Gaussian noise instead of Rician noise.",cmd,false);
75  TCLAP::SwitchArg matchArg("M","match-snr","Make Gaussian noise comparable to Rician noise in terms of SNR.",cmd,false);
76  TCLAP::SwitchArg verboseArg("V","verbose","Outputs noise calculations to the console.",cmd,false);
77 
78  TCLAP::ValueArg<unsigned int> nbpArg("p","numberofthreads","Number of threads to run on (default : all cores).",false,itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(),"number of threads",cmd);
79 
80  try
81  {
82  cmd.parse(argc,argv);
83  }
84  catch (TCLAP::ArgException& e)
85  {
86  std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl;
87  return EXIT_FAILURE;
88  }
89 
90  // Put SNR back in linear scale
91  double snr = std::pow(10.0, snrArg.getValue() / 20.0);
92 
93  // Find average signal value in reference image
94  typedef itk::Image<double,3> Input3DImageType;
95  typedef itk::ImageRegionConstIterator<Input3DImageType> Input3DIteratorType;
96 
97  Input3DImageType::Pointer referenceImage = anima::readImage<Input3DImageType>(refArg.getValue());
98  Input3DIteratorType refItr(referenceImage, referenceImage->GetLargestPossibleRegion());
99 
100  double meanReferenceValue = 0;
101  unsigned int numNonZeroVoxels = 0;
102 
103  while (!refItr.IsAtEnd())
104  {
105  double referenceValue = refItr.Get();
106 
107  if (referenceValue == 0)
108  {
109  ++refItr;
110  continue;
111  }
112 
113  meanReferenceValue *= (numNonZeroVoxels / (numNonZeroVoxels + 1.0));
114  meanReferenceValue += referenceValue / (numNonZeroVoxels + 1.0);
115  ++numNonZeroVoxels;
116  ++refItr;
117  }
118 
119  // Retrieve Rice sigma parameter
120  double riceSigmaParameter = meanReferenceValue / snr;
121 
122  // Compute mean and variance from Rice distribution
123  double snrValue = meanReferenceValue / riceSigmaParameter;
124  double laguerreArgument = -1.0 * snrValue * snrValue / 2.0;
125  double laguerreValue = anima::GetKummerFunctionValue(laguerreArgument, -0.5, 1.0);
126  double riceMeanValue = riceSigmaParameter * std::sqrt(M_PI / 2.0) * laguerreValue;
127  double riceStdValue = 2.0 * riceSigmaParameter * riceSigmaParameter + meanReferenceValue * meanReferenceValue - M_PI * riceSigmaParameter * riceSigmaParameter * laguerreValue * laguerreValue / 2.0;
128  if (riceStdValue < 0.0)
129  {
130  std::cerr << "The evaluation of the Kummer M function was too imprecise and produced a negative variance." << std::endl;
131  return EXIT_FAILURE;
132  }
133  riceStdValue = std::sqrt(riceStdValue);
134 
135  // Deduce effective SNR
136  double effSnrValue = riceMeanValue / riceStdValue;
137 
138  // Compute Gaussian sigma parameter
139  double gaussSigmaParameter = (matchArg.isSet()) ? meanReferenceValue / effSnrValue : riceSigmaParameter;
140 
141  // Set noise standard deviation
142  double noiseSigmaParameter = (gaussArg.isSet()) ? gaussSigmaParameter : riceSigmaParameter;
143 
144  if (verboseArg.isSet())
145  {
146  // Output some information about the noise structure in the console
147  std::cout << " - User-defined average SNR: " << snrArg.getValue() << " dB." << std::endl;
148  std::cout << " - Reference image used for noise variance calculation: " << refArg.getValue() << std:: endl;
149  std::cout << " - Average signal in foreground voxels of the reference image: " << meanReferenceValue << std::endl;
150  std::cout << " - Resulting coil standard deviation (Rice sigma parameter): " << riceSigmaParameter << std::endl;
151  std::cout << " - Mean value of Rice distribution: " << riceMeanValue << std::endl;
152  std::cout << " - Standard deviation of Rice distribution: " << riceStdValue << std::endl;
153  std::cout << " - Effective average SNR: " << 20.0 * std::log10(effSnrValue) << " dB." << std::endl;
154 
155  if (gaussArg.isSet())
156  std::cout << " - Noise distribution: Gaussian" << std::endl;
157  else
158  std::cout << " - Noise distribution: Rician" << std::endl;
159 
160  std::cout << " - Resulting noise sigma parameter: " << noiseSigmaParameter << std::endl;
161  }
162 
163  // Find out the type of the image in file
164  itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(inArg.getValue().c_str(),itk::ImageIOFactory::ReadMode);
165 
166  if (!imageIO)
167  {
168  std::cerr << "Itk could not find a suitable IO factory for the input" << std::endl;
169  return EXIT_FAILURE;
170  }
171 
172  // Now that we found the appropriate ImageIO class, ask it to read the meta data from the image file.
173  imageIO->SetFileName(inArg.getValue());
174  imageIO->ReadImageInformation();
175 
176  arguments args;
177 
178  args.input = inArg.getValue();
179  args.output = outArg.getValue();
180  args.sigma = noiseSigmaParameter;
181  args.nreplicates = repArg.getValue();
182  args.nthreads = nbpArg.getValue();
183  args.gaussianNoise = gaussArg.isSet();
184 
185  try
186  {
187  ANIMA_RETRIEVE_COMPONENT_TYPE(imageIO, retrieveNbDimensions, imageIO, args);
188  }
189  catch (itk::ExceptionObject &err)
190  {
191  std::cerr << err << std::endl;
192  return EXIT_FAILURE;
193  }
194 
195  return EXIT_SUCCESS;
196 }
int main(int argc, char **argv)
unsigned int nreplicates
std::string output
void retrieveNbDimensions(itk::ImageIOBase::Pointer imageIO, const arguments &args)
void addNoise(itk::ImageIOBase::Pointer imageIO, const arguments &args)
itk::ImageIOBase::Pointer imageIO
#define ANIMA_RETRIEVE_COMPONENT_TYPE(imageIO, function,...)
unsigned int nthreads
double GetKummerFunctionValue(const double &x, const double &a, const double &b, const unsigned int maxIter, const double tol)
Computes the confluent hypergeometric function 1F1 also known as the Kummer function M...
#define ANIMA_RETRIEVE_NUMBER_OF_DIMENSIONS(imageIO, ComponentType, function,...)
std::string input