package thresholding;

import GUI.internalFrame;
import ij.ImagePlus;
import ij.gui.NewImage;
import ij.plugin.filter.PlugInFilter;
import ij.process.ByteProcessor;
import ij.process.ImageProcessor;

/**
* Automatic thresholding technique based on the entopy of the histogram.
* See: P.K. Sahoo, S. Soltani, K.C. Wong and, Y.C. Chen "A Survey of
* Thresholding Techniques", Computer Vision, Graphics, and Image
* Processing, Vol. 41, pp.233-260, 1988.
*
* @author Jarek Sacha
*/
public class Entropy_Threshold implements PlugInFilter {
	
 public static internalFrame[] intFrameEntropy;	
 private int number = 0;
 private String title;	
 int numberResult;
 boolean draw;
 ImagePlus imp;
 ImageProcessor nip;
 /*
  *
  */
 
 public Entropy_Threshold(String title, int numberResult, boolean draw) {
		this.title = title;
		this.numberResult = numberResult;
		this.draw = draw;
	} 
 
 public int setup(String s, ImagePlus imagePlus) {
   return PlugInFilter.DOES_8G | PlugInFilter.DOES_STACKS;
 }

 /*
  *
  */
 public void run(ImageProcessor imageProcessor) {
	// byte[] pixels = (byte[]) ((ByteProcessor) imageProcessor).getPixels();
	 
   int[] hist = imageProcessor.getHistogram();
   int threshold = entropySplit(hist);
   System.out.println("threshold "+threshold);
  // imageProcessor.threshold(threshold);
   
   int width =  imageProcessor.getWidth();
   int height = imageProcessor.getHeight();   
   imp = NewImage.createByteImage ("Threshold", width, height, 1, NewImage.FILL_WHITE);
   
	nip = imp.getProcessor();
	
	byte[] pixels = (byte[]) ((ByteProcessor) imageProcessor).getPixels();

	int offset = 0;

	for (int y=0; y<height; y++) {
	    offset = y*width;
	    for (int x=0; x<width; x++) {
		if ((pixels[offset + x] & 0xff) <= threshold)
		    nip.putPixel(x, y, 0);
	    }
    }   
   
	if (draw) {
	
		intFrameEntropy = new internalFrame[20];
		
		intFrameEntropy[number] = new internalFrame("Entropy Thresholding" , imp, number, numberResult, 1, true);
		try {
			intFrameEntropy[number].setSelected(true);
		} catch(Exception e){
			System.out.println("Chyba");
		}
		number ++;   
	}
 }
 
	public ImagePlus getImg(){
		return imp;
	}
	
	public ImageProcessor getIp(){
		return nip;
	}

 /**
  * Calculate maximum entropy split of a histogram.
  *
  * @param hist histogram to be thresholded.
  *
  * @return index of the maximum entropy split.`
  */
 private int entropySplit(int[] hist) {

   // Normalize histogram, that is makes the sum of all bins equal to 1.
   double sum = 0;
   for (int i = 0; i < hist.length; ++i) {
     sum += hist[i];
   }
   if (sum == 0) {
     // This should not normally happen, but...
     throw new IllegalArgumentException("Empty histogram: sum of all bins is zero.");
   }

   double[] normalizedHist = new double[hist.length];
   for (int i = 0; i < hist.length; i++) {
     normalizedHist[i] = hist[i] / sum;
   }

   //
   double[] pT = new double[hist.length];
   pT[0] = normalizedHist[0];
   for (int i = 1; i < hist.length; i++) {
     pT[i] = pT[i - 1] + normalizedHist[i];
   }

   // Entropy for black and white parts of the histogram
   final double epsilon = Double.MIN_VALUE;
   double[] hB = new double[hist.length];
   double[] hW = new double[hist.length];
   for (int t = 0; t < hist.length; t++) {
     // Black entropy
     if (pT[t] > epsilon) {
       double hhB = 0;
       for (int i = 0; i <= t; i++) {
         if (normalizedHist[i] > epsilon) {
           hhB -= normalizedHist[i] / pT[t] * Math.log(normalizedHist[i] / pT[t]);
         }
       }
       hB[t] = hhB;
     } else {
       hB[t] = 0;
     }

     // White  entropy
     double pTW = 1 - pT[t];
     if (pTW > epsilon) {
       double hhW = 0;
       for (int i = t + 1; i < hist.length; ++i) {
         if (normalizedHist[i] > epsilon) {
           hhW -= normalizedHist[i] / pTW * Math.log(normalizedHist[i] / pTW);
         }
       }
       hW[t] = hhW;
     } else {
       hW[t] = 0;
     }
   }

   // Find histogram index with maximum entropy
   double jMax = hB[0] + hW[0];
   int tMax = 0;
   for (int t = 1; t < hist.length; ++t) {
     double j = hB[t] + hW[t];
     if (j > jMax) {
       jMax = j;
       tMax = t;
     }
   }

   return tMax;
 }
 
 
	protected void finalize() throws Throwable {
	    try {
	        //close();        // close open files
	    } finally {
	        super.finalize();
	        System.out.println("entropy has been finalized.");
	    }
	} 
}
