/*
 * @(#) ROIUtil.java 1.0 2000/3/10
 * Copyright (c) 2000 Larry Rodrigues
 */
package com.vistech.roi;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.color.*;
import com.sun.image.codec.jpeg.*;
import java.util.*;

/** A utility class that provides several static methods for computing ROI-related data.
   * @version 1.0  10 March 2000
   * @author Lawrence Rodrigues
   */
public class ROIUtil{
    /** Returns ROI statistics of all the pixel samples in a two dimensional array.
      * @param img the image from which statistics are computed.
      * @param points an array of positions of pixels, values of which are used
      * for computing statistics.
      * @return the two dimensional array containing statistics of all the samples.
      * The first subscript represents the position of the sample in the pixel array,
      * and the second subscript represents the type of statistics.

      * The size of the first dimension equals the number of bands. The size of the second dimension
      * equals the number of types of statistics computations, which is 5.
      * The order of types of computations in the second dimension are:
      * minimum, maximum, mean, area, and standard deviation.
      * Example:
      *    double[][] stats = ROIUtil.getStats(img,points);
      *
      * stats[2][1] represents the maximum value of the second sample. In an ARGB image,
      * this represents the maximum value of the green sample.
      *
      */
    public static double[][] getROIStats(BufferedImage img, Point2D[] points) {
       return computeStats(getROIPixelSamples(img, points));
    }

    /** Returns ROI statistics o all the pixel samples in a two dimensional array.
      * @param img the image from which statistics are computed.
      * @param shape the shape drawn over image. Pixels that lie inside this shape are  used
      * for computing statistics.
      * @return the two dimensional array containing statistics of all the samples.
      */
    public static double[][] getROIStats(BufferedImage img, Shape shape) {
       return computeStats(getROIPixelSamples(img, shape));
    }

    /** Returns ROI statistics for all the pixel samples in a two dimensional array.
      * @param sm the sample model of the image.
      * @param db the data buffer object that contains the image data.
      * @param points an array of positions of pixels, values of which are used
      * for computing statistics.
      * @return the two dimensional array containing statistics of all the samples.
      */
    public static double[][] getROIStats(SampleModel sm, DataBuffer db, Point2D[] points) {
       int data[][] = getROIPixelSamples(sm, db, points);
       return computeStats(data);
    }

    /** Returns ROI statistics for all the pixel samples in a two dimensional array.
      * @param sm the sample model of the image.
      * @param db the data buffer object that contains the image data.
      * @param shape the shape drawn over image. Pixels that lie inside this shape are  used
      * for computing statistics.
      * @return the two dimensional array containing statistics of all the samples.
      */
    public static double[][] getROIStats(SampleModel sm, DataBuffer db, Shape shape) {
       int data[][] = getROIPixelSamples(sm, db, shape);
       return computeStats(data);
    }

    /** Computes different types of statistics from an array of pixel values.
      * The types of statistics computer are: minimum, maximum, mean, area, and standard deviation.
      * @param pixel two dimesional array of pixel values. The first dimension represents the
      * index of the sample and the second dimension the sample valus. 
      * @return the two dimensional array containing statistics of all the samples.
      */

    public static double[][] computeStats(int[][] pixel) {
       if((pixel == null)||(pixel[0] == null)) return null;
       int len = pixel.length;
       int totalPixels =  pixel[0].length;
       int sum[] = new int[len];
       int max[] = new int[len];
       int min[] = new int[len];
       double[][] roiStats = new double[len][5];

       for(int j=0;j<len;j++) {
           sum[j] =0;
           max[j] = Integer.MIN_VALUE;
           min[j] = Integer.MAX_VALUE;
           for(int i=0;i<pixel[j].length;i++) {
               int pix = pixel[j][i];
               if(pix > max[j]) max[j] = pix;
               if(pix < min[j] ) min[j] = pix;
               sum[j] += pix;
           }
           
           roiStats[j][0] = (double)min[j];
           roiStats[j][1] = (double)max[j];
           roiStats[j][2] = sum[j]/(double)totalPixels;
           roiStats[j][4] = totalPixels;
           roiStats[j][3] = computeStdDev(pixel[j], roiStats[j][2]);
       }
       return roiStats;
    }

    /** Computes standard deviation.
      * @param data an array of values for which the standard deviation is computed.
      * @param mean mean of the data values.
      * @return the standard deviation. 
      */
    public static double computeStdDev(int[] data, double mean){
       if(data == null) return 0;
       int n = data.length;
       if (n == 1) return 1;
       double sumOfSquares = 0.0;
       for(int i=0; i<data.length;i++){
           sumOfSquares += data[i]*data[i];
       }
       double stdDev = Math.sqrt((sumOfSquares/(n-1)) - mean*mean*(n/(double)(n-1)));
       return stdDev;
    }

