
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.net.*;
import java.util.*;
import java.io.*;
import java.awt.Color.*;
import java.lang.Math.*;

/**
 *ZeroCrossing is an algorithm to find the zero crossings in an image
 *@author:Timothy Sharman
 */

public class ZeroCrossing extends Thread{

  //The Convolution operator used as part of the process.

  Convolution convolution;

  //The Gaussian Smoothing operator used as part of the process
  
  GaussianSmooth gaussiansmooth;

  //the width and height of the output image

  private int d_w;
  private int d_h;
  private int [] dest_1d;

  //The possibls kernels to be used in the Laplacian function

  private double [][] laplacian1 = {{0,1,0},{1,-4,1},{0,1,0}};
  private double [][] laplacian2 = {{1,1,1},{1,-8,1},{1,1,1}};
  private double [][] laplacian3 = {{-1,2,-1},{2,-4,2},{-1,2,-1}};


  /**
   *Applies the zero crossing detector to the input image
   *@param src_1d The source image as a pixel array
   *@param width width of the destination image in pixels
   *@param height height of the destination image in pixels
   *@param kersize The size of kernel to be applied
   *@param theta The standard deviation of the gaussian smoothing
   *@param limiter specifies whether to limit the zero crossing
   *@param limit The limit applied to the zero crossings
   *@return The output array with the zero crossing points
   */

  //Tim's Zero Crossing Detection Algorithm
  /*
    a) assume the image is grey level (hence RR=GG=BB)
    b) use value &0x000000ff to get the BB value
    c) Apply gaussian smoothng to the image
    d) Convolve the laplacian kernel with the image
    e) Find the points where the answer switches from +ve to -ve
    f) If required limit these points using the supplied thereshold
    g) Return the zero crossing image
    */

  public int [] apply_zeroCrossing(int [] src_1d, int width, int height, 
				   int kersize, float theta, boolean limiter,
				   float limit){
    
    d_w = width;
    d_h = height;
    dest_1d = new int[d_w*d_h];
    int [] tmp_1d;
    double [] lapResult;
    double [][] tmp_2d;
    double thresh;

    //Set the threshold value
    if(limiter){
      thresh = limit;
    }
    else {
      thresh = 0;
    }

    //Initialise the output array
    for(int i = 0; i < dest_1d.length; i++){
      dest_1d[i] = Color.black.getRGB();
    }

    tmp_1d = new int[d_w*d_h];

    //Smooth the initial image
    tmp_1d = gaussiansmooth. smooth_image(src_1d, width, height, 
					  kersize, theta);

    // Create an array to be used for the laplacian convolution result
    lapResult = new double [d_w*d_h];

    // Create a temp 2d int array
    double [][] tmp_2ddouble = new double[d_w][d_h];

    //Convert tmp_1d from 1_d to 2_d 
    for(int i = 0; i < d_w; i++){
      for(int j = 0; j < d_h; j++){
	tmp_2ddouble[i][j] = (double)(tmp_1d[i+(j*d_w)] & 0x000000ff);
      }
    }

    //Apply the laplacian operator
    lapResult = convolution. convolutionDoublePadded(tmp_2ddouble, d_w, d_h, 
						     laplacian1, 3, 3);

    //Now need to find points in the array which are zero crossings  

    //First convert from 1_d to 2_d for ease of processing
    tmp_2d = new double [d_w][d_h];

    for(int i = 0; i < d_w; i++){
      for(int j = 0; j < d_h; j++){
	tmp_2d[i][j] = lapResult[i+(j*d_w)];
      }
    }
    
    /**
     *To find the zero crossings in the image you must check each point 
     *in the array to see if it lies on a zero crossing. This is done by
     *checking the neighbours around the pixel.
     */

    int [][] tmp2_2d = new int[d_w][d_h];

    for(int i = 1; i < (d_w-1); i++){
      for(int j = 1; j < (d_h-1); j++){
	tmp2_2d = check_neighbours(tmp_2d, i, j, tmp2_2d, thresh);
      }
    }
  
    //Rescale again.
    d_w = d_w - 3;
    d_h = d_h - 3;

    //Convert the output from 2_d to 1_d 
    for(int i = 0; i < d_w; i++){
      for(int j = 0; j < d_h; j++){
	dest_1d[i+(j*d_w)] = tmp2_2d[i][j];
      }
    }

    d_w = d_w + 3;
    d_h = d_h + 3; 

    return dest_1d;

  }

  /**
   *check_neighbours is used to check the neighbourhood of a 
   *pixel to see if there is a zero crossing at the pixel
   *currently being considered. This is done by seeing if there
   *is a change in sign between the two opposite pixels on either
   *side of the middle pixel. This is done in each of the four
   *relevant directions. If there is a change the point is set to
   *white and if there is no change it is set to black
   */ 
  
  private int [][] check_neighbours(double [][] tmp_2d, int i, int j, 
				    int [][] tmp2_2d, double thresh){

    /*If when neighbouring points are multiplied the result is -ve
     *then there must be a change in sign between these two points.
     *If the change is also above the thereshold then set it as a 
     *zero crossing.
     */

    if(tmp_2d[i-1][j]*tmp_2d[i+1][j] < 0){
      if(Math.abs(tmp_2d[i-1][j]) + Math.abs(tmp_2d[i+1][j]) > thresh){
	tmp2_2d[i-1][j-1] = Color.white.getRGB();
      }
      else {
	tmp2_2d[i-1][j-1] = Color.black.getRGB();
      }
    }
    
    else if(tmp_2d[i-1][j-1]*tmp_2d[i+1][j+1] < 0){
      if(Math.abs(tmp_2d[i-1][j-1])+Math.abs(tmp_2d[i+1][j+1]) > thresh){
	tmp2_2d[i-1][j-1] = Color.white.getRGB();
      }
      else {
	tmp2_2d[i-1][j-1] = Color.black.getRGB();
      }
    }

    else if(tmp_2d[i+1][j+1]*tmp_2d[i-1][j-1] < 0){
      if(Math.abs(tmp_2d[i+1][j+1])+Math.abs(tmp_2d[i-1][j-1]) > thresh){
	tmp2_2d[i-1][j-1] = Color.white.getRGB();
      }
      else {
	tmp2_2d[i-1][j-1] = Color.black.getRGB();
      }
    }

    else if(tmp_2d[i][j-1]*tmp_2d[i][j+1] < 0){
      if(Math.abs(tmp_2d[i][j-1])+Math.abs(tmp_2d[i][j+1]) > thresh){
	tmp2_2d[i-1][j-1] = Color.white.getRGB();
      }
      else {
	tmp2_2d[i-1][j-1] = Color.black.getRGB();
      }
    }
    
    else {
      tmp2_2d[i-1][j-1] = Color.black.getRGB();
    }
    return tmp2_2d;
  }
  
}

