@Override
  public void applyInPlace(FastBitmap fastBitmap) {

    if (fastBitmap.isRGB()) {
      FastBitmap layerA = new FastBitmap(fastBitmap);
      FastBitmap layerB = new FastBitmap(fastBitmap);

      SaturationCorrection sc = new SaturationCorrection(saturationCorrection);
      sc.applyInPlace(layerA);

      layerB.setImage(layerA);
      Invert i = new Invert();
      i.applyInPlace(layerB);

      GaussianBlur gb = new GaussianBlur(sigma, size);
      gb.applyInPlace(layerB);

      Blend b = new Blend(layerB, Blend.Algorithm.ColorDodge);
      b.applyInPlace(layerA);

      GammaCorrection gc = new GammaCorrection(gamma);
      gc.applyInPlace(layerA);

      b.setAlgorithm(Blend.Algorithm.Overlay);
      b.setOverlay(layerA);
      b.applyInPlace(fastBitmap);
    }
  }
  private void Watershed(FastBitmap fastBitmap) {

    DistanceTransform dt = new DistanceTransform(distance);
    float[][] distance = dt.Compute(fastBitmap);

    // Convert 2D to 1D - ImageJ Compatibility
    float[] distance1D = new float[distance.length * distance[0].length];
    int p = 0;
    for (int i = 0; i < fastBitmap.getHeight(); i++) {
      for (int j = 0; j < fastBitmap.getWidth(); j++) {
        distance1D[p++] = distance[i][j];
      }
    }

    // Make directions offsets
    makeDirectionOffsets(distance[0].length);

    int width = fastBitmap.getWidth();
    int height = fastBitmap.getHeight();

    FastBitmap back = new FastBitmap(width, height, FastBitmap.ColorSpace.Grayscale);

    // Pegue as maxima
    long[] maxPoints =
        getSortedMaxPoints(distance, distance1D, back, 0, dt.getMaximumDistance(), -808080.0);

    // Analise e marque as maxima em imagem de background
    float maxSortingError = 1.1f * SQRT2 / 2f;
    analyseAndMarkMaxima(distance1D, back, maxPoints, tolerance, maxSortingError);

    // Transform em 8bit 0..255
    FastBitmap outImage = make8Bit(distance, back, dt.getMaximumDistance(), -808080.0);

    cleanupMaxima(outImage, back, maxPoints);
    watershedSegment(outImage);
    watershedPostProcess(outImage);

    fastBitmap.setImage(outImage);
  }
  @Override
  public void applyInPlace(FastBitmap fastBitmap) {

    if (fastBitmap.isGrayscale()) {

      int width = fastBitmap.getWidth();
      int height = fastBitmap.getHeight();
      double oldIradius = (double) (height - 1) / 2;
      double oldJradius = (double) (width - 1) / 2;

      CalculateNewSize(fastBitmap);
      FastBitmap destinationData =
          new FastBitmap(newWidth, newHeight, FastBitmap.ColorSpace.Grayscale);

      // get destination image size
      double newIradius = (double) (newHeight - 1) / 2;
      double newJradius = (double) (newWidth - 1) / 2;

      // angle's sine and cosine
      double angleRad = -angle * Math.PI / 180;
      double angleCos = Math.cos(angleRad);
      double angleSin = Math.sin(angleRad);

      // destination pixel's coordinate relative to image center
      double ci, cj;

      // coordinates of source points and cooefficiens
      double oi, oj, di, dj, k1, k2;
      int oi1, oj1, oi2, oj2;

      // width and height decreased by 1
      int imax = height - 1;
      int jmax = width - 1;

      ci = -newIradius;
      for (int i = 0; i < newHeight; i++) {
        cj = -newJradius;
        for (int j = 0; j < newWidth; j++) {

          // coordinate of the nearest point
          oi = angleCos * ci + angleSin * cj + oldIradius;
          oj = -angleSin * ci + angleCos * cj + oldJradius;

          oi1 = (int) oi;
          oj1 = (int) oj;

          // validate source pixel's coordinates
          if ((oi1 < 0) || (oj1 < 0) || (oi1 >= height) || (oj1 >= width)) {
            // fill destination image with filler
            destinationData.setGray(i, j, fillGray);
          } else {

            di = oi - (double) oi1;
            dj = oj - (double) oj1;

            // initial pixel value
            int g = 0;

            for (int n = -1; n < 3; n++) {
              // get Y cooefficient
              k1 = Interpolation.BiCubicKernel(dj - (double) n);

              oj2 = oj1 + n;
              if (oj2 < 0) oj2 = 0;
              if (oj2 > jmax) oj2 = jmax;

              for (int m = -1; m < 3; m++) {
                // get X cooefficient
                k2 = k1 * Interpolation.BiCubicKernel((double) m - di);

                oi2 = oi1 + m;
                if (oi2 < 0) oi2 = 0;
                if (oi2 > imax) oi2 = imax;

                g += k2 * fastBitmap.getGray(oi2, oj2);
              }
            }

            destinationData.setGray(i, j, Math.max(0, Math.min(255, g)));
          }
          cj++;
        }
        ci++;
      }

      fastBitmap.setImage(destinationData);
      destinationData.recycle();

    } else if (fastBitmap.isRGB()) {
      int width = fastBitmap.getWidth();
      int height = fastBitmap.getHeight();
      double oldIradius = (double) (height - 1) / 2;
      double oldJradius = (double) (width - 1) / 2;

      CalculateNewSize(fastBitmap);
      FastBitmap destinationData = new FastBitmap(newWidth, newHeight, FastBitmap.ColorSpace.RGB);

      // get destination image size
      double newIradius = (double) (newHeight - 1) / 2;
      double newJradius = (double) (newWidth - 1) / 2;

      // angle's sine and cosine
      double angleRad = -angle * Math.PI / 180;
      double angleCos = Math.cos(angleRad);
      double angleSin = Math.sin(angleRad);

      // destination pixel's coordinate relative to image center
      double ci, cj;

      // coordinates of source points and cooefficiens
      double oi, oj, di, dj, k1, k2;
      int oi1, oj1, oi2, oj2;

      // width and height decreased by 1
      int imax = height - 1;
      int jmax = width - 1;

      ci = -newIradius;
      for (int i = 0; i < newHeight; i++) {
        cj = -newJradius;
        for (int j = 0; j < newWidth; j++) {

          // coordinate of the nearest point
          oi = angleCos * ci + angleSin * cj + oldIradius;
          oj = -angleSin * ci + angleCos * cj + oldJradius;

          oi1 = (int) oi;
          oj1 = (int) oj;

          // validate source pixel's coordinates
          if ((oi < 0) || (oj < 0) || (oi >= height) || (oj >= width)) {
            // fill destination image with filler
            destinationData.setRGB(i, j, fillRed, fillGreen, fillBlue);
          } else {

            di = oi - (double) oi1;
            dj = oj - (double) oj1;

            // initial pixel value
            int r = 0;
            int g = 0;
            int b = 0;

            for (int n = -1; n < 3; n++) {
              // get Y cooefficient
              k1 = Interpolation.BiCubicKernel(dj - (double) n);

              oj2 = oj1 + n;
              if (oj2 < 0) oj2 = 0;
              if (oj2 > jmax) oj2 = jmax;

              for (int m = -1; m < 3; m++) {
                // get X cooefficient
                k2 = k1 * Interpolation.BiCubicKernel((double) m - di);

                oi2 = oi1 + m;
                if (oi2 < 0) oi2 = 0;
                if (oi2 > imax) oi2 = imax;

                r += k2 * fastBitmap.getRed(oi2, oj2);
                g += k2 * fastBitmap.getGreen(oi2, oj2);
                b += k2 * fastBitmap.getBlue(oi2, oj2);
              }
            }

            r = Math.max(0, Math.min(255, r));
            g = Math.max(0, Math.min(255, g));
            b = Math.max(0, Math.min(255, b));
            destinationData.setRGB(i, j, r, g, b);
          }
          cj++;
        }
        ci++;
      }

      fastBitmap.setImage(destinationData);
      destinationData.recycle();
    }
  }