import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class imageUtilsDemo{    

   public static void displayImage(BufferedImage img)
       {
       ImageIcon icon=new ImageIcon(img);
      JFrame frame=new JFrame();
      frame.setLayout(new FlowLayout());
      frame.setSize(img.getWidth() + 100  ,img.getHeight() + 100);
      JLabel lbl=new JLabel();
      lbl.setIcon(icon);
      frame.add(lbl);
      frame.setVisible(true);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }
   
   public static void displayImage(int[][][] matrix){
      displayImage(convert3DMatrixToBufferedImage(matrix));
   }
      
   public static BufferedImage convert3DMatrixToBufferedImage(int[][][] matrix)    {
        BufferedImage img = new BufferedImage(matrix[0].length, matrix.length, BufferedImage.TYPE_INT_RGB);
        for (int i = 0; i < img.getHeight(); i++){
            for (int j = 0; j < img.getWidth(); j++){
                img.setRGB(j, i, new Color(matrix[i][j][0], matrix[i][j][1], matrix[i][j][2]).getRGB());
            }
        }
        return img;
    }

   public static BufferedImage loadImageAsBufferedImage(String path)      {
        BufferedImage img = null;
        try {
            img = ImageIO.read(new File(path));
        } catch (IOException e) {}
        return img;
    }
   
   public static int[][][] convertBufferedImageTo3DMatrix(BufferedImage img)    {
        int[][][] matrix = new int[img.getHeight()][img.getWidth()][3];
        
        for (int i = 0; i < img.getHeight(); i++)        {
            for (int j = 0; j < img.getWidth(); j++)            {
                Color color = new Color(img.getRGB(j,i));
                matrix[i][j][0] = color.getRed();
                matrix[i][j][1] = color.getGreen();
                matrix[i][j][2] = color.getBlue();
            }
        }
        return matrix;
    }
    
   public static int[][][] loadImageAs3DMatrix(String path)    {
        return convertBufferedImageTo3DMatrix(loadImageAsBufferedImage(path));
    }    
       
   public static int[][][] grayToNormalizedColor(int [][] I){   
      int [][][] G = new int[I.length][I[0].length][3];
      int max=0;
      int min = 10000;
      int color;
      for(int r=0;r         for(int c=0;c            if(I[r][c] > max)
               max = I[r][c];
            else
               if (I[r][c] < min)
                  min = I[r][c];      
      for(int r=0;r         for(int c=0;c            color = 255*(I[r][c] - min)/(max-min);
            for(int k=0;k<3;k++)
               G[r][c][k]=color;
         }
      return G;
   }
   
    //---------------------------------------------------------------------------------------------------------------------------
  
       //This method receives a left image IL and a right image IR as 3D arrays, m X n X 3 
     //which means row X column X RGBcolor
     //it returns a disparity map D of size m X n    (which means row X disparity)
     public static int[][] getImageDisparity(int[][][] IL, int[][][] IR, int occlusionCost) {            
         
         int[][] disparity = new int[IL.length][IL[0].length];            
         
         for (int i=0;i             disparity[i] = getRowDisparity(IL[i], IR[i], occlusionCost);    
                                                                         
         } 
         //displayTwoD(disparity); //for debugging
         return disparity;
     }
     
     // integer counters to try to get an idea about the path from the bottom right corner to the top left corner of the matrix
     public static int vX_Counter;
     public static int vY_Counter;
     public static int vZ_Counter;     
     public static int lcX_Counter;
     public static int lcY_Counter;
     public static int lcZ_Counter;
     
     //this method receives the rows as 2D arrays, rows X color and returns the disparity of each row, stored in a 1D array
     public static int[] getRowDisparity(int[][] IL, int[][] IR, int occlusionCost) {
                  
         // for the motorcycle image, row and column are 901 here
         // because like the input strings in edit distance the inputs here are of the same length
         // otherwise it would not be a good comparison 
         int [][]disparity = new int[IL.length+1][IR.length+1];

         //The first row and column of the the matrix are filled with the cost of not matching pixels
         //meaning:  d[i][0] = i*oclussionCost
         // and      d[0][j] = j*oclussionCost
         for (int i=0; i//rows
             disparity[i][0] = i*occlusionCost;
         }
         for (int j=0; j//columns
             disparity[0][j] = j*occlusionCost;                
         }
         
         //The rest of the matrix is filled as follows:
         for (int j=0; j//columns
             for (int i=0; i//rows
                 int x = disparity[i][j+1]+occlusionCost;
                 int y = disparity[i+1][j]+occlusionCost;
                 int z = disparity[i][j] + (int)Math.sqrt( (int)Math.pow((IL[i][0]-IR[j][0]), 2)+(int)Math.pow((IL[i][1]-IR[j][1]), 2)+(int)Math.pow((IL[i][2]-IR[j][2]), 2) ) ;
                                  
                 disparity[i+1][j+1] = min(x, y, z);
                 int lcMinimum = disparity[i+1][j+1];
                
                 //added this part for debugging.  It's not really necessary
                if (lcMinimum == y) {
                    lcY_Counter++;
                }
                if (lcMinimum == x) {
                    lcX_Counter++;
                }
                if (lcMinimum == z) {
                    lcZ_Counter++;
                }         
            }
        }
         //for debugging
         //displayImage(grayToNormalizedColor(disparity));
         //displayTwoD(disparity);           
        //System.out.println("So I think this means the edit distance of the row is : " + disparity[disparity.length-1][disparity[0].length-1]); //prints bottom right hand corner
                
        int row=disparity.length-1;                //has to be minus one 
        int col=disparity[0].length-1;            //has to be minus one
               
        int current = disparity[disparity.length-1][disparity[0].length-1];        //current is initialized, to go through the path from the end to the beginning
        //System.out.println("current is: " + current);  //for debugging
        
        int[] disp = new int[disparity.length];
        //System.out.println("disp.length is: " + disp.length); //disp.length for the motorcycle image is: 901
        
        try {
            while(true) {

                // according to the notes disp[i-1]=i-j; so:
                disp[row-1] = row-col;

                int vX = disparity[row-1][col-1-1]+occlusionCost;   //add -1 to offset
                //System.out.println("vX is: " + vX); //for debugging
                int vY = disparity[row-1-1][col-1]+occlusionCost;    
                //System.out.println("vY is: " + vY); //for debugging
                int vZ = disparity[row-1][col-1] + (int)Math.sqrt( (int)Math.pow((IL[col-1][0]-IR[col-1][0]), 2)+(int)Math.pow((IL[col-1][1]-IR[col-1][1]), 2)+(int)Math.pow((IL[col-1][2]-IR[col-1][2]), 2) ) ;
                //System.out.println("vZ is: " + vZ); //for debugging

                int minimum = min(vX, vY, vZ);
                
                if (minimum == vY) {
                    vY_Counter++;
                    //System.out.println("Y->" + disparity[row-1][col]); //for debugging
                    current = disparity[row-1][col];
                    row--;
                }                
                if (minimum == vX) {
                    vX_Counter++;
                    //System.out.println("X->" + disparity[row][col-1]); //for debugging
                    current = disparity[row][col-1];                    
                    col--;
                }                
                if (minimum == vZ) {
                    vZ_Counter++;
                    //System.out.println("Z->" + disparity[row-1][col-1]); //for debugging
                    current = disparity[row-1][col-1];
                    row--;
                    col--;
                }
            }
        }
        catch (java.lang.ArrayIndexOutOfBoundsException e) {
            //System.out.println("0"); //for debugging
        }
        //This line prints the 1D array to the screen in a line
        //so when the getImageDisparity method calls this method i times this line winds up displaying over and over
        //which is useful to see in the konsole, because if all the numbers are zero's then some thing went wrong.
        displayOneD(disp);

        return disp;
    }
     
     //this method returns the minimum of the 3 input integers
     public static int min(int A, int B, int C) {
         
         int result = A;
         
         if (B             result = B; 
         }
         if (C             result = C;
         }
         return result;
     }
     
     // auxiliary method to display to screen for debugging
       public static void displayTwoD(int[][] arr) {
              
         for (int i=0; i             //System.out.print(" " + i + " ");
             for (int j=0; j                 System.out.print( arr[i][j] + "\t" );
             }
         System.out.println("");    
         }        
     }
       
       // auxiliary method to display to screen 
       public static void displayOneD(int[] arr) {
           
           for (int i=0; i               System.out.print( arr[i] + "\t" );
           }
           System.out.println("");
       }

    public static void main(String[] args) {

        long timeBefore = System.currentTimeMillis();
        
        //int [][][] L = loadImageAs3DMatrix("i0_l.jpg"); //given
        //int [][][] R = loadImageAs3DMatrix("i0_r.jpg");
        
        //int [][][] L = loadImageAs3DMatrix("/home/andreas/i0_l.jpg"); //works        
        //int [][][] R = loadImageAs3DMatrix("/home/andreas/i0_r.jpg");

        //int [][][] L = loadImageAs3DMatrix("/home/andreas/rsz_umb_1.gif"); //works        
        //int [][][] R = loadImageAs3DMatrix("/home/andreas/rsz_umb_2.gif");
        
        int [][][] L = loadImageAs3DMatrix("/home/andreas/rsz_bluetrashcanim0.jpg");    //works    
        int [][][] R = loadImageAs3DMatrix("/home/andreas/rsz_bluetrashcanim1.jpg");

        displayImage(L);
        displayImage(R);

        int[][] A = (getImageDisparity(L,R, 15 ));   //20 is good for occlusionCost

        displayImage(grayToNormalizedColor(A));
        
        int Lheight = L.length;
        int Lwidth = L[0].length;        
        System.out.println("the height and width of Image L are: " + Lheight + " by " + Lwidth + " pixels");
        int Rheight = R.length;
        int Rwidth = R[0].length;        
        System.out.println("the height and width of Image R are: " + Rheight + " by " + Rwidth + " pixels");

        /*
        // integer counters to try to troubleshoot the path from the bottom right corner to the top left corner 
        System.out.println("The xCounter is: " + vX_Counter);
        System.out.println("The yCounter is: " + vY_Counter);
        System.out.println("The zCounter is: " + vZ_Counter);
        System.out.println("------------------------------------------");
        System.out.println("The lcX_Counter is: " + lcX_Counter);
        System.out.println("The lcY_Counter is: " + lcY_Counter);
        System.out.println("The lcZ_Counter is: " + lcZ_Counter);
        */

        
        long timeAfter = System.currentTimeMillis();
        long timeFor = timeAfter-timeBefore;
        System.out.println("The time it took is: " + timeFor + " milli seconds");
        System.out.println("Which is: " +((timeFor)/(1000)) + " seconds");
        //System.out.println("Which is: " +((timeFor)/(1000))/60 + "minutes");
        System.out.println("done."); //to let the user know the program has stopped running
    }
}