
import java.awt.*;
import java.awt.image.*;
import java.awt.Color;
import java.net.*;
import java.io.*;
import java.util.*;

/**
 *Contrast Stretch stretches the contrast range of an image
 *
 *@author Tim Sharman
 *@version July 1999 
 *@see code.iface.contrast
 */

public class ContrastStretch extends Thread{
  
  //width and height of the image
  private int i_w=0;
  private int i_h=0;
 
  //pixel arrays for input image and destination image
  private int[] dest_1d;
  private int [] src_1d;
  
  /**
   *Creates an image array for an image which has been contrast stretched
   *@param src Image array to be stretched
   *@param width the width of the image
   *@param height the height of the image
   *@param uplim The upper limit for the values to be stretched between
   *@param lolim The lower limit for the values to be stretched between
   *@return An image array representing the stretched image
   */

  public int[]  normal_stretch(int [] src, int width, int height, 
			       int uplim, int lolim){
    
    i_w = width;
    i_h = height;
    src_1d = new int[i_w * i_h];
    src_1d = src;
    dest_1d = new int[i_w*i_h];
    int min = src_1d[0] & 0x000000ff;
    int max = src_1d[0] & 0x000000ff;
    int src_rgb;
    int result;
    float quotient = 0;
 
    //Find the max and min values   
    for(int i = 0; i < src_1d.length; i++){
      src_rgb = src_1d[i] & 0x000000ff;
      if(src_rgb < min){
	min = src_rgb;
      }
      else if(src_rgb > max){
	max = src_rgb;
      }
      
    }
    //Work out (b-a)/(d-c)
    quotient = (((float)(uplim - lolim))/((float)(max - min)));
    //Calculate the output values
    for(int i = 0; i < src_1d.length; i++){
      src_rgb = src_1d[i] & 0x000000ff;
      result = (int)( (src_rgb - min) * quotient )+lolim;
      dest_1d[i] =  0xff000000 | (result + (result << 16) + (result << 8));
    }
    return dest_1d;
  }

  
  /**
   *Creates an image array for an image which has been contrast stretched
   *In this case it is done using the cutoff method
   *@param src Image array to be stretched
   *@param width The width of the image
   *@param height The height of the image
   *@param cutoff The scale value used in cutting off low values
   *@param uplim The upper limit for the values to be stretched between
   *@param lolim The lower limit for the values to be stretched between
   *@return An image array representing the stretched image
   */
  
  public int[]  cutoff_stretch(int [] src, int width, int height, float cutoff, 
			       int uplim, int lolim){
    
    i_w = width;
    i_h = height;
    src_1d = new int[i_w * i_h];
    src_1d = src;
    int [] inter = new int[i_w*i_h];
    dest_1d = new int[i_w*i_h];
    int max = 0;
    int min;
    int src_rgb;
    int result;
    int scale;
    float quotient = 0;
    int [] hist = new int[256];

    //Initialise the distribution array
    for(int i = 0; i < 256; i++){
      hist[i] = 0;
    }
    
    //Find the distribution of intensity values
    for(int i = 0; i < src_1d.length; i++) {
      src_rgb = src_1d[i] & 0x000000ff;
      hist[src_rgb]++;
    }

    //Find max value of the intensity values
    for(int i = 0; i < hist.length; i++) {
      if(hist[i] > max){
	max = hist[i];
      }
    }
    //Calculate the minimum amount of intensity required
    //in order to be stretched
    scale = (int) ((float) max * cutoff);
    
    //Find the first and last intensity values which are above the cutoff
    int first = 0;
    int last = 0;
    //Flag indicating if either value has been found yet
    boolean flag = false;
    for(int i = 0; i < hist.length; i++){
      if(!flag){
	if(hist[i] >= scale){
	  first = i;
	  flag = true;
	}
      }
    }
    flag = false;
    for(int i = 255; i >= 0; i--){
      if(!flag){
	if(hist[i] >= scale){
	  last = i;
	  flag = true;
	}
      }
    }
    //Find the max and min values   
    max = last;
    min = first;
    
    //Work out (b-a)/(d-c)
    quotient = (((float)(uplim - lolim))/((float)(max - min)));
    
    //Calculate the output values
    for(int i = 0; i < src_1d.length; i++){
      result = (int)( ((src_1d[i]&0x000000ff) - min) * quotient )+lolim;
      if(result < 0){result = 0;}
      if(result > 255){result = 255;}
      dest_1d[i] =  0xff000000 | (result + (result << 16) + (result << 8));
    }
    return dest_1d;
  }
  