    /** Returns the values of the pixel samples of the entire image in a two dimensional array in which the
      * the first dimension represents the sample index, and the second dimension represents the
      * the sample value.
      * @param img the image.
      * @return a two dimensional array containing values of all the pixel samples.
      * The first subscript represents the position of the sample in the pixel array,
      * and the second subscript represents the sample value.
      **/
    public static int[][] getPixelSamples(BufferedImage img) {
       WritableRaster raster = img.getRaster();
       SampleModel sm = raster.getSampleModel();
       DataBuffer db = raster.getDataBuffer();
       Dimension size = new Dimension(img.getWidth(), img.getHeight());
       return getPixelSamples(sm, db, size);
    }

    /** Returns the values of the pixel samples of the entire image in a two dimensional array in which the
      * the first dimension represents the sample index, and the second dimension represents the
      * the sample value.
      * @param sm the sample model of the image.
      * @param db the data buffer object that contains the image data.
      * @param imageSize the image size.
      * @return a two dimensional array containing values of all the pixel samples.
      * The first subscript represents the position of the sample in the pixel array,
      * and the second subscript represents the sample value.
      **/
    public static int[][] getPixelSamples(SampleModel sm, DataBuffer db, Dimension imageSize){
       if((sm == null) ||(db == null) ||(imageSize ==null)) return null;
       int imageWidth = imageSize.width;
       int imageHeight = imageSize.height;
       int totalPix = imageHeight*imageWidth;
       int sample[][] = new int[totalPix][];
       for(int i=0;i<imageHeight;i++){
           for(int j=0; j<imageWidth;j++) {
               int pix[] = null;
               sample[i*imageWidth+j] =  sm.getPixel(j,i, pix, db);
           }
       }
       int pixel[][] = new int[sample[0].length][ totalPix];
       for(int i=0; i<pixel.length;i++){
           for(int j=0; j<totalPix;j++) {
               pixel[i][j] = sample[j][i];
           }
       }
       return pixel;
    }

    /** Returns the values of the pixel samples from specified positions in a two dimensional array in which the
      * the first dimension represents the sample index, and the second dimension represents the
      * the sample value.
      * @param img the image from which the pixels values are retrieved.
      * @param points an array of pixel positions.
      * @return a two dimensional array containing values of all the pixel samples.
      * The first subscript represents the position of the sample in the pixel array,
      * and the second subscript represents the sample value.
      **/
    public static int[][] getROIPixelSamples( BufferedImage img, Point2D[] points){
       if((img == null)||(points == null)) return null;
       SampleModel sm = img.getSampleModel();
       WritableRaster wr = img.getRaster();
       DataBuffer db = wr.getDataBuffer();
       return getROIPixelSamples(sm, db, points);
    }

    /** Returns the values of the pixel samples from specified positions in a two dimensional array in which the
      * the first dimension represents the sample index, and the second dimension represents the
      * the sample value.
      * @param sm the sample model of the image.
      * @param db the data buffer object that contains the image data.
      * @param points an array of pixel positions.
      * @return a two dimensional array containing values of all the pixel samples.
      * The first subscript represents the position of the sample in the pixel array,
      * and the second subscript represents the sample value.
      **/
    public static int[][] getROIPixelSamples(SampleModel sm, DataBuffer db, Point2D[] points){
       int totalPixels = points.length;
       int sample[][] = new int[totalPixels][];
       int k=0;
       for(int i=0;i<totalPixels;i++) {
           int x = (int)points[i].getX();
           int y = (int)points[i].getY();
           int pix[] = null;
           sample[k++] = (sm.getPixel(x,y, pix, db));
       }
       int len = sample[0].length;
       int roiPixels[][] = new int[len][k];
       for(int l=0;l<len;l++) {
           for(int j=0; j<k;j++) {
               roiPixels[l][j] = sample[j][l];
           }
       }
       return roiPixels;
    }

     /** Returns the values of the pixel samples within a specified shape in a two dimensional array in which the
      * the first dimension represents the sample index, and the second dimension represents the
      * the sample value.
      * @param img the image from which the pixels values are retrieved.
      * @param shape the shape drawn over image. Pixels that lie inside this shape are returned.
      * @return a two dimensional array containing values of all the pixel samples.
      * The first subscript represents the position of the sample in the pixel array,
      * and the second subscript represents the sample value.
      **/
    public static int[][] getROIPixelSamples(BufferedImage img, Shape shape){
       if(img == null) return null;
       SampleModel sm = img.getSampleModel();
       WritableRaster wr = img.getRaster();
       DataBuffer db = wr.getDataBuffer();
       ColorModel cm = img.getColorModel();
       return  getROIPixelSamples(sm, db, shape);
    }

