package ij.process;
import java.util.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.Rectangle2D;
import java.awt.font.GlyphVector;
import ij.gui.*;
import ij.util.*;
import ij.plugin.filter.GaussianBlur;
import ij.plugin.Binner;
import ij.plugin.Colors;
import ij.process.AutoThresholder.Method;
import ij.Prefs;
import ij.measure.Measurements;
public abstract class ImageProcessor implements Cloneable {
public static final int BLACK = 0xFF000000;
public static final double NO_THRESHOLD = -808080.0;
public static final int LEFT_JUSTIFY = 0;
public static final int CENTER_JUSTIFY = 1;
public static final int RIGHT_JUSTIFY = 2;
public static final int ISODATA = 0;
public static final int ISODATA2 = 1;
public static final int UPDATE_RED=1, UPDATE_GREEN=2, UPDATE_BLUE=3, SET_FIRST_CHANNEL=4,
SUM_PROJECTION=5, MAX_PROJECTION=6, MIN_PROJECTION=7, INVERT_PROJECTION=8;
public static final int NEAREST_NEIGHBOR=0, NONE=0, BILINEAR=1, BICUBIC=2;
public static final int BLUR_MORE=0, FIND_EDGES=1, MEDIAN_FILTER=2, MIN=3, MAX=4, CONVOLVE=5;
static public final int RED_LUT=0, BLACK_AND_WHITE_LUT=1, NO_LUT_UPDATE=2, OVER_UNDER_LUT=3;
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, SET=15;
static final String WRONG_LENGTH = "width*height!=pixels.length";
int fgColor = 0;
protected int lineWidth = 1;
protected int cx, cy; protected Font font = ij.IJ.font12;
protected FontMetrics fontMetrics;
protected boolean antialiasedText;
protected boolean boldFont;
private static String[] interpolationMethods;
private static int overRed, overGreen=255, overBlue;
private static int underRed, underGreen, underBlue=255;
private static boolean useBicubic;
private int sliceNumber;
private Overlay overlay;
private boolean noReset;
ProgressBar progressBar;
protected int width, snapshotWidth;
protected int height, snapshotHeight;
protected int roiX, roiY, roiWidth, roiHeight;
protected int xMin, xMax, yMin, yMax;
boolean snapshotCopyMode;
ImageProcessor mask;
protected ColorModel baseCM; protected ColorModel cm;
protected byte[] rLUT1, gLUT1, bLUT1; protected byte[] rLUT2, gLUT2, bLUT2; protected boolean interpolate; protected int interpolationMethod = NONE;
protected double minThreshold=NO_THRESHOLD, maxThreshold=NO_THRESHOLD;
protected int histogramSize = 256;
protected double histogramMin, histogramMax;
protected float[] cTable;
protected boolean lutAnimation;
protected MemoryImageSource source; protected Image img;
protected boolean newPixels; protected Color drawingColor = Color.black;
protected int clipXMin, clipXMax, clipYMin, clipYMax; protected int justification = LEFT_JUSTIFY;
protected int lutUpdateMode;
protected WritableRaster raster;
protected BufferedImage image;
protected BufferedImage fmImage;
protected Graphics2D fmGraphics;
protected ColorModel cm2;
protected SampleModel sampleModel;
protected static IndexColorModel defaultColorModel;
protected boolean minMaxSet;
protected static double seed = Double.NaN;
protected static Random rnd;
protected boolean fillValueSet;
protected void showProgress(double percentDone) {
if (progressBar!=null)
progressBar.show(percentDone);
}
protected void hideProgress() {
showProgress(1.0);
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getBitDepth() {
Object pixels = getPixels();
if (pixels==null)
return 0;
else if (pixels instanceof byte[])
return 8;
else if (pixels instanceof short[])
return 16;
else if (pixels instanceof int[])
return 24;
else if (pixels instanceof float[])
return 32;
else
return 0;
}
public ColorModel getColorModel() {
if (cm==null)
makeDefaultColorModel();
if (baseCM!=null)
return baseCM;
else
return cm;
}
private IndexColorModel getIndexColorModel() {
ColorModel cm2 = baseCM;
if (cm2==null)
cm2 = cm;
if (cm2!=null && (cm2 instanceof IndexColorModel))
return (IndexColorModel)cm2;
else
return null;
}
public ColorModel getCurrentColorModel() {
if (cm==null) makeDefaultColorModel();
return cm;
}
public void setColorModel(ColorModel cm) {
if (cm!=null && !(cm instanceof IndexColorModel))
throw new IllegalArgumentException("IndexColorModel required");
if (cm!=null && cm instanceof LUT)
cm = ((LUT)cm).getColorModel();
this.cm = cm;
baseCM = null;
rLUT1 = rLUT2 = null;
inversionTested = false;
minThreshold = NO_THRESHOLD;
}
public LUT getLut() {
ColorModel cm2 = getColorModel();
if (cm2!=null && (cm2 instanceof IndexColorModel))
return new LUT((IndexColorModel)cm2, getMin(), getMax());
else
return null;
}
public void setLut(LUT lut) {
if (lut==null)
setColorModel(null);
else {
setColorModel(lut.getColorModel());
if (lut.min!=0.0 || lut.max!=0.0)
setMinAndMax(lut.min, lut.max);
}
}
protected void makeDefaultColorModel() {
cm = getDefaultColorModel();
}
public void invertLut() {
IndexColorModel icm = (IndexColorModel)getColorModel();
int mapSize = icm.getMapSize();
byte[] reds = new byte[mapSize];
byte[] greens = new byte[mapSize];
byte[] blues = new byte[mapSize];
byte[] reds2 = new byte[mapSize];
byte[] greens2 = new byte[mapSize];
byte[] blues2 = new byte[mapSize];
icm.getReds(reds);
icm.getGreens(greens);
icm.getBlues(blues);
for (int i=0; i<mapSize; i++) {
reds2[i] = (byte)(reds[mapSize-i-1]&255);
greens2[i] = (byte)(greens[mapSize-i-1]&255);
blues2[i] = (byte)(blues[mapSize-i-1]&255);
}
ColorModel cm = new IndexColorModel(8, mapSize, reds2, greens2, blues2);
double min=getMin(), max=getMax();
setColorModel(cm);
setMinAndMax(min, max);
}
public int getBestIndex(Color c) {
IndexColorModel icm;
if (cm==null)
makeDefaultColorModel();
if (minThreshold!=NO_THRESHOLD) {
double saveMin = getMinThreshold();
double saveMax = getMaxThreshold();
resetThreshold();
icm = (IndexColorModel)cm;
setThreshold(saveMin, saveMax, lutUpdateMode);
} else
icm = (IndexColorModel)cm;
int mapSize = icm.getMapSize();
byte[] rLUT = new byte[mapSize];
byte[] gLUT = new byte[mapSize];
byte[] bLUT = new byte[mapSize];
icm.getReds(rLUT);
icm.getGreens(gLUT);
icm.getBlues(bLUT);
int minDistance = Integer.MAX_VALUE;
int distance;
int minIndex = 0;
int r1 = c.getRed();
int g1 = c.getGreen();
int b1 = c.getBlue();
if (!(r1==g1&&g1==b1&&r1==b1) && icm==defaultColorModel) {
double[] w = ColorProcessor.getWeightingFactors();
r1 = (int)Math.round(3*r1*w[0]);
g1 = (int)Math.round(3*g1*w[1]);
b1 = (int)Math.round(3*b1*w[2]);
}
int r2,b2,g2;
for (int i=0; i<mapSize; i++) {
r2 = rLUT[i]&0xff; g2 = gLUT[i]&0xff; b2 = bLUT[i]&0xff;
distance = (r2-r1)*(r2-r1)+(g2-g1)*(g2-g1)+(b2-b1)*(b2-b1);
if (distance<minDistance) {
minDistance = distance;
minIndex = i;
}
if (minDistance==0.0)
break;
}
return minIndex;
}
protected boolean inversionTested = false;
protected boolean invertedLut;
public boolean isInvertedLut() {
if (inversionTested)
return invertedLut;
IndexColorModel icm = getIndexColorModel();
if (icm==null)
return false;
boolean hasAscendingStep = false;
int v1, v2;
for (int i=1; i<255; i++) {
v1 = icm.getRed(i-1)+icm.getGreen(i-1)+icm.getBlue(i-1);
v2 = icm.getRed(i)+icm.getGreen(i)+icm.getBlue(i);
if (v1<v2) {
hasAscendingStep = true;
break;
}
}
invertedLut = !hasAscendingStep;
inversionTested = true;
return invertedLut;
}
public boolean isGrayscale() {
return !isColorLut();
}
public boolean isColorLut() {
IndexColorModel icm = getIndexColorModel();
if (icm==null)
return false;
int mapSize = icm.getMapSize();
byte[] reds = new byte[mapSize];
byte[] greens = new byte[mapSize];
byte[] blues = new byte[mapSize];
icm.getReds(reds);
icm.getGreens(greens);
icm.getBlues(blues);
boolean isColor = false;
for (int i=0; i<mapSize; i++) {
if ((reds[i] != greens[i]) || (greens[i] != blues[i])) {
isColor = true;
break;
}
}
return isColor;
}
public boolean isPseudoColorLut() {
IndexColorModel icm = getIndexColorModel();
if (icm==null)
return false;
if (getMinThreshold()!=NO_THRESHOLD)
return true;
int mapSize = icm.getMapSize();
if (mapSize!=256)
return false;
byte[] reds = new byte[mapSize];
byte[] greens = new byte[mapSize];
byte[] blues = new byte[mapSize];
icm.getReds(reds);
icm.getGreens(greens);
icm.getBlues(blues);
int r, g, b, d;
int r2=reds[0]&255, g2=greens[0]&255, b2=blues[0]&255;
double sum=0.0, sum2=0.0;
for (int i=0; i<mapSize; i++) {
r=reds[i]&255; g=greens[i]&255; b=blues[i]&255;
d=r-r2; sum+=d; sum2+=d*d;
d=g-g2; sum+=d; sum2+=d*d;
d=b-b2; sum+=d; sum2+=d*d;
r2=r; g2=g; b2=b;
}
double stdDev = (768*sum2-sum*sum)/768.0;
if (stdDev>0.0)
stdDev = Math.sqrt(stdDev/(767.0));
else
stdDev = 0.0;
boolean isPseudoColor = stdDev<20.0;
if ((int)stdDev==67) isPseudoColor = true; return isPseudoColor;
}
public boolean isDefaultLut() {
if (cm==null)
makeDefaultColorModel();
IndexColorModel icm = getIndexColorModel();
if (icm==null)
return false;
int mapSize = icm.getMapSize();
if (mapSize!=256)
return false;
byte[] reds = new byte[mapSize];
byte[] greens = new byte[mapSize];
byte[] blues = new byte[mapSize];
icm.getReds(reds);
icm.getGreens(greens);
icm.getBlues(blues);
boolean isDefault = true;
for (int i=0; i<mapSize; i++) {
if ((reds[i]&255)!=i || (greens[i]&255)!=i || (blues[i]&255)!=i) {
isDefault = false;
break;
}
}
return isDefault;
}
public abstract void setColor(Color color);
public void setColor(String color) {
setColor(Colors.decode(color, Color.white));
}
public void setBackgroundColor(Color color) {
}
public void setColor(int value) {
setValue(value);
}
public void setColor(double value) {
setValue(value);
}
public abstract void setValue(double value);
public abstract double getForegroundValue();
public boolean fillValueSet() {
return fillValueSet;
}
public abstract void setBackgroundValue(double value);
public abstract double getBackgroundValue();
public void setGlobalForegroundColor() {
double value = Toolbar.getForegroundValue();
if (Double.isNaN(value))
setColor(Toolbar.getForegroundColor());
else
setValue(value);
}
public void setGlobalBackgroundColor() {
double value = Toolbar.getBackgroundValue();
if (Double.isNaN(value))
setColor(Toolbar.getBackgroundColor());
else
setValue(value);
}
public abstract double getMin();
public abstract double getMax();
public abstract void setMinAndMax(double min, double max);
public void resetMinAndMax() {}
public void setThreshold(double minThreshold, double maxThreshold) {
setThreshold(minThreshold, maxThreshold, NO_LUT_UPDATE);
}
public void setThreshold(double minThreshold, double maxThreshold, int lutUpdate) {
if (this instanceof ColorProcessor)
return;
this.minThreshold = minThreshold;
this.maxThreshold = maxThreshold;
lutUpdateMode = lutUpdate;
if (minThreshold==NO_THRESHOLD) {
resetThreshold();
return;
}
if (lutUpdate==NO_LUT_UPDATE)
return;
if (rLUT1==null) {
if (cm==null)
makeDefaultColorModel();
baseCM = cm;
IndexColorModel m = (IndexColorModel)cm;
rLUT1 = new byte[256]; gLUT1 = new byte[256]; bLUT1 = new byte[256];
m.getReds(rLUT1); m.getGreens(gLUT1); m.getBlues(bLUT1);
rLUT2 = new byte[256]; gLUT2 = new byte[256]; bLUT2 = new byte[256];
}
int t1 = (int)minThreshold;
int t2 = (int)maxThreshold;
int index;
if (lutUpdate==RED_LUT)
for (int i=0; i<256; i++) {
if (i>=t1 && i<=t2) {
rLUT2[i] = (byte)255;
gLUT2[i] = (byte)0;
bLUT2[i] = (byte)0;
} else {
rLUT2[i] = rLUT1[i];
gLUT2[i] = gLUT1[i];
bLUT2[i] = bLUT1[i];
}
}
else if (lutUpdate==BLACK_AND_WHITE_LUT) {
byte foreground = Prefs.blackBackground?(byte)255:(byte)0;
byte background = (byte)(255 - foreground);
for (int i=0; i<256; i++) {
if (i>=t1 && i<=t2) {
rLUT2[i] = foreground;
gLUT2[i] = foreground;
bLUT2[i] = foreground;
} else {
rLUT2[i] = background;
gLUT2[i] =background;
bLUT2[i] =background;
}
}
} else {
for (int i=0; i<256; i++) {
if (i>=t1 && i<=t2) {
rLUT2[i] = rLUT1[i];
gLUT2[i] = gLUT1[i];
bLUT2[i] = bLUT1[i];
} else if (i>t2) {
rLUT2[i] = (byte)overRed;
gLUT2[i] = (byte)overGreen;
bLUT2[i] = (byte)overBlue;
} else {
rLUT2[i] = (byte)underRed;
gLUT2[i] = (byte)underGreen;
bLUT2[i] = (byte)underBlue;
}
}
}
cm = new IndexColorModel(8, 256, rLUT2, gLUT2, bLUT2);
}
public void setAutoThreshold(String method) {
if (method==null)
throw new IllegalArgumentException("Null method");
boolean darkBackground = method.contains("dark");
noReset = method.contains("no-reset");
int lut = RED_LUT;
if (method.contains("b&w"))
lut = BLACK_AND_WHITE_LUT;
if (method.contains("over"))
lut = OVER_UNDER_LUT;
if (method.contains("no-lut"))
lut = NO_LUT_UPDATE;
int index = method.indexOf(" ");
if (index!=-1)
method = method.substring(0, index);
setAutoThreshold(method, darkBackground, lut);
noReset = false;
}
public void setAutoThreshold(String mString, boolean darkBackground, int lutUpdate) {
Method m = null;
try {
m = Method.valueOf(Method.class, mString);
} catch(Exception e) {
m = null;
}
if (m==null)
throw new IllegalArgumentException("Invalid method (\""+mString+"\")");
setAutoThreshold(m, darkBackground, lutUpdate);
}
public void setAutoThreshold(Method method, boolean darkBackground) {
setAutoThreshold(method, darkBackground, RED_LUT);
}
public void setAutoThreshold(Method method, boolean darkBackground, int lutUpdate) {
if (method==null || (this instanceof ColorProcessor))
return;
double min=0.0, max=0.0;
boolean notByteData = !(this instanceof ByteProcessor);
ImageProcessor ip2 = this;
if (notByteData) {
ImageProcessor mask = ip2.getMask();
Rectangle rect = ip2.getRoi();
if (!noReset || lutUpdate==OVER_UNDER_LUT)
ip2.resetMinAndMax();
noReset = false;
min = ip2.getMin(); max = ip2.getMax();
ip2 = ip2.convertToByte(true);
ip2.setMask(mask);
ip2.setRoi(rect);
}
ImageStatistics stats = ip2.getStats();
AutoThresholder thresholder = new AutoThresholder();
int threshold = thresholder.getThreshold(method, stats.histogram);
double lower, upper;
if (darkBackground) {
if (isInvertedLut())
{lower=0.0; upper=threshold;}
else
{lower=threshold+1; upper=255.0;}
} else {
if (isInvertedLut())
{lower=threshold+1; upper=255.0;}
else
{lower=0.0; upper=threshold;}
}
if (lower>255) lower = 255;
scaleAndSetThreshold(lower, upper, lutUpdate);
}
public void setAutoThreshold(int method, int lutUpdate) {
if (method<0 || method>ISODATA2)
throw new IllegalArgumentException("Invalid thresholding method");
if (this instanceof ColorProcessor)
return;
boolean notByteData = !(this instanceof ByteProcessor);
ImageProcessor ip2 = this;
if (notByteData) {
ImageProcessor mask = ip2.getMask();
Rectangle rect = ip2.getRoi();
resetMinAndMax();
ip2 = convertToByte(true);
ip2.setMask(mask);
ip2.setRoi(rect);
}
ImageStatistics stats = ip2.getStats();
int[] histogram = stats.histogram;
int originalModeCount = histogram[stats.mode];
if (method==ISODATA2) {
int maxCount2 = 0;
for (int i = 0; i<stats.nBins; i++) {
if ((histogram[i] > maxCount2) && (i!=stats.mode))
maxCount2 = histogram[i];
}
int hmax = stats.maxCount;
if ((hmax>(maxCount2 * 2)) && (maxCount2 != 0)) {
hmax = (int)(maxCount2 * 1.5);
histogram[stats.mode] = hmax;
}
}
int threshold = ip2.getAutoThreshold(stats.histogram);
histogram[stats.mode] = originalModeCount;
float[] hist = new float[256];
for (int i=0; i<256; i++)
hist[i] = stats.histogram[i];
FloatProcessor fp = new FloatProcessor(256, 1, hist, null);
GaussianBlur gb = new GaussianBlur();
gb.blur1Direction(fp, 2.0, 0.01, true, 0);
float maxCount=0f, sum=0f, mean, count;
int mode = 0;
for (int i=0; i<256; i++) {
count = hist[i];
sum += count;
if (count>maxCount) {
maxCount = count;
mode = i;
}
}
double avg = sum/256.0;
double lower, upper;
if (maxCount/avg>1.5) {
if ((stats.max-mode)>(mode-stats.min))
{lower=threshold; upper=255.0;}
else
{lower=0.0; upper=threshold;}
} else {
if (isInvertedLut())
{lower=threshold; upper=255.0;}
else
{lower=0.0; upper=threshold;}
}
scaleAndSetThreshold(lower, upper, lutUpdate);
}
public void scaleAndSetThreshold(double lower, double upper, int lutUpdate) {
int bitDepth = getBitDepth();
if (bitDepth!=8 && lower!=NO_THRESHOLD) {
double min = getMin();
double max = getMax();
if (max>min) {
if (lower==0.0) {
if (bitDepth==32)
lower = Math.min(min, -1e30); } else
lower = min + (lower/255.0)*(max-min);
if (upper==255.0) {
if (bitDepth==16)
upper = 65535;
else if (bitDepth==32)
upper = Math.max(max, 1e30);
} else
upper = min + (upper/255.0)*(max-min);
} else
lower = upper = min;
}
setThreshold(lower, upper, lutUpdate);
}
public void resetThreshold() {
minThreshold = NO_THRESHOLD;
if (baseCM!=null) {
cm = baseCM;
baseCM = null;
}
rLUT1 = rLUT2 = null;
inversionTested = false;
}
public double getMinThreshold() {
return minThreshold;
}
public double getMaxThreshold() {
return maxThreshold;
}
public int getLutUpdateMode() {
return lutUpdateMode;
}
public void setBinaryThreshold() {
if (!(this instanceof ByteProcessor)) return;
double t1=255.0, t2=255.0;
boolean invertedLut = isInvertedLut();
if ((invertedLut&&ij.Prefs.blackBackground) || (!invertedLut&&!ij.Prefs.blackBackground)) {
t1 = 0.0;
t2 = 0.0;
}
setThreshold(t1, t2, ImageProcessor.NO_LUT_UPDATE);
}
public void resetBinaryThreshold() {
if (minThreshold==maxThreshold && lutUpdateMode==NO_LUT_UPDATE)
resetThreshold();
}
public void setRoi(Rectangle roi) {
if (roi==null)
resetRoi();
else
setRoi(roi.x, roi.y, roi.width, roi.height);
}
public void setRoi(int x, int y, int rwidth, int rheight) {
if (x<0 || y<0 || x+rwidth>width || y+rheight>height) {
Rectangle r1 = new Rectangle(x, y, rwidth, rheight);
Rectangle r2 = r1.intersection(new Rectangle(0, 0, width, height));
if (r2.width<=0 || r2.height<=0) {
roiX=0; roiY=0; roiWidth=0; roiHeight=0;
xMin=0; xMax=0; yMin=0; yMax=0;
mask=null;
return;
}
if (mask!=null && mask.getWidth()==rwidth && mask.getHeight()==rheight) {
Rectangle r3 = new Rectangle(0, 0, r2.width, r2.height);
if (x<0) r3.x = -x;
if (y<0) r3.y = -y;
mask.setRoi(r3);
if (mask!=null)
mask = mask.crop();
}
roiX=r2.x; roiY=r2.y; roiWidth=r2.width; roiHeight=r2.height;
} else {
roiX=x; roiY=y; roiWidth=rwidth; roiHeight=rheight;
}
if (mask!=null && (mask.getWidth()!=roiWidth||mask.getHeight()!=roiHeight))
mask = null;
xMin = Math.max(roiX, 1);
xMax = Math.min(roiX + roiWidth - 1, width - 2);
yMin = Math.max(roiY, 1);
yMax = Math.min(roiY + roiHeight - 1, height - 2);
}
public void setRoi(Roi roi) {
if (roi==null)
resetRoi();
else {
if ((roi instanceof PointRoi) && roi.size()==1) {
setMask(null);
Polygon p = roi.getPolygon();
setRoi(p.xpoints[0], p.ypoints[0], 1, 1);
} else {
setMask(roi.getMask());
setRoi(roi.getBounds());
}
}
}
public void setRoi(Polygon roi) {
if (roi==null)
{resetRoi(); return;}
Rectangle bounds = roi.getBounds();
for (int i=0; i<roi.npoints; i++) {
roi.xpoints[i] -= bounds.x;
roi.ypoints[i] -= bounds.y;
}
PolygonFiller pf = new PolygonFiller();
pf.setPolygon(roi.xpoints, roi.ypoints, roi.npoints);
ImageProcessor mask = pf.getMask(bounds.width, bounds.height);
setMask(mask);
setRoi(bounds);
for (int i=0; i<roi.npoints; i++) {
roi.xpoints[i] += bounds.x;
roi.ypoints[i] += bounds.y;
}
}
public void resetRoi() {
roiX=0; roiY=0; roiWidth=width; roiHeight=height;
xMin=1; xMax=width-2; yMin=1; yMax=height-2;
mask=null;
clipXMin=0; clipXMax=width-1; clipYMin=0; clipYMax=height-1;
}
public Rectangle getRoi() {
return new Rectangle(roiX, roiY, roiWidth, roiHeight);
}
public void setMask(ImageProcessor mask) {
this.mask = mask;
}
public ImageProcessor getMask() {
return mask;
}
public byte[] getMaskArray() {
return mask!=null?(byte[])mask.getPixels():null;
}
public void setProgressBar(ProgressBar pb) {
progressBar = pb;
}
public void setInterpolate(boolean interpolate) {
this.interpolate = interpolate;
if (interpolate)
interpolationMethod = useBicubic?BICUBIC:BILINEAR;
else
interpolationMethod = NONE;
}
public void setInterpolationMethod(int method) {
if (method<NONE || method>BICUBIC)
throw new IllegalArgumentException("Invalid interpolation method");
interpolationMethod = method;
interpolate = method!=NONE?true:false;
}
public int getInterpolationMethod() {
return interpolationMethod;
}
public static String[] getInterpolationMethods() {
if (interpolationMethods==null)
interpolationMethods = new String[] {"None", "Bilinear", "Bicubic"};
return interpolationMethods;
}
public boolean getInterpolate() {
return interpolate;
}
public boolean isKillable() {
return false;
}
private void process(int op, double value) {
double SCALE = 255.0/Math.log(255.0);
int v;
int[] lut = new int[256];
for (int i=0; i<256; i++) {
switch(op) {
case INVERT:
v = 255 - i;
break;
case FILL:
v = fgColor;
break;
case SET:
v = (int)value;
break;
case ADD:
v = i + (int)value;
break;
case MULT:
v = (int)Math.round(i * value);
break;
case AND:
v = i & (int)value;
break;
case OR:
v = i | (int)value;
break;
case XOR:
v = i ^ (int)value;
break;
case GAMMA:
v = (int)(Math.exp(Math.log(i/255.0)*value)*255.0);
break;
case LOG:
if (i==0)
v = 0;
else
v = (int)(Math.log(i) * SCALE);
break;
case EXP:
v = (int)(Math.exp(i/SCALE));
break;
case SQR:
v = i*i;
break;
case SQRT:
v = (int)Math.sqrt(i);
break;
case MINIMUM:
if (i<value)
v = (int)value;
else
v = i;
break;
case MAXIMUM:
if (i>value)
v = (int)value;
else
v = i;
break;
default:
v = i;
}
if (v < 0)
v = 0;
if (v > 255)
v = 255;
lut[i] = v;
}
applyTable(lut);
}
public double[] getLine(double x1, double y1, double x2, double y2) {
double dx = x2-x1;
double dy = y2-y1;
int n = (int)Math.round(Math.sqrt(dx*dx + dy*dy));
double xinc = n>0?dx/n:0;
double yinc = n>0?dy/n:0;
if (!((xinc==0&&n==height) || (yinc==0&&n==width)))
n++;
double[] data = new double[n];
double rx = x1;
double ry = y1;
if (interpolate) {
for (int i=0; i<n; i++) {
data[i] = getInterpolatedValue(rx, ry);
rx += xinc;
ry += yinc;
}
} else {
for (int i=0; i<n; i++) {
data[i] = getPixelValue((int)Math.round(rx), (int)Math.round(ry));
rx += xinc;
ry += yinc;
}
}
return data;
}
public void getRow(int x, int y, int[] data, int length) {
for (int i=0; i<length; i++)
data[i] = getPixel(x++, y);
}
public float[] getRow(int x, int y, float[] data, int length) {
if (data==null)
data = new float[length];
for (int i=0; i<length; i++)
data[i] = getf(x++, y);
return data;
}
public void getColumn(int x, int y, int[] data, int length) {
for (int i=0; i<length; i++)
data[i] = getPixel(x, y++);
}
public float[] getColumn(int x, int y, float[] data, int length) {
if (data==null)
data = new float[length];
for (int i=0; i<length; i++)
data[i] = getf(x, y++);
return data;
}
public void putRow(int x, int y, int[] data, int length) {
for (int i=0; i<length; i++)
putPixel(x++, y, data[i]);
}
public void putRow(int x, int y, float[] data, int length) {
for (int i=0; i<length; i++)
setf(x++, y, data[i]);
}
public void putColumn(int x, int y, int[] data, int length) {
for (int i=0; i<length; i++)
putPixel(x, y++, data[i]);
}
public void putColumn(int x, int y, float[] data, int length) {
for (int i=0; i<length; i++)
setf(x, y++, data[i]);
}
public void moveTo(int x, int y) {
cx = x;
cy = y;
}
public void setLineWidth(int width) {
lineWidth = width;
if (lineWidth<1) lineWidth = 1;
}
public int getLineWidth() {
return lineWidth;
}
public void lineTo(int x2, int y2) {
int xMin = clipXMin-lineWidth/2-1; int xMax = clipXMax+lineWidth/2+1;
int yMin = clipYMin-lineWidth/2-1;
int yMax = clipYMax+lineWidth/2+1;
int dx = x2-cx;
int dy = y2-cy;
int absdx = dx>=0?dx:-dx;
int absdy = dy>=0?dy:-dy;
int n = absdy>absdx?absdy:absdx;
double xinc = dx!=0 ? (double)dx/n : 0; double yinc = dy!=0 ? (double)dy/n : 0;
double x = cx;
double y = cy;
cx = x2; cy = y2; int i1 = 0;
if (dx>0)
i1 = Math.max(i1, (int)((xMin-x)/xinc));
else if (dx<0)
i1 = Math.max(i1, (int)((xMax-x)/xinc));
else if (x<xMin || x>xMax)
return; if (dy>0)
i1 = Math.max(i1, (int)((yMin-y)/yinc));
else if (dy<0)
i1 = Math.max(i1, (int)((yMax-y)/yinc));
else if (y<yMin || y>yMax)
return; int i2 = n;
if (dx>0)
i2 = Math.min(i2, (int)((xMax-x)/xinc));
else if (dx<0)
i2 = Math.min(i2, (int)((xMin-x)/xinc));
if (dy>0)
i2 = Math.min(i2, (int)((yMax-y)/yinc));
else if (dy<0)
i2 = Math.min(i2, (int)((yMin-y)/yinc));
x += i1*xinc;
y += i1*yinc;
for (int i=i1; i<=i2; i++) {
if (lineWidth==1)
drawPixel((int)Math.round(x), (int)Math.round(y));
else if (lineWidth==2)
drawDot2((int)Math.round(x), (int)Math.round(y));
else
drawDot((int)Math.round(x), (int)Math.round(y));
x += xinc;
y += yinc;
}
}
public void drawLine(int x1, int y1, int x2, int y2) {
moveTo(x1, y1);
lineTo(x2, y2);
}
public void drawLine4(int x1, int y1, int x2, int y2) {
int dx = Math.abs(x2 - x1);
int dy = Math.abs(y2 - y1);
int sgnX = x1 < x2 ? 1 : -1;
int sgnY = y1 < y2 ? 1 : -1;
int e = 0;
for (int i=0; i < dx+dy; i++) {
putPixel(x1, y1, fgColor);
int e1 = e + dy;
int e2 = e - dx;
if (Math.abs(e1) < Math.abs(e2)) {
x1 += sgnX;
e = e1;
} else {
y1 += sgnY;
e = e2;
}
}
putPixel(x2, y2, fgColor);
}
public void drawRect(int x, int y, int width, int height) {
if (width<1 || height<1)
return;
if (lineWidth==1) {
moveTo(x, y);
lineTo(x+width-1, y);
lineTo(x+width-1, y+height-1);
lineTo(x, y+height-1);
lineTo(x, y);
} else {
moveTo(x, y);
lineTo(x+width, y);
lineTo(x+width, y+height);
lineTo(x, y+height);
lineTo(x, y);
}
}
public void fillRect(int x, int y, int width, int height) {
setRoi(x, y, width, height);
fill();
resetRoi();
}
public void drawOval(int x, int y, int width, int height) {
if ((long)width*height>4L*this.width*this.height)
return;
OvalRoi oval = new OvalRoi(x, y, width, height);
drawPolygon(oval.getPolygon());
}
public void fillOval(int x, int y, int width, int height) {
if ((long)width*height>4L*this.width*this.height)
return;
OvalRoi oval = new OvalRoi(x, y, width, height);
fillPolygon(oval.getPolygon());
}
public void drawPolygon(Polygon p) {
moveTo(p.xpoints[0], p.ypoints[0]);
for (int i=0; i<p.npoints; i++)
lineTo(p.xpoints[i], p.ypoints[i]);
lineTo(p.xpoints[0], p.ypoints[0]);
}
public void fillPolygon(Polygon p) {
setRoi(p);
fill(getMask());
resetRoi();
}
public void drawDot2(int x, int y) {
drawPixel(x, y);
drawPixel(x-1, y);
drawPixel(x, y-1);
drawPixel(x-1, y-1);
}
public void drawDot(int xcenter, int ycenter) {
double r = lineWidth/2.0;
int xmin=(int)(xcenter-r+0.5), ymin=(int)(ycenter-r+0.5);
int xmax=xmin+lineWidth, ymax=ymin+lineWidth;
if (xmin<clipXMin || ymin<clipYMin || xmax>clipXMax || ymax>clipYMax ) {
double r2 = r*r;
r -= 0.5;
double xoffset=xmin+r, yoffset=ymin+r;
double xx, yy;
for (int y=ymin; y<ymax; y++) {
for (int x=xmin; x<xmax; x++) {
xx = x-xoffset; yy = y-yoffset;
if (xx*xx+yy*yy<=r2)
drawPixel(x, y);
}
}
} else {
if (dotMask==null || lineWidth!=dotMask.getWidth()) {
OvalRoi oval = new OvalRoi(0, 0, lineWidth, lineWidth);
dotMask = oval.getMask();
}
setRoi(xmin, ymin, lineWidth, lineWidth);
fill(dotMask);
roiX=0; roiY=0; roiWidth=width; roiHeight=height;
xMin=1; xMax=width-2; yMin=1; yMax=height-2;
mask=null;
}
}
private ImageProcessor dotMask;
private void setupFontMetrics() {
if (fontMetrics==null) {
fmImage=new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
fmGraphics = (Graphics2D)fmImage.getGraphics();
fmGraphics.setFont(font);
Java2.setAntialiasedText(fmGraphics, antialiasedText);
fontMetrics = fmGraphics.getFontMetrics(font);
}
}
public void drawString(String s) {
if (s==null || s.equals("")) return;
setupFontMetrics();
if (s.indexOf("\n")==-1)
drawString2(s);
else {
String[] s2 = Tools.split(s, "\n");
for (int i=0; i<s2.length; i++)
drawString2(s2[i]);
}
}
private void drawString2(String s) {
int w = getStringWidth(s);
int cxx = cx;
if (justification==CENTER_JUSTIFY)
cxx -= w/2;
else if (justification==RIGHT_JUSTIFY)
cxx -= w;
int h = fontMetrics.getHeight();
if (w<=0 || h<=0) return;
Image bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics g = bi.getGraphics();
FontMetrics metrics = g.getFontMetrics(font);
int fontHeight = metrics.getHeight();
int descent = metrics.getDescent();
g.setFont(font);
if (antialiasedText && cxx>=0 && cy-h>=0) {
Java2.setAntialiasedText(g, true);
setRoi(cxx, cy-h, w, h);
ImageProcessor ip = crop();
resetRoi();
if (ip.getWidth()==0||ip.getHeight()==0)
return;
g.drawImage(ip.createImage(), 0, 0, null);
g.setColor(drawingColor);
g.drawString(s, 0, h-descent);
g.dispose();
ip = new ColorProcessor(bi);
if (this instanceof ByteProcessor) {
ip = ip.convertToByte(false);
if (isInvertedLut()) ip.invert();
}
insert(ip, cxx, cy-h);
cy += h;
return;
}
Java2.setAntialiasedText(g, false);
g.setColor(Color.white);
g.fillRect(0, 0, w, h);
g.setColor(Color.black);
g.drawString(s, 0, h-descent);
g.dispose();
ImageProcessor ip = new ColorProcessor(bi);
ImageProcessor textMask = ip.convertToByte(false);
byte[] mpixels = (byte[])textMask.getPixels();
textMask.invert();
if (cxx<width && cy-h<height) {
setMask(textMask);
setRoi(cxx,cy-h,w,h);
fill(getMask());
}
resetRoi();
cy += h;
}
public void drawString(String s, int x, int y) {
moveTo(x, y);
drawString(s);
}
public void drawString(String s, int x, int y, Color background) {
Color foreground = drawingColor;
FontMetrics metrics = getFontMetrics();
int w = 0;
int h = metrics.getAscent() + metrics.getDescent();
int y2 = y;
if (s.indexOf("\n")!=-1) {
String[] s2 = Tools.split(s, "\n");
for (int i=0; i<s2.length; i++) {
int w2 = getStringWidth(s2[i]);
if (w2>w) w = w2;
}
int h2 = metrics.getHeight();
y2 += h2*(s2.length-1);
h += h2*(s2.length-1);
} else
w = getStringWidth(s);
int x2 = x;
if (justification==CENTER_JUSTIFY)
x2 -= w/2;
else if (justification==RIGHT_JUSTIFY)
x2 -= w;
setColor(background);
setRoi(x2, y2-h, w, h);
fill();
resetRoi();
setColor(foreground);
drawString(s, x, y);
}
public void setJustification(int justification) {
if (justification<LEFT_JUSTIFY || justification>RIGHT_JUSTIFY)
justification = LEFT_JUSTIFY;
this.justification = justification;
}
public void setFont(Font font) {
this.font = font;
boldFont = font.isBold();
setupFontMetrics();
fmGraphics.setFont(font);
Java2.setAntialiasedText(fmGraphics, antialiasedText);
fontMetrics = fmGraphics.getFontMetrics(font);
}
public void setFontSize(int size) {
setFont(font.deriveFont(font.getStyle(), size));
if (size>15)
setAntialiasedText(true);
}
public void setAntialiasedText(boolean antialiasedText) {
setupFontMetrics();
if (antialiasedText && (((this instanceof ByteProcessor)&&getMin()==0.0&&getMax()==255.0) || (this instanceof ColorProcessor)))
this.antialiasedText = true;
else
this.antialiasedText = false;
Java2.setAntialiasedText(fmGraphics, this.antialiasedText);
fontMetrics = fmGraphics.getFontMetrics(font);
}
public int getStringWidth(String s) {
Rectangle2D rect = getStringBounds(s);
return (int)Math.max(fontMetrics.getStringBounds(s, fmGraphics).getWidth(), rect.getX()+rect.getWidth());
}
public Rectangle getStringBounds(String s) {
setupFontMetrics();
GlyphVector gv = font.createGlyphVector(fmGraphics.getFontRenderContext(), s);
Rectangle2D rect = gv.getPixelBounds(null, 0.f, -fontMetrics.getDescent());
return new Rectangle((int)rect.getX(), (int)rect.getY(), (int)rect.getWidth(), (int)rect.getHeight());
}
public Font getFont() {
return font;
}
public FontMetrics getFontMetrics() {
setupFontMetrics();
return fontMetrics;
}
public void smooth() {
if (width>1)
filter(BLUR_MORE);
}
public void sharpen() {
if (width>1) {
int[] kernel = {-1, -1, -1,
-1, 12, -1,
-1, -1, -1};
convolve3x3(kernel);
}
}
public void findEdges() {
if (width>1)
filter(FIND_EDGES);
}
public abstract void flipVertical();
public void flipHorizontal() {
int[] col1 = new int[roiHeight];
int[] col2 = new int[roiHeight];
for (int x=0; x<roiWidth/2; x++) {
getColumn(roiX+x, roiY, col1, roiHeight);
getColumn(roiX+roiWidth-x-1, roiY, col2, roiHeight);
putColumn(roiX+x, roiY, col2, roiHeight);
putColumn(roiX+roiWidth-x-1, roiY, col1, roiHeight);
}
}
public ImageProcessor rotateRight() {
int width2 = height;
int height2 = width;
ImageProcessor ip2 = createProcessor(width2, height2);
int[] arow = new int[width];
for (int row=0; row<height; row++) {
getRow(0, row, arow, width);
ip2.putColumn(width2-row-1, 0, arow, height2);
}
return ip2;
}
public ImageProcessor rotateLeft() {
int width2 = height;
int height2 = width;
ImageProcessor ip2 = createProcessor(width2, height2);
int[] arow = new int[width];
int[] arow2 = new int[width];
for (int row=0; row<height; row++) {
getRow(0, row, arow, width);
for (int i=0; i<width; i++) {
arow2[i] = arow[width-i-1];
}
ip2.putColumn(row, 0, arow2, height2);
}
return ip2;
}
public void insert(ImageProcessor ip, int xloc, int yloc) {
copyBits(ip, xloc, yloc, Blitter.COPY);
}
public String toString() {
return ("ip[width="+width+", height="+height+", bits="+getBitDepth()+", min="+getMin()+", max="+getMax()+"]");
}
public void fill() {
process(FILL, 0.0);
}
public abstract void fill(ImageProcessor mask);
public void fill(Roi roi) {
if (roi!=null && roi.isLine()) {
if ((roi instanceof Line) && roi.getStrokeWidth()>1 && !(roi instanceof Arrow))
fillPolygon(roi.getPolygon());
else
roi.drawPixels(this);
return;
}
ImageProcessor m = getMask();
Rectangle r = getRoi();
setRoi(roi);
fill(getMask());
setMask(m);
setRoi(r);
}
public void fillOutside(Roi roi) {
if (roi==null || !roi.isArea()) return;
ImageProcessor m = getMask();
Rectangle r = getRoi();
ShapeRoi s1, s2;
if (roi instanceof ShapeRoi)
s1 = (ShapeRoi)roi;
else
s1 = new ShapeRoi(roi);
s2 = new ShapeRoi(new Roi(0,0, width, height));
setRoi(s2.xor(s1));
fill(getMask());
setMask(m);
setRoi(r);
}
public void draw(Roi roi) {
roi.drawPixels(this);
}
public void drawRoi(Roi roi) {
Image img = createImage();
Graphics g = img.getGraphics();
ij.ImagePlus imp = roi.getImage();
if (imp!=null) {
roi.setImage(null);
roi.drawOverlay(g);
roi.setImage(imp);
} else
roi.drawOverlay(g);
}
public void drawOverlay(Overlay overlay) {
Roi[] rois = overlay.toArray();
for (int i=0; i<rois.length; i++)
drawRoi(rois[i]);
}
public void setCalibrationTable(float[] cTable) {
this.cTable = cTable;
}
public float[] getCalibrationTable() {
return cTable;
}
public void setHistogramSize(int size) {
histogramSize = size;
if (histogramSize<1) histogramSize = 1;
}
public int getHistogramSize() {
return histogramSize;
}
public void setHistogramRange(double histMin, double histMax) {
if (histMin>histMax) {
histMin = 0.0;
histMax = 0.0;
}
histogramMin = histMin;
histogramMax = histMax;
}
public double getHistogramMin() {
return histogramMin;
}
public double getHistogramMax() {
return histogramMax;
}
public abstract Object getPixels();
public abstract Object getPixelsCopy();
public abstract int getPixel(int x, int y);
public abstract int get(int x, int y);
public abstract int get(int index);
public abstract void set(int x, int y, int value);
public abstract void set(int index, int value);
public abstract float getf(int x, int y);
public abstract float getf(int index);
public abstract void setf(int x, int y, float value);
public abstract void setf(int index, float value);
public int getPixelCount() {
return width*height;
}
public int[][] getIntArray() {
int[][] a = new int [width][height];
for(int y=0; y<height; y++) {
for(int x=0; x<width; x++)
a[x][y]=get(x,y);
}
return a;
}
public void setIntArray(int[][] a) {
for(int y=0; y<height; y++) {
for(int x=0; x<width; x++)
set(x, y, a[x][y]);
}
}
public float[][] getFloatArray() {
float[][] a = new float[width][height];
for(int y=0; y<height; y++) {
for(int x=0; x<width; x++)
a[x][y]=getf(x,y);
}
return a;
}
public void setFloatArray(float[][] a) {
for(int y=0; y<height; y++) {
for(int x=0; x<width; x++)
setf(x, y, a[x][y]);
}
}
public void getNeighborhood(int x, int y, double[][] arr) {
int nx=arr.length;
int ny=arr[0].length;
int nx2 = (nx-1)/2;
int ny2 = (ny-1)/2;
if (x>=nx2 && y>=ny2 && x<width-nx2-1 && y<height-ny2-1) {
int index = (y-ny2)*width + (x-nx2);
for (int y2=0; y2<ny; y2++) {
for (int x2=0; x2<nx; x2++)
arr[x2][y2] = getf(index++);
index += (width - nx);
}
} else {
for (int y2=0; y2<ny; y2++) {
for (int x2=0; x2<nx; x2++)
arr[x2][y2] = getPixelValue(x2, y2);
}
}
}
public int[] getPixel(int x, int y, int[] iArray) {
if (iArray==null) iArray = new int[1];
iArray[0] = getPixel(x, y);
return iArray;
}
public void putPixel(int x, int y, int[] iArray) {
putPixel(x, y, iArray[0]);
}
public abstract double getInterpolatedPixel(double x, double y);
public abstract int getPixelInterpolated(double x, double y);
public final double getInterpolatedValue(double x, double y) {
if (useBicubic)
return getBicubicInterpolatedPixel(x, y, this);
if (x<0.0 || x>=width-1.0 || y<0.0 || y>=height-1.0) {
if (x<-1.0 || x>=width || y<-1.0 || y>=height)
return 0.0;
else
return getInterpolatedEdgeValue(x, y);
}
int xbase = (int)x;
int ybase = (int)y;
double xFraction = x - xbase;
double yFraction = y - ybase;
if (xFraction<0.0) xFraction = 0.0;
if (yFraction<0.0) yFraction = 0.0;
double lowerLeft = getPixelValue(xbase, ybase);
double lowerRight = getPixelValue(xbase+1, ybase);
double upperRight = getPixelValue(xbase+1, ybase+1);
double upperLeft = getPixelValue(xbase, ybase+1);
double upperAverage = upperLeft + xFraction * (upperRight - upperLeft);
double lowerAverage = lowerLeft + xFraction * (lowerRight - lowerLeft);
return lowerAverage + yFraction * (upperAverage - lowerAverage);
}
public double getBicubicInterpolatedPixel(double x0, double y0, ImageProcessor ip2) {
int u0 = (int) Math.floor(x0); int v0 = (int) Math.floor(y0);
if (u0<=0 || u0>=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.get(u,v) * cubic(x0 - u);
}
q = q + p * cubic(y0 - v);
}
return q;
}
final double getBilinearInterpolatedPixel(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; public static final double cubic(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;
}
private final double getInterpolatedEdgeValue(double x, double y) {
int xbase = (int)x;
int ybase = (int)y;
double xFraction = x - xbase;
double yFraction = y - ybase;
if (xFraction<0.0) xFraction = 0.0;
if (yFraction<0.0) yFraction = 0.0;
double lowerLeft = getEdgeValue(xbase, ybase);
double lowerRight = getEdgeValue(xbase+1, ybase);
double upperRight = getEdgeValue(xbase+1, ybase+1);
double upperLeft = getEdgeValue(xbase, ybase+1);
double upperAverage = upperLeft + xFraction * (upperRight - upperLeft);
double lowerAverage = lowerLeft + xFraction * (lowerRight - lowerLeft);
return lowerAverage + yFraction * (upperAverage - lowerAverage);
}
private float getEdgeValue(int x, int y) {
if (x<=0) x = 0;
if (x>=width) x = width-1;
if (y<=0) y = 0;
if (y>=height) y = height-1;
return getPixelValue(x, y);
}
public abstract void putPixel(int x, int y, int value);
public double getValue(int x, int y) {
return getPixelValue(x,y);
}
public abstract float getPixelValue(int x, int y);
public abstract void putPixelValue(int x, int y, double value);
public abstract void drawPixel(int x, int y);
public abstract void setPixels(Object pixels);
public abstract void copyBits(ImageProcessor ip, int xloc, int yloc, int mode);
public abstract void applyTable(int[] lut);
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 subtract(double value) {
add(-value);
}
public void multiply(double value) {process(MULT, value);}
public void set(double value) {process(SET, value);}
public void and(int value) {process(AND, value);}
public void or(int value) {process(OR, value);}
public void xor(int value) {process(XOR, value);}
public void gamma(double value) {process(GAMMA, value);}
public void log() {process(LOG, 0.0);}
public void ln() {log();}
public void exp() {process(EXP, 0.0);}
public void sqr() {process(SQR, 0.0);}
public void sqrt() {process(SQRT, 0.0);}
public void abs() {}
public void min(double value) {process(MINIMUM, value);}
public void max(double value) {process(MAXIMUM, value);}
public abstract Image createImage();
public BufferedImage getBufferedImage() {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D)bi.getGraphics();
g.drawImage(createImage(), 0, 0, null);
return bi;
}
public abstract ImageProcessor createProcessor(int width, int height);
public abstract void snapshot();
public abstract void reset();
public abstract void swapPixelArrays();
public abstract void reset(ImageProcessor mask);
public abstract void setSnapshotPixels(Object pixels);
public abstract Object getSnapshotPixels();
public abstract void convolve3x3(int[] kernel);
public abstract void filter(int type);
public abstract void medianFilter();
public abstract void noise(double standardDeviation);
public abstract ImageProcessor crop();
public abstract void threshold(int level);
public abstract ImageProcessor duplicate();
public abstract void scale(double xScale, double yScale);
public abstract ImageProcessor resize(int dstWidth, int dstHeight);
public ImageProcessor resize(int dstWidth) {
return resize(dstWidth, (int)(dstWidth*((double)roiHeight/roiWidth)));
}
public ImageProcessor resize(int dstWidth, int dstHeight, boolean useAverging) {
Rectangle r = getRoi();
int rWidth = r.width;
int rHeight = r.height;
if ((dstWidth>=rWidth && dstHeight>=rHeight) || !useAverging)
return resize(dstWidth, dstHeight); else { ImageProcessor ip2 = createProcessor(dstWidth, dstHeight);
FloatProcessor fp = null;
int channels = getNChannels();
boolean showStatus = getProgressIncrement(width,height)>0;
boolean showProgress = showStatus && channels>1;
if (showProgress) showProgress(0.15);
for (int channelNumber=0; channelNumber<channels; channelNumber++) {
fp = toFloat(channelNumber, fp);
fp.setInterpolationMethod(interpolationMethod);
fp.setRoi(getRoi());
String msg = showStatus?" ("+(channelNumber+1)+"/"+channels+")":null;
FloatProcessor fp2 = fp.downsize(dstWidth, dstHeight, msg);
ip2.setPixels(channelNumber, fp2);
if (showProgress) showProgress(0.40+0.25*channelNumber);
}
if (showProgress) showProgress(1.0);
return ip2;
}
}
protected ImageProcessor resizeLinearly(int width2, int height2) {
int bitDepth = getBitDepth();
ImageProcessor ip1 = this;
boolean rotate = width==1;
if (rotate) {
ip1=ip1.rotateLeft();
int w2 = width2;
width2 = height2;
height2 = w2;
}
ip1 = ip1.convertToFloat();
int width1 = ip1.getWidth();
int height1 = ip1.getHeight();
ImageProcessor ip2 = ip1.createProcessor(width2, height2);
double scale = (double)(width1-1)/(width2-1);
float[] data1 = new float[width1];
float[] data2 = new float[width2];
ip1.getRow(0, 0, data1, width1);
double fraction;
for (int x=0; x<width2; x++) {
int x1 = (int)(x*scale);
int x2 = x1+1;
if (x2==width1) x2=width1-1;
fraction = x*scale - x1;
data2[x] = (float)((1.0-fraction)*data1[x1] + fraction*data1[x2]);
}
for (int y=0; y<height2; y++)
ip2.putRow(0, y, data2, width2);
if (bitDepth==8)
ip2 = ip2.convertToByte(false);
else if (bitDepth==16)
ip2 = ip2.convertToShort(false);
if (rotate)
ip2=ip2.rotateRight();
return ip2;
}
public ImageProcessor bin(int shrinkFactor) {
return new Binner().shrink(this, shrinkFactor, shrinkFactor, 0);
}
public abstract void rotate(double angle);
public void translate(double xOffset, double yOffset) {
ImageProcessor ip2 = this.duplicate();
ip2.setBackgroundValue(0.0);
boolean integerOffsets = xOffset==(int)xOffset && yOffset==(int)yOffset;
if (integerOffsets || interpolationMethod==NONE) {
for (int y=roiY; y<(roiY + roiHeight); y++) {
for (int x=roiX; x<(roiX + roiWidth); x++)
putPixel(x, y, ip2.getPixel(x-(int)xOffset, y-(int)yOffset));
}
} else {
if (interpolationMethod==BICUBIC && (this instanceof ColorProcessor))
((ColorProcessor)this).filterRGB(ColorProcessor.RGB_TRANSLATE, xOffset, yOffset);
else {
for (int y=roiY; y<(roiY + roiHeight); y++) {
for (int x=roiX; x<(roiX + roiWidth); x++)
putPixel(x, y, ip2.getPixelInterpolated(x-xOffset, y-yOffset));
}
}
}
}
public void translate(int xOffset, int yOffset, boolean eraseBackground) {
translate(xOffset, yOffset);
}
public abstract int[] getHistogram();
public int[] getHistogram(int nBins) {
ImageProcessor ip;
if (((this instanceof ByteProcessor)||(this instanceof ColorProcessor)) && nBins!=256)
ip = convertToShort(false);
else
ip = this;
ip.setHistogramSize(nBins);
ip.setHistogramRange(0.0, 0.0);
ImageStatistics stats = ImageStatistics.getStatistics(ip);
ip.setHistogramSize(256);
return stats.histogram;
}
public abstract void erode();
public abstract void dilate();
public void setLutAnimation(boolean lutAnimation) {
this.lutAnimation = lutAnimation;
}
void resetPixels(Object pixels) {
if (pixels==null) {
if (img!=null) {
img.flush();
img = null;
}
}
}
public ImageProcessor convertToByte(boolean doScaling) {
TypeConverter tc = new TypeConverter(this, doScaling);
return tc.convertToByte();
}
public ImageProcessor convertToShort(boolean doScaling) {
TypeConverter tc = new TypeConverter(this, doScaling);
return tc.convertToShort();
}
public ImageProcessor convertToFloat() {
TypeConverter tc = new TypeConverter(this, false);
return tc.convertToFloat(cTable);
}
public ImageProcessor convertToRGB() {
TypeConverter tc = new TypeConverter(this, true);
return tc.convertToRGB();
}
public ByteProcessor convertToByteProcessor() {
return convertToByteProcessor(true);
}
public ByteProcessor convertToByteProcessor(boolean scale) {
ByteProcessor bp;
if (this instanceof ByteProcessor)
bp = (ByteProcessor)this.duplicate();
else
bp = (ByteProcessor)this.convertToByte(scale);
return bp;
}
public ShortProcessor convertToShortProcessor() {
return convertToShortProcessor(true);
}
public ShortProcessor convertToShortProcessor(boolean scale) {
ShortProcessor sp;
if (this instanceof ShortProcessor)
sp = (ShortProcessor)this.duplicate();
else
sp = (ShortProcessor)this.convertToShort(scale);
return sp;
}
public FloatProcessor convertToFloatProcessor() {
FloatProcessor fp;
if (this instanceof FloatProcessor)
fp = (FloatProcessor)this.duplicate();
else
fp = (FloatProcessor)this.convertToFloat();
return fp;
}
public ColorProcessor convertToColorProcessor() {
ColorProcessor cp;
if (this instanceof ColorProcessor)
cp = (ColorProcessor)this.duplicate();
else
cp = (ColorProcessor)this.convertToRGB();
return cp;
}
public abstract void convolve(float[] kernel, int kernelWidth, int kernelHeight);
public void autoThreshold() {
threshold(getAutoThreshold());
}
public int getAutoThreshold() {
return getAutoThreshold(getHistogram());
}
public int getAutoThreshold(int[] histogram) {
int level;
int maxValue = histogram.length - 1;
double result, sum1, sum2, sum3, sum4;
int count0 = histogram[0];
histogram[0] = 0; int countMax = histogram[maxValue];
histogram[maxValue] = 0;
int min = 0;
while ((histogram[min]==0) && (min<maxValue))
min++;
int max = maxValue;
while ((histogram[max]==0) && (max>0))
max--;
if (min>=max) {
histogram[0]= count0; histogram[maxValue]=countMax;
level = histogram.length/2;
return level;
}
int movingIndex = min;
int inc = Math.max(max/40, 1);
do {
sum1=sum2=sum3=sum4=0.0;
for (int i=min; i<=movingIndex; i++) {
sum1 += (double)i*histogram[i];
sum2 += histogram[i];
}
for (int i=(movingIndex+1); i<=max; i++) {
sum3 += (double)i*histogram[i];
sum4 += histogram[i];
}
result = (sum1/sum2 + sum3/sum4)/2.0;
movingIndex++;
} while ((movingIndex+1)<=result && movingIndex<max-1);
histogram[0]= count0; histogram[maxValue]=countMax;
level = (int)Math.round(result);
return level;
}
public void setClipRect(Rectangle clipRect) {
if (clipRect==null) {
clipXMin = 0;
clipXMax = width-1;
clipYMin = 0;
clipYMax = height-1;
} else {
clipXMin = clipRect.x;
clipXMax = clipRect.x + clipRect.width - 1;
clipYMin = clipRect.y;
clipYMax = clipRect.y + clipRect.height - 1;
if (clipXMin<0) clipXMin = 0;
if (clipXMax>=width) clipXMax = width-1;
if (clipYMin<0) clipYMin = 0;
if (clipYMax>=height) clipYMax = height-1;
}
}
protected String maskSizeError(ImageProcessor mask) {
return "Mask size ("+mask.getWidth()+"x"+mask.getHeight()+") != ROI size ("+
roiWidth+"x"+roiHeight+")";
}
protected SampleModel getIndexSampleModel() {
if (sampleModel==null) {
IndexColorModel icm = getDefaultColorModel();
WritableRaster wr = icm.createCompatibleWritableRaster(1, 1);
sampleModel = wr.getSampleModel();
sampleModel = sampleModel.createCompatibleSampleModel(width, height);
}
return sampleModel;
}
public IndexColorModel getDefaultColorModel() {
if (defaultColorModel==null) {
byte[] r = new byte[256];
byte[] g = new byte[256];
byte[] b = new byte[256];
for(int i=0; i<256; i++) {
r[i]=(byte)i;
g[i]=(byte)i;
b[i]=(byte)i;
}
defaultColorModel = new IndexColorModel(8, 256, r, g, b);
}
return defaultColorModel;
}
public void setSnapshotCopyMode(boolean b) {
snapshotCopyMode = b;
}
public int getNChannels() {
return 1;
}
public abstract FloatProcessor toFloat(int channelNumber, FloatProcessor fp);
public abstract void setPixels(int channelNumber, FloatProcessor fp);
public double minValue() {
return 0.0;
}
public double maxValue() {
return 255.0;
}
public void updateComposite(int[] rgbPixels, int mode) {
int redValue, greenValue, blueValue;
int size = width*height;
if (bytes==null || !lutAnimation)
bytes = create8BitImage();
if (cm==null)
makeDefaultColorModel();
if (reds==null || cm!=cm2)
updateLutBytes();
switch (mode) {
case UPDATE_RED: for (int i=0; i<size; i++)
rgbPixels[i] = (rgbPixels[i]&0xff00ffff) | (reds[bytes[i]&0xff]);
break;
case UPDATE_GREEN: for (int i=0; i<size; i++)
rgbPixels[i] = (rgbPixels[i]&0xffff00ff) | (greens[bytes[i]&0xff]);
break;
case UPDATE_BLUE: for (int i=0; i<size; i++)
rgbPixels[i] = (rgbPixels[i]&0xffffff00) | blues[bytes[i]&0xff];
break;
case SET_FIRST_CHANNEL:
for (int i=0; i<size; i++) {
int index = bytes[i]&0xff;
rgbPixels[i] = reds[index] | greens[index] | blues[index];
}
break;
case SUM_PROJECTION:
for (int i=0; i<size; i++) {
int pixel = rgbPixels[i];
redValue = (pixel&0x00ff0000) + reds[bytes[i]&0xff];
greenValue = (pixel&0x0000ff00) + greens[bytes[i]&0xff];
blueValue = (pixel&0x000000ff) + blues[bytes[i]&0xff];
if (redValue>16711680) redValue = 16711680;
if (greenValue>65280) greenValue = 65280;
if (blueValue>255) blueValue = 255;
rgbPixels[i] = redValue | greenValue | blueValue;
}
break;
case MAX_PROJECTION:
for (int i=0; i<size; i++) {
int pixel = rgbPixels[i];
int index = bytes[i]&0xff;
redValue = reds[index]&0x00ff0000;
if (redValue>(pixel&0x00ff0000))
rgbPixels[i] = (rgbPixels[i]&0xff00ffff) | redValue;
greenValue = greens[index]&0x0000ff00;
if (greenValue>(pixel&0x0000ff00))
rgbPixels[i] = (rgbPixels[i]&0xffff00ff) | greenValue;
blueValue = blues[index]&0xff;
if (blueValue>(pixel&0xff))
rgbPixels[i] = (rgbPixels[i]&0xffffff00) | blueValue;
}
break;
case MIN_PROJECTION:
int pixel;
for (int i=0; i<size; i++) {
pixel = rgbPixels[i];
int index = bytes[i]&0xff;
redValue = reds[index]&0x00ff0000;
if (redValue<(pixel&0x00ff0000))
rgbPixels[i] = (rgbPixels[i]&0xff00ffff) | redValue;
greenValue = greens[index]&0x0000ff00;
if (greenValue<(pixel&0x0000ff00))
rgbPixels[i] = (rgbPixels[i]&0xffff00ff) | greenValue;
blueValue = blues[index]&0xff;
if (blueValue<(pixel&0xff))
rgbPixels[i] = (rgbPixels[i]&0xffffff00) | blueValue;
}
break;
}
lutAnimation = false;
}
byte[] create8BitImage() {return null;}
private byte[] bytes;
private int[] reds, greens, blues;
void updateLutBytes() {
IndexColorModel icm = (IndexColorModel)cm;
int mapSize = icm.getMapSize();
if (reds==null || reds.length!=mapSize) {
reds = new int[mapSize];
greens = new int[mapSize];
blues = new int[mapSize];
}
byte[] tmp = new byte[mapSize];
icm.getReds(tmp);
for (int i=0; i<mapSize; i++) reds[i] = (tmp[i]&0xff)<<16;
icm.getGreens(tmp);
for (int i=0; i<mapSize; i++) greens[i] = (tmp[i]&0xff)<<8;
icm.getBlues(tmp);
for (int i=0; i<mapSize; i++) blues[i] = tmp[i]&0xff;
cm2 = cm;
}
public static void setOverColor(int red, int green, int blue) {
overRed=red; overGreen=green; overBlue=blue;
}
public static void setUnderColor(int red, int green, int blue) {
underRed=red; underGreen=green; underBlue=blue;
}
public boolean isBinary() {
return false;
}
public boolean isSigned16Bit() {
return false;
}
public static void setUseBicubic(boolean b) {
useBicubic = b;
}
public ImageStatistics getStats() {
return ImageStatistics.getStatistics(this);
}
public ImageStatistics getStatistics() {
return ImageStatistics.getStatistics(this, Measurements.ALL_STATS, null);
}
public void blurGaussian(double sigma) {
resetRoi();
GaussianBlur gb = new GaussianBlur();
gb.showProgress(false);
gb.blurGaussian(this, sigma);
}
public void applyMacro(String macro) {
ij.plugin.filter.ImageMath.applyMacro(this, macro, false);
}
public int getSliceNumber() {
if (sliceNumber<1)
return 1;
else
return sliceNumber;
}
public void setSliceNumber(int slice) {
sliceNumber = slice;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
public void setOverlay(Overlay overlay) {
this.overlay = overlay;
}
public Overlay getOverlay() {
return overlay;
}
protected int getProgressIncrement(int w, int h) {
if (progressBar==null)
return 0;
int inc = 0;
int threshold = 15000000;
if (interpolationMethod==BICUBIC)
threshold = 5000000;
boolean isBig = w*h>threshold;
if (isBig) {
inc = h/30;
if (inc<1) inc=1;
}
return inc;
}
public static void setRandomSeed(double randomSeed) {
seed = randomSeed;
}
public ByteProcessor createMask() {
return null;
}
protected IndexColorModel getThresholdColorModel() {
byte[] r = new byte[256];
byte[] g = new byte[256];
byte[] b = new byte[256];
for(int i=0; i<255; i++) {
r[i]=(byte)i;
g[i]=(byte)i;
b[i]=(byte)i;
}
r[255] = (byte)255;
g[255] = (byte)0;
b[255] = (byte)0;
return new IndexColorModel(8, 256, r, g, b);
}
}