package yi.iu.processor.filter;

import java.awt.Dimension;
import yi.util.ImageUtil;

/**
 * Class Average applies an average mask on the given image.
 * <BR/>
 * The pixels on the boundaries may use part of the mask (the part of the mask
 * outside the boundary of the image will not be applied to the boundary pixels).
 * For example, the masked pixel for (0,0) is resulted from original pixel (0,0),
 * (1,0), (0,1), (1,1) even though the size of the mask is 3*3.
 * <BR/>
 * In most cases, the mask size should be odd. (eg. 3*3, 5*5, etc.)
 */
public class Average {
  /**
   * Average the given image using the given width and height of the average
   * mask.  This function depends on
   *   average(int[][] pixels, int imgWidth, int imgHeight, int maskWidth, int maskHeight);
   * @param pixels the original image pixels.  In most case, they should be in
   *               gray levels.
   * @param imgWidth image width
   * @param imgHeight image height
   * @param maskWidth mask width
   * @param maskHeight mask height
   * @return the averaged image
   */
  public static int[] average(int[] pixels, int imgWidth, int imgHeight,
    int maskWidth, int maskHeight) {
      int[][] pixels2d = yi.util.ImageUtil.oneD2twoD(pixels, new Dimension(imgWidth, imgHeight));
      int[][] rst = average(pixels2d, imgWidth, imgHeight, maskWidth, maskHeight);
      return yi.util.ImageUtil.twoD2oneD(rst);
  }

  /**
   * Average the given image using the given width and height of the average
   * mask.  In most cases, the mask size should be odd. (eg. 3*3, 5*5, etc.)
   * @param pixels the original image pixels.  In most case, they should be in
   *               gray levels.
   * @param imgWidth image width
   * @param imgHeight image height
   * @param maskWidth mask width
   * @param maskHeight mask height
   * @return the averaged image
   */
  public static int[][] average(int[][] pixels, int imgWidth, int imgHeight, int maskWidth, int maskHeight) {

    int i, j, m, n, sx, sy, ex, ey ;  //start-x, start-y, endx, endy
    int	cell_count, total_value ;
    int[][] averaged = new int[imgHeight][imgWidth];

    int halfMaskHeight = maskHeight/2, halfMaskWidth = maskWidth/2;
    for (i = 0 ; i < imgHeight ; i++) {
      for (j = 0 ; j < imgWidth ; j++) {
        // on the boundary
        sy = (i < halfMaskHeight) ? 0 : i - halfMaskHeight;
        sx = (j < halfMaskWidth) ?  0 : j - halfMaskWidth;
        ey = ((i + halfMaskHeight) >= imgHeight) ? imgHeight-1 : i + halfMaskHeight ;
        ex = ((j + halfMaskWidth) >= imgWidth) ? imgWidth-1 : j + halfMaskWidth ;

        // sum
        cell_count = 0 ;
        total_value = 0 ;
        for (m = sy ; m <= ey ; m++) {
          for (n = sx ; n <= ex ; n++) {
            ++cell_count ;
            total_value += pixels[m][n] ;
          }
        }

        // average
        averaged[i][j] = (int)Math.round((double)total_value / cell_count) ;
      }
    }
    return averaged;
  }

  public static double[] average(double[] pixels, int imgWidth, int imgHeight,
    int maskWidth, int maskHeight) {
      double[][] pixels2d = yi.util.data.Conversion.from1Dto2D(pixels, imgWidth, imgHeight);
      double[][] rst = average(pixels2d, imgWidth, imgHeight, maskWidth, maskHeight);
      return yi.util.data.Conversion.from2Dto1D(rst, imgWidth, imgHeight);
  }

  public static double[][] average(double[][] pixels, int imgWidth, int imgHeight, int maskWidth, int maskHeight) {

    int i, j, m, n, sx, sy, ex, ey ;  //start-x, start-y, endx, endy
    int	cell_count;
    double total_value ;
    double[][] averaged = new double[imgHeight][imgWidth];

    int halfMaskHeight = maskHeight/2, halfMaskWidth = maskWidth/2;
    for (i = 0 ; i < imgHeight ; i++) {
      for (j = 0 ; j < imgWidth ; j++) {
        // on the boundary
        sy = (i < halfMaskHeight) ? 0 : i - halfMaskHeight;
        sx = (j < halfMaskWidth) ?  0 : j - halfMaskWidth;
        ey = ((i + halfMaskHeight) >= imgHeight) ? imgHeight-1 : i + halfMaskHeight ;
        ex = ((j + halfMaskWidth) >= imgWidth) ? imgWidth-1 : j + halfMaskWidth ;

        // sum
        cell_count = 0 ;
        total_value = 0 ;
        for (m = sy ; m <= ey ; m++) {
          for (n = sx ; n <= ex ; n++) {
            ++cell_count ;
            total_value += pixels[m][n] ;
          }
        }

        // average
        averaged[i][j] = total_value / cell_count ;
      }
    }
    return averaged;
  }