    /** Returns the values of the pixel samples within a specified shape in a two dimensional array in which the
      * the first dimension represents the sample index, and the second dimension represents the
      * the sample value.
      * @param sm the sample model of the image.
      * @param db the data buffer object that contains the image data.
      * @param shape the shape drawn over image. Pixels that lie inside this shape are returned.
      * @return a two dimensional array containing values of all the pixel samples.
      * The first subscript represents the position of the sample in the pixel array,
      * and the second subscript represents the sample value.
      **/
    public static int[][] getROIPixelSamples(SampleModel sm, DataBuffer db, Shape shape) {
       Rectangle2D rect = shape.getBounds();
       int x = (int) rect.getX();
       int y = (int) rect.getY();

       int wid = (int)rect.getWidth();
       int ht = (int) rect.getHeight();

       int k=0;
       int totalPixels = wid*ht;
       int sample[][] = new int[totalPixels][];
       for(int i=0; i<wid;i++){
           for(int j=0;j<ht;j++){
               if(shape.contains((double)(x+j),(double)(y+i))){
                  int pix[] = null;
                  sample[k++] = (sm.getPixel(x+j,y+i, pix, db));
               }
           }
       }
       int len = sample[0].length;
       int roiPixels[][] = new int[len][k];
       for(int l=0;l<len;l++) {
           for(int j=0; j<k;j++) {
               roiPixels[l][j] = sample[j][l];
           }
       }
       return roiPixels;
   }

   /** Returns the positions of pixels within a specified shape that are above the specified threshold.
      * @param img the image.
      * @param shape the shape drawn over image. Thresholded pixels that lie inside this shape are returned.
      * @param threshold the threshold value.
      * @return an array of pixels positions that are above the specified threshold.
      **/
   public static Point[] getThresholdPoints(BufferedImage img, Shape shape, int threshold){
      Rectangle rect = shape.getBounds();
      int x = rect.x;
      int y = rect.y;
      int wid = rect.width;
      int ht = rect.height;
      Raster ra =  img.getRaster();
      //int k=0;
      Vector spoints = new Vector();
      for(int j=y; j< y+ht; j++ ){
         for(int i=x; i< x+wid; i++ ){
             if(shape.contains((double)(i),(double)(j))){
                int pix[]= null;
                int[] pixels = ra.getPixel(i,j, pix);
                if(pixels[0] > threshold) {
                   spoints.addElement(new Point(i,j));
                }
             }
         }
      }
      int size = spoints.size();
      Point srcPoint[] = new Point[size];
      spoints.copyInto(srcPoint);
      return srcPoint;
   }

   /** Returns all the positions of pixels within a specified shape. These positions don't include
      * pixel positions on the boundary of the shape.
      * @param img the image.
      * @param shape the shape drawn over image. Thresholded pixels that lie inside this shape are returned.
      * @param threshold the threshold value.
      * @return an array of pixels positions that are above the specified threshold.
      **/
   public static Point[] getROIPoints(BufferedImage img, Shape shape){
      if((img == null) || (shape == null)) return null;
      Rectangle rect = shape.getBounds();
      int x = rect.x;
      int y = rect.y;
      int wid = rect.width;
      int ht = rect.height;
      //Raster ra =  img.getRaster();
      //int k=0;
      Vector spoints = new Vector();
      for(int j=y; j< y+ht; j++ ){
         for(int i=x; i< x+wid; i++ ){
             if(shape.contains((double)(i),(double)(j))){
                //int pix[]= null;
                //int[] pixels = ra.getPixel(i,j, pix);
                spoints.addElement(new Point(i,j));
                   //  System.out.print(i+" "+j + ", ");
             }
         }
      }

      int size = spoints.size();
      Point srcPoint[] = new Point[size];
      spoints.copyInto(srcPoint);
      return srcPoint;
   }

   /** Returns all the positions of pixels that lie on the boundary of a specified shape.
      * @param img the image.
      * @param shape the shape drawn over image. Thresholded pixels that lie inside this shape are returned.
      * @param threshold the threshold value.
      * @return an array of pixels positions that are above the specified threshold.
      **/
   public static Point[] getBoundaryPoints(Shape shape, int viewerWid, int viewerHt){
         int totpix = viewerWid*viewerHt;
         short[] data = new short[totpix];
         for(int i=0;i<viewerWid*viewerHt;i++){
             data[i] = 0;
         }
         BufferedImage gr = com.vistech.util.GrayscaleImageFactory.createImage(viewerWid, viewerHt, 8, data);
         Graphics2D g2d = gr.createGraphics();
         g2d.setColor(Color.white);
         g2d.draw(shape);
         Rectangle rect = shape.getBounds();
         int x = rect.x;
         int y = rect.y;
         int wid = rect.width;
         int ht = rect.height;
         Raster ra =  gr.getRaster();
         int k=0;
         Vector spoints = new Vector();

         for(int j=y; j< y+ht; j++ ){
             for (int i=x; i< x+wid; i++ ){
                  int pix[]= null;
                  int[] pixels = ra.getPixel(i,j, pix);
                  if(pixels[0] == 255) {
                     spoints.addElement(new Point(i,j));
                  }
             }
         }
         System.out.println();
         int size = spoints.size();
         Point srcPoint[] = new Point[size];
         spoints.copyInto(srcPoint);
         return srcPoint;
      }
}