  /**
   *Creates an image array for an image which has been contrast stretched
   *In this case it is done using the percentile method
   *@param src Image array to be stretched
   *@param width The width of the image
   *@param height The height of the image
   *@param high The upper percentage value for the histogram cutoff
   *@param low The lower percentage value for the histogram cutoff
   *@param uplim The upper limit for the values to be stretched between
   *@param lolim The lower limit for the values to be stretched between
   *@return An image array representing the stretched image
   */
  
  public int[]  percentile_stretch(int [] src, int width, int height, float high, 
				   float low, int uplim, int lolim){
    
    i_w = width;
    i_h = height;
    src_1d = new int[i_w * i_h];
    src_1d = src;
    int [] hist = new int[256];
    dest_1d = new int[i_w*i_h];
    int max;
    int min;
    int src_rgb;
    float lowpercent = low;
    float highpercent = high;
    int result;
    float quotient = 0;
    int tmp, tmp2;
    boolean flag = false;

    //Initialise the distribution array
    for(int i = 0; i < 256; i++){
      hist[i] = 0;
    }
    
    //Find the distribution of intensity values
    for(int i = 0; i < src_1d.length; i++) {
      src_rgb = src_1d[i] & 0x000000ff;
      hist[src_rgb]++;
    }
    
    //Edit the input array so that anything outside the range 
    //is set to zero
    tmp = 0;
    while(!flag){
      //Check if setting the next section to zero will take you past limit
      if((low - (((float)hist[tmp])/((float)(i_w*i_h)))) >= 0){
	//If not set to zero and reduce the limit
	low = low - (((float)hist[tmp])/((float)(i_w*i_h)));
	hist[tmp] = 0;
	tmp++;
      }
      else{
	/**Otherwise reduce by the required fraction to bring to zero
	 *Note: Casting to int makes this inexact, so it is unlikely
	 *that zero will be exactly reached. However the process is
	 *close enough to make little difference
	 */
	hist[tmp] = hist[tmp] - (int)(low * (float)(i_w*i_h));
	flag = true;
      }
    }

    tmp = 255;
    flag = false;
    while(!flag){
      //Check if setting the next section to zero will take you past limit
      if((high + (((float)hist[tmp])/((float)(i_w*i_h)))) <= 1){
	high = high + (((float)hist[tmp])/((float)(i_w*i_h)));
	hist[tmp] = 0;
	tmp--;
      }
      else{
	//Otherwise increase by the required fraction to bring to one
	hist[tmp] = hist[tmp] + (int)(high * (float)(i_w*i_h));
	flag = true;
      }
    }

    //Process image now that low values have been eliminated
    
    //Find the max and min values that are within the range
    
    //Find the first value pixel within the range
    flag = false;
    tmp = 0;
    while(!flag){
      src_rgb = src_1d[tmp] & 0x000000ff;
      if(hist[src_rgb] != 0){
	flag = true;
      }
      else {
	tmp++;
      }
    }
    
    //Set the max and min according to this value
    max = src_1d[tmp] & 0x000000ff;
    min = src_1d[tmp] & 0x000000ff;
    
    //Now check the rest of the image
    for(int i = 0; i < src_1d.length; i++){
      src_rgb = src_1d[i] & 0x000000ff;
      if((src_rgb < min) && (hist[src_rgb] != 0)){
	min = src_rgb;
      }
      else if((src_rgb > max) && (hist[src_rgb] != 0)){
	max = src_rgb;
      }
      
    }

    //Work out (b-a)/(d-c)
    quotient = (((float)(uplim - lolim))/((float)(max - min)));
    
    //Calculate the output values
    for(int i = 0; i < src_1d.length; i++){
      src_rgb = src_1d[i] & 0x000000ff;
      result = (int)( (src_rgb - min) * quotient )+lolim;
      if(result < 0){result = 0;}
      if(result > 255){result = 255;}
      dest_1d[i] =  0xff000000 | (result + (result << 16) + (result << 8));
    }
    return dest_1d;
  }
}





