package org.hipi.image; import org.hipi.image.PixelArray; import org.hipi.image.HipiImageHeader; import org.hipi.image.HipiImageHeader.HipiColorSpace; import org.hipi.image.HipiImage; import org.hipi.image.PixelArray; import org.apache.hadoop.io.BinaryComparable; import org.apache.hadoop.io.RawComparator; import org.apache.hadoop.io.Writable; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.lang.IllegalArgumentException; /** * Abstract class that represents a raster (uncompressed) 2D image. A RasterImage extends the * abstract base class {@link HipiImage} and consists of a {@link HipiImageHeader} and a {@link * PixelArray}, which represents a flat array of uncompressed image pixel data stored in interleaved * raster-scan order (e.g., RGBRGBRGB...). */ public abstract class RasterImage extends HipiImage { protected PixelArray pixelArray; protected RasterImage(PixelArray pixelArray) { this.pixelArray = pixelArray; } public void setHeader(HipiImageHeader header) throws IllegalArgumentException { super.setHeader(header); int size = header.getWidth()*header.getHeight()*header.getNumBands(); pixelArray.setSize(size); } public PixelArray getPixelArray() { return pixelArray; } /** * Compares two RasterImage objects for equality allowing for some * amount of differences in pixel values. * * @return True if the two images have equal dimensions, color * spaces, and are found to deviate by less than a specified maximum * difference, false otherwise. */ public abstract boolean equalsWithTolerance(RasterImage thatImage, float maxDifference); /** * Compares two RasterImage objects for equality. * * @return True if the two images are found to deviate by an amount * that is not representable in the underlying pixel type, false * otherwise. */ @Override public abstract boolean equals(Object that); /** * Crops a raster image to a (width x height) rectangular region * with top-left corner at (x,y) pixel location. * * @param x horizontal position of upper left corner of crop rectangle * @param y vertical position of upper left corner of crop rectangle * @param width width of crop rectangle * @param height height of crop rectangle * @param output output {@link RasterImage} target (must be initialized) */ public void crop(int x, int y, int width, int height, RasterImage output) throws IllegalArgumentException { int w = this.getWidth(); int h = this.getHeight(); int b = this.getNumBands(); // Verify crop dimensions if (x < 0 || width <= 0 || x+width > w || y < 0 || height <= 0 || y+height > h) { throw new IllegalArgumentException("Invalid crop region."); } // Verify crop output target if (width != output.getWidth() || height != output.getHeight() || b != output.getNumBands()) { throw new IllegalArgumentException("Mismatch between size of crop region and size of crop " + "output target."); } PixelArray pa = output.getPixelArray(); // Assemble cropped output for (int j=y; j<y+height; j++) { for (int i=x; i<x+width; i++) { for (int c=0; c<b; c++) { pa.setElem(((j-y)*width+(i-x))*b+c,pixelArray.getElem((j*w+i)*b+c)); } } } } /** * Convert image to another color space. * * @param colorSpace target color space * @param output output {@link RasterImage} target (must be initialized) * */ public void convertToColorSpace(HipiColorSpace colorSpace, RasterImage output) throws IllegalArgumentException { if (getColorSpace() == colorSpace) { throw new IllegalArgumentException("Cannot convert color space to itself."); } if (getColorSpace() == HipiColorSpace.RGB && output.getColorSpace() == HipiColorSpace.LUM) { int w = this.getWidth(); int h = this.getHeight(); int b = this.getNumBands(); assert b == 3; // Verify color conversion output target if (w != output.getWidth() || h != output.getHeight() || 1 != output.getNumBands()) { throw new IllegalArgumentException("Invalid dimensions in color convert output target."); } PixelArray pa = output.getPixelArray(); // Perform color conversion for (int j=0; j<h; j++) { for (int i=0; i<w; i++) { float red = pixelArray.getElem((j*w+i)*3+0); float grn = pixelArray.getElem((j*w+i)*3+0); float blu = pixelArray.getElem((j*w+i)*3+0); float lum = red * 0.30f + grn * 0.59f + blu * 0.11f; pa.setElemFloat((j*w+i),lum); } } } else { throw new IllegalArgumentException("Not implemented."); } } /** * Produces a string representation of the image that concatenates * image dimensions with RGB values of up to first 10 pixels in * raster-scan order. * * @see java.lang.Object#toString */ @Override public String toString() { String typeString = "UNDEFINED IMAGE TYPE"; switch (getType()) { case FLOAT: typeString = "FloatImage"; break; case BYTE: typeString = "ByteImage"; break; default: } int w = this.getWidth(); int h = this.getHeight(); int b = this.getNumBands(); StringBuilder result = new StringBuilder(); result.append(String.format("%s: %d x %d x %d [", typeString, w, h, b)); int n = Math.min(10,w*h); for (int i=0; i<n; i++) { result.append("("); for (int c=0; c<b; c++) { if (getType() == HipiImageType.FLOAT) { result.append(String.format("%.2f",pixelArray.getElemFloat(i*b+c))); } else { result.append(pixelArray.getElem(i*b+c)); } if (c<(b-1)) result.append(" "); else result.append(")"); } if (i<(n-1)) { result.append(" "); } } result.append("]"); return result.toString(); } /** * Sets the current object to be equal to another * RasterImage. Performs a shallow copy of the image header and * pixel data array. * * @param image Target image. */ public void set(RasterImage image) { this.header = image.header; this.pixelArray = image.pixelArray; } /** * Writes raster image in a simple uncompressed binary format. * @see org.apache.hadoop.io.Writable#write */ @Override public void write(DataOutput output) throws IOException { header.write(output); output.write(pixelArray.getByteArray()); } /** * Reads a raster image stored in a simple uncompressed binary * format. * @see org.apache.hadoop.io.Writable#readFields */ @Override public void readFields(DataInput input) throws IOException { // Create and read header header = new HipiImageHeader(input); int w = this.getWidth(); int h = this.getHeight(); int b = this.getNumBands(); int numBytes = w*h*b*PixelArray.getDataTypeSize(pixelArray.getDataType()); // Read pixel data byte[] pixelBytes = new byte[numBytes]; input.readFully(pixelBytes); pixelArray.setFromByteArray(pixelBytes); } } // public abstract class RasterImage<T>...