import ij.*;
import ij.process.*;
import ij.measure.*;
import ij.plugin.*;
import ij.plugin.frame.*;
import ij.gui.Roi;

/* Albert Cardona 2008
 *
 * Applies an 'auto' Brightness and Contrast to each slice in the stack,
 * based on each slice parameters.
 */
public class Slice_AutoBC implements PlugIn, Measurements {

	public void run(String arg) {
		process(IJ.getImage(), 1);
	}

	/** Not accepted: 16 or 32 bit images. */
	static public void process(ImagePlus imp, int iterations) {
		if (null == imp) return;
		switch (imp.getType()) {
			case ImagePlus.GRAY16:
			case ImagePlus.GRAY32:
				IJ.log("16- and 32-bit not supported.");
				return;
		}
		Calibration cal = imp.getCalibration();
		imp.setCalibration(null);
		for (int i=imp.getStackSize(); i>0; i--) {
			imp.setSlice(i);
			autoAdjust(imp, iterations);
		}
		imp.setCalibration(cal);
		imp.unlock();
		imp.changes = true;
	}

	/** Stripped from ij.plugin.frame.ContrastAdjuster class by Wayne Rasband.
	 *  Adjust only the current slice of the ImagePlus imp.
	 */
	static public void autoAdjust(ImagePlus imp, int iterations) {
		ImageProcessor ip = imp.getProcessor();
		boolean RGBImage = imp.getType() == ImagePlus.COLOR_RGB;
 		if (RGBImage)
			ip.reset();

		ImageStatistics stats = ImageStatistics.getStatistics(ip, AREA+MEAN+MODE+MIN_MAX, null); // get uncalibrated stats
		int limit = stats.pixelCount/10;
		int[] histogram = stats.histogram;
		int autoThreshold = 0;

		double min = ip.getMin();
		double max = ip.getMax();

		int i = -1;
		boolean found = false;
		int count;
		int hmin = 0;

		for (int it=0; it<iterations; it++) {
			if (autoThreshold<10)
				autoThreshold = 5000; //AUTO_THRESHOLD;
			else
				autoThreshold /= 2;
			int threshold = stats.pixelCount/autoThreshold;
			do {
				i++;
				count = histogram[i];
				if (count>limit) count = 0;
				found = count > threshold;
			} while (!found && i<255);
			hmin = i;
			i = 256;
			do {
				i--;
				count = histogram[i];
				if (count>limit) count = 0;
				found = count > threshold;
			} while (!found && i>0);
		}
		int hmax = i;
		Roi roi = imp.getRoi();
		if (hmax>=hmin) {
			if (RGBImage) imp.killRoi();
			min = stats.histMin+hmin*stats.binSize;
			max = stats.histMin+hmax*stats.binSize;
			if (min==max)
				{min=stats.min; max=stats.max;}
			ip.setMinAndMax(min, max);
			if (RGBImage && roi!=null) imp.setRoi(roi);
		} else {
			//reset(imp, ip);
			if (RGBImage) ip.reset();
			if ((ip instanceof ShortProcessor) || (ip instanceof FloatProcessor)) {
				imp.resetDisplayRange();
			}
			ip.setMinAndMax(min, max);
			return;
		}
		if (roi!=null) {
			ImageProcessor mask = roi.getMask();
			if (mask!=null)
				ip.reset(mask);
		}
		apply(imp, ip);
	}

	static private void apply(ImagePlus imp, ImageProcessor ip) {
		String option = null;
		boolean RGBImage = imp.getType() == ImagePlus.COLOR_RGB;
		if (RGBImage) {
			ip.snapshot();
			ip.reset();
			return;
		}
		if (imp.getType()!=ImagePlus.GRAY8) {
			return;
		}
		int[] table = new int[256];
		int min = (int)ip.getMin();
		int max = (int)ip.getMax();
		for (int i=0; i<256; i++) {
			if (i<=min)
				table[i] = 0;
			else if (i>=max)
				table[i] = 255;
			else
				table[i] = (int)(((double)(i-min)/(max-min))*255);
		}
		ip.setRoi(imp.getRoi());
		if (ip.getMask()!=null) ip.snapshot();
		ip.applyTable(table);
		ip.reset(ip.getMask());

		if (RGBImage) ip.reset();
		else ip.setMinAndMax(0, 255);
	}
}

