/**
   * Get standard deviation of the image
   *
   * @param img input image
   * @return sigma
   */
  protected double getStdDev(MTBImage img) {

    int sizeStack = img.getSizeStack();
    int sizeX = img.getSizeX();
    int sizeY = img.getSizeY();

    double val;
    double mu = 0.0;
    double mu2 = 0.0;
    double N = 0.0;
    double sigma;

    for (int i = 0; i < sizeStack; i++) {
      img.setCurrentSliceIndex(i);

      for (int y = 0; y < sizeY; y++) {
        for (int x = 0; x < sizeX; x++) {
          val = img.getValueDouble(x, y);
          mu += val;
          mu2 += val * val;
          N++;
        }
      }
    }

    mu /= N;
    mu2 /= N;
    sigma = Math.sqrt(mu2 - mu * mu);

    return sigma;
  }
  /**
   * Denoise wavelet coefficients using Jeffrey's noninformative prior for a given sigma of noise
   *
   * @param img input image
   * @param sigma sigma of noise
   */
  protected void denoise(MTBImage img, double sigma) {
    int sizeStack = img.getSizeStack();
    int sizeX = img.getSizeX();
    int sizeY = img.getSizeY();

    double s2 = 3.0 * sigma * sigma;
    double val, val2;
    for (int i = 0; i < sizeStack; i++) {
      img.setCurrentSliceIndex(i);

      for (int y = 0; y < sizeY; y++) {
        for (int x = 0; x < sizeX; x++) {
          val = img.getValueDouble(x, y);

          val2 = (val * val - s2);

          if (val2 < 0.0) val2 = 0.0;

          if (val != 0.0) img.putValueDouble(x, y, val2 / val);
          else img.putValueDouble(x, y, 0.0);
        }
      }
    }
    img.setCurrentSliceIndex(0);
  }
  /**
   * Create an image with Gaussian noise
   *
   * @param mean
   * @param sigma
   * @param clippingFactor
   * @param bins
   * @param sizeX
   * @param sizeY
   * @param sizeZ
   * @param sizeT
   * @param sizeC
   * @return
   */
  protected MTBImage createGaussianNoiseImage(
      double mean,
      double sigma,
      double clippingFactor,
      int bins,
      int sizeX,
      int sizeY,
      int sizeZ,
      int sizeT,
      int sizeC) {
    MTBImage gImg =
        MTBImage.createMTBImage(sizeX, sizeY, sizeZ, sizeT, sizeC, MTBImageType.MTB_DOUBLE);
    int sizeStack = gImg.getSizeStack();

    double[] dist = new double[bins];
    double cs = -clippingFactor * sigma;
    double gFactor = 1.0 / (Math.sqrt(2.0 * Math.PI) * sigma);

    double lastVal = 0.0;

    double X;

    // cumulative distribution
    for (int i = 0; i < bins; i++) {
      X = ((double) i / (double) (bins - 1)) * 2.0 * cs - cs;

      dist[i] = lastVal + gFactor * Math.exp(-0.5 * (X * X) / (sigma * sigma));
      lastVal = dist[i];
    }

    // normalization
    //		for (int i = 0; i < bins; i++) {
    //			dist[i] /= dist[bins-1];
    //		}
    double sample;
    for (int i = 0; i < sizeStack; i++) {
      gImg.setCurrentSliceIndex(i);

      for (int y = 0; y < sizeY; y++) {
        for (int x = 0; x < sizeX; x++) {
          sample = getSample(dist);
          sample = sample * 2.0 * cs - cs - mean;
          gImg.putValueDouble(x, y, sample);
        }
      }
    }
    gImg.setCurrentSliceIndex(0);

    return gImg;
  }