package fr.unistra.pelican;
import java.io.Serializable;
import java.util.Arrays;
/**
* This class represents a double valued image Range for a double pixel is
* [0.0; 1.0]
*
* @version 1.0
*/
public class DoubleImage extends Image implements Serializable {
/**
* Pixel data array
*/
protected double[] pixels;
/**
* Serial version ID
*/
private static final long serialVersionUID = 6L;
/**
* Constructs a DoubleImage
*/
protected DoubleImage(){
super();
}
/**
* Constructs a DoubleImage identical to the given argument
*
* @param image
* DoubleImage to copy
*/
public DoubleImage(DoubleImage image) {
super(image);
this.pixels = (double[]) image.pixels.clone();
}
/**
* Constructs a DoubleImage identical to the given argument
*
* @param image
* Image to copy
*/
public DoubleImage(Image image) {
this(image, true);
}
/**
* Constructs a DoubleImage identical to the given argument
*
* @param image
* Image to copy
* @param copyData
* if and only if it is set to true are the pixels copied
*/
public DoubleImage(Image image, boolean copyData) {
super(image);
this.pixels = new double[image.getXDim() * image.getYDim()
* image.getZDim() * image.getTDim() * image.getBDim()];
if (copyData == true)
for (int i = 0; i < pixels.length; i++)
setPixelDouble(i, image.getPixelDouble(i));
}
/**
* Constructs a ByteImage from the given argument. The pixels are copied if
* and only of ''copy'' is set to true.
*
* @param image
* ByteImage to copy
* @param copy
* if and only if it is set to true are the pixels copied
*/
public DoubleImage(DoubleImage image, boolean copy) {
super(image);
if (copy == true)
this.pixels = (double[]) image.pixels.clone();
else
this.pixels = new double[image.getXDim() * image.getYDim()
* image.getZDim() * image.getTDim() * image.getBDim()];
}
/**
* Constructs a DoubleImage with the given dimensions
*
* @param xdim
* the horizontal dimension
* @param ydim
* the vertical dimension
* @param zdim
* the depth
* @param tdim
* the frame number
* @param bdim
* the channel number
*/
public DoubleImage(int xdim, int ydim, int zdim, int tdim, int bdim) {
super(xdim, ydim, zdim, tdim, bdim);
this.pixels = new double[xdim * ydim * zdim * tdim * bdim];
}
/**
* Creates a new instance of DoubleImage
*
* @param xdim
* the horizontal dimension
* @param ydim
* the vertical dimension
* @param zdim
* the depth
* @param tdim
* the frame number
* @param bdim
* the channel number
*/
public Image newInstance(int xdim, int ydim, int zdim, int tdim, int bdim) {
return new DoubleImage(xdim, ydim, zdim, tdim, bdim);
}
/**
* Sets all the pixel values to the given value
*
* @param b
* Desired value for the pixels
*/
public void fill(double b) {
Arrays.fill(pixels, b);
}
/**
* Sets all the pixel values of the given band to the given value
*
* @param band
* @param b
* Desired value for the pixels
*/
public void fill(int band, double b) {
for (int i = band; i < size(); i += bdim) {
pixels[i] = b;
}
}
/**
* Gets a copy of the pixel array
*
* @return a copy of the pixel array
*/
public double[] getPixels() {
return (double[]) pixels.clone();
}
/**
* Gets the pixel array (not a copy !), use it at your own risk.
*
* Note that you will probably be cursed by the daemon of border effect for 7 generations ! ahahahah!
*
*
* @return the pixel array
*/
public double[] getPixelsUnsafe() {
return pixels;
}
/**
* Sets the pixels to the copy of the given array
*
* @param values
* pixel array to copy
*/
public void setPixels(double[] values) {
pixels = (double[]) values.clone();
}
/**
* Sets the pixels to the given array, no checks are made on array length,
* use it at your own risk.
*
* Note that you will probably be cursed by the daemon of border effect for 7 generations ! ahahahah!
*
* @param values
* pixel array to copy
*/
public void setPixelsUnsafe(double[] values) {
pixels = values;
}
/**
* Compares with the given DoubleImage
*
* @param im
* image to compare
* @return <code>true</code> if and only if the given image has the same
* pixel values as this image
*/
public boolean equals(Image im) {
if (im==null || !(im instanceof DoubleImage))
return false;
if (!haveSameDimensions(this, im))
return false;
int size = size();
for (int i = 0; i < size; i++) {
if (im.getPixelDouble(i) != getPixelDouble(i))
return false;
}
return true;
}
/**
* Computes the number of different pixels divided by the total size
*
* @param im
* image to compare
* @return the number of different pixels divided by the total size or -1.0 if
* the images have different dimensions
*/
public double nbDifferentPixels(DoubleImage im) {
double ctr = 0.0;
int size = size();
if (!haveSameDimensions(this, im))
return -1.0;
for (int i = 0; i < size; i++) {
if ((im.getPixelDouble(i) != getPixelDouble(i)))
ctr++;
}
return ctr / size;
}
/**
* Scales all channels and frames independently to [0,1] for visualisation
* purposes. As the relative contrast gets messed up, even if the channel in
* question is already in [0,1] it is stretched all the same.
*
* @return the scaled image
*/
public DoubleImage scaleToZeroOneIndep() {
DoubleImage d = (DoubleImage) this.copyImage(false);
// the scaling must be realized on XYZ volumes for every channel/frame
// separately
for (int b = 0; b < bdim; b++)
for (int t = 0; t < tdim; t++) {
double min = Double.MAX_VALUE;
double max = Double.NEGATIVE_INFINITY;
// get the extrema
for (int z = 0; z < zdim; z++)
for (int x = 0; x < xdim; x++)
for (int y = 0; y < ydim; y++) {
double tmp = this.getPixelXYZTBDouble(x, y, z, t, b);
if (min > tmp)
min = tmp;
if (max < tmp)
max = tmp;
}
double dist = max - min;
if (max != min) {
for (int z = 0; z < zdim; z++)
for (int x = 0; x < xdim; x++)
for (int y = 0; y < ydim; y++) {
double tmp = this.getPixelXYZTBDouble(x, y, z, t, b);
tmp = (tmp - min) / dist;
d.setPixelXYZTBDouble(x, y, z, t, b, tmp);
}
} else {
for (int z = 0; z < zdim; z++)
for (int x = 0; x < xdim; x++)
for (int y = 0; y < ydim; y++)
d.setPixelXYZTBDouble(x, y, z, t, b, min);
}
}
return d;
}
/**
* Scales all values to [0,1] for visualisation purposes.
*
* @return the scaled image
*/
public DoubleImage scaleToZeroOne() {
DoubleImage d = (DoubleImage) this.copyImage(false);
double min = Double.MAX_VALUE;
double max = Double.NEGATIVE_INFINITY;
for (int p = 0; p < size(); p++) {
double tmp = this.getPixelDouble(p);
if (min > tmp)
min = tmp;
if (max < tmp)
max = tmp;
}
double dist = max - min;
if (max != min) {
for (int p = 0; p < size(); p++) {
double tmp = this.getPixelDouble(p);
tmp = (tmp - min) / dist;
d.setPixelDouble(p, tmp);
}
} else
System.err.println("Scaling error");
return d;
}
/**
* Slides all values to [0,inf] independently for each channel...or should
* it???
*
* @param img
* input image
* @return the resulting image
*/
public static DoubleImage slide(DoubleImage img) {
DoubleImage output = null;
int bdim = img.getBDim();
int tdim = img.getTDim();
int zdim = img.getZDim();
int xdim = img.getXDim();
int ydim = img.getYDim();
// the sliding must be realized for every channel/frame separately
for (int b = 0; b < bdim; b++) {
for (int t = 0; t < tdim; t++) {
for (int z = 0; z < zdim; z++) {
double min = Double.MAX_VALUE;
// get the minimum
for (int x = 0; x < xdim; x++) {
for (int y = 0; y < ydim; y++) {
double tmp = img.getPixelXYZTBDouble(x, y, z, t, b);
if (min > tmp)
min = tmp;
}
}
if (min < 0.0) {
output = (DoubleImage) img.copyImage(false);
for (int x = 0; x < xdim; x++)
for (int y = 0; y < ydim; y++) {
double d = img.getPixelXYZTBDouble(x, y, z, t, b);
output.setPixelXYZTBDouble(x, y, z, t, b, d - min);
}
} else
output = (DoubleImage) img.copyImage(true);
}
}
}
return output;
}
/**
* Replaces every pixel with its absolute value
*
* @param img
* input image
* @return the resulting image
*/
public static DoubleImage abs(DoubleImage img) {
DoubleImage output = (DoubleImage) img.copyImage(false);
int size = img.size();
for (int i = 0; i < size; i++)
output.setPixelDouble(i, Math.abs(img.getPixelDouble(i)));
return output;
}
@Override
public DoubleImage copyImage(boolean copyData) {
return new DoubleImage(this, copyData);
}
@Override
public double getPixelDouble(int loc) {
return pixels[loc];
}
@Override
public int getPixelInt(int loc) {
return doubleToInt(pixels[loc]);
//return (int) (IntegerImage.doubleToInt * (pixels[loc] - 0.5));
}
@Override
public int getPixelByte(int loc) {
return doubleToUnsignedByte(pixels[loc]);
//return (int) Math.round(ByteImage.doubleToByte * pixels[loc]);
}
@Override
public boolean getPixelBoolean(int loc) {
return doubleToBoolean(pixels[loc]);
//return (pixels[loc] >= 0.5) ? true : false;
}
@Override
public void setPixelDouble(int loc, double value) {
pixels[loc] = value;
}
@Override
public void setPixelInt(int loc, int value) {
pixels[loc] = intToDouble(value);
//pixels[loc] = intToDouble * (double) value + intToDoubleOffset;
}
@Override
public void setPixelByte(int loc, int value) {
pixels[loc] = unsignedByteToDouble(value);
//pixels[loc] = byteToDouble * (double) value;
}
@Override
public void setPixelBoolean(int loc, boolean value) {
pixels[loc] = booleanToDouble(value);
//pixels[loc] = value ? 1.0 : 0.0;
}
@Override
public int size() {
return pixels.length;
}
@Override
public void setPixel(Image input, int x1, int y1, int z1, int t1, int b1,
int x2, int y2, int z2, int t2, int b2) {
this.setPixelDouble(x1, y1, z1, t1, b1, input.getPixelDouble(x2, y2, z2,
t2, b2));
}
/**
* Computes the maximum value of the image
* @return
* a double representation of the maximum
*/
public double maximum() {
double val = Double.NEGATIVE_INFINITY;
for (int p = 0; p < size(); p++)
if (getPixelDouble(p) > val)
val = getPixelDouble(p);
return val;
}
/**
* Computes the minimum value of the image
* @return
* a double representation of the minimum
*/
public double minimum() {
double val = Double.MAX_VALUE;
for (int p = 0; p < size(); p++)
if (getPixelDouble(p) < val)
val = getPixelDouble(p);
return val;
}
/**
* Computes the maximum value of the image in the specified band
* @param band
* Band.
* @return
* the double representation of the maximum in the specified band
*/
public double maximum(int band) {
double val = Double.NEGATIVE_INFINITY;
for (int p = band; p < size(); p+=this.getBDim())
if (getPixelDouble(p) > val)
val = getPixelDouble(p);
return val;
}
/**
* Computes the minimum value of the image in the specified band
* @param band
* Band.
* @return
* a double representation of the minimum in the specified band
*/
public double minimum(int band) {
double val = Double.MAX_VALUE;
for (int p = band; p < size(); p+=this.getBDim())
if (getPixelDouble(p) < val)
val = getPixelDouble(p);
return val;
}
/**
* Computes the maximum value of the image in the specified band (Ignore NaN and Infinite values)
* @param band
* Band.
* @return
* a double representation of the maximum in the specified band
*/
public double maximumIgnoreNonRealValues(int band) {
double val = Double.NEGATIVE_INFINITY;
for (int p = band; p < size(); p+=this.getBDim())
if (getPixelDouble(p) > val)
val = getPixelDouble(p);
return val;
}
/**
* Computes the minimum value of the image in the specified band (Ignore NaN and Infinite values)
* @param band
* Band.
* @return
* a double representation of the minimum in the specified band
*/
public double minimumIgnoreNonRealValues(int band) {
double val = Double.MAX_VALUE;
for (int p = band; p < size(); p+=this.getBDim())
if (getPixelDouble(p) < val)
val = getPixelDouble(p);
return val;
}
@Override
public double maximumDouble() {
return this.maximum();
}
@Override
public double maximumDouble(int band) {
return this.maximum(band);
}
@Override
public double maximumDoubleIgnoreNonRealValues(int band) {
return this.maximumIgnoreNonRealValues(band);
}
@Override
public double minimumDouble() {
return this.minimum();
}
@Override
public double minimumDouble(int band) {
return this.minimum(band);
}
@Override
public double minimumDoubleIgnoreNonRealValues(int band) {
return this.minimumIgnoreNonRealValues(band);
}
@Override
public boolean maximumBoolean() {
return doubleToBoolean(this.maximumDouble());
}
@Override
public int maximumByte() {
return doubleToUnsignedByte(this.maximumDouble());
}
@Override
public int maximumInt() {
return doubleToInt(this.maximumDouble());
}
@Override
public boolean minimumBoolean() {
return doubleToBoolean(this.minimumDouble());
}
@Override
public int minimumByte() {
return doubleToUnsignedByte(this.minimumDouble());
}
@Override
public int minimumInt() {
return doubleToInt(this.minimumDouble());
}
}