content: /*
   * Copyright (C) 2010-2014 Andreas Maier
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */
  package edu.stanford.rsl.conrad.io;
  
  import ij.process.FloatBlitter;
  import ij.process.FloatProcessor;
  import ij.process.ImageProcessor;
  import java.awt.Color;
  import java.awt.Image;
  import java.awt.Toolkit;
  import java.awt.image.BufferedImage;
  import java.awt.image.ColorModel;
  import java.awt.image.MemoryImageSource;
  import java.io.Serializable;
  import java.util.Random;
  
  
  public class SerializableFloatProcessor extends ImageProcessor implements
  Serializable {
  
  	/**
  	 * 
  	 */
  	private int width = 0;
  	private int height = 0;
  	private ColorModel cm = null;
  	private static final long serialVersionUID = -5805594811209369617L;
  	private float min, max, snapshotMin, snapshotMax;
  	private float[] pixels;
  	protected byte[] pixels8;
  	private float[] snapshotPixels = null;
  	private float fillColor =  Float.MAX_VALUE;
  	private boolean fixedScale = false;
  
  	static final int INVERT=0, FILL=1, ADD=2, MULT=3, AND=4, OR=5,
  	XOR=6, GAMMA=7, LOG=8, MINIMUM=9, MAXIMUM=10, SQR=11, SQRT=12, EXP=13, ABS=14;
  	static final int BLUR_MORE=0, FIND_EDGES=1, MEDIAN_FILTER=2, MIN=3, MAX=4, CONVOLVE=5;
  	static final String WRONG_LENGTH = "width*height!=pixels.length";
  
  	/** Creates a new FloatProcessor using the specified pixel array and ColorModel.
  		Set 'cm' to null to use the default grayscale LUT. */
  	public SerializableFloatProcessor(int width, int height, float[] pixels, ColorModel cm) {
  		if (pixels!=null && width*height!=pixels.length)
  			throw new IllegalArgumentException(WRONG_LENGTH);
  		this.width = width;
  		this.height = height;
  		super.width = width;
  		super.height = height;
  		this.pixels = pixels;
  		this.cm = cm;
  		super.cm = cm;
  		resetRoi();
  		if (pixels!=null)
  			findMinAndMax();
  	}
  
  	/** Creates a new FloatProcessor using the specified FloatProcessor.
  	Set 'cm' to null to use the default grayscale LUT. */
  	public SerializableFloatProcessor(FloatProcessor floatProcessor) {
  		floatProcessor = (FloatProcessor) floatProcessor.duplicate();
  		this.width = floatProcessor.getWidth();
  		this.height = floatProcessor.getHeight();
  		this.pixels = (float []) floatProcessor.getPixels();
  		this.cm = floatProcessor.getColorModel();
  		resetRoi();
  		if (pixels!=null)
  			findMinAndMax();
  	}
  
  	/** Creates a new FloatProcessor using the specified ImageProcessor.
  	Set 'cm' to null to use the default grayscale LUT. */
  	public SerializableFloatProcessor(ImageProcessor imageProcessor) {
  		imageProcessor = imageProcessor.duplicate();
  		FloatProcessor floatProcessor = imageProcessor.toFloat(0, null);
  		this.width = floatProcessor.getWidth();
  		this.height = floatProcessor.getHeight();
  		this.pixels = (float []) floatProcessor.getPixels();
  		this.cm = floatProcessor.getColorModel();
  		resetRoi();
  		if (pixels!=null)
  			findMinAndMax();
  	}
  
  	/** Creates a blank FloatProcessor using the default grayscale LUT that
  		displays zero as black. Call invertLut() to display zero as white. */
  	public SerializableFloatProcessor(int width, int height) {
  		this(width, height, new float[width*height], null);
  	}
  
  	/** Creates a FloatProcessor from an int array using the default grayscale LUT. */
  	public SerializableFloatProcessor(int width, int height, int[] pixels) {
  		this(width, height);
  		for (int i=0; i<pixels.length; i++)
  			this.pixels[i] = (float)(pixels[i]);
  		findMinAndMax();
  	}
  
  	/** Creates a FloatProcessor from a double array using the default grayscale LUT. */
  	public SerializableFloatProcessor(int width, int height, double[] pixels) {
  		this(width, height);
  		for (int i=0; i<pixels.length; i++)
  			this.pixels[i] = (float)pixels[i];
  		findMinAndMax();
  	}
  
  	/** Creates a FloatProcessor from a 2D float array using the default LUT. */
  	public SerializableFloatProcessor(float[][] array) {
  		width = array.length;
  		height = array[0].length;
  		pixels = new float[width*height];
  		int i=0;
  		for (int y=0; y<height; y++) {
  			for (int x=0; x<width; x++) {
  				pixels[i++] = array[x][y];
  			}
  		}
  		resetRoi();
  		findMinAndMax();
  	}
  
  	/** Creates a FloatProcessor from an int[][] array. */
  	public SerializableFloatProcessor(int[][] array) {
  		this(array.length, array[0].length);
  		setIntArray(array);
  		findMinAndMax();
  	}
  
  	/**
  	Calculates the minimum and maximum pixel value for the entire image. 
  	Returns without doing anything if fixedScale has been set true as a result
  	of calling setMinAndMax(). In this case, getMin() and getMax() return the
  	fixed min and max defined by setMinAndMax(), rather than the calculated min
  	and max.
  	@see #getMin()
  	@see #getMin()
  	 */
  	public void findMinAndMax() {
  		//CONRAD.log("findMinAndMax: "+fixedScale);
  		if (fixedScale)
  			return;
  		min = Float.MAX_VALUE;
  		max = -Float.MAX_VALUE;
  		for (int i=0; i < width*height; i++) {
  			float value = pixels[i];
  			if (!Float.isInfinite(value)) {
  				if (value<min)
  					min = value;
  				if (value>max)
  					max = value;
  			}
  		}
  		showProgress(1.0);
  	}
  
  	/**
  	Sets the min and max variables that control how real
  	pixel values are mapped to 0-255 screen values. Use
  	resetMinAndMax() to enable auto-scaling;
  	 */
  	public void setMinAndMax(double min, double max) {
  		if (min==0.0 && max==0.0)
  		{resetMinAndMax(); return;}
  		this.min = (float)min;
  		this.max = (float)max;
  		fixedScale = true;
  		resetThreshold();
  	}
  
  	/** Recalculates the min and max values used to scale pixel
  		values to 0-255 for display. This ensures that this 
  		FloatProcessor is set up to correctly display the image. */
  	public void resetMinAndMax() {
  		fixedScale = false;
  		findMinAndMax();
  		resetThreshold();
  	}
  
  	/** Returns the smallest displayed pixel value. */
  	public double getMin() {
  		return min;
  	}
  
  	/** Returns the largest displayed pixel value. */
  	public double getMax() {
  		return max;
  	}
  
  	public Image createImage() {
  		boolean firstTime = pixels8==null;
  		if (firstTime || !lutAnimation)
  			create8BitImageS();
  		if (cm==null)
  			makeDefaultColorModel();
  		if (source==null) {
  			source = new MemoryImageSource(width, height, cm, pixels8, 0, width);
  			source.setAnimated(true);
  			source.setFullBufferUpdates(true);
  			img = Toolkit.getDefaultToolkit().createImage(source);
  		} else if (newPixels) {
  			source.newPixels(pixels8, cm, 0, width);
  			newPixels = false;
  		} else
  			source.newPixels();
  		lutAnimation = false;
  		return img;
  	}
  
  	protected byte[] create8BitImageS() {
  		// scale from float to 8-bits
  		int size = width*height;
  		if (pixels8==null)
  			pixels8 = new byte[size];
  		float value;
  		int ivalue;
  		float scale = 255f/(max-min);
  		for (int i=0; i<size; i++) {
  			value = pixels[i]-min;
  			if (value<0f) value = 0f;
  			ivalue = (int)((value*scale)+0.5f);
  			if (ivalue>255) ivalue = 255;
  			pixels8[i] = (byte)ivalue;
  		}
  		return pixels8;
  	}
  
  	/** Returns this image as an 8-bit BufferedImage. */
  	public BufferedImage getBufferedImage() {
  		return convertToByte(true).getBufferedImage();
  	}
  
  	/** Returns a new, blank FloatProcessor with the specified width and height. */
  	public SerializableFloatProcessor createProcessor(int width, int height) {
  		SerializableFloatProcessor ip2 = new SerializableFloatProcessor(width, height, new float[width*height], getColorModel());
  		ip2.setMinAndMax(getMin(), getMax());
  		ip2.setInterpolationMethod(interpolationMethod);
  		return ip2;
  	}
  
  	public void snapshot() {
  		snapshotWidth=width;
  		snapshotHeight=height;
  		snapshotMin=min;
  		snapshotMax=max;
  		if (snapshotPixels==null || (snapshotPixels!=null && snapshotPixels.length!=pixels.length))
  			snapshotPixels = new float[width * height];
  		System.arraycopy(pixels, 0, snapshotPixels, 0, width*height);
  	}
  
  	public void reset() {
  		if (snapshotPixels==null)
  			return;
  		min=snapshotMin;
  		max=snapshotMax;
  		System.arraycopy(snapshotPixels,0,pixels,0,width*height);
  	}
  
  	public void reset(ImageProcessor mask) {
  		if (mask==null || snapshotPixels==null)
  			return;	
  		if (mask.getWidth()!=roiWidth||mask.getHeight()!=roiHeight)
  			throw new IllegalArgumentException(maskSizeError(mask));
  		byte[] mpixels = (byte[])mask.getPixels();
  		for (int y=roiY, my=0; y<(roiY+roiHeight); y++, my++) {
  			int i = y * width + roiX;
  			int mi = my * roiWidth;
  			for (int x=roiX; x<(roiX+roiWidth); x++) {
  				if (mpixels[mi++]==0)
  					pixels[i] = snapshotPixels[i];
  				i++;
  			}
  		}
  	}
  
  	public void setSnapshotPixels(Object pixels) {
  		snapshotPixels = (float[])pixels;
  		snapshotWidth=width;
  		snapshotHeight=height;
  	}
  
  	public Object getSnapshotPixels() {
  		return snapshotPixels;
  	}
  
  	/** Returns a pixel value that must be converted using
  		Float.intBitsToFloat(). */
  	public int getPixel(int x, int y) {
  		if (x>=0 && x<width && y>=0 && y<height)
  			return Float.floatToIntBits(pixels[y*width+x]);
  		else
  			return 0;
  	}
  
  	public final int get(int x, int y) {
  		return Float.floatToIntBits(pixels[y*width+x]);
  	}
  
  	public final void set(int x, int y, int value) {
  		pixels[y*width + x] = Float.intBitsToFloat(value);
  	}
  
  	public final int get(int index) {
  		return Float.floatToIntBits(pixels[index]);
  	}
  
  	public final void set(int index, int value) {
  		pixels[index] = Float.intBitsToFloat(value);
  	}
  
  	public final float getf(int x, int y) {
  		return pixels[y*width+x];
  	}
  
  	public final void setf(int x, int y, float value) {
  		pixels[y*width + x] = value;
  	}
  
  	public final float getf(int index) {
  		return pixels[index];
  	}
  
  	public final void setf(int index, float value) {
  		pixels[index] = value;
  	}
  
  
  	/** Returns the value of the pixel at (x,y) in a
      	one element int array. iArray is an optiona
  		preallocated array. */
  	public int[] getPixel(int x, int y, int[] iArray) {
  		if (iArray==null) iArray = new int[1];
  		iArray[0] = (int)getPixelValue(x, y);
  		return iArray;
  	}
  
  	/** Sets a pixel in the image using a one element int array. */
  	public final void putPixel(int x, int y, int[] iArray) {
  		putPixelValue(x, y, iArray[0]);
  	}
  
  	/** Uses the current interpolation method (BILINEAR or BICUBIC) 
  		to calculate the pixel value at real coordinates (x,y). */
  	public double getInterpolatedPixel(double x, double y) {
  		if (interpolationMethod==BICUBIC)
  			return getBicubicInterpolatedPixel(x, y, this);
  		else {
  			if (x<0.0) x = 0.0;
  			if (x>=width-1.0) x = width-1.001;
  			if (y<0.0) y = 0.0;
  			if (y>=height-1.0) y = height-1.001;
  			return getInterpolatedPixel(x, y, pixels);
  		}
  	}
  
  	final public int getPixelInterpolated(double x, double y) {
  		if (interpolationMethod==BILINEAR) {
  			if (x<0.0 || y<0.0 || x>=width-1 || y>=height-1)
  				return 0;
  			else
  				return Float.floatToIntBits((float)getInterpolatedPixel(x, y, pixels));
  		} else if (interpolationMethod==BICUBIC)
  			return Float.floatToIntBits((float)getBicubicInterpolatedPixel(x, y, this));
  		else
  			return getPixel((int)(x+0.5), (int)(y+0.5));
  	}
  
  	/** Stores the specified value at (x,y). The value is expected to be a
  		float that has been converted to an int using Float.floatToIntBits(). */
  	public final void putPixel(int x, int y, int value) {
  		if (x>=0 && x<width && y>=0 && y<height)
  			pixels[y*width + x] = Float.intBitsToFloat(value);
  	}
  
  	/** Stores the specified real value at (x,y). */
  	public void putPixelValue(int x, int y, double value) {
  		if (x>=0 && x<width && y>=0 && y<height)
  			pixels[y*width + x] = (float)value;
  	}
  
  	/** Returns the value of the pixel at (x,y) as a float. */
  	public float getPixelValue(int x, int y) {
  		if (x>=0 && x<width && y>=0 && y<height)
  			return pixels[y*width + x];
  		else
  			return 0f;
  	}
  
  	/** Draws a pixel in the current foreground color. */
  	public void drawPixel(int x, int y) {
  		if (x>=clipXMin && x<=clipXMax && y>=clipYMin && y<=clipYMax)
  			putPixel(x, y, Float.floatToIntBits(fillColor));
  	}
  
  	/**	Returns a reference to the float array containing
  		this image's pixel data. */
  	public Object getPixels() {
  		return (Object)pixels;
  	}
  
  	boolean snapshotCopyMode;
  
  	/** Returns a copy of the pixel data. Or returns a reference to the
  		snapshot buffer if it is not null and 'snapshotCopyMode' is true.
  		@see ImageProcessor#snapshot
  		@see ImageProcessor#setSnapshotCopyMode
  	 */
  	public Object getPixelsCopy() {
  		if (snapshotCopyMode && snapshotPixels!=null) {
  			snapshotCopyMode = false;
  			return snapshotPixels;
  		} else {
  			float[] pixels2 = new float[width*height];
  			System.arraycopy(pixels, 0, pixels2, 0, width*height);
  			return pixels2;
  		}
  	}
  
  	void resetPixelsS(Object pixels) {
  		if (pixels==null) {
  			if (img!=null) {
  				img.flush();
  				img = null;
  			}
  			source = null;
  		}
  		newPixels = true;
  		source = null;
  	}
  
  	public void setPixels(Object pixels) {
  		this.pixels = (float[])pixels;
  		resetPixelsS(pixels);
  		if (pixels==null) snapshotPixels = null;
  		if (pixels==null) pixels8 = null;
  	}
  
  	/** Copies the image contained in 'ip' to (xloc, yloc) using one of
  		the transfer modes defined in the Blitter interface. */
  	public void copyBits(ImageProcessor ip, int xloc, int yloc, int mode) {
  		ip = ip.convertToFloat();
  		new FloatBlitter(toFloat(this)).copyBits(ip, xloc, yloc, mode);
  	}
  
  	public void applyTable(int[] lut) {}
  
  	private void process(int op, double value) {
  		float c, v1, v2;
  		boolean resetMinMax = roiWidth==width && roiHeight==height && !(op==FILL);
  		c = (float)value;
  		for (int y=roiY; y<(roiY+roiHeight); y++) {
  			int i = y * width + roiX;
  			for (int x=roiX; x<(roiX+roiWidth); x++) {
  				v1 = pixels[i];
  				switch(op) {
  				case INVERT:
  					v2 = max - (v1 - min);
  					break;
  				case FILL:
  					v2 = fillColor;
  					break;
  				case ADD:
  					v2 = v1 + c;
  					break;
  				case MULT:
  					v2 = v1 * c;
  					break;
  				case GAMMA:
  					if (v1<=0f)
  						v2 = 0f;
  					else
  						v2 = (float)Math.exp(c*Math.log(v1));
  					break;
  				case LOG:
  					if (v1<=0f)
  						v2 = 0f;
  					else
  						v2 = (float)Math.log(v1);
  					break;
  				case EXP:
  					v2 = (float)Math.exp(v1);
  					break;
  				case SQR:
  					v2 = v1*v1;
  					break;
  				case SQRT:
  					if (v1<=0f)
  						v2 = 0f;
  					else
  						v2 = (float)Math.sqrt(v1);
  					break;
  				case ABS:
  					v2 = (float)Math.abs(v1);
  					break;
  				case MINIMUM:
  					if (v1<value)
  						v2 = (float)value;
  					else
  						v2 = v1;
  					break;
  				case MAXIMUM:
  					if (v1>value)
  						v2 = (float)value;
  					else
  						v2 = v1;
  					break;
  				default:
  					v2 = v1;
  				}
  				pixels[i++] = v2;
  			}
  		}
  		if (resetMinMax)
  			findMinAndMax();
  	}
  
  	public void invert() {process(INVERT, 0.0);}
  	public void add(int value) {process(ADD, value);}
  	public void add(double value) {process(ADD, value);}
  	public void multiply(double value) {process(MULT, value);}
  	public void and(int value) {}
  	public void or(int value) {}
  	public void xor(int value) {}
  	public void gamma(double value) {process(GAMMA, value);}
  	public void log() {process(LOG, 0.0);}
  	public void exp() {process(EXP, 0.0);}
  	public void sqr() {process(SQR, 0.0);}
  	public void sqrt() {process(SQRT, 0.0);}
  	public void abs() {process(ABS, 0.0);}
  	public void min(double value) {process(MINIMUM, value);}
  	public void max(double value) {process(MAXIMUM, value);}
  
  
  	/** Fills the current rectangular ROI. */
  	public void fill() {process(FILL, 0.0);}
  
  	/** Fills pixels that are within roi and part of the mask.
  		Does nothing if the mask is not the same as the the ROI. */
  	public void fill(ImageProcessor mask) {
  		if (mask==null)
  		{fill(); return;}
  		int roiWidth=this.roiWidth, roiHeight=this.roiHeight;
  		int roiX=this.roiX, roiY=this.roiY;
  		if (mask.getWidth()!=roiWidth||mask.getHeight()!=roiHeight)
  			return;
  		byte[] mpixels = (byte[])mask.getPixels();
  		for (int y=roiY, my=0; y<(roiY+roiHeight); y++, my++) {
  			int i = y * width + roiX;
  			int mi = my * roiWidth;
  			for (int x=roiX; x<(roiX+roiWidth); x++) {
  				if (mpixels[mi++]!=0)
  					pixels[i] = fillColor;
  				i++;
  			}
  		}
  	}
  
  	/** Does 3x3 convolution. */
  	public void convolve3x3(int[] kernel) {
  		filter3x3(CONVOLVE, kernel);
  	}
  
  	/** Filters using a 3x3 neighborhood. */
  	public void filter(int type) {
  		filter3x3(type, null);
  	}
  
  	/** 3x3 filter operations, code partly based on 3x3 convolution code
  	 *  contributed by Glynne Casteel. */
  	void filter3x3(int type, int[] kernel) {
  		float v1, v2, v3;           //input pixel values around the current pixel
  		float v4, v5, v6;
  		float v7, v8, v9;
  		float k1=0f, k2=0f, k3=0f;  //kernel values (used for CONVOLVE only)
  		float k4=0f, k5=0f, k6=0f;
  		float k7=0f, k8=0f, k9=0f;
  		float scale = 0f;
  		if (type==CONVOLVE) {
  			k1=kernel[0]; k2=kernel[1]; k3=kernel[2];
  			k4=kernel[3]; k5=kernel[4]; k6=kernel[5];
  			k7=kernel[6]; k8=kernel[7]; k9=kernel[8];
  			for (int i=0; i<kernel.length; i++)
  				scale += kernel[i];
  			if (scale==0) scale = 1f;
  			scale = 1f/scale; //multiplication factor (multiply is faster than divide)
  		}
  		int inc = roiHeight/25;
  		if (inc<1) inc = 1;
  
  		float[] pixels2 = (float[])getPixelsCopy();
  		//float[] pixels2 = (float[])getPixelsCopy();
  		int xEnd = roiX + roiWidth;
  		int yEnd = roiY + roiHeight;
  		for (int y=roiY; y<yEnd; y++) {
  			int p  = roiX + y*width;            //points to current pixel
  			int p6 = p - (roiX>0 ? 1 : 0);      //will point to v6, currently lower
  			int p3 = p6 - (y>0 ? width : 0);    //will point to v3, currently lower
  			int p9 = p6 + (y<height-1 ? width : 0); // ...  to v9, currently lower
  			v2 = pixels2[p3];
  			v5 = pixels2[p6];
  			v8 = pixels2[p9];
  			if (roiX>0) { p3++; p6++; p9++; }
  			v3 = pixels2[p3];
  			v6 = pixels2[p6];
  			v9 = pixels2[p9];
  
  			switch (type) {
  			case BLUR_MORE:
  				for (int x=roiX; x<xEnd; x++,p++) {
  					if (x<width-1) { p3++; p6++; p9++; }
  					v1 = v2; v2 = v3;
  					v3 = pixels2[p3];
  					v4 = v5; v5 = v6;
  					v6 = pixels2[p6];
  					v7 = v8; v8 = v9;
  					v9 = pixels2[p9];
  					pixels[p] = (v1+v2+v3+v4+v5+v6+v7+v8+v9)*0.11111111f; //0.111... = 1/9
  				}
  				break;
  			case FIND_EDGES:
  				for (int x=roiX; x<xEnd; x++,p++) {
  					if (x<width-1) { p3++; p6++; p9++; }
  					v1 = v2; v2 = v3;
  					v3 = pixels2[p3];
  					v4 = v5; v5 = v6;
  					v6 = pixels2[p6];
  					v7 = v8; v8 = v9;
  					v9 = pixels2[p9];
  					float sum1 = v1 + 2*v2 + v3 - v7 - 2*v8 - v9;
  					float sum2 = v1  + 2*v4 + v7 - v3 - 2*v6 - v9;
  					pixels[p] = (float)Math.sqrt(sum1*sum1 + sum2*sum2);
  				}
  				break;
  			case CONVOLVE:
  				for (int x=roiX; x<xEnd; x++,p++) {
  					if (x<width-1) { p3++; p6++; p9++; }
  					v1 = v2; v2 = v3;
  					v3 = pixels2[p3];
  					v4 = v5; v5 = v6;
  					v6 = pixels2[p6];
  					v7 = v8; v8 = v9;
  					v9 = pixels2[p9];
  					float sum = k1*v1 + k2*v2 + k3*v3
  					+ k4*v4 + k5*v5 + k6*v6
  					+ k7*v7 + k8*v8 + k9*v9;
  					sum *= scale;
  					pixels[p] = sum;
  				}
  				break;
  			}
  			if (y%inc==0)
  				showProgress((double)(y-roiY)/roiHeight);
  		}
  		showProgress(1.0);
  	}
  
  	/** Rotates the image or ROI 'angle' degrees clockwise.
  		@see ImageProcessor#setInterpolate
  	 */
  	public void rotate(double angle) {
  		float[] pixels2 = (float[])getPixelsCopy();
  		ImageProcessor ip2 = null;
  		if (interpolationMethod==BICUBIC)
  			ip2 = new FloatProcessor(getWidth(), getHeight(), pixels2, null);
  		double centerX = roiX + (roiWidth-1)/2.0;
  		double centerY = roiY + (roiHeight-1)/2.0;
  		int xMax = roiX + this.roiWidth - 1;
  
  		double angleRadians = -angle/(180.0/Math.PI);
  		double ca = Math.cos(angleRadians);
  		double sa = Math.sin(angleRadians);
  		double tmp1 = centerY*sa-centerX*ca;
  		double tmp2 = -centerX*sa-centerY*ca;
  		double tmp3, tmp4, xs, ys;
  		int index, ixs, iys;
  
  		if (interpolationMethod==BICUBIC) {
  			for (int y=roiY; y<(roiY + roiHeight); y++) {
  				index = y*width + roiX;
  				tmp3 = tmp1 - y*sa + centerX;
  				tmp4 = tmp2 + y*ca + centerY;
  				for (int x=roiX; x<=xMax; x++) {
  					xs = x*ca + tmp3;
  					ys = x*sa + tmp4;
  					pixels[index++] = (float)getBicubicInterpolatedPixel(xs, ys, ip2);
  				}
  				if (y%30==0) showProgress((double)(y-roiY)/roiHeight);
  			}
  		} else {
  			double dwidth=width,dheight=height;
  			double xlimit = width-1.0, xlimit2 = width-1.001;
  			double ylimit = height-1.0, ylimit2 = height-1.001;
  			for (int y=roiY; y<(roiY + roiHeight); y++) {
  				index = y*width + roiX;
  				tmp3 = tmp1 - y*sa + centerX;
  				tmp4 = tmp2 + y*ca + centerY;
  				for (int x=roiX; x<=xMax; x++) {
  					xs = x*ca + tmp3;
  					ys = x*sa + tmp4;
  					if ((xs>=-0.01) && (xs<dwidth) && (ys>=-0.01) && (ys<dheight)) {
  						if (interpolationMethod==BILINEAR) {
  							if (xs<0.0) xs = 0.0;
  							if (xs>=xlimit) xs = xlimit2;
  							if (ys<0.0) ys = 0.0;			
  							if (ys>=ylimit) ys = ylimit2;
  							pixels[index++] = (float)getInterpolatedPixel(xs, ys, pixels2);
  						} else {
  							ixs = (int)(xs+0.5);
  							iys = (int)(ys+0.5);
  							if (ixs>=width) ixs = width - 1;
  							if (iys>=height) iys = height -1;
  							pixels[index++] = pixels2[width*iys+ixs];
  						}
  					} else
  						pixels[index++] = 0;
  				}
  				if (y%30==0)
  					showProgress((double)(y-roiY)/roiHeight);
  			}
  		}
  		showProgress(1.0);
  	}
  
  	public void flipVertical() {
  		int index1,index2;
  		float tmp;
  		for (int y=0; y<roiHeight/2; y++) {
  			index1 = (roiY+y)*width+roiX;
  			index2 = (roiY+roiHeight-1-y)*width+roiX;
  			for (int i=0; i<roiWidth; i++) {
  				tmp = pixels[index1];
  				pixels[index1++] = pixels[index2];
  				pixels[index2++] = tmp;
  			}
  		}
  	}
  
  	public void noise(double range) {
  		Random rnd=new Random();
  		for (int y=roiY; y<(roiY+roiHeight); y++) {
  			int i = y * width + roiX;
  			for (int x=roiX; x<(roiX+roiWidth); x++) {
  				float RandomBrightness = (float)(rnd.nextGaussian()*range);
  				pixels[i] = pixels[i] + RandomBrightness;
  				i++;
  			}
  		}
  		resetMinAndMax();
  	}
  
  	public ImageProcessor crop() {
  		ImageProcessor ip2 = createProcessor(roiWidth, roiHeight);
  		float[] pixels2 = (float[])ip2.getPixels();
  		for (int ys=roiY; ys<roiY+roiHeight; ys++) {
  			int offset1 = (ys-roiY)*roiWidth;
  			int offset2 = ys*width+roiX;
  			for (int xs=0; xs<roiWidth; xs++)
  				pixels2[offset1++] = pixels[offset2++];
  		}
  		return ip2;
  	}
  
  	/** Returns a duplicate of this image. */ 
  	public synchronized SerializableFloatProcessor duplicate() { 
  		SerializableFloatProcessor ip2 = createProcessor(width, height); 
  		float[] pixels2 = (float[])ip2.getPixels(); 
  		System.arraycopy(pixels, 0, pixels2, 0, width*height); 
  		return ip2; 
  	} 
  
  	/** Scales the image or selection using the specified scale factors.
  		@see ImageProcessor#setInterpolate
  	 */
  	public void scale(double xScale, double yScale) {
  		double xCenter = roiX + roiWidth/2.0;
  		double yCenter = roiY + roiHeight/2.0;
  		int xmin, xmax, ymin, ymax;
  
  		if ((xScale>1.0) && (yScale>1.0)) {
  			//expand roi
  			xmin = (int)(xCenter-(xCenter-roiX)*xScale);
  			if (xmin<0) xmin = 0;
  			xmax = xmin + (int)(roiWidth*xScale) - 1;
  			if (xmax>=width) xmax = width - 1;
  			ymin = (int)(yCenter-(yCenter-roiY)*yScale);
  			if (ymin<0) ymin = 0;
  			ymax = ymin + (int)(roiHeight*yScale) - 1;
  			if (ymax>=height) ymax = height - 1;
  		} else {
  			xmin = roiX;
  			xmax = roiX + roiWidth - 1;
  			ymin = roiY;
  			ymax = roiY + roiHeight - 1;
  		}
  		float[] pixels2 = (float[])getPixelsCopy();
  		ImageProcessor ip2 = null;
  		if (interpolationMethod==BICUBIC)
  			ip2 = new FloatProcessor(getWidth(), getHeight(), pixels2, null);
  		boolean checkCoordinates = (xScale < 1.0) || (yScale < 1.0);
  		int index1, index2, xsi, ysi;
  		double ys, xs;
  		if (interpolationMethod==BICUBIC) {
  			for (int y=ymin; y<=ymax; y++) {
  				ys = (y-yCenter)/yScale + yCenter;
  				index1 = y*width + xmin;
  				for (int x=xmin; x<=xmax; x++) {
  					xs = (x-xCenter)/xScale + xCenter;
  					pixels[index1++] = (float)getBicubicInterpolatedPixel(xs, ys, ip2);
  				}
  				if (y%30==0) showProgress((double)(y-ymin)/height);
  			}
  		} else {
  			double xlimit = width-1.0, xlimit2 = width-1.001;
  			double ylimit = height-1.0, ylimit2 = height-1.001;
  			for (int y=ymin; y<=ymax; y++) {
  				ys = (y-yCenter)/yScale + yCenter;
  				ysi = (int)ys;
  				if (ys<0.0) ys = 0.0;			
  				if (ys>=ylimit) ys = ylimit2;
  				index1 = y*width + xmin;
  				index2 = width*(int)ys;
  				for (int x=xmin; x<=xmax; x++) {
  					xs = (x-xCenter)/xScale + xCenter;
  					xsi = (int)xs;
  					if (checkCoordinates && ((xsi<xmin) || (xsi>xmax) || (ysi<ymin) || (ysi>ymax)))
  						pixels[index1++] = (float)min;
  					else {
  						if (interpolationMethod==BILINEAR) {
  							if (xs<0.0) xs = 0.0;
  							if (xs>=xlimit) xs = xlimit2;
  							pixels[index1++] = (float)getInterpolatedPixel(xs, ys, pixels2);
  						} else
  							pixels[index1++] = pixels2[index2+xsi];
  					}
  				}
  				if (y%30==0) showProgress((double)(y-ymin)/height);
  			}
  		}
  		showProgress(1.0);
  	}
  
  	/** Uses bilinear interpolation to find the pixel value at real coordinates (x,y). */
  	private final double getInterpolatedPixel(double x, double y, float[] pixels) {
  		int xbase = (int)x;
  		int ybase = (int)y;
  		double xFraction = x - xbase;
  		double yFraction = y - ybase;
  		int offset = ybase * width + xbase;
  		double lowerLeft = pixels[offset];
  		double lowerRight = pixels[offset + 1];
  		double upperRight = pixels[offset + width + 1];
  		double upperLeft = pixels[offset + width];
  		double upperAverage = upperLeft + xFraction * (upperRight - upperLeft);
  		double lowerAverage = lowerLeft + xFraction * (lowerRight - lowerLeft);
  		return lowerAverage + yFraction * (upperAverage - lowerAverage);
  	}
  
  	/** Creates a new FloatProcessor containing a scaled copy of this image or selection. */
  	public ImageProcessor resize(int dstWidth, int dstHeight) {
  		double srcCenterX = roiX + roiWidth/2.0;
  		double srcCenterY = roiY + roiHeight/2.0;
  		double dstCenterX = dstWidth/2.0;
  		double dstCenterY = dstHeight/2.0;
  		double xScale = (double)dstWidth/roiWidth;
  		double yScale = (double)dstHeight/roiHeight;
  		if (interpolationMethod!=NONE) {
  			dstCenterX += xScale/2.0;
  			dstCenterY += yScale/2.0;
  		}
  		ImageProcessor ip2 = createProcessor(dstWidth, dstHeight);
  		float[] pixels2 = (float[])ip2.getPixels();
  		double xs, ys;
  		if (interpolationMethod==BICUBIC) {
  			for (int y=0; y<=dstHeight-1; y++) {
  				ys = (y-dstCenterY)/yScale + srcCenterY;
  				int index = y*dstWidth;
  				for (int x=0; x<=dstWidth-1; x++) {
  					xs = (x-dstCenterX)/xScale + srcCenterX;
  					pixels2[index++] = (float)getBicubicInterpolatedPixel(xs, ys, this);
  				}
  				if (y%30==0) showProgress((double)y/dstHeight);
  			}
  		} else {
  			double xlimit = width-1.0, xlimit2 = width-1.001;
  			double ylimit = height-1.0, ylimit2 = height-1.001;
  			int index1, index2;
  			for (int y=0; y<=dstHeight-1; y++) {
  				ys = (y-dstCenterY)/yScale + srcCenterY;
  				if (interpolationMethod==BILINEAR) {
  					if (ys<0.0) ys = 0.0;
  					if (ys>=ylimit) ys = ylimit2;
  				}
  				index1 = width*(int)ys;
  				index2 = y*dstWidth;
  				for (int x=0; x<=dstWidth-1; x++) {
  					xs = (x-dstCenterX)/xScale + srcCenterX;
  					if (interpolationMethod==BILINEAR) {
  						if (xs<0.0) xs = 0.0;
  						if (xs>=xlimit) xs = xlimit2;
  						pixels2[index2++] = (float)getInterpolatedPixel(xs, ys, pixels);
  					} else
  						pixels2[index2++] = pixels[index1+(int)xs];
  				}
  				if (y%30==0) showProgress((double)y/dstHeight);
  			}
  		}
  		showProgress(1.0);
  		return ip2;
  	}
  
  	final double getBilinearInterpolatedPixelS(double x, double y) {
  		if (x>=-1 && x<width && y>=-1 && y<height) {
  			int method = interpolationMethod;
  			interpolationMethod = BILINEAR;
  			double value = getInterpolatedPixel(x, y);
  			interpolationMethod = method;
  			return value;
  		} else
  			return getBackgroundValue();
  	}
  
  	static final double a = 0.5; // Catmull-Rom interpolation
  	final double cubicS(double x) {
  		if (x < 0.0) x = -x;
  		double z = 0.0;
  		if (x < 1.0) 
  			z = x*x*(x*(-a+2.0) + (a-3.0)) + 1.0;
  		else if (x < 2.0) 
  			z = -a*x*x*x + 5.0*a*x*x - 8.0*a*x + 4.0*a;
  		return z;
  	}
  
  	/** This method is from Chapter 16 of "Digital Image Processing:
  		An Algorithmic Introduction Using Java" by Burger and Burge
  		(http://www.imagingbook.com/). */
  	public double getBicubicInterpolatedPixel(double x0, double y0, SerializableFloatProcessor ip2) {
  		int u0 = (int) Math.floor(x0);	//use floor to handle negative coordinates too
  		int v0 = (int) Math.floor(y0);
  		if (u0<=0 || u0>=width-2 || v0<=0 || v0>=height-2)
  			return ip2.getBilinearInterpolatedPixelS(x0, y0);
  		double q = 0;
  		for (int j = 0; j <= 3; j++) {
  			int v = v0 - 1 + j;
  			double p = 0;
  			for (int i = 0; i <= 3; i++) {
  				int u = u0 - 1 + i;
  				p = p + ip2.getf(u,v) * cubicS(x0 - u);
  			}
  			q = q + p * cubicS(y0 - v);
  		}
  		return q;
  	}
  
  	/** Sets the foreground fill/draw color. */
  	public void setColor(Color color) {
  		int bestIndex = getBestIndex(color);
  		if (bestIndex>0 && getMin()==0.0 && getMax()==0.0) {
  			fillColor = bestIndex;
  			setMinAndMax(0.0,255.0);
  		} else if (bestIndex==0 && getMin()>0.0 && (color.getRGB()&0xffffff)==0)
  			fillColor = 0f;
  		else
  			fillColor = (float)(min + (max-min)*(bestIndex/255.0));
  	}
  
  	/** Sets the default fill/draw value. */
  	public void setValue(double value) {
  		fillColor = (float)value;
  	}
  
  	/** Does nothing. The rotate() and scale() methods always zero fill. */
  	public void setBackgroundValue(double value) {
  	}
  
  	/** Always returns 0. */
  	public double getBackgroundValue() {
  		return 0.0;
  	}
  
  	public void setThreshold(double minThreshold, double maxThreshold, int lutUpdate) {
  		if (minThreshold==NO_THRESHOLD)
  		{resetThreshold(); return;}
  		if (max>min) {
  			double minT = Math.round(((minThreshold-min)/(max-min))*255.0);
  			double maxT = Math.round(((maxThreshold-min)/(max-min))*255.0);
  			super.setThreshold(minT, maxT, lutUpdate); // update LUT
  		} else
  			super.resetThreshold();
  		this.minThreshold = minThreshold;
  		this.maxThreshold = maxThreshold;
  	}
  
  	/** Performs a convolution operation using the specified kernel. */
  	public void convolve(float[] kernel, int kernelWidth, int kernelHeight) {
  		snapshot();
  		FloatProcessor fp = this.toFloat(this);
  		new ij.plugin.filter.Convolver().convolve(fp, kernel, kernelWidth, kernelHeight);
  		this.pixels = (float []) fp.getPixels();
  	}
  
  	
  
  	/** Not implemented. */
  	public void threshold(int level) {}
  	/** Not implemented. */
  	public void autoThreshold() {}
  	/** Not implemented. */
  	public void medianFilter() {}
  	/** Not implemented. */
  	public int[] getHistogram() {return null;}
  	/** Not implemented. */
  	public void erode() {}
  	/** Not implemented. */
  	public void dilate() {}
  
  	/** Returns this FloatProcessor.
  	 *  @param channelNumber   Ignored (needed for compatibility with ColorProcessor.toFloat)
  	 *  @param fp              Ignored (needed for compatibility with the other ImageProcessor types).
  	 *  @return This FloatProcessor
  	 */
  	public FloatProcessor toFloat(int channelNumber, FloatProcessor fp) {
  		return new FloatProcessor(width, height, pixels, cm);
  	}
  
  	/** Returns this FloatProcessor.
  	 *  @param fp              Ignored (needed for compatibility with the other ImageProcessor types).
  	 *  @return This FloatProcessor
  	 */
  	public FloatProcessor toFloat(SerializableFloatProcessor fp) {
  		return new FloatProcessor(this.width, this.height, this.pixels, this.cm);
  	}
  
  	/** Sets the pixels, and min&max values from a FloatProcessor.
  	 *  Also the  values are taken from the FloatProcessor.
  	 *  @param channelNumber   Ignored (needed for compatibility with ColorProcessor.toFloat)
  	 *  @param fp              The FloatProcessor where the image data are read from.
  	 */
  	public void setPixels(int channelNumber, FloatProcessor fp) {
  		if (fp.getPixels() != getPixels())
  			setPixels(fp.getPixels());
  		setMinAndMax(fp.getMin(), fp.getMax());
  	}
  
  	/** Returns the smallest possible positive nonzero pixel value. */
  	public double minValue() {
  		return Float.MIN_VALUE;
  	}
  
  	/** Returns the largest possible positive finite pixel value. */
  	public double maxValue() {
  		return Float.MAX_VALUE;
  	}
  
  	public int getWidth(){
  		return width;
  	}
  	public int getHeight(){
  		return height;
  	}
  	/** Not implemented. */
  	public void swapPixelArrays() {	}
  }
  