/* Returns the unique set of pixel values present in the given ImageData.
   * @param
   * 		o	the ImageData explored for this set
   */
  public ArrayList unique(ImageData o) {
    int pixel_values[] = new int[256]; // use for counting pixel values actually seen
    ArrayList<Double> list =
        new ArrayList(); // use for storing only those pixel values that are non-zero when it's all
                         // said and done

    // initialize pixel value array
    // for (int i=0; i < 256; i++) { pixel_values[i] = 0; }
    for (int i = 0; i < 255; i++) {
      pixel_values[i] = 0;
    }

    // walk and count pixel values
    for (int row = 0; row < o.getNumRows(); row++) {
      for (int col = 0; col < o.getNumCols(); col++) {
        for (int band = 0; band < o.getNumBands(); band++) {
          pixel_values[o.getInt(row, col, band)]++;
        }
      }
    }

    // identify and collect only unique pixel values (i.e., those with non-zero counts)
    int total_unique = 0;
    // for (int i=0; i < 256; i++) {
    for (int i = 0; i < 255; i++) {
      if (pixel_values[i] != 0) {
        list.add(new Double(i));
      }
    }

    return list;
  }
  /*
   * Determine whether or not 2 ImageObject objects are the "same size"
   * based on whether their respective dimensions are equal (e.g., height and width,
   * in the terminology of "rows" and "columns").
   *
   */
  public boolean sameSize(ImageData o1, ImageData o2) {
    int o1_cols = o1.getNumCols();
    int o1_rows = o1.getNumRows();
    int o2_cols = o2.getNumCols();
    int o2_rows = o2.getNumRows();

    if (o1_cols != o2_cols || o1_rows != o2_rows) {
      return false;
    }
    return true;
  }
  /*
   * Returns the sum of the pixel values of a given ImageData.
   * @param o
   * 		The ImageData providing pixels to be counted
   */
  public double sum(ImageData o) {
    double sum = 0;

    for (int row = 0; row < o.getNumRows(); row++) {
      for (int col = 0; col < o.getNumCols(); col++) {
        for (int band = 0; band < o.getNumBands(); band++) {
          sum += o.getValue(row, col, band);
        }
      }
    }
    return sum;
  }
  public ArrayList vectorize(ImageData o) {
    ArrayList list = new ArrayList();

    for (int row = 0; row < o.getNumRows(); row++) {
      for (int col = 0; col < o.getNumCols(); col++) {
        for (int band = 0; band < o.getNumBands(); band++) {
          list.add(new Double(o.getValue(row, col, band)));
        }
      }
    }

    return list;
  }
  public boolean equals(ImageData a) {
    if (rows != a.getNumRows()) return false;
    if (cols != a.getNumCols()) return false;
    if (bands != a.getNumBands()) return false;

    double[][][] avals = a.getValues();
    int len1 = values.length;
    int len2 = values[0].length;
    int len3 = values[0][0].length;
    for (int i = 0; i < len1; i++)
      for (int j = 0; j < len2; j++)
        for (int k = 0; k < len3; k++) if (values[i][j][k] != avals[i][j][k]) return false;
    return true;
  }
  /*
   * Implements an element-wise addition operation for a pair of ImageData objects.
   * @param o1
   * 		The first ImageData object
   * @param o2
   * 		The second ImageData object
   */
  public ImageData add(ImageData o1, ImageData o2) {

    double pixel1 = 0.0;
    double pixel2 = 0.0;
    double value = 0.0;
    ImageData r = new ImageData();

    try {
      r =
          (ImageData)
              o1
                  .clone(); // BJL: This algorithm assumes the input images are the same size, so
                            // can choose either one as a template for the result.
    } catch (Exception e) {
      System.out.println(e.getMessage());
      return null;
    }

    for (int row = 0; row < r.getNumRows(); row++) {
      for (int col = 0; col < r.getNumCols(); col++) {
        for (int band = 0; band < r.getNumBands(); band++) {
          pixel1 = o1.getInt(row, col, band);
          pixel2 = o2.getInt(row, col, band);
          value = pixel1 + pixel2;
          r.set(row, col, band, value);
        }
      }
    }
    return r;
  }
  /*
   * Implements a "logical negation" operation for a single ImageData object.
   * @param o
   * 		The ImageData object to be negated.
   */
  public ImageData match(ImageData o, double value) {
    int pixel;
    ImageData r = new ImageData();

    try {
      r =
          (ImageData)
              o
                  .clone(); // BJL: This algorithm assumes the input images are the same size, so
                            // can choose either one as a template for the result.
    } catch (Exception e) {
      System.out.println(e.getMessage());
      return null;
    }

    for (int row = 0; row < r.getNumRows(); row++) {
      for (int col = 0; col < r.getNumCols(); col++) {
        for (int band = 0; band < r.getNumBands(); band++) {
          pixel = o.getInt(row, col, band);
          if (pixel == (int) value) {
            r.set(row, col, band, 1); // if pixel = value, set it to 1
          } else {
            r.set(row, col, band, 0); // otherwise, set it to 0
          }
        }
      }
    }

    return r;
  }
  /*
   * Converts an input image object to a binary one (with each pixel value taking on only 1 or 0).
   * @param o
   * 		input image object to be converted
   */
  public ImageData logical(ImageData o) {
    ImageData r = new ImageData();
    int pixel;

    try {
      r = (ImageData) o.clone();
    } catch (Exception e) {
      System.out.println(e.getMessage());
      return null;
    }

    // System.out.println("logical:");
    for (int row = 0; row < r.getNumRows(); row++) {

      for (int col = 0; col < r.getNumCols(); col++) {
        for (int band = 0; band < r.getNumBands(); band++) {
          pixel = r.getInt(row, col, band);
          // System.out.print(""+pixel + " ");
          if (pixel > 0) {
            r.set(row, col, band, 1); // if pixel > 0, set it to 1
          } else {
            r.set(row, col, band, 0); // otherwise, set it to 0
          }
        }
      }
      // System.out.println("");
    }
    //	System.out.println(">logical.");
    return r;
  }
  /*
   * Traverses the original and mask images in tandem.
   * The mask should be identical to the original except it
   * should have been preselected to be a binary image.
   *
   * Wherever the mask has a 1 value, add that pixel value to
   * the list.
   *
   * Return the list unsorted and only containing pixel values of
   * the original matching a corresponding mask 1-value.
   *
   * @param orig
   * 		The original ImageData image
   * @param mask
   * 		The corresponding ImageData mask (having identical dimensions and containing binary values)
   *
   */
  public ArrayList mask(ImageData orig, ImageData mask) {
    ArrayList list =
        new ArrayList(); // a column-wise traversal of the input images populates this 1D list

    for (int col = 0; col < orig.getNumCols(); col++) {
      for (int row = 0; row < orig.getNumRows(); row++) {
        for (int band = 0; band < orig.getNumBands(); band++) {
          if (mask.getValue(row, col, band) == 1) {
            list.add(new Double(orig.getValue(row, col, band)));
          }
        }
      }
    }
    return list;
  }
 public ImageData clone() {
   ImageData o = new ImageData();
   o.setValues(values);
   o.setRows(rows);
   o.setCols(cols);
   o.setBands(bands);
   o.setDebug(debug);
   return o;
 }
  /*
   * Implements a "logical negation" operation for a single ImageData object.
   * @param o
   * 		The ImageData object to be negated.
   */
  public ImageData not(ImageData o) {

    int pixel;
    ImageData r = new ImageData();
    ImageData bin_o = logical(o);

    try {
      r =
          (ImageData)
              bin_o
                  .clone(); // BJL: This algorithm assumes the input images are the same size, so
                            // can choose either one as a template for the result.
    } catch (Exception e) {
      System.out.println(e.getMessage());
      return null;
    }

    // System.out.println("not:");
    for (int row = 0; row < r.getNumRows(); row++) {
      for (int col = 0; col < r.getNumCols(); col++) {
        for (int band = 0; band < r.getNumBands(); band++) {
          pixel = bin_o.getInt(row, col, band);
          // System.out.print(""+pixel+" ");

          if (pixel == 1) {
            r.set(row, col, band, 0); // if pixel = 1, set it to 0
          } else {
            r.set(row, col, band, 1); // otherwise, set it to 1
          }
        }
      }
      // System.out.println();
    }
    // System.out.println(">not.");

    return r;
  }
  /*
   * Implements a "logical or" operation for a pair of ImageData objects.
   * @param o1
   * 		The first ImageData object
   * @param o2
   * 		The second ImageData object
   */
  public ImageData or(ImageData o1, ImageData o2) {

    int pixel_o1;
    int pixel_o2;
    ImageData r = new ImageData();
    ImageData bin_o1 =
        logical(
            o1); // BJL: We convert them to logical/binary form by default. Helpful if not
                 // unconverted. Otherwise, this action is redundant. Covered both cases here.
    ImageData bin_o2 = logical(o2);

    try {
      r =
          (ImageData)
              o1
                  .clone(); // BJL: This algorithm assumes the input images are the same size, so
                            // can choose either one as a template for the result.
    } catch (Exception e) {
      System.out.println(e.getMessage());
      return null;
    }

    for (int row = 0; row < r.getNumRows(); row++) {
      for (int col = 0; col < r.getNumCols(); col++) {
        for (int band = 0; band < r.getNumBands(); band++) {
          pixel_o1 = bin_o1.getInt(row, col, band);
          pixel_o2 = bin_o2.getInt(row, col, band);
          if ((pixel_o1 == 1) || (pixel_o2 == 1)) {
            r.set(row, col, band, 1); // if either pixel is 1, set it to 1
          } else {
            r.set(row, col, band, 0); // otherwise, set it to 0
          }
        }
      }
    }

    return r;
  }