package test;

import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;

import preprocessing.Util;

public class resize {
	
	public static void main(String[] args) throws Exception{ 
		
		String image_name = args[0];
		
		BufferedImage in_image = ImageIO.read(new File(image_name));
		
		double[] hog_vector = hog (in_image);
		
	}
	
	public static double[] hog (BufferedImage original) {
		
		int number_of_cells_x = 1;
		
		int number_of_cells_y = 3;
		
		boolean debug = true;
		
		int new_height = 16;
		
		BufferedImage resize = Util.imageResize (original, new_height);
		
		/*Resized image width*/
		int width  = resize.getWidth();

		/*Resized image height*/
		int height = resize.getHeight();
		
		assert(height == new_height);
		
		/*Image pixels*/
		int rwt = 3;
		
		int n = width * height;
		
		double[] grey = new double[n];
		
		double[] grel = new double[n];
		
		double[] dx = new double[n];
		
		double[] dy = new double[n];
		
		double[] dnorm = new double[n];
		
		double[] dtheta = new double[n];
		
		double[] weight = new double[2*rwt+1];
		
		compute_weights (weight, rwt);
		
		for (int i = 0; i < 2*rwt+1; i++) {
			System.err.printf(" %8.6f ", weight[i]);
		}
		System.err.printf("\n");
		
		get_grey_image (resize, grey);
		
		normalize_grey_image (grey, width, height, weight, rwt, grel);

		
		/*Cell size in pixels in X direction*/
		int cells_size_in_pixels_x = (int)(Math.floor((double)width/(double)number_of_cells_x));

		/*Cell size in pixels in  direction*/
		int cells_size_in_pixels_y = (int)(Math.floor((double)height/(double)number_of_cells_y));
		
		/*Number of Cells*/
		int number_of_cells = number_of_cells_x * number_of_cells_y;
		
		/*Number of gradient directions*/
		int bins_per_cell = 8;	
				
		int total_bins = number_of_cells*bins_per_cell;
				
		/*Creating a matrix to hold the cell histogram*/
		double[] cells_histogram = new double[total_bins];
		
		for (int i = 0; i < total_bins; i++) {
			cells_histogram[i] = 0;
		}
		
		for (int x = 1; x < (width - 1); x++) {

			for (int y = 1; y < (height - 1); y++) {

				int position = y * width + x;
				int kxm = position - 1;
				int kxp = position + 1;
				int kym = position - width;
				int kyp = position + width;
				
				dx[position] = (grel[kxp] - grel[kxm])/2;
				
				dy[position] = (grel[kyp] - grel[kym])/2;
				
				dnorm[position] = Math.hypot(dx[position], dy[position]);
				
				dtheta[position] = Math.atan2(dy[position], dx[position]);
				
				if (dtheta[position] < 0) {
					dtheta[position] += Math.PI;
				}
				
				int cx = (number_of_cells_x * (x-1))/(width-1);
				
				assert( (cx >= 0) && (cx < number_of_cells_x)); 
				
				int cy = (number_of_cells_y * (y-1))/(height-1);
				
				assert( (cy >= 0) && (cy < number_of_cells_y));
				
				int c_pos = cy * number_of_cells_x + cx;
				
				int bin = (int) Math.floor(bins_per_cell*(dtheta[position])/(Math.PI));
				
				if (bin == -1) { bin = 0; }
				if (bin == bins_per_cell) { bin = bins_per_cell - 1; }
				
				assert ( (bin >= 0) && (bin < bins_per_cell));
								
				int bin_pos = c_pos * bins_per_cell + bin;
				
				cells_histogram[bin_pos] += dnorm[position];
				
				
			}	
				
		}
			
		/*Normalize the histogram of each cell to unit L2 norm*/
		for (int cy = 0; cy < number_of_cells_y; cy++) {

			for (int cx = 0; cx < number_of_cells_x; cx++) {

				int c_pos = cy * number_of_cells_x + cx;

				double sum_v2 = 0.0;

				for (int bin = 0; bin < bins_per_cell; bin++) {
					int bin_pos = c_pos * bins_per_cell + bin;
					double v = cells_histogram [bin_pos];
					sum_v2 += v * v;
				}

				double cell_norm = Math.sqrt(sum_v2 + 0.0000001);

				for (int bin = 0; bin < bins_per_cell; bin++) {

					int bin_pos = c_pos * bins_per_cell + bin;
					double v = cells_histogram [bin_pos];
					cells_histogram [bin_pos] = v/cell_norm;
					if (debug) {
						System.err.printf("cell : %d,%d bin : %d, sum : %f\n", cx, cy, bin, cells_histogram [bin_pos]); 
					}
				}
			}	
		}
			
		
		if (debug) {
			
			Util.writeDoubletoPGM (grel, width, height, 0.0, 1.0, "norm");
			Util.writeDoubletoPGM (dx, width, height, -1.0, 1.0, "dx");
			Util.writeDoubletoPGM (dy, width, height, -1.0, 1.0, "dy");
			Util.writeDoubletoPGM (dnorm, width, height, 0.0, 1.42, "dnorm");
			Util.writeDoubletoPGM (dtheta, width, height, 0, Math.PI, "dtheta");

			try {
				String outname1 = "original.png";
				String outname2 = "resized.png";
				ImageIO.write(original, "png", new File(outname1));
				ImageIO.write(resize, "png", new File(outname2));
			}
			catch (Exception e1) {
				System.err.println("error: fail to save the images");
			}
		}
		
		
		return null;
	}	

