package ij.process;
import java.util.*;
import java.awt.*;
import java.awt.image.*;
import ij.gui.*;
/** This is an 32-bit floating-point image and methods that operate on that image. */
public class FloatProcessor extends ImageProcessor {
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;
/** Creates a new FloatProcessor using the specified pixel array. */
public FloatProcessor(int width, int height, float[] pixels) {
this(width, height, pixels, null);
}
/** Creates a new FloatProcessor using the specified pixel array and ColorModel. */
public FloatProcessor(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;
this.pixels = pixels;
this.cm = cm;
resetRoi();
}
/** Creates a blank FloatProcessor using the default grayscale LUT that
displays zero as black. Call invertLut() to display zero as white. */
public FloatProcessor(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 FloatProcessor(int width, int height, int[] pixels) {
this(width, height);
for (int i=0; imax)
max = value;
}
}
minMaxSet = true;
}
/**
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;
@see ij.plugin.frame.ContrastAdjuster
*/
public void setMinAndMax(double minimum, double maximum) {
if (minimum==0.0 && maximum==0.0)
{resetMinAndMax(); return;}
min = (float)minimum;
max = (float)maximum;
fixedScale = true;
minMaxSet = 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() {
if (!minMaxSet) findMinAndMax();
return min;
}
/** Returns the largest displayed pixel value. */
public double getMax() {
if (!minMaxSet) findMinAndMax();
return max;
}
/** Create an 8-bit AWT image by scaling pixels in the range min-max to 0-255. */
public Image createImage() {
boolean firstTime = pixels8==null;
if (firstTime || !lutAnimation)
create8BitImage();
if (cm==null)
makeDefaultColorModel();
if (ij.IJ.isJava16())
return createBufferedImage();
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;
}
// scale from float to 8-bits
protected byte[] create8BitImage() {
int size = width*height;
if (pixels8==null)
pixels8 = new byte[size];
float value;
int ivalue;
float min2=(float)getMin(), max2=(float)getMax();
float scale = 255f/(max2-min2);
for (int i=0; i255) ivalue = 255;
pixels8[i] = (byte)ivalue;
}
return pixels8;
}
Image createBufferedImage() {
if (raster==null) {
SampleModel sm = getIndexSampleModel();
DataBuffer db = new DataBufferByte(pixels8, width*height, 0);
raster = Raster.createWritableRaster(sm, db, null);
}
if (image==null || cm!=cm2) {
if (cm==null) cm = getDefaultColorModel();
image = new BufferedImage(cm, raster, false, null);
cm2 = cm;
}
lutAnimation = false;
return image;
}
/** 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 ImageProcessor createProcessor(int width, int height) {
ImageProcessor ip2 = new FloatProcessor(width, height, new float[width*height], getColorModel());
ip2.setMinAndMax(getMin(), getMax());
ip2.setInterpolationMethod(interpolationMethod);
return ip2;
}
public void snapshot() {
snapshotWidth=width;
snapshotHeight=height;
snapshotMin=(float)getMin();
snapshotMax=(float)getMax();
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;
minMaxSet = true;
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++;
}
}
}
/** Swaps the pixel and snapshot (undo) arrays. */
public void swapPixelArrays() {
if (snapshotPixels==null) return;
float pixel;
for (int i=0; i=0 && x=0 && y=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=0 && y=0 && x=0 && y=0 && x=0 && y=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;
}
/** 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;
}
}
public void setPixels(Object pixels) {
this.pixels = (float[])pixels;
resetPixels(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(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;
float min2=0f, max2=0f;
if (op==INVERT)
{min2=(float)getMin(); max2=(float)getMax();}
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 = max2 - (v1 - min2);
break;
case FILL:
v2 = fillColor;
break;
case SET:
v2 = c;
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:
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 (v1value)
v2 = (float)value;
else
v2 = v1;
break;
default:
v2 = v1;
}
pixels[i++] = v2;
}
}
}
/** Each pixel in the image is inverted using p=max-(p-min), where 'min'
and 'max' are the display range limits set using setMinAndMax(). */
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 set(double value) {process(SET, 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; i0 ? 1 : 0); //will point to v6, currently lower
int p3 = p6 - (y>0 ? width : 0); //will point to v3, currently lower
int p9 = p6 + (y0) { p3++; p6++; p9++; }
v3 = pixels2[p3];
v6 = pixels2[p6];
v9 = pixels2[p9];
switch (type) {
case BLUR_MORE:
for (int x=roiX; x=-0.01) && (xs=-0.01) && (ys=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;
}
}
}
}
public void flipVertical() {
int index1,index2;
float tmp;
for (int y=0; y1.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);
}
}
} 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 && ((xsixmax) || (ysiymax)))
pixels[index1++] = (float)getMin();
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];
}
}
}
}
}
/** 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;
if (Double.isNaN(upperLeft ) && xFraction>=0.5)
upperAverage = upperRight;
else if (Double.isNaN(upperRight) && xFraction<0.5 )
upperAverage = upperLeft;
else
upperAverage = upperLeft + xFraction * (upperRight-upperLeft);
double lowerAverage;
if (Double.isNaN(lowerLeft) && xFraction>=0.5)
lowerAverage = lowerRight;
else if (Double.isNaN(lowerRight) && xFraction<0.5 )
lowerAverage = lowerLeft;
else
lowerAverage = lowerLeft + xFraction * (lowerRight-lowerLeft);
if (Double.isNaN(lowerAverage) && yFraction>=0.5)
return upperAverage;
else if (Double.isNaN(upperAverage) && yFraction<0.5 )
return lowerAverage;
else
return lowerAverage + yFraction * (upperAverage-lowerAverage);
}
/*
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) {
if (roiWidth==dstWidth && roiHeight==dstHeight)
return crop();
if ((width==1||height==1) && interpolationMethod!=NONE)
return resizeLinearly(dstWidth, 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) {
if (dstWidth!=width) dstCenterX+=xScale/4.0;
if (dstHeight!=height) dstCenterY+=yScale/4.0;
}
int inc = getProgressIncrement(dstWidth,dstHeight);
ImageProcessor ip2 = createProcessor(dstWidth, dstHeight);
float[] pixels2 = (float[])ip2.getPixels();
double xs, ys;
if (interpolationMethod==BICUBIC) {
for (int y=0; y<=dstHeight-1; y++) {
if (inc>0&&y%inc==0) showProgress((double)y/dstHeight);
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);
}
}
} 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++) {
if (inc>0&&y%inc==0) showProgress((double)y/dstHeight);
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 (inc>0) showProgress(1.0);
return ip2;
}
FloatProcessor downsize(int dstWidth, int dstHeight, String msg) {
FloatProcessor ip2 = this;
if (msg!=null)
ij.IJ.showStatus("downsizing in x"+msg);
if (dstWidth=width-2 || v0<=0 || v0>=height-2)
return ip2.getBilinearInterpolatedPixel(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) * cubic(x0 - u);
}
q = q + p * cubic(y0 - v);
}
return q;
}
/** Sets the foreground fill/draw color. */
public void setColor(Color color) {
drawingColor = 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)(getMin() + (getMax()-getMin())*(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 (getMax()>getMin()) {
double minT = Math.round(((minThreshold-getMin())/(getMax()-getMin()))*255.0);
double maxT = Math.round(((maxThreshold-getMin())/(getMax()-getMin()))*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();
new ij.plugin.filter.Convolver().convolve(this, kernel, kernelWidth, kernelHeight);
}
/** Returns a 256 bin histogram of the current ROI or of the entire image if there is no ROI. */
public int[] getHistogram() {
return getStatistics().histogram;
}
/** Not implemented. */
public void threshold(int level) {}
/** Not implemented. */
public void autoThreshold() {}
/** Not implemented. */
public void medianFilter() {}
/** 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 this;
}
/** 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 getBitDepth() {
return 32;
}
}
Ads help maintain this website.