package yi.iu.processor.texture.laws;

import java.awt.*;
import yi.iu.processor.filter.Average;
import yi.iu.processor.channel.CYMaker;
import yi.util.config.*;
import yi.util.io.ReadArgu;

/**
 * Class DPU_FastLaws is to provide a fast calculation of the convolutions when
 * computing Laws Texture Energy Measures.
 * <BR/>
 * From experiments, this doesn't save too much time (48sec vs 50sec)
 */
public class DPU_FastLaws extends DPU_Laws{
  /**
   * After the proprocessing, each of the sixteen 5*5 masks are applied to the
   * preprocessed image, producing sixteen filtered images. ... Then the texture
   * energy map for filter i is defined Ek(L. Shapiro)
   * <BR/>
   * Method convolutions() overrides the one defined in its parent class,
   * DPU_Laws, to provide a fast computing.
   *
   * @param source the source data
   * @param width the width of the source data
   * @param height the height of the source data
   * @return the convolution result
   */
  int[][][] convolutions(int[][] source, int width, int height) {

    // first, do vertical convolution for each vector (L5, E5, S5, R5)
    int[][][] verticalConvoluted = new int[VECTORS.length][][];
    for (int i=0; i<VECTORS.length; i++) {
      verticalConvoluted[i] = verticalConvolution(source, width, height, VECTORS[i]);
    }

    // second, do horizontal convolutions based on vertical convolution results
    int[][][] filtered = new int[MASKS.length][][];
    int count = 0;
    for (int i=0; i<VECTORS.length; i++) {
      for (int j=0; j<VECTORS.length; j++) {
        filtered[count] = horizontalConvolution(verticalConvoluted[i], width, height, VECTORS[j]);
        filtered[count] = Average.averageOfAbsolutes(filtered[count], width, height,
          POST_SMOOTH_WIN_WIDTH, POST_SMOOTH_WIN_HEIGHT);
        count++;
      }
    }
    return filtered;


  }

  /**
   * Vertical Convolution Calculation.  Use those pixels vertical to the current
   * pixel to make up the second vector and the filter as vector to be multiplied
   * <PRE>
   *           y
   * (f f f) * p
   *           y
   * </PRE>
   *
   * @param input input image
   * @param w image width
   * @param h image height
   * @param filter filter
   * @return convolution result
   */
  int[][] verticalConvolution(int[][] input, int w, int h, int[] filter) {
    int[][] rst = new int[h][w];

    int i, j, m, sy, ey ;  //start-y, endy
    int	cell_count, total_value ;

    int half = filter.length/2;

    for (i = 0 ; i < h ; i++) {
      for (j = 0 ; j < w ; j++) {
        // on the boundary
        sy = (i < half) ? 0 : i - half;
        ey = ((i + half) >= h) ? h-1 : i + half ;

        // sum
        cell_count = 0 ;
        total_value = 0 ;
        for (m = sy ; m <= ey ; m++) {
          ++cell_count ;
          total_value += input[m][j] * filter[m-i+half];
        }

        // average
        rst[i][j] = total_value;
      }
    }
    return rst;
  }

  /**
   * Horizontal Convolution Calculation.  Use those pixels horizontal to the current
   * pixel to make up the first vector and the filter as vector to be multiplied
   * <PRE>
   *           f
   * (x p x) * f
   *           f
   * </PRE>
   *
   * @param input input image
   * @param w image width
   * @param h image height
   * @param filter filter
   * @return convolution result
   */
  int[][] horizontalConvolution(int[][] input, int w, int h, int[] filter) {
    int[][] rst = new int[h][w];

    int i, j, m, sx, ex ;  //start-y, endy
    int	cell_count, total_value ;

    int half = filter.length/2;

    for (i = 0 ; i < h ; i++) {
      for (j = 0 ; j < w ; j++) {
        // on the boundary
        sx = (j < half) ? 0 : j - half;
        ex = ((j + half) >= w) ? w-1 : j + half ;

        // sum
        cell_count = 0 ;
        total_value = 0 ;
        for (m = sx ; m <= ex ; m++) {
          ++cell_count ;
          total_value += input[i][m] * filter[m-j+half];
        }

        // average
        rst[i][j] = total_value;
      }
    }
    return rst;
  }

  public static void main(String[] args) {
    // init configuration
    Configuration.init(args);

    // get the image name
    String img_name = ReadArgu.getOption('i', args);
    if (img_name == null) {
      System.err.println("Usage: -i image_name");
      System.exit(1);
    }

    // read in image
    Image image = null;
    Dimension d = new Dimension();
    int[] pixels = null;
    try {
      image = yi.util.io.ImageReader.readImageFile(img_name);
      pixels = yi.util.io.ImageReader.getPixels(image, d);
    }
    catch (java.io.IOException e) {
      System.err.println("Can't read image " + img_name);
      e.printStackTrace();
      System.exit(2);
    }

    // process
    // change to 2D gray level image first
    pixels = CYMaker.getLuminance(pixels, d);               // change to gray
    int[][] pixels2D = yi.util.ImageUtil.oneD2twoD(pixels, d);  // change to 2D
    Configuration.setParameter("SOURCE_NAME", img_name);
    DPU_FastLaws dp = new DPU_FastLaws();
    long startTime = System.currentTimeMillis();
    int[][][] rst = dp.getTextureEnergyMeasures(pixels2D, d.width, d.height);
    long endTime = System.currentTimeMillis();
    System.out.println("running time: " + (endTime-startTime));

    for (int h =0; h<4; h++) {
      for (int w=0; w<4; w++) {
        System.out.print("("+w+","+h+"):");
        for (int i=0;i<rst.length;i++) {
          System.out.print(rst[i][h][w]);
          System.out.print(" ");
        }
        System.out.println();
      }
    }

    System.exit(0);
  }
}