import focus.*;
import ij.*;
import ij.plugin.filter.PlugInFilter;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import java.util.*;

/** This plugin divides a stack of RGB images from an NTSC Digital Video camera
	into separate 32 bit red, green and blue stacks.  It attemps to reverse the "gamma" correction
	to produce a linear intensity scale in the range of 0 to 100.  It also corrects the 0.9 aspect
	ratio of the pixels in NTSC Digial Video images, changing the 720x480 rectangular pixel format to
 	720x533 square pixels.  (This plugin is not right for PAL DV images.)  The gamma un-correction is
 	very approximate, and was derived from data from a Canon XL1 video camera.
	Bob Dougherty 4/19/2002
 */
/*	License:
	Copyright (c) 2002, 2005, OptiNav, Inc.
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions
	are met:

		Redistributions of source code must retain the above copyright
	notice, this list of conditions and the following disclaimer.
		Redistributions in binary form must reproduce the above copyright
	notice, this list of conditions and the following disclaimer in the
	documentation and/or other materials provided with the distribution.
		Neither the name of OptiNav, Inc. nor the names of its contributors
	may be used to endorse or promote products derived from this software
	without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public class NTSC_DV2Float implements PlugInFilter {

	ImagePlus imp;
	boolean canceled = false;
	static final double ASPECT = 0.9; //Pixel aspect ratio of a 720x480 pixel NTSC DV television image
	static final double CALIBRATE = 195d; //Empirical constant for level correction

	public int setup(String arg, ImagePlus imp) {
		if (IJ.versionLessThan("1.17j"))
			return DONE;
		this.imp = imp;
		return DOES_RGB;
	}

	public void run(ImageProcessor ip) {

		int numSlices = imp.getStackSize();
		ImageStack stack = imp.getStack();
		int w = stack.getWidth();
		int h = stack.getHeight();

		//New image height after rectangular to square correction.
		int hNew = (int)(h/ASPECT);


		//Create new stack for the output.
        ImageStack imsR = new ImageStack(w,hNew);
        ImageStack imsG = new ImageStack(w,hNew);
        ImageStack imsB = new ImageStack(w,hNew);

		IJ.showStatus("NTSC_DV2Float: converting "+numSlices+" images");



        //Go through the slices of the input stack.
		for (int iSlice = 0; iSlice < numSlices; iSlice++){
 			ImageProcessor ipSlice = stack.getProcessor(iSlice+1);

      		ImageProcessor ipR = new FloatProcessor(w,hNew);
      		ImageProcessor ipG = new FloatProcessor(w,hNew);
      		ImageProcessor ipB = new FloatProcessor(w,hNew);

			//Extract the pixels, with level correction.
      		float[][] pixels = rgbDivide(ipSlice, CALIBRATE);

      		for (int color = 0; color < 3; color++){

      			//Scale the pixels for this color to correct the aspect ratio.
      			//This scaling approach is specialized to vertical scaling.
      			//(The PAL version of this code will do horizontal scaling, and should have
      			//the i and j loops interchanged.)
       			float[] scaledPixelsThisColor = new float[w*hNew];
       			for (int j = 0; j < hNew; j++){
       				double jScaled = j*ASPECT;

       				//Find jL and jR, rows in the source image for linear interpolation
       				int jL = (int)jScaled;
       				if (jL == (h-1))jL--;
       				int jR = jL + 1;
       				double weightL = jR - jScaled;
       				double weightR = jScaled - jL;

       				for (int i = 0; i < w; i++){
           				double pL = pixels[color][i + w*jL];
         				double pR = pixels[color][i + w*jR];
          				scaledPixelsThisColor[i + j*w] = (float)(weightL*pL + weightR*pR);
          			}
					IJ.showProgress((double)(j + color*hNew)/(3*hNew));
         		}

     			//Put the scaled image into the appropriate stack
				switch (color) {
					case 0:
    					ipR.setPixels(scaledPixelsThisColor);
						ipR.setMinAndMax(0,0);
						imsR.addSlice("R",ipR);
						break;
					case 1:
      					ipG.setPixels(scaledPixelsThisColor);
						ipG.setMinAndMax(0,0);
						imsG.addSlice("G",ipG);
						break;
					case 2:
      					ipB.setPixels(scaledPixelsThisColor);
						ipB.setMinAndMax(0,0);
						imsB.addSlice("B",ipB);
						break;
					default:
				}
			}
		}
		String impTitle = imp.getShortTitle();
		ImagePlus impB = new ImagePlus("B scaled from "+impTitle,imsB);
		ImagePlus impG = new ImagePlus("G scaled from "+impTitle,imsG);
		ImagePlus impR = new ImagePlus("R scaled from "+impTitle,imsR);

      	impB.setStack(null,imsB);
        impG.setStack(null,imsG);
       	impR.setStack(null,imsR);

		// Display the new stacks.
		impB.show();
		impG.show();
		impR.show();
	}

	/** Given an RGB image, return float[][] pixels[color][index], where color goes from 0 to 2 for RGB and
		index goes from 0 to width*height.  This verision scales the data to the range 0 to 1, where the
		approximate response of a video cameara is removed to try to produce linear intensity.
		Bob Dougherty 4/19/2002

		Disclamer: This level correction was created by
		(1) Reading just enough about video camera range/highlight compression ("gamma") to be dangereous
		(2) Calibrating a Canon XL1 camera by grabbing a series of frames at the camera's 27 exposure times
			with fixed lens setting and incident light
		(3) Plotting the result (RGB vs. exposure) and noticing that it approximately fits an arc tangent shape
	*/
	public float[][] rgbDivide(ImageProcessor ip, double cal){
		float[] table = new float[256];
		float scale = (float)Math.tan(255d/cal);
		for (int R = 0; R < 256; R++){
			table[R] = (float)(100*Math.tan(R/cal)/scale);
		}

		int size = ip.getWidth()*ip.getHeight();
		float[][] result = new float[3][size];
 		int[] pixels = (int[])ip.getPixels();
 		for (int i = 0; i < size; i++){
 			int c = pixels[i];
			int R = (c&0xff0000)>>16;
			int G = (c&0x00ff00)>>8;
			int B = (c&0x0000ff);
			result[0][i] = table[R];
			result[1][i] = table[G];
			result[2][i] = table[B];
		}
		return result;
	}
}