  /**
   * Average within the pixels having the same label
   * @param pixels
   * @param labels
   * @param imgWidth
   * @param imgHeight
   * @param maskWidth
   * @param maskHeight
   * @return
   */
  public static double[][] average(double[][] pixels, int[] labels, int imgWidth, int imgHeight, int maskWidth, int maskHeight) {

    int i, j, m, n, sx, sy, ex, ey ;  //start-x, start-y, endx, endy
    int	cell_count;
    double total_value ;
    double[][] averaged = new double[imgHeight][imgWidth];

    int halfMaskHeight = maskHeight/2, halfMaskWidth = maskWidth/2;
    for (i = 0 ; i < imgHeight ; i++) {
      for (j = 0 ; j < imgWidth ; j++) {
        // on the boundary
        sy = (i < halfMaskHeight) ? 0 : i - halfMaskHeight;
        sx = (j < halfMaskWidth) ?  0 : j - halfMaskWidth;
        ey = ((i + halfMaskHeight) >= imgHeight) ? imgHeight-1 : i + halfMaskHeight ;
        ex = ((j + halfMaskWidth) >= imgWidth) ? imgWidth-1 : j + halfMaskWidth ;

        // sum
        cell_count = 0 ;
        total_value = 0 ;
        for (m = sy ; m <= ey ; m++) {
          for (n = sx ; n <= ex ; n++) {
            if (labels[i*imgWidth+j] != labels[m*imgWidth+j]) continue;
            ++cell_count ;
            total_value += pixels[m][n] ;
          }
        }

        // average
        averaged[i][j] = total_value / cell_count ;
      }
    }
    return averaged;
  }

  public static double[] average(double[] pixels, int[] labels, int imgWidth, int imgHeight,
    int maskWidth, int maskHeight) {
      double[][] pixels2d = yi.util.data.Conversion.from1Dto2D(pixels, imgWidth, imgHeight);
      double[][] rst = average(pixels2d, labels, imgWidth, imgHeight, maskWidth, maskHeight);
      return yi.util.data.Conversion.from2Dto1D(rst, imgWidth, imgHeight);
  }

    /**
   * Average the given image using the given width and height of the average
   * mask.  The value of each pixel is converted to absoluted value frist.
   * In most cases, the mask size should be odd. (eg. 3*3, 5*5, etc.)
   * @param pixels the original image pixels.  In most case, they should be in
   *               gray levels.
   * @param imgWidth image width
   * @param imgHeight image height
   * @param maskWidth mask width
   * @param maskHeight mask height
   * @return the averaged image
   */
  public static int[][] averageOfAbsolutes(int[][] pixels, int imgWidth, int imgHeight, int maskWidth, int maskHeight) {

    int i, j, m, n, sx, sy, ex, ey ;  //start-x, start-y, endx, endy
    int	cell_count, total_value ;
    int[][] averaged = new int[imgHeight][imgWidth];

    int halfMaskHeight = maskHeight/2, halfMaskWidth = maskWidth/2;

    for (i = 0 ; i < imgHeight ; i++) {
      for (j = 0 ; j < imgWidth ; j++) {
        // on the boundary
        sy = (i < halfMaskHeight) ? 0 : i - halfMaskHeight;
        sx = (j < halfMaskWidth) ?  0 : j - halfMaskWidth;
        ey = ((i + halfMaskHeight) >= imgHeight) ? imgHeight-1 : i + halfMaskHeight ;
        ex = ((j + halfMaskWidth) >= imgWidth) ? imgWidth-1 : j + halfMaskWidth ;

        // sum
        cell_count = 0 ;
        total_value = 0 ;
        for (m = sy ; m <= ey ; m++) {
          for (n = sx ; n <= ex ; n++) {
            ++cell_count ;
            total_value += Math.abs(pixels[m][n]) ;
          }
        }

        // average
        averaged[i][j] = (int)Math.round((double)total_value / cell_count) ;
      }
    }
    return averaged;
  }
}