/**
   * Calculates the approximate gradient magnitude for each pixel in a specified area and stores the
   * result in a RealGrayImage pointer. parameters: x1 - the left bound of the specified area y1 -
   * the right bottom bound of the specified area x2 - the right bound of the specified area y2 -
   * the top bound of the specified area
   */
  public RealGrayImage calcApproximateGradientMagnitude(int x1, int y1, int x2, int y2) {
    IxImage = calcIx(x1, y1, x2, y2);
    IyImage = calcIy(x1, y1, x2, y2);

    int X = image.X();
    int Y = image.Y();

    double Iso = 0;
    float Ix, Iy;

    if (GradientMagnitude == null) GradientMagnitude = new RealGrayImage(X, Y);

    for (int x = 0; x < X; x++) {
      for (int y = 0; y < Y; y++) {

        // calculate Iso
        Ix = IxImage.get(x, y);
        Iy = IyImage.get(x, y);

        Iso = (Iy + Ix);

        // put the result floato output image
        GradientMagnitude.set(x, y, (float) Iso);
      }
    }
    return GradientMagnitude;
  }
  /**
   * Scales the range of <code>image</code> to an arbitrary min/max in a Region of Interest. The
   * range used for all planes is the global max, min of the image.
   *
   * @param image RealColorImage to scale.
   * @param roi Region of Interest of <code>image</code>.
   * @return <code>image</code>.
   */
  protected Image apply(RealColorImage image, ROI roi) {
    RealGrayImage plane;

    float min = image.min(roi);
    float max = image.max(roi);
    float r = 0;
    float range = max - min;
    float value = 0;
    if (range == 0) range = 1; // to avoid divide by zero problems

    for (int i = 0; i < 3; i++) {
      plane = image.plane(i);

      for (int y = roi.uy(); y <= roi.ly(); y++) {
        for (int x = roi.ux(); x <= roi.lx(); x++) {
          r = plane.get(x, y);
          value = (((r - min) / (range) * (float_max - float_min)) + float_min);
          plane.set(x, y, value);
        }
      }

      image.setPlane(i, plane);
    }

    return image;
  }
  /**
   * Calculates the gradient magnitude for each pixel in a specified area and stores the result in a
   * RealGrayImage pointer. parameters: x1 - the left bound of the specified area y1 - the right
   * bottom bound of the specified area x2 - the right bound of the specified area y2 - the top
   * bound of the specified area
   */
  public RealGrayImage calcGradientMagnitude(int x1, int y1, int x2, int y2) {
    IxImage = calcIx(x1, y1, x2, y2);
    IyImage = calcIy(x1, y1, x2, y2);

    int X = image.X();
    int Y = image.Y();

    double Iso = 0;
    float Ix, Iy;

    if (GradientMagnitude == null) GradientMagnitude = new RealGrayImage(X, Y);

    for (int x = x1; x < x2; x++) {
      for (int y = y1; y < y2; y++) {
        // calculate Iso
        Ix = IxImage.get(x, y);
        Iy = IyImage.get(x, y);
        Iso = Math.sqrt((double) ((Iy * Iy) + (Ix * Ix)));

        // put the result floato output image
        GradientMagnitude.set(x, y, (float) Iso);
      }
    }
    return GradientMagnitude;
  }
  /**
   * Calculates the gradient phase for each pixel in a specified area and stores the result in a
   * RealGrayImage pointer. parameters: x1 - the left bound of the specified area y1 - the right
   * bottom bound of the specified area x2 - the right bound of the specified area y2 - the top
   * bound of the specified area
   */
  public RealGrayImage calcOrientationMap(int x1, int y1, int x2, int y2) {
    IxImage = calcIx(x1, y1, x2, y2);
    IyImage = calcIy(x1, y1, x2, y2);

    int X = image.X();
    int Y = image.Y();

    double Iso = 0;

    float Ix, Iy;

    if (OrientationMap == null) OrientationMap = new RealGrayImage(X, Y);

    for (int x = x1; x < x2; x++) {
      for (int y = y1; y < y2; y++) {

        // calculate Iso
        Ix = IxImage.get(x, y);
        Iy = IyImage.get(x, y);
        Iso = Math.atan2(Iy, Ix);
        // put the result floato output image
        OrientationMap.set(x, y, (float) Iso);
      }
    }
    return OrientationMap;
  }
  /**
   * Overloaded methods to calculate Ix and the result is stored in the image pointed to by <code>
   * dest</code>. The origin of <code>*dest</code> is (x1,y1) of image <code>*src</code>.
   */
  public void calcIx(RealGrayImage src, RealGrayImage dest, int x1, int y1, int x2, int y2) {
    // input image dimensions
    int X = src.X();
    int Y = src.Y();

    // variables for calculating Isophote
    // float c;
    // float n;
    float ne;
    float e;
    float se;
    // float s;
    float sw;
    float w;
    float nw;

    float temp;

    // calculate derivatives
    for (int x = x1; x < x2; x++) {
      for (int y = y1; y < y2; y++) {
        // c = src.get(x, y);
        // if (y - 1 > 0)
        // n = src.get(x, y - 1);
        // else
        // n = 0;

        if (y - 1 > 0 && x + 1 < X) ne = src.get(x + 1, y - 1);
        else ne = 0;

        if (x + 1 < X) e = src.get(x + 1, y);
        else e = 0;

        if (y + 1 < Y && x + 1 < X) se = src.get(x + 1, y + 1);
        else se = 0;

        // if (y + 1 < Y)
        // s = src.get(x, y + 1);
        // else
        // s = 0;

        if (x - 1 > 0 && y + 1 < Y) sw = src.get(x - 1, y + 1);
        else sw = 0;

        if (x - 1 > 0) w = src.get(x - 1, y);
        else w = 0;

        if (y - 1 > 0 && x - 1 > 0) nw = src.get(x - 1, y - 1);
        else nw = 0;

        // calculate Ix
        temp = (float) ((-nw - 2.0 * w - sw + ne + 2.0 * e + se) / 8.0);

        // put the result float to output image
        dest.set(x - x1, y - y1, temp);
      }
    }
  } // calcIx
  /**
   * Calculates Iyy (second derivative in the y direction) of the image in a specified area.
   * parameters: x1 - the left bound of the specified area y1 - the right bottom bound of the
   * specified area x2 - the right bound of the specified area y2 - the top bound of the specified
   * area
   */
  public RealGrayImage calcIyy(int x1, int y1, int x2, int y2) {
    if (IyyImage == null) IyyImage = new RealGrayImage(image.X(), image.Y());

    try {
      ImageKernel ik = new ImageKernel(10); // D_YY
      Convolve c = new Convolve(ik);
      IyyImage = (RealGrayImage) c.apply((Image) image, new ROI(x1, y1, x2, y2));

      IyyImage = IyyImage.divide(3f);
    } catch (Exception e) {
    }
    return IyyImage;
  }
  /** isophote flow??? */
  public RealGrayImage calcIsophoteFlowStore(int x1, int y1, int x2, int y2) {
    IxImage = calcIx(x1, y1, x2, y2);
    IyImage = calcIy(x1, y1, x2, y2);
    IxxImage = calcIxx(x1, y1, x2, y2);
    IyyImage = calcIyy(x1, y1, x2, y2);
    IxyImage = calcIxy(x1, y1, x2, y2);

    int X = image.X();
    int Y = image.Y();

    double Iso = 0;
    float Ix, Iy, Ixx, Iyy, Ixy;

    if (IsophoteCurvatureFlow == null) IsophoteCurvatureFlow = new RealGrayImage(X, Y);

    for (int x = x1; x < x2; x++) {
      for (int y = y1; y < y2; y++) {

        // calculate Iso
        Ix = IxImage.get(x, y);
        Iy = IyImage.get(x, y);
        Ixx = IxxImage.get(x, y);
        Iyy = IyyImage.get(x, y);
        Ixy = IxyImage.get(x, y);

        Iso =
            ((Iy * Iy * Ixx) - (2 * Ix * Ixy * Iy) + (Iyy * Ix * Ix)) / (1 + (Ix * Ix) + (Iy * Iy));

        // put the result float to output image
        IsophoteCurvatureFlow.set(x, y, (float) Iso);
      }
    }
    return IsophoteCurvatureFlow;
  }
  /**
   * Calculate Iy (first derivative in the y direction) of the image in a specified area.
   * parameters: x1 - the left bound of the specified area y1 - the right bottom bound of the
   * specified area x2 - the right bound of the specified area y2 - the top bound of the specified
   * area
   */
  public RealGrayImage calcIy(int x1, int y1, int x2, int y2) {
    if (IyImage == null) IyImage = new RealGrayImage(image.X(), image.Y());

    try {
      ImageKernel ik = new ImageKernel(2); // Sobel_Y
      Convolve c = new Convolve(ik);
      IyImage = (RealGrayImage) c.apply((Image) image, new ROI(x1, y1, x2, y2));

      for (int x = x1; x < x2; x++)
        for (int y = y1; y < y2; y++) IyImage.set(x, y, (float) (IyImage.get(x, y) / 8.0));
    } catch (Exception e) {
    }
    return IyImage;
  }
  /**
   * Calculates Ix (first derivative in the x direction) of the image in a specified area.
   * parameters: x1 - the left bound of the specified area y1 - the right bottom bound of the
   * specified area x2 - the right bound of the specified area y2 - the top bound of the specified
   * area
   */
  public RealGrayImage calcIx(int x1, int y1, int x2, int y2) {
    if (IxImage == null) IxImage = new RealGrayImage(image.X(), image.Y());

    try {
      ImageKernel ik = new ImageKernel(1); // Sobel_X
      Convolve c = new Convolve(ik);
      ROI roi = new ROI(x1, y1, x2, y2);
      IxImage = (RealGrayImage) c.apply((Image) image, roi); // x1, y1, x2,
      // y2);

      for (int x = x1; x < x2; x++)
        for (int y = y1; y < y2; y++) IxImage.set(x, y, IxImage.get(x, y) / 8.0f);
    } catch (Exception e) {
    }

    return IxImage;
  }
  /**
   * Scales the range of <code>image</code> to an arbitrary min/max in a Region of Interest.
   *
   * @param image RealGrayImage to scale.
   * @param roi Region of Interest of <code>image</code>.
   * @return <code>image</code>.
   */
  protected Image apply(RealGrayImage image, ROI roi) {
    float min = image.min(roi);
    float max = image.max(roi);

    float r = 0;
    float range = max - min;

    if (range == 0)
      range = 1; // to avoid divide by zero (r-min will be zero so value will always be float_min)

    float value = 0;
    for (int y = roi.uy(); y <= roi.ly(); y++) {
      for (int x = roi.ux(); x <= roi.lx(); x++) {
        r = (float) image.get(x, y);
        value = ((float) ((r - min) / (range)) * (float_max - float_min)) + float_min;
        image.set(x, y, (float) value);
      }
    }
    return image;
  }
  /** Writes a RealGrayImage to a file, byteSizing first */
  public void writeByteSized(RealGrayImage im) throws IOException {

    X = im.X();
    Y = im.Y();

    // convert to byte size
    RealGrayImage tmpim = (RealGrayImage) im.copy();
    tmpim.byteSize();

    // write PGM in raw format

    writeRawPRGMHeader(X, Y);
    for (int y = 0; y < Y; y++) {
      for (int x = 0; x < X; x++) {
        data.write((byte) tmpim.get(x, y));
      }
    }
  }
 /**
  * Scales the range of <code>image</code> to an arbitrary min/max.
  *
  * @param image RealGrayImage to scale.
  * @return <code>image</code>.
  */
 protected Image apply(RealGrayImage image) {
   return apply(image, new ROI(0, 0, image.X() - 1, image.Y() - 1));
 }
  /**
   * This method caluculates the isophote curvature image in a specified area. This function is less
   * memory floatensive than <i>calcIsophoteStore</i>. parameters: x1 - the left bound of the
   * specified area y1 - the right bottom bound of the specified area x2 - the right bound of the
   * specified area y2 - the top bound of the specified area
   */
  public RealGrayImage calcIsophote(int x1, int y1, int x2, int y2) {
    // check for null images
    if (image == null) return null;

    // input image dimensions
    int X = image.X();
    int Y = image.Y();

    if (IsophoteCurvature == null) IsophoteCurvature = new RealGrayImage(X, Y);

    // variables for calculating Isophote
    float Iso;
    float Ix;
    float Iy;
    float Ixx;
    float Ixy;
    float Iyy;

    float c;
    float n;
    float ne;
    float e;
    float se;
    float s;
    float sw;
    float w;
    float nw;

    // calculate derivatives
    for (int x = x1; x < x2; x++) {
      for (int y = y1; y < y2; y++) {

        try {
          c = image.get(x, y);
          n = image.get(x, y - 1);
          ne = image.get(x + 1, y - 1);
          e = image.get(x + 1, y);
          se = image.get(x + 1, y + 1);
          s = image.get(x, y + 1);
          sw = image.get(x - 1, y + 1);
          w = image.get(x - 1, y);
          nw = image.get(x - 1, y - 1);

          // calculate Ix
          Ix = (-w + e) / 2;

          // calculate Iy
          Iy = (-n + s) / 2;

          // calculate Ixx
          Ixx = w + (-2 * c) + e;

          // calculate Iyy
          Iyy = n + (-2 * c) + s;

          // calculate Ixy
          Ixy = (nw + -ne + -sw + se) / 4;

          // calculate Iso
          Iso =
              (float)
                  (((Iy * Iy * Ixx) - (2 * Ix * Ixy * Iy) + (Iyy * Ix * Ix))
                      / Math.sqrt(
                          (double)
                              (((Ix * Ix) + (Iy * Iy))
                                  * ((Ix * Ix) + (Iy * Iy))
                                  * ((Ix * Ix) + (Iy * Iy)))));

          // put the result float to output image
          IsophoteCurvature.set(x, y, Iso);
        } catch (Exception ex) {
        }
      }
    }

    return IsophoteCurvature;
  } // calcIsophote
  /**
   * This method caluculates the Isophote flow of a RealGrayImage image at the specified area. This
   * function is less memory floatensive than <i>calcIsophoteFlowStore</i>
   */
  public RealGrayImage calcIsophoteFlow(int x1, int y1, int x2, int y2) {

    // check for null images
    if (image == null) return null;

    // input image dimensions
    int X = image.X();
    int Y = image.Y();

    // variables for calculating Isophote
    float Iso;
    float Ix;
    float Iy;
    float Ixx;
    float Ixy;
    float Iyy;

    float c;
    float n;
    float ne;
    float e;
    float se;
    float s;
    float sw;
    float w;
    float nw;

    if (IsophoteCurvatureFlow == null) IsophoteCurvatureFlow = new RealGrayImage(X, Y);

    // calculate derivatives
    for (int x = x1; x < x2; x++) {
      for (int y = y1; y < y2; y++) {
        c = image.get(x, y);
        if (y - 1 > 0) n = image.get(x, y - 1);
        else n = 0;
        if (y - 1 > 0 && x + 1 < X) ne = image.get(x + 1, y - 1);
        else ne = 0;
        if (x + 1 < X) e = image.get(x + 1, y);
        else e = 0;
        if (y + 1 < Y && x + 1 < X) se = image.get(x + 1, y + 1);
        else se = 0;
        if (y + 1 < Y) s = image.get(x, y + 1);
        else s = 0;
        if (x - 1 > 0 && y + 1 < Y) sw = image.get(x - 1, y + 1);
        else sw = 0;
        if (x - 1 > 0) w = image.get(x - 1, y);
        else w = 0;
        if (y - 1 > 0 && x - 1 > 0) nw = image.get(x - 1, y - 1);
        else nw = 0;

        // calculate Ix
        Ix = (-w + e) / 2;

        // calculate Iy
        Iy = (-n + s) / 2;

        // calculate Ixx
        Ixx = w + (-2 * c) + e;

        // calculate Iyy
        Iyy = n + (-2 * c) + s;

        // calculate Ixy
        Ixy = (nw + -ne + -sw + se) / 4;

        // calculate Iso
        double temp =
            ((Iy * Iy * Ixx) - (2 * Ix * Ixy * Iy) + (Iyy * Ix * Ix))
                / (0.01 + (Ix * Ix) + (Iy * Iy));
        Iso = (float) temp;
        // put the result floato output image
        IsophoteCurvatureFlow.set(x, y, Iso);
      }
    }

    return IsophoteCurvatureFlow;
  } // calcIsophote
 /**
  * Substitutes current image by another image in a specified rectangle area. parameters: x1 - the
  * left bound of the specified area y1 - the right bottom bound of the specified area x2 - the
  * right bound of the specified area y2 - the top bound of the specified area
  */
 public void setImage(RealGrayImage im, int x1, int y1, int x2, int y2) {
   for (int x = x1; x < x2; x++) for (int y = y1; y < y2; y++) image.set(x, y, im.get(x, y));
 }
  /**
   * Overloaded function to stores the result in a RealGrayImage object pointed by <code>dest</code>
   * .
   */
  public void calcIsophoteFlow(
      int x1, int y1, int x2, int y2, RealGrayImage src, RealGrayImage dest) {
    // input image dimensions
    int X = src.X();
    int Y = src.Y();

    // variables for calculating Isophote
    float Iso;
    float Ix;
    float Iy;
    float Ixx;
    float Ixy;
    float Iyy;

    float c;
    float n;
    float ne;
    float e;
    float se;
    float s;
    float sw;
    float w;
    float nw;

    // calculate derivatives
    for (int x = x1; x < x2; x++) {
      for (int y = y1; y < y2; y++) {
        c = src.get(x, y);
        if (y - 1 > 0) n = src.get(x, y - 1);
        else n = 0;
        if (y - 1 > 0 && x + 1 < X) ne = src.get(x + 1, y - 1);
        else ne = 0;
        if (x + 1 < X) e = src.get(x + 1, y);
        else e = 0;
        if (y + 1 < Y && x + 1 < X) se = src.get(x + 1, y + 1);
        else se = 0;
        if (y + 1 < Y) s = src.get(x, y + 1);
        else s = 0;
        if (x - 1 > 0 && y + 1 < Y) sw = src.get(x - 1, y + 1);
        else sw = 0;
        if (x - 1 > 0) w = src.get(x - 1, y);
        else w = 0;
        if (y - 1 > 0 && x - 1 > 0) nw = src.get(x - 1, y - 1);
        else nw = 0;

        // calculate Ix
        Ix = (-w + e) / 2;

        // calculate Iy
        Iy = (-n + s) / 2;

        // calculate Ixx
        Ixx = w + (-2 * c) + e;

        // calculate Iyy
        Iyy = n + (-2 * c) + s;

        // calculate Ixy
        Ixy = (nw + -ne + -sw + se) / 4;

        // calculate Iso
        double temp =
            ((Iy * Iy * Ixx) - (2 * Ix * Ixy * Iy) + (Iyy * Ix * Ix))
                / (0.1 + (Ix * Ix) + (Iy * Iy)); // why 0.1????
        Iso = (float) temp;
        // put the result float to output image
        dest.set(x - x1, y - y1, Iso);
      }
    }
  } // calcIsophote
  /**
   * Overloaded function to stores the result in a RealGrayImage object pointed by <code>dest</code>
   * .
   */
  public void calcIsophoteCurvature(
      int x1, int y1, int x2, int y2, RealGrayImage src, RealGrayImage dest) {
    double totalPix = 0;
    double totalSquare = 0;
    double totalAbsKappa = 0;
    // double totalQuad = 0;

    // input image dimensions
    int X = src.X();
    int Y = src.Y();

    // variables for calculating Isophote
    float Iso;
    float Ix;
    float Iy;
    float Ixx;
    float Ixy;
    float Iyy;

    float c;
    float n;
    float ne;
    float e;
    float se;
    float s;
    float sw;
    float w;
    float nw;

    // calculate derivatives
    for (int x = x1; x < x2; x++) {
      for (int y = y1; y < y2; y++) {
        c = src.get(x, y);
        if (y - 1 > 0) n = src.get(x, y - 1);
        else n = 0;
        if (y - 1 > 0 && x + 1 < X) ne = src.get(x + 1, y - 1);
        else ne = 0;
        if (x + 1 < X) e = src.get(x + 1, y);
        else e = 0;
        if (y + 1 < Y && x + 1 < X) se = src.get(x + 1, y + 1);
        else se = 0;
        if (y + 1 < Y) s = src.get(x, y + 1);
        else s = 0;
        if (x - 1 > 0 && y + 1 < Y) sw = src.get(x - 1, y + 1);
        else sw = 0;
        if (x - 1 > 0) w = src.get(x - 1, y);
        else w = 0;
        if (y - 1 > 0 && x - 1 > 0) nw = src.get(x - 1, y - 1);
        else nw = 0;

        // calculate Ix
        Ix = (-w + e) / 2;

        // calculate Iy
        Iy = (-n + s) / 2;

        // calculate Ixx
        Ixx = w + (-2 * c) + e;

        // calculate Iyy
        Iyy = n + (-2 * c) + s;

        // calculate Ixy
        Ixy = (nw + -ne + -sw + se) / 4;

        // calculate Iso
        double temp =
            ((Iy * Iy * Ixx) - (2 * Ix * Ixy * Iy) + (Iyy * Ix * Ix))
                / (1e-0 + Math.pow((Ix * Ix) + (Iy * Iy), 1.5));
        Iso = (float) temp;
        // put the result float to output image
        dest.set(x - x1, y - y1, Iso);

        totalAbsKappa += (float) Math.abs(Iso);
        totalSquare += Iso * Iso;
        // totalQuad += totalSquare*totalSquare;
        totalPix++;
      }
    }
    avgKappaSquare = totalSquare / totalPix;
    avgAbsKappa = totalAbsKappa / totalPix;
    stdDevAbsKappa = Math.sqrt(avgKappaSquare - (avgAbsKappa * avgAbsKappa));
    // stdDevKappaSquare = sqrt((totalQuad/totalPix) - avgKappaSquare);
  } // calcIsophote