package fr.unistra.pelican; import java.io.Serializable; import java.util.Arrays; /** * This class represents a byte valued image with pixels values in the interval * [0,255] Range for an byte pixel is [0; 255]. Note that the internal storage * go from Byte.MIN_VALUE to Byte.MAX_VALUE, so a + Byte.MIN_VALUE shit is * applied. * * @author PELICAN team * @version 1.0 */ public class ByteImage extends Image implements Serializable { /** * Pixel data array */ private byte[] pixels; /** * Serial version ID */ private static final long serialVersionUID = 4L; /** * Constructs a ByteImage */ protected ByteImage(){ super(); } /** * Constructs a ByteImage identical to the given argument * * @param image * ByteImage to copy */ public ByteImage(ByteImage image) { super(image); this.pixels = (byte[]) image.pixels.clone(); } /** * Constructs a ByteImage identical to the given argument * * @param image * Image to copy */ public ByteImage(Image image) { this(image, true); } /** * Constructs a ByteImage 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 ByteImage(Image image, boolean copyData) { super(image); this.pixels = new byte[image.getXDim() * image.getYDim() * image.getZDim() * image.getTDim() * image.getBDim()]; if (copyData == true) for (int i = 0; i < pixels.length; i++) setPixelByte(i, image.getPixelByte(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 copyData * if and only if it is set to true are the pixels copied */ public ByteImage(ByteImage image, boolean copyData) { super(image); if (copyData == true) this.pixels = (byte[]) image.pixels.clone(); else this.pixels = new byte[image.getXDim() * image.getYDim() * image.getZDim() * image.getTDim() * image.getBDim()]; } /** * Constructs a ByteImage 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 ByteImage(int xdim, int ydim, int zdim, int tdim, int bdim) { super(xdim, ydim, zdim, tdim, bdim); this.pixels = new byte[xdim * ydim * zdim * tdim * bdim]; } /** * Sets all the pixel values to the given value * * @param b * Desired value for the pixels */ public void fill(byte b) { Arrays.fill(pixels, (byte) b); } @Override public void fill(double d) { this.fill(doubleToSignedByte(d)); } /** * Creates a copy of this ByteImage * * @return an axact copy of this ByteImage */ public ByteImage copyImage(boolean copyData) { return new ByteImage(this, copyData); } /** * Creates a new instance of ByteImage * * @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 ByteImage(xdim, ydim, zdim, tdim, bdim); } /** * Checks if the image is empty, i.e. it contains only 0 pixels * * @return true if the image is empty */ public boolean isEmpty() { for (int p = 0; p < pixels.length; p++) if (pixels[p] > 0) return false; return true; } /** * Duplicates the given channel to all the available dimensions * * @param band * channel to duplicate */ public void duplicateBand(int band) { for (int x = 0; x < this.xdim; x++) for (int y = 0; y < this.ydim; y++) for (int z = 0; z < this.zdim; z++) for (int t = 0; t < this.tdim; t++) for (int b = 0; b < this.bdim; b++) if (b != band) setPixelByte(x, y, z, t, b, getPixelByte(x, y, z, t, band)); } /** * Extracts the desired channel as mono-channel ByteImage * * @param b * channel to extract * @return the resulting mono-channel ByteImage */ @Deprecated public ByteImage getBand(int b) { ByteImage im = new ByteImage(xdim, ydim, zdim, tdim, 1); for (int x = 0; x < this.xdim; x++) for (int y = 0; y < this.ydim; y++) for (int z = 0; z < this.zdim; z++) for (int t = 0; t < this.tdim; t++) im.setPixelByte(x, y, z, t, 0, this.getPixelByte(x, y, z, t, b)); return im; } /** * Sets the given channel number to the given mono-channel ByteImage * * @param b * channel to set * @param im * ByteImage to set the channel to */ @Deprecated public void setBand(int b, ByteImage im) { for (int x = 0; x < this.xdim; x++) for (int y = 0; y < this.ydim; y++) for (int z = 0; z < this.zdim; z++) for (int t = 0; t < this.tdim; t++) setPixelByte(x, y, z, t, b, im.getPixelByte(x, y, z, t, 0)); } /** * Computes the pixel sum of the image * * @return the pixel sum of the image */ public long getSum() { long sum=0; for(int i=this.size()-1;i>=0;i--) { sum+=this.getPixelByte(i); } return sum; } /** * Computes the pixel sum of a given channel * * @param b * channel to compute * @return the pixel sum of the given channel */ public long getPixelSum(int b) { long sum = 0; for (int x = 0; x < this.xdim; x++) { for (int y = 0; y < this.ydim; y++) sum += this.getPixelXYBByte(x, y, b); } return sum; } /** * Computes the pixel average of a given channel * * @param b * channel to compute * @return the pixel sum of the given channel */ public double getPixelAverage(int b) { long sum = this.getPixelSum(b); long cnt = this.xdim * this.ydim; return sum / cnt; } /** * Extracts the frames between the given limits * * @param t1 * lower frame limit * @param t2 * upper frame limit * @return the resulting ByteImage between the given two limits */ @Deprecated public ByteImage getFrames(int t1, int t2) { if (t1 < 0 || t2 > this.getTDim() || t2 < t1) return new ByteImage(this); ByteImage im = new ByteImage(this.xdim, this.ydim, this.zdim, t2 - t1 + 1, this.bdim); im.copyAttributes(this); for (int x = 0; x < xdim; x++) for (int y = 0; y < ydim; y++) for (int z = 0; z < zdim; z++) for (int b = 0; b < bdim; b++) for (int t = 0; t < t2 - t1 + 1; t++) im.setPixelByte(x, y, z, t, b, this.getPixelByte(x, y, z, t1 + t, b)); return im; } /** * Extracts the frames at the give position * * @param t * frame to extract * @return the resulting monoframe ByteImage at the given position */ @Deprecated public ByteImage getFrame(int t) { ByteImage im = new ByteImage(this.xdim, this.ydim, this.zdim, 1, this.bdim); im.copyAttributes(this); for (int x = 0; x < this.xdim; x++) for (int y = 0; y < this.ydim; y++) for (int z = 0; z < this.zdim; z++) for (int b = 0; b < this.bdim; b++) im.setPixelByte(x, y, z, 0, b, this.getPixelByte(x, y, z, t, b)); return im; } /** * Extracts a frame from the given view axis and position * * @param f * frame number to extract * @param axis * the view axis (Image.X,Image.Y,Image.Z) * @return the resulting monoframe ByteImage at the given position from the * given view point */ @Deprecated public ByteImage getFrame(int f, int axis) { ByteImage im = null; switch (axis) { case Image.Y: /* Y axis */ im = new ByteImage(xdim, zdim, 1, 1, 1); if (f > ydim) f = ydim - 1; else if (f < 0) f = 0; for (int x = 0; x < xdim; x++) for (int z = 0; z < zdim; z++) im.setPixelXYByte(x, z, this.getPixelXYZByte(x, f, z)); break; case Image.X: /* X axis */ im = new ByteImage(ydim, zdim, 1, 1, 1); if (f > xdim) f = xdim - 1; else if (f < 0) f = 0; for (int y = 0; y < ydim; y++) for (int z = 0; z < zdim; z++) im.setPixelXYByte(y, z, this.getPixelXYZByte(f, y, z)); break; case Image.Z: /* Z axis */ im = new ByteImage(xdim, ydim, 1, 1, 1); if (f > zdim) f = zdim - 1; else if (f < 0) f = 0; for (int x = 0; x < xdim; x++) for (int y = 0; y < im.ydim; y++) im.setPixelXYByte(x, y, this.getPixelXYZByte(x, y, f)); break; } return im; } /** * Sets the given time frame to the given ByteImage * * @param t * time frame to set * @param im * ByteImage to set the time frame to */ @Deprecated public void setFrame(int t, ByteImage im) { for (int x = 0; x < xdim; x++) for (int y = 0; y < ydim; y++) for (int z = 0; z < zdim; z++) for (int b = 0; b < bdim; b++) setPixelByte(x, y, z, t, b, im.getPixelByte(x, y, z, 0, b)); } /** * Computes the total number of pixels in all dimensions * * @return the number of pixels */ public int size() { return pixels.length; } /** * Gets a copy of the pixel array * * @return a copy of the pixel array */ public byte[] getPixels() { return pixels; } /** * Sets the pixels to the copy of the given array * * @param values * pixel array to copy */ public void setPixels(byte[] values) { pixels = (byte[]) values.clone(); } /** * Sets the pixels to the copy of the given array * * @param values * pixel array to copy */ public void setPixelsUnsafe(byte[] values) { pixels = values; } /** * Compares with the given ByteImage * * @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 ByteImage)) return false; if (!haveSameDimensions(this, im)) return false; int size = size(); for (int i = 0; i < size; i++) if (im.getPixelByte(i) != getPixelByte(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 total size or -1 if the * images have different dimensions */ public double nbDifferentPixels(ByteImage im) { double ctr = 0.0; if (!haveSameDimensions(this, im)) return -1; for (int i = 0; i < size(); i++) if ((im.getPixelByte(i) != this.getPixelByte(i))) ctr++; return ctr / size(); } /** * Computes the difference ratio of the given image with this image * * @param im * image to compare * @return the number of different pixels or -1 if the images have different * dimensions */ public double differenceRatio(ByteImage im) { double ctr = 0.0; if (!haveSameDimensions(this, im)) return -1.0; for (int i = 0; i < size(); i++) ctr += Math.abs(im.getPixelByte(i) - this.getPixelByte(i)); return ctr / size(); } @Override public double getPixelDouble(int loc) { return signedByteToDouble(pixels[loc]); //return DoubleImage.byteToDouble * (double) (pixels[loc] + 128); } @Override public int getPixelInt(int loc) { return signedByteToInt(pixels[loc]); //return pixels[loc] << 24; } @Override public int getPixelByte(int loc) { return signedByteToUnsignedByte(pixels[loc]); //return pixels[loc] - Byte.MIN_VALUE; } @Override public boolean getPixelBoolean(int loc) { return signedByteToBoolean(pixels[loc]); //return pixels[loc] >= 0 ? true : false; } @Override public void setPixelDouble(int loc, double value) { pixels[loc] = doubleToSignedByte(value); //pixels[loc] = (byte) Math.round(doubleToByte * value - 128); } @Override public void setPixelInt(int loc, int value) { pixels[loc] = intToSignedByte(value); //pixels[loc] = (byte) (value >> 24); } @Override public void setPixelByte(int loc, int value) { pixels[loc] = unsignedByteToSignedByte(value); //pixels[loc] = (byte) (value + Byte.MIN_VALUE); } @Override public void setPixelBoolean(int loc, boolean value) { pixels[loc] = booleanToSignedByte(value); //pixels[loc] = value ? Byte.MAX_VALUE : Byte.MIN_VALUE; } @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.setPixelByte(x1, y1, z1, t1, b1, input .getPixelByte(x2, y2, z2, t2, b2)); } @Override public final int[] getVectorPixelByte(int index) { int[] vector = new int[bdim]; for (int b = 0; b < bdim; b++) vector[b] = pixels[index++]-Byte.MIN_VALUE; return vector; } /** * Convert the ByteImage to a IntegerImage without modifying the values * @return a byte image */ public IntegerImage copyToIntegerImage() { IntegerImage i=new IntegerImage(this,false); for (int p=0;p<i.size();p++) i.setPixelInt(p,getPixelByte(p)); return i; } /** * Computes the minimum value of the image * @return * the signed Byte representation of the minimum */ public byte minimum(){ byte val = Byte.MAX_VALUE; for (int p = 0; p < size(); p++) if ( pixels[p] < val) val = pixels[p]; return val; } /** * Computes the maximum value of the image * @return * the signed Byte representation of the maximum */ public byte maximum(){ byte val = Byte.MIN_VALUE; for (int p = 0; p < size(); p++) if ( pixels[p] > val) val = pixels[p]; return val; } /** * Computes the minimum value of the image in the specified band * @param band * Band. * @return * the signed Byte representation of the minimum in the specified band */ public byte minimum(int band){ byte val = Byte.MAX_VALUE; for (int p = band; p < size(); p+=this.getBDim()) if ( pixels[p] < val) val = pixels[p]; return val; } /** * Computes the maximum value of the image in the specified band * @param band * Band. * @return * the signed Byte representation of the maximum in the specified band */ public byte maximum(int band){ byte val = Byte.MIN_VALUE; for (int p = band; p < size(); p+=this.getBDim()) if ( pixels[p] > val) val = pixels[p]; return val; } @Override public boolean maximumBoolean() { return signedByteToBoolean(this.maximum()); } @Override public int maximumByte() { return signedByteToUnsignedByte(this.maximum()); } @Override public double maximumDouble() { return signedByteToDouble(this.maximum()); } @Override public double maximumDouble(int band) { return signedByteToDouble(this.maximum(band)); } @Override public double maximumDoubleIgnoreNonRealValues(int band) { return signedByteToDouble(this.maximum(band)); } @Override public int maximumInt() { return signedByteToInt(this.maximum()); } @Override public boolean minimumBoolean() { return signedByteToBoolean(this.minimum()); } @Override public int minimumByte() { return signedByteToUnsignedByte(this.minimum()); } @Override public double minimumDouble() { return signedByteToDouble(this.minimum()); } @Override public double minimumDouble(int band) { return signedByteToDouble(this.minimum(band)); } @Override public double minimumDoubleIgnoreNonRealValues(int band) { return signedByteToDouble(this.minimum(band)); } @Override public int minimumInt() { return signedByteToInt(this.minimum()); } }