
ij.process.ColorProcessor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ij Show documentation
Show all versions of ij Show documentation
ImageJ is an open source Java image processing program inspired by NIH Image for the Macintosh.
The newest version!
package ij.process;
import java.util.*;
import java.awt.*;
import java.awt.image.*;
import ij.gui.*;
import ij.ImageStack;
/**
This is an 32-bit RGB image and methods that operate on that image.. Based on the ImageProcessor class from
"KickAss Java Programming" by Tonny Espeset (1996).
*/
public class ColorProcessor extends ImageProcessor {
protected int[] pixels;
protected int[] snapshotPixels = null;
private int bgColor = 0xffffffff; //white
protected int min=0, max=255;
private WritableRaster rgbRaster;
private SampleModel rgbSampleModel;
private boolean caSnapshot;
// Weighting factors used by getPixelValue(), getHistogram() and convertToByte().
// Enable "Weighted RGB Conversion" in Edit/Options/Conversions
// to use 0.299, 0.587 and 0.114.
private static double rWeight=1d/3d, gWeight=1d/3d, bWeight=1d/3d;
private double[] weights; // Overrides rWeight, etc. when set by setWeights()
/**Creates a ColorProcessor from an AWT Image or BufferedImage. */
public ColorProcessor(Image img) {
width = img.getWidth(null);
height = img.getHeight(null);
pixels = new int[width * height];
PixelGrabber pg = new PixelGrabber(img, 0, 0, width, height, pixels, 0, width);
try {
pg.grabPixels();
} catch (InterruptedException e){};
createColorModel();
fgColor = 0xff000000; //black
resetRoi();
}
/**Creates a blank ColorProcessor of the specified dimensions. */
public ColorProcessor(int width, int height) {
this(width, height, new int[width*height]);
}
/**Creates a ColorProcessor from a pixel array. */
public ColorProcessor(int width, int height, int[] pixels) {
if (pixels!=null && width*height!=pixels.length)
throw new IllegalArgumentException(WRONG_LENGTH);
this.width = width;
this.height = height;
createColorModel();
fgColor = 0xff000000; //black
resetRoi();
this.pixels = pixels;
}
void createColorModel() {
cm = new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
}
public Image createImage() {
return createBufferedImage();
}
Image createBufferedImage() {
if (rgbSampleModel==null)
rgbSampleModel = getRGBSampleModel();
if (rgbRaster==null) {
DataBuffer dataBuffer = new DataBufferInt(pixels, width*height, 0);
rgbRaster = Raster.createWritableRaster(rgbSampleModel, dataBuffer, null);
}
if (image==null) {
if (cm==null) createColorModel();
image = new BufferedImage(cm, rgbRaster, false, null);
}
return image;
}
SampleModel getRGBSampleModel() {
WritableRaster wr = cm.createCompatibleWritableRaster(1, 1);
SampleModel sampleModel = wr.getSampleModel();
sampleModel = sampleModel.createCompatibleSampleModel(width, height);
return sampleModel;
}
public void setColorModel(ColorModel cm) {
if (cm!=null && (cm instanceof IndexColorModel))
throw new IllegalArgumentException("RGB images do not support IndexColorModels");
this.cm = cm;
rgbSampleModel = null;
rgbRaster = null;
}
/** Returns a new, blank ColorProcessor with the specified width and height. */
public ImageProcessor createProcessor(int width, int height) {
ImageProcessor ip2 = new ColorProcessor(width, height);
ip2.setInterpolationMethod(interpolationMethod);
return ip2;
}
public Color getColor(int x, int y) {
int c = pixels[y*width+x];
int r = (c&0xff0000)>>16;
int g = (c&0xff00)>>8;
int b = c&0xff;
return new Color(r,g,b);
}
/** Sets the foreground color. */
public void setColor(Color color) {
fgColor = color.getRGB();
drawingColor = color;
}
/** Sets the background fill/draw color. */
public void setBackgroundColor(Color color) {
setBackgroundValue(color.getRGB());
}
/** Sets the fill/draw color, where color
is an RGB int. */
public void setColor(int color) {
fgColor = color;
}
/** Sets the default fill/draw value, where value
is interpreted as an RGB int. */
public void setValue(double value) {
fgColor = (int)value;
}
/** Returns the foreground fill/draw value. */
public double getForegroundValue() {
return fgColor;
}
/** Sets the background fill value, where value
is interpreted as an RGB int. */
public void setBackgroundValue(double value) {
bgColor = (int)value;
}
/** Returns the background fill value. */
public double getBackgroundValue() {
return bgColor;
}
/** Returns the smallest displayed pixel value. */
public double getMin() {
return min;
}
/** Returns the largest displayed pixel value. */
public double getMax() {
return max;
}
/** Uses a table look-up to map the pixels in this image from min-max to 0-255. */
public void setMinAndMax(double min, double max) {
setMinAndMax(min, max, 7);
}
public void setMinAndMax(double min, double max, int channels) {
if (max 255)
v = 255;
lut[i] = v;
}
reset();
if (channels==7)
applyTable(lut);
else
applyTable(lut, channels);
}
public void snapshot() {
snapshotWidth = width;
snapshotHeight = height;
if (snapshotPixels==null || (snapshotPixels!=null && snapshotPixels.length!=pixels.length))
snapshotPixels = new int[width * height];
System.arraycopy(pixels, 0, snapshotPixels, 0, width*height);
caSnapshot = false;
}
public void reset() {
if (snapshotPixels!=null)
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++;
}
}
}
/** Used by the ContrastAdjuster */
public void caSnapshot(boolean set) {
caSnapshot = set;
}
/** Used by the ContrastAdjuster */
public boolean caSnapshot() {
return caSnapshot;
}
/** Swaps the pixel and snapshot (undo) arrays. */
public void swapPixelArrays() {
if (snapshotPixels==null)
return;
int pixel;
for (int i=0; i=0 && x=0 && y>16;
iArray[1] = (c&0xff00)>>8;
iArray[2] = c&0xff;
return iArray;
}
/** Sets a pixel in the image using a 3 element (R, G and B)
int array of samples. */
public final void putPixel(int x, int y, int[] iArray) {
int r=iArray[0], g=iArray[1], b=iArray[2];
putPixel(x, y, (r<<16)+(g<<8)+b);
}
/** Calls getPixelValue(x,y). */
public double getInterpolatedPixel(double x, double y) {
int ix = (int)(x+0.5);
int iy = (int)(y+0.5);
if (ix<0) ix = 0;
if (ix>=width) ix = width-1;
if (iy<0) iy = 0;
if (iy>=height) iy = height-1;
return getPixelValue(ix, iy);
}
final public int getPixelInterpolated(double x,double y) {
if (x<0.0 || y<0.0 || x>=width-1 || y>=height-1)
return 0;
else
return getInterpolatedPixel(x, y, pixels);
}
/** Stores the specified value at (x,y). */
public final void putPixel(int x, int y, int value) {
if (x>=0 && x=0 && y=0 && x=0 && y255.0)
value = 255;
else if (value<0.0)
value = 0.0;
int gray = (int)(value+0.5);
pixels[y*width + x] = 0xff000000 + (gray<<16) + (gray<<8) + gray;
}
}
/** Converts the specified pixel to grayscale using the
formula g=(r+g+b)/3 and returns it as a float.
Call setWeightingFactors() to specify different conversion
factors. */
public float getPixelValue(int x, int y) {
if (x>=0 && x=0 && y>16;
int g = (c&0xff00)>>8;
int b = c&0xff;
if (weights!=null)
return (float)(r*weights[0] + g*weights[1] + b*weights[2]);
else
return (float)(r*rWeight + g*gWeight + b*bWeight);
}
else
return Float.NaN;
}
/** Draws a pixel in the current foreground color. */
public void drawPixel(int x, int y) {
if (x>=clipXMin && x<=clipXMax && y>=clipYMin && y<=clipYMax)
pixels[y*width + x] = fgColor;
}
/** Returns a reference to the int array containing
this image's pixel data. */
public Object getPixels() {
return (Object)pixels;
}
public void setPixels(Object pixels) {
this.pixels = (int[])pixels;
resetPixels(pixels);
if (pixels==null)
snapshotPixels = null;
rgbRaster = null;
image = null;
caSnapshot = false;
}
/** Returns hue, saturation and brightness in 3 byte arrays. */
public void getHSB(byte[] H, byte[] S, byte[] B) {
int c, r, g, b;
float[] hsb = new float[3];
for (int i=0; i < width*height; i++) {
c = pixels[i];
r = (c&0xff0000)>>16;
g = (c&0xff00)>>8;
b = c&0xff;
hsb = Color.RGBtoHSB(r, g, b, hsb);
H[i] = (byte)((int)(hsb[0]*255.0));
S[i] = (byte)((int)(hsb[1]*255.0));
B[i] = (byte)((int)(hsb[2]*255.0));
}
}
/** Returns hue, saturation and brightness in 3 float arrays. */
public void getHSB(float[] H, float[] S, float[] B) {
int c, r, g, b;
float[] hsb = new float[3];
for (int i=0; i < width*height; i++) {
c = pixels[i];
r = (c&0xff0000)>>16;
g = (c&0xff00)>>8;
b = c&0xff;
hsb = Color.RGBtoHSB(r, g, b, hsb);
H[i] = hsb[0];
S[i] = hsb[1];
B[i] = hsb[2];
}
}
/** Returns an ImageStack with three 8-bit slices,
representing hue, saturation and brightness */
public ImageStack getHSBStack() {
int width = getWidth();
int height = getHeight();
byte[] H = new byte[width*height];
byte[] S = new byte[width*height];
byte[] B = new byte[width*height];
getHSB(H, S, B);
ColorModel cm = getDefaultColorModel();
ImageStack stack = new ImageStack(width, height, cm);
stack.addSlice("Hue", H);
stack.addSlice("Saturation", S);
stack.addSlice("Brightness", B);
return stack;
}
/** Returns an ImageStack with three 32-bit slices,
representing hue, saturation and brightness */
public ImageStack getHSB32Stack() {
int width = getWidth();
int height = getHeight();
float[] H = new float[width*height];
float[] S = new float[width*height];
float[] B = new float[width*height];
getHSB(H, S, B);
ColorModel cm = getDefaultColorModel();
ImageStack stack = new ImageStack(width, height, cm);
stack.addSlice("Hue", H);
stack.addSlice("Saturation", S);
stack.addSlice("Brightness", B);
return stack;
}
/** Returns brightness as a FloatProcessor. */
public FloatProcessor getBrightness() {
int c, r, g, b;
int size = width*height;
float[] brightness = new float[size];
float[] hsb = new float[3];
for (int i=0; i>16;
g = (c&0xff00)>>8;
b = c&0xff;
hsb = Color.RGBtoHSB(r, g, b, hsb);
brightness[i] = hsb[2];
}
return new FloatProcessor(width, height, brightness, null);
}
/** Returns the red, green and blue planes as 3 byte arrays. */
public void getRGB(byte[] R, byte[] G, byte[] B) {
int c, r, g, b;
for (int i=0; i < width*height; i++) {
c = pixels[i];
r = (c&0xff0000)>>16;
g = (c&0xff00)>>8;
b = c&0xff;
R[i] = (byte)r;
G[i] = (byte)g;
B[i] = (byte)b;
}
}
/** Returns the specified plane (1=red, 2=green, 3=blue, 4=alpha) as a byte array. */
public byte[] getChannel(int channel) {
ByteProcessor bp = getChannel(channel, null);
return (byte[])bp.getPixels();
}
/** Returns the specified plane (1=red, 2=green, 3=blue, 4=alpha) as a ByteProcessor. */
public ByteProcessor getChannel(int channel, ByteProcessor bp) {
int size = width*height;
if (bp == null || bp.getWidth()!=width || bp.getHeight()!=height)
bp = new ByteProcessor(width, height);
byte[] bPixels = (byte[])bp.getPixels();
int shift = 16 - 8*(channel-1);
if (channel==4) shift=24;
for (int i=0; i>shift);
return bp;
}
/** Sets the pixels of one color channel from a ByteProcessor.
* @param channel Determines the color channel, 1=red, 2=green, 3=blue, 4=alpha
* @param bp The ByteProcessor where the image data are read from.
*/
public void setChannel(int channel, ByteProcessor bp) {
byte[] bPixels = (byte[])bp.getPixels();
int value;
int size = width*height;
int shift = 16 - 8*(channel-1);
if (channel==4) shift=24;
int resetMask = 0xffffffff^(255<>16;
g = (c&0xff00)>>8;
b = c&0xff;
hsb = Color.RGBtoHSB(r, g, b, hsb);
float bvalue = brightness[i];
if (bvalue<0f) bvalue = 0f;
if (bvalue>1.0f) bvalue = 1.0f;
pixels[i] = Color.HSBtoRGB(hsb[0], hsb[1], bvalue);
}
}
/** 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.convertToRGB();
new ColorBlitter(this).copyBits(ip, xloc, yloc, mode);
}
/* Filters start here */
public void applyTable(int[] lut) {
int c, r, g, b;
for (int y=roiY; y<(roiY+roiHeight); y++) {
int i = y * width + roiX;
for (int x=roiX; x<(roiX+roiWidth); x++) {
c = pixels[i];
r = lut[(c&0xff0000)>>16];
g = lut[(c&0xff00)>>8];
b = lut[c&0xff];
pixels[i] = 0xff000000 + (r<<16) + (g<<8) + b;
i++;
}
}
}
public void applyTable(int[] lut, int channels) {
int c, r=0, g=0, b=0;
for (int y=roiY; y<(roiY+roiHeight); y++) {
int i = y * width + roiX;
for (int x=roiX; x<(roiX+roiWidth); x++) {
c = pixels[i];
if (channels==4) {
r = lut[(c&0xff0000)>>16];
g = (c&0xff00)>>8;
b = c&0xff;
} else if (channels==2) {
r = (c&0xff0000)>>16;
g = lut[(c&0xff00)>>8];
b = c&0xff;
} else if (channels==1) {
r = (c&0xff0000)>>16;
g = (c&0xff00)>>8;
b = lut[c&0xff];
} else if ((channels&6)==6) {
r = lut[(c&0xff0000)>>16];
g = lut[(c&0xff00)>>8];
b = c&0xff;
} else if ((channels&5)==5) {
r = lut[(c&0xff0000)>>16];
g = (c&0xff00)>>8;
b = lut[c&0xff];
} else if ((channels&3)==3) {
r = (c&0xff0000)>>16;
g = lut[(c&0xff00)>>8];
b = lut[c&0xff];
}
pixels[i] = 0xff000000 + (r<<16) + (g<<8) + b;
i++;
}
}
}
/** Fills the current rectangular ROI. */
public void fill() {
for (int y=roiY; y<(roiY+roiHeight); y++) {
int i = y * width + roiX;
for (int x=roiX; x<(roiX+roiWidth); x++)
pixels[i++] = fgColor;
}
}
public static final int RGB_NOISE=0, RGB_MEDIAN=1, RGB_FIND_EDGES=2,
RGB_ERODE=3, RGB_DILATE=4, RGB_THRESHOLD=5, RGB_ROTATE=6,
RGB_SCALE=7, RGB_RESIZE=8, RGB_TRANSLATE=9, RGB_MIN=10, RGB_MAX=11;
/** Performs the specified filter on the red, green and blue planes of this image. */
public void filterRGB(int type, double arg) {
filterRGB(type, arg, 0.0);
}
final ImageProcessor filterRGB(int type, double arg, double arg2) {
showProgress(0.01);
byte[] R = new byte[width*height];
byte[] G = new byte[width*height];
byte[] B = new byte[width*height];
getRGB(R, G, B);
Rectangle roi = new Rectangle(roiX, roiY, roiWidth, roiHeight);
ByteProcessor r = new ByteProcessor(width, height, R, null);
r.setRoi(roi);
ByteProcessor g = new ByteProcessor(width, height, G, null);
g.setRoi(roi);
ByteProcessor b = new ByteProcessor(width, height, B, null);
b.setRoi(roi);
r.setBackgroundValue((bgColor&0xff0000)>>16);
g.setBackgroundValue((bgColor&0xff00)>>8);
b.setBackgroundValue(bgColor&0xff);
r.setInterpolationMethod(interpolationMethod);
g.setInterpolationMethod(interpolationMethod);
b.setInterpolationMethod(interpolationMethod);
showProgress(0.15);
switch (type) {
case RGB_NOISE:
r.noise(arg); showProgress(0.40);
g.noise(arg); showProgress(0.65);
b.noise(arg); showProgress(0.90);
break;
case RGB_MEDIAN:
r.medianFilter(); showProgress(0.40);
g.medianFilter(); showProgress(0.65);
b.medianFilter(); showProgress(0.90);
break;
case RGB_FIND_EDGES:
r.findEdges(); showProgress(0.40);
g.findEdges(); showProgress(0.65);
b.findEdges(); showProgress(0.90);
break;
case RGB_ERODE:
r.erode(); showProgress(0.40);
g.erode(); showProgress(0.65);
b.erode(); showProgress(0.90);
break;
case RGB_DILATE:
r.dilate(); showProgress(0.40);
g.dilate(); showProgress(0.65);
b.dilate(); showProgress(0.90);
break;
case RGB_THRESHOLD:
r.autoThreshold(); showProgress(0.40);
g.autoThreshold(); showProgress(0.65);
b.autoThreshold(); showProgress(0.90);
break;
case RGB_ROTATE:
ij.IJ.showStatus("Rotating red");
r.rotate(arg); showProgress(0.40);
ij.IJ.showStatus("Rotating green");
g.rotate(arg); showProgress(0.65);
ij.IJ.showStatus("Rotating blue");
b.rotate(arg); showProgress(0.90);
break;
case RGB_SCALE:
ij.IJ.showStatus("Scaling red");
r.scale(arg, arg2); showProgress(0.40);
ij.IJ.showStatus("Scaling green");
g.scale(arg, arg2); showProgress(0.65);
ij.IJ.showStatus("Scaling blue");
b.scale(arg, arg2); showProgress(0.90);
break;
case RGB_RESIZE:
int w=(int)arg, h=(int)arg2;
ij.IJ.showStatus("Resizing red");
ImageProcessor r2 = r.resize(w, h); showProgress(0.40);
ij.IJ.showStatus("Resizing green");
ImageProcessor g2 = g.resize(w, h); showProgress(0.65);
ij.IJ.showStatus("Resizing blue");
ImageProcessor b2 = b.resize(w, h); showProgress(0.90);
R = (byte[])r2.getPixels();
G = (byte[])g2.getPixels();
B = (byte[])b2.getPixels();
ColorProcessor ip2 = new ColorProcessor(w, h);
ip2.setRGB(R, G, B);
showProgress(1.0);
return ip2;
case RGB_TRANSLATE:
ij.IJ.showStatus("Translating red");
r.translate(arg, arg2); showProgress(0.40);
ij.IJ.showStatus("Translating green");
g.translate(arg, arg2); showProgress(0.65);
ij.IJ.showStatus("Translating blue");
b.translate(arg, arg2); showProgress(0.90);
break;
case RGB_MIN:
r.filter(MIN); showProgress(0.40);
g.filter(MIN); showProgress(0.65);
b.filter(MIN); showProgress(0.90);
break;
case RGB_MAX:
r.filter(MAX); showProgress(0.40);
g.filter(MAX); showProgress(0.65);
b.filter(MAX); showProgress(0.90);
break;
}
R = (byte[])r.getPixels();
G = (byte[])g.getPixels();
B = (byte[])b.getPixels();
setRGB(R, G, B);
showProgress(1.0);
return null;
}
public void noise(double range) {
filterRGB(RGB_NOISE, range);
}
public void medianFilter() {
filterRGB(RGB_MEDIAN, 0.0);
}
public void findEdges() {
filterRGB(RGB_FIND_EDGES, 0.0);
}
public void erode() {
filterRGB(RGB_ERODE, 0.0);
}
public void dilate() {
filterRGB(RGB_DILATE, 0.0);
}
public void autoThreshold() {
filterRGB(RGB_THRESHOLD, 0.0);
}
/** Scales the image or selection using the specified scale factors.
@see ImageProcessor#setInterpolate
*/
public void scale(double xScale, double yScale) {
if (interpolationMethod==BICUBIC) {
filterRGB(RGB_SCALE, xScale, yScale);
return;
}
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;
}
int[] pixels2 = (int[])getPixelsCopy();
boolean checkCoordinates = (xScale < 1.0) || (yScale < 1.0);
int index1, index2, xsi, ysi;
double ys, xs;
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++] = bgColor;
else {
if (interpolationMethod==BILINEAR) {
if (xs<0.0) xs = 0.0;
if (xs>=xlimit) xs = xlimit2;
pixels[index1++] = getInterpolatedPixel(xs, ys, pixels2);
} else
pixels[index1++] = pixels2[index2+xsi];
}
}
if (y%20==0)
showProgress((double)(y-ymin)/height);
}
showProgress(1.0);
}
public ImageProcessor crop() {
int[] pixels2 = new int[roiWidth*roiHeight];
for (int ys=roiY; ys=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);
}
/** Uses bilinear interpolation to find the pixel value at real coordinates (x,y). */
private final int getInterpolatedPixel(double x, double y, int[] pixels) {
int xbase = (int)x;
int ybase = (int)y;
double xFraction = x - xbase;
double yFraction = y - ybase;
int offset = ybase * width + xbase;
int lowerLeft = pixels[offset];
int rll = (lowerLeft&0xff0000)>>16;
int gll = (lowerLeft&0xff00)>>8;
int bll = lowerLeft&0xff;
int lowerRight = pixels[offset + 1];
int rlr = (lowerRight&0xff0000)>>16;
int glr = (lowerRight&0xff00)>>8;
int blr = lowerRight&0xff;
int upperRight = pixels[offset + width + 1];
int rur = (upperRight&0xff0000)>>16;
int gur = (upperRight&0xff00)>>8;
int bur = upperRight&0xff;
int upperLeft = pixels[offset + width];
int rul = (upperLeft&0xff0000)>>16;
int gul = (upperLeft&0xff00)>>8;
int bul = upperLeft&0xff;
int r, g, b;
double upperAverage, lowerAverage;
upperAverage = rul + xFraction * (rur - rul);
lowerAverage = rll + xFraction * (rlr - rll);
r = (int)(lowerAverage + yFraction * (upperAverage - lowerAverage)+0.5);
upperAverage = gul + xFraction * (gur - gul);
lowerAverage = gll + xFraction * (glr - gll);
g = (int)(lowerAverage + yFraction * (upperAverage - lowerAverage)+0.5);
upperAverage = bul + xFraction * (bur - bul);
lowerAverage = bll + xFraction * (blr - bll);
b = (int)(lowerAverage + yFraction * (upperAverage - lowerAverage)+0.5);
return 0xff000000 | ((r&0xff)<<16) | ((g&0xff)<<8) | b&0xff;
}
/** Creates a new ColorProcessor containing a scaled copy of this image or selection.
@see ImageProcessor#setInterpolate
*/
public ImageProcessor resize(int dstWidth, int dstHeight) {
if (roiWidth==dstWidth && roiHeight==dstHeight)
return crop();
if (interpolationMethod!=NONE && (width==1||height==1)) {
ByteProcessor r2 = (ByteProcessor)getChannel(1,null).resizeLinearly(dstWidth, dstHeight);
ByteProcessor g2 = (ByteProcessor)getChannel(2,null).resizeLinearly(dstWidth, dstHeight);
ByteProcessor b2 = (ByteProcessor)getChannel(3,null).resizeLinearly(dstWidth, dstHeight);
ColorProcessor ip2 = new ColorProcessor(dstWidth, dstHeight);
ip2.setChannel(1, r2); ip2.setChannel(2, g2); ip2.setChannel(3, b2);
return ip2;
}
if (interpolationMethod==BICUBIC)
return filterRGB(RGB_RESIZE, 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;
double xlimit = width-1.0, xlimit2 = width-1.001;
double ylimit = height-1.0, ylimit2 = height-1.001;
if (interpolationMethod==BILINEAR) {
if (dstWidth!=width) dstCenterX+=xScale/4.0;
if (dstHeight!=height) dstCenterY+=yScale/4.0;
}
ImageProcessor ip2 = createProcessor(dstWidth, dstHeight);
int[] pixels2 = (int[])ip2.getPixels();
double xs, ys;
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++] = getInterpolatedPixel(xs, ys, pixels);
} else
pixels2[index2++] = pixels[index1+(int)xs];
}
if (y%20==0)
showProgress((double)y/dstHeight);
}
showProgress(1.0);
return ip2;
}
/** Uses averaging to creates a new ColorProcessor containing
a downsized copy of this image or selection. */
public ImageProcessor makeThumbnail(int width2, int height2, double smoothFactor) {
return resize(width2, height2, true);
}
/** Rotates the image or ROI 'angle' degrees clockwise.
@see ImageProcessor#setInterpolationMethod
*/
public void rotate(double angle) {
if (angle%360==0)
return;
if (interpolationMethod==BICUBIC) {
filterRGB(RGB_ROTATE, angle);
return;
}
int[] pixels2 = (int[])getPixelsCopy();
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;
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=-0.01) && (ys=xlimit) xs = xlimit2;
if (ys<0.0) ys = 0.0;
if (ys>=ylimit) ys = ylimit2;
pixels[index++] = 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++] = bgColor;
}
if (y%30==0)
showProgress((double)(y-roiY)/roiHeight);
}
showProgress(1.0);
}
public void flipVertical() {
int index1,index2;
int tmp;
for (int y=0; y> 16)
+ k2*((p2 & 0xff0000) >> 16)
+ k3*((p3 & 0xff0000) >> 16)
+ k4*((p4 & 0xff0000) >> 16)
+ k5*((p5 & 0xff0000) >> 16)
+ k6*((p6 & 0xff0000) >> 16)
+ k7*((p7 & 0xff0000) >> 16)
+ k8*((p8 & 0xff0000) >> 16)
+ k9*((p9 & 0xff0000) >> 16);
rsum /= scale;
if(rsum>255) rsum = 255;
if(rsum<0) rsum = 0;
gsum = k1*((p1 & 0xff00) >> 8)
+ k2*((p2 & 0xff00) >> 8)
+ k3*((p3 & 0xff00) >> 8)
+ k4*((p4 & 0xff00) >> 8)
+ k5*((p5 & 0xff00) >> 8)
+ k6*((p6 & 0xff00) >> 8)
+ k7*((p7 & 0xff00) >> 8)
+ k8*((p8 & 0xff00) >> 8)
+ k9*((p9 & 0xff00) >> 8);
gsum /= scale;
if(gsum>255) gsum = 255;
else if(gsum<0) gsum = 0;
bsum = k1*(p1 & 0xff)
+ k2*(p2 & 0xff)
+ k3*(p3 & 0xff)
+ k4*(p4 & 0xff)
+ k5*(p5 & 0xff)
+ k6*(p6 & 0xff)
+ k7*(p7 & 0xff)
+ k8*(p8 & 0xff)
+ k9*(p9 & 0xff);
bsum /= scale;
if (bsum>255) bsum = 255;
if (bsum<0) bsum = 0;
pixels[offset++] = 0xff000000
| ((rsum << 16) & 0xff0000)
| ((gsum << 8 ) & 0xff00)
| (bsum & 0xff);
}
if (y%inc==0)
showProgress((double)(y-roiY)/roiHeight);
}
showProgress(1.0);
}
/** A 3x3 filter operation, where the argument (ImageProcessor.BLUR_MORE, FIND_EDGES,
MEDIAN_FILTER, MIN or MAX) determines the filter type. */
public void filter(int type) {
if (type == FIND_EDGES)
filterRGB(RGB_FIND_EDGES, 0, 0);
else if (type == MEDIAN_FILTER)
filterRGB(RGB_MEDIAN, 0, 0);
else if (type == MIN)
filterRGB(RGB_MIN, 0, 0);
else if (type == MAX)
filterRGB(RGB_MAX, 0, 0);
else
blurMore();
}
/** BLUR MORE: 3x3 unweighted smoothing is implemented directly, does not convert the image to three ByteProcessors. */
private void blurMore() {
int p1 = 0, p2, p3, p4 = 0, p5, p6, p7 = 0, p8, p9;
int[] prevRow = new int[width];
int[] thisRow = new int[width];
int[] nextRow = new int[width];
System.arraycopy(pixels, Math.max(roiY-1,0)*width, thisRow, 0, width);
System.arraycopy(pixels, roiY*width, nextRow, 0, width);
for (int y=roiY; y1000000 && (y&0xff)==0)
showProgress((double)(y-roiY)/roiHeight);
}
showProgress(1.0);
}
public int[] getHistogram() {
if (mask!=null)
return getHistogram(mask);
double rw=rWeight, gw=gWeight, bw=bWeight;
if (weights!=null)
{rw=weights[0]; gw=weights[1]; bw=weights[2];}
int c, r, g, b, v;
int[] histogram = new int[256];
for (int y=roiY; y<(roiY+roiHeight); y++) {
int i = y * width + roiX;
for (int x=roiX; x<(roiX+roiWidth); x++) {
c = pixels[i++];
r = (c&0xff0000)>>16;
g = (c&0xff00)>>8;
b = c&0xff;
v = (int)(r*rw + g*gw + b*bw + 0.5);
histogram[v]++;
}
}
return histogram;
}
public int[] getHistogram(ImageProcessor mask) {
if (mask.getWidth()!=roiWidth||mask.getHeight()!=roiHeight)
throw new IllegalArgumentException(maskSizeError(mask));
double rw=rWeight, gw=gWeight, bw=bWeight;
if (weights!=null)
{rw=weights[0]; gw=weights[1]; bw=weights[2];}
byte[] mpixels = (byte[])mask.getPixels();
int c, r, g, b, v;
int[] histogram = new int[256];
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) {
c = pixels[i];
r = (c&0xff0000)>>16;
g = (c&0xff00)>>8;
b = c&0xff;
v = (int)(r*rw + g*gw + b*bw + 0.5);
histogram[v]++;
}
i++;
}
}
return histogram;
}
public synchronized boolean weightedHistogram() {
if (weights!=null && (weights[0]!=1d/3d||weights[1]!=1d/3d||weights[2]!=1d/3d))
return true;
if (rWeight!=1d/3d || gWeight!=1d/3d || bWeight!=1d/3d)
return true;
return false;
}
/** Performs a convolution operation using the specified kernel. */
public void convolve(float[] kernel, int kernelWidth, int kernelHeight) {
int size = width*height;
byte[] r = new byte[size];
byte[] g = new byte[size];
byte[] b = new byte[size];
getRGB(r,g,b);
ImageProcessor rip = new ByteProcessor(width, height, r, null);
ImageProcessor gip = new ByteProcessor(width, height, g, null);
ImageProcessor bip = new ByteProcessor(width, height, b, null);
ImageProcessor ip2 = rip.convertToFloat();
Rectangle roi = getRoi();
ip2.setRoi(roi);
ip2.convolve(kernel, kernelWidth, kernelHeight);
ImageProcessor r2 = ip2.convertToByte(false);
ip2 = gip.convertToFloat();
ip2.setRoi(roi);
ip2.convolve(kernel, kernelWidth, kernelHeight);
ImageProcessor g2 = ip2.convertToByte(false);
ip2 = bip.convertToFloat();
ip2.setRoi(roi);
ip2.convolve(kernel, kernelWidth, kernelHeight);
ImageProcessor b2 = ip2.convertToByte(false);
setRGB((byte[])r2.getPixels(), (byte[])g2.getPixels(), (byte[])b2.getPixels());
}
/** Sets the weighting factors used by getPixelValue(), getHistogram()
and convertToByte() to do color conversions. The default values are
1/3, 1/3 and 1/3. Check "Weighted RGB Conversions" in
Edit/Options/Conversions to use 0.299, 0.587 and 0.114.
@see #getWeightingFactors
@see #setRGBWeights
*/
public static void setWeightingFactors(double rFactor, double gFactor, double bFactor) {
rWeight = rFactor;
gWeight = gFactor;
bWeight = bFactor;
}
/** Returns the three weighting factors used by getPixelValue(),
getHistogram() and convertToByte() to do color conversions.
@see #setWeightingFactors
@see #getRGBWeights
*/
public static double[] getWeightingFactors() {
double[] weights = new double[3];
weights[0] = rWeight;
weights[1] = gWeight;
weights[2] = bWeight;
return weights;
}
/** This is a thread-safe (non-static) version of setWeightingFactors(). */
public void setRGBWeights(double rweight, double gweight, double bweight) {
weights = new double[3];
weights[0] = rweight;
weights[1] = gweight;
weights[2] = bweight;
}
/** This is a thread-safe (non-static) version of setWeightingFactors(). */
public void setRGBWeights(double[] weights) {
this.weights = weights;
}
/** Returns the values set by setRGBWeights(), or null if setRGBWeights() has not been called. */
public double[] getRGBWeights() {
return weights;
}
/** Always returns false since RGB images do not use LUTs. */
public boolean isInvertedLut() {
return false;
}
/** Returns 'true' if this is a grayscale image. */
public final boolean isGrayscale() {
int c, r, g, b;
for (int i=0; i>16;
g = (c&0xff00)>>8;
b = c&0xff;
if (r!=g || r!=b) return false;
}
return true;
}
/** Always returns 0 since RGB images do not use LUTs. */
public int getBestIndex(Color c) {
return 0;
}
/** Does nothing since RGB images do not use LUTs. */
public void invertLut() {
}
public void updateComposite(int[] rgbPixels, int channel) {
}
/** Not implemented. */
public void threshold(int level) {}
/** Returns the number of color channels (3). */
public int getNChannels() {
return 3;
}
/** Returns a FloatProcessor with one color channel of the image.
* The roi and mask are also set for the FloatProcessor.
* @param channelNumber Determines the color channel, 0=red, 1=green, 2=blue
* @param fp Here a FloatProcessor can be supplied, or null. The FloatProcessor
* is overwritten by this method (re-using its pixels array
* improves performance).
* @return A FloatProcessor with the converted image data of the color channel selected
*/
public FloatProcessor toFloat(int channelNumber, FloatProcessor fp) {
int size = width*height;
if (fp == null || fp.getWidth()!=width || fp.getHeight()!=height)
fp = new FloatProcessor(width, height, new float[size], null);
float[] fPixels = (float[])fp.getPixels();
int shift = 16 - 8*channelNumber;
int byteMask = 255<>shift;
fp.setRoi(getRoi());
fp.setMask(mask);
fp.setMinAndMax(0, 255);
return fp;
}
/** Sets the pixels of one color channel from a FloatProcessor.
* @param channelNumber Determines the color channel, 0=red, 1=green, 2=blue
* @param fp The FloatProcessor where the image data are read from.
*/
public void setPixels(int channelNumber, FloatProcessor fp) {
float[] fPixels = (float[])fp.getPixels();
float value;
int size = width*height;
int shift = 16 - 8*channelNumber;
int resetMask = 0xffffffff^(255<255f) value = 255f;
pixels[i] = (pixels[i]&resetMask) | ((int)value<
© 2015 - 2025 Weber Informatics LLC | Privacy Policy