	public static void get_grey_image (BufferedImage image, double[] grey) {

		int w = image.getWidth(null);
		int h = image.getHeight(null);
		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {				
				int pixel = image.getRGB(x, y);
				int position = y * w + x;				
				double R = (pixel >> 16) & 255;
				double G = (pixel >> 8) & 255;
				double B = (pixel & 255);
				grey[position] = 0.299*R + 0.587*G + 0.114*B;
			}
		}
	}
	
	public static void compute_weights (double[] weight, int rwt) {
		int nwt = 2*rwt+1;
		weight[0] = 1;
		for (int i = 1; i < nwt; i++) { 
			weight[i] = 0.0; 
			for (int j = i; j >=1; j--) {
				weight[j] = (weight[j] + weight[j-1])/2;
			}
			weight[0] /= 2;
		}
	}
	
	public static void normalize_grey_image (double[] grey, int w, int h, double[] weight, int rwt, double[] grel) {
		double AVG, DEV;
		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {				
				int position = y * w + x;	
				AVG = get_grey_avg (grey, w, h, x, y, weight, rwt);
				DEV = get_grey_dev (grey, w, h, x, y, weight, rwt, AVG);
				grel[position] = (grey[position] - AVG)/DEV;
			}
		}
	
	}
	
	public static double get_grey_avg (double[] grey, int w, int h, int x, int y, double[] weight, int rwt) {
		double sum_vwt = 0.0, sum_wt = 0.0;
		for (int dy = -rwt; dy <= rwt; dy++) {
			for (int dx = -rwt; dx <= rwt; dx++) {
				int x1 = x + dx;
				int y1 = y + dy;
				if ( (x1 >= 0) && (x1 < w) && (y1 >= 0) && (y1 < h)) {
				    int position = y1 * w + x1; 
				    double v = grey[position];
				    double wt = weight[rwt+dx]*weight[rwt+dx];
				    sum_vwt += v * wt;
				    sum_wt += wt; 
				}

			}
		}
		return sum_vwt/sum_wt;
	}
	
	public static double get_grey_dev (double[] grey, int w, int h, int x, int y, double[] weight, int rwt, double AVG) {
		double sum_v2wt = 0.0, sum_wt = 0.0;
		for (int dy = -rwt; dy <= rwt; dy++) {
			for (int dx = -rwt; dx <= rwt; dx++) {
				int x1 = x + dx;
				int y1 = y + dy;
				if ( (x1 >= 0) && (x1 < w) && (y1 >= 0) && (y1 < h)) {
				    int position = y1 * w + x1; 
				    double v = grey[position]-AVG;
				    double wt = weight[rwt+dx]*weight[rwt+dx];
				    sum_v2wt += v * v * wt;
				    sum_wt += wt; 
				}

			}
		}
		double noise = 0.01; /*Assumed standard deviation of noise*/
		return Math.sqrt(sum_v2wt/sum_wt + noise*noise);
	}
}
