All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openimaj.image.FImage Maven / Gradle / Ivy

Go to download

Core definitions of images, pixels and connected components. Also contains interfaces for processors for these basic types. Includes loading, saving and displaying images.

There is a newer version: 1.3.10
Show newest version
/**
 * Copyright (c) 2011, The University of Southampton and the individual contributors.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *   * 	Redistributions of source code must retain the above copyright notice,
 * 	this list of conditions and the following disclaimer.
 *
 *   *	Redistributions in binary form must reproduce the above copyright notice,
 * 	this list of conditions and the following disclaimer in the documentation
 * 	and/or other materials provided with the distribution.
 *
 *   *	Neither the name of the University of Southampton nor the names of its
 * 	contributors may be used to endorse or promote products derived from this
 * 	software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.openimaj.image;

import java.util.Comparator;

import org.apache.log4j.Logger;
import org.openimaj.image.analyser.PixelAnalyser;
import org.openimaj.image.colour.ColourSpace;
import org.openimaj.image.pixel.FValuePixel;
import org.openimaj.image.pixel.Pixel;
import org.openimaj.image.processor.KernelProcessor;
import org.openimaj.image.processor.PixelProcessor;
import org.openimaj.image.renderer.FImageRenderer;
import org.openimaj.image.renderer.RenderHints;
import org.openimaj.math.geometry.shape.Rectangle;
import org.openimaj.math.util.Interpolation;

import Jama.Matrix;

/**
 * Class representing a single-band floating-point image; that is an image where
 * each pixel is represented by a floating-point number.
 * 

* {@link FImage}s can be created from PGM files or from pixel arrays. If you * wish to read other types of files then use the {@link ImageUtilities} class * that provides read/write functions for {@link Image} objects. * * @author Jonathon Hare ([email protected]) */ public class FImage extends SingleBandImage { private static final long serialVersionUID = 1L; /** The logging class */ protected static Logger logger = Logger.getLogger(FImage.class); /** * The default number of sigmas at which the Gaussian function is truncated * when building a kernel */ protected static final float DEFAULT_GAUSS_TRUNCATE = 4.0f; /** The underlying pixels */ public float pixels[][]; /** * Create an {@link FImage} from an array of floating point values with the * given width and height. The length of the array must equal the width * multiplied by the height. * * @param array * An array of floating point values. * @param width * The width of the resulting image. * @param height * The height of th resulting image. */ public FImage(final float[] array, final int width, final int height) { assert (array.length == width * height); this.pixels = new float[height][width]; this.height = height; this.width = width; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) this.pixels[y][x] = array[y * width + x]; } /** * Create an {@link FImage} from an array of double values with the given * width and height. The length of the array must equal the width multiplied * by the height. The values will be downcast to floats. * * @param array * An array of floating point values. * @param width * The width of the resulting image. * @param height * The height of th resulting image. */ public FImage(final double[] array, final int width, final int height) { assert (array.length == width * height); this.pixels = new float[height][width]; this.height = height; this.width = width; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) this.pixels[y][x] = (float) array[y * width + x]; } /** * Create an {@link FImage} from an array of double values with the given * width and height. The length of the array must equal the width multiplied * by the height. The values will be downcast to floats. * * @param array * An array of floating point values. * @param width * The width of the resulting image. * @param height * The height of the resulting image. * @param offset * The offset in the array to begin reading from */ public FImage(final double[] array, final int width, final int height, int offset) { assert (array.length == width * height); this.pixels = new float[height][width]; this.height = height; this.width = width; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) this.pixels[y][x] = (float) array[offset + y * width + x]; } /** * Create an {@link FImage} from an array of floating point values. * * @param array * the array representing pixel values to copy data from. */ public FImage(final float[][] array) { this.pixels = array; this.height = array.length; this.width = array[0].length; } /** * Create an empty {@link FImage} of the given size. * * @param width * image width (number of columns) * @param height * image height (number of rows) */ public FImage(final int width, final int height) { this.pixels = new float[height][width]; this.height = height; this.width = width; } /** * Construct an {@link FImage} from an array of packed ARGB integers. * * @param data * array of packed ARGB pixels * @param width * the image width * @param height * the image height */ public FImage(final int[] data, final int width, final int height) { this.internalAssign(data, width, height); } /** * Construct an {@link FImage} from an array of packed ARGB integers using * the specified plane. * * @param data * array of packed ARGB pixels * @param width * the image width * @param height * the image height * @param plane * The {@link ARGBPlane} to copy data from */ public FImage(final int[] data, final int width, final int height, final ARGBPlane plane) { this.width = width; this.height = height; this.pixels = new float[height][width]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { final int rgb = data[x + y * width]; int colour = 0; switch (plane) { case RED: colour = ((rgb >> 16) & 0xff); break; case GREEN: colour = ((rgb >> 8) & 0xff); break; case BLUE: colour = ((rgb) & 0xff); break; default: break; } this.pixels[y][x] = colour; } } } /** * {@inheritDoc} * * @see org.openimaj.image.Image#abs() */ @Override public FImage abs() { for (int r = 0; r < this.height; r++) for (int c = 0; c < this.width; c++) this.pixels[r][c] = Math.abs(this.pixels[r][c]); return this; } /** * Adds the pixel values of the given {@link FImage} to the pixels of this * image. Returns a new {@link FImage} and does not affect this image or the * given image. This is a version of {@link Image#add(Image)} which takes an * {@link FImage}. This method directly accesses the underlying float[][] * and is therefore fast. This function returns a new {@link FImage}. * * @see org.openimaj.image.Image#add(Image) * @param im * {@link FImage} to add into this one. * @return A new {@link FImage} */ public FImage add(final FImage im) { if (!ImageUtilities.checkSameSize(this, im)) throw new AssertionError("images must be the same size"); final FImage newImage = new FImage(im.width, im.height); for (int r = 0; r < im.height; r++) for (int c = 0; c < im.width; c++) newImage.pixels[r][c] = this.pixels[r][c] + im.pixels[r][c]; return newImage; } /** * Returns a new {@link FImage} that contains the pixels of this image * increased by the given value. {@inheritDoc} * * @see org.openimaj.image.Image#add(java.lang.Object) */ @Override public FImage add(final Float num) { final FImage newImage = new FImage(this.width, this.height); final float fnum = num; for (int r = 0; r < this.height; r++) for (int c = 0; c < this.width; c++) newImage.pixels[r][c] = this.pixels[r][c] + fnum; return newImage; } /** * {@inheritDoc} This method throws an {@link UnsupportedOperationException} * if the given image is not an {@link FImage}. * * @see org.openimaj.image.Image#add(org.openimaj.image.Image) * @exception UnsupportedOperationException * if an unsupported type is added * @return a reference to this {@link FImage} */ @Override public FImage add(final Image im) { if (im instanceof FImage) return this.add((FImage) im); else throw new UnsupportedOperationException("Unsupported Type"); } /*** * Adds the given image pixel values to the pixel values of this image. * Version of {@link Image#addInplace(Image)} which takes an {@link FImage}. * This directly accesses the underlying float[][] and is therefore fast. * This function side-affects the pixels in this {@link FImage}. * * @see Image#addInplace(Image) * @param im * the FImage to add * @return a reference to this */ public FImage addInplace(final FImage im) { if (!ImageUtilities.checkSameSize(this, im)) throw new AssertionError("images must be the same size"); for (int r = 0; r < im.height; r++) for (int c = 0; c < im.width; c++) this.pixels[r][c] += im.pixels[r][c]; return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#addInplace(java.lang.Object) */ @Override public FImage addInplace(final Float num) { final float fnum = num; for (int r = 0; r < this.height; r++) for (int c = 0; c < this.width; c++) this.pixels[r][c] += fnum; return this; } /** * {@inheritDoc} This method throws an {@link UnsupportedOperationException} * if the given image is not an {@link FImage}. * * @see org.openimaj.image.Image#addInplace(org.openimaj.image.Image) * @exception UnsupportedOperationException * if an unsupported type is added * @return a reference to this {@link FImage} */ @Override public FImage addInplace(final Image im) { if (im instanceof FImage) return this.addInplace((FImage) im); else throw new UnsupportedOperationException("Unsupported Type"); } /** * {@inheritDoc} * * @see org.openimaj.image.Image#clip(java.lang.Object, java.lang.Object) */ @Override public FImage clip(final Float min, final Float max) { int r, c; for (r = 0; r < this.height; r++) { for (c = 0; c < this.width; c++) { if (this.pixels[r][c] < min) this.pixels[r][c] = 0; if (this.pixels[r][c] > max) this.pixels[r][c] = 1; } } return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#clipMax(java.lang.Object) */ @Override public FImage clipMax(final Float thresh) { final float fthresh = thresh; for (int r = 0; r < this.height; r++) { for (int c = 0; c < this.width; c++) { if (this.pixels[r][c] > fthresh) this.pixels[r][c] = 1; } } return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#clipMin(java.lang.Object) */ @Override public FImage clipMin(final Float thresh) { final float fthresh = thresh; for (int r = 0; r < this.height; r++) { for (int c = 0; c < this.width; c++) { if (this.pixels[r][c] < fthresh) this.pixels[r][c] = 0; } } return this; } /** * {@inheritDoc} * * @see org.openimaj.image.SingleBandImage#clone() */ @Override public FImage clone() { final FImage cpy = new FImage(this.width, this.height); int r; for (r = 0; r < this.height; r++) System.arraycopy(this.pixels[r], 0, cpy.pixels[r], 0, this.width); return cpy; } @Override public FImageRenderer createRenderer() { return new FImageRenderer(this); } @Override public FImageRenderer createRenderer(final RenderHints options) { return new FImageRenderer(this, options); } /** * Divides the pixels values of this image with the values from the given * image. This is a version of {@link Image#divide(Image)} which takes an * {@link FImage}. This directly accesses the underlying float[][] and is * therefore fast. This function returns a new {@link FImage}. * * @see Image#divide(Image) * @param im * the {@link FImage} to be the denominator. * @return A new {@link FImage} */ public FImage divide(final FImage im) { if (!ImageUtilities.checkSameSize(this, im)) throw new AssertionError("images must be the same size"); final FImage newImage = new FImage(im.width, im.height); int r, c; for (r = 0; r < im.height; r++) for (c = 0; c < im.width; c++) newImage.pixels[r][c] = this.pixels[r][c] / im.pixels[r][c]; return newImage; } /** * Divides the pixel values of this image with the values from the given * image. This is a version of {@link Image#divideInplace(Image)} which * takes an {@link FImage}. This directly accesses the underlying float[][] * and is therefore fast. This function side-affects this image. * * @see Image#divideInplace(Image) * @param im * the {@link FImage} to be the denominator * @return a reference to this {@link FImage} */ public FImage divideInplace(final FImage im) { if (!ImageUtilities.checkSameSize(this, im)) throw new AssertionError("images must be the same size"); for (int y = 0; y < this.height; y++) { for (int x = 0; x < this.width; x++) { this.pixels[y][x] /= im.pixels[y][x]; } } return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#divideInplace(java.lang.Object) */ @Override public FImage divideInplace(final Float val) { final float fval = val; for (int y = 0; y < this.height; y++) for (int x = 0; x < this.width; x++) this.pixels[y][x] /= fval; return this; } /** * Divide all pixels by a given value * * @param fval * the value * @return this image * @see org.openimaj.image.Image#divideInplace(java.lang.Object) */ public FImage divideInplace(final float fval) { for (int y = 0; y < this.height; y++) for (int x = 0; x < this.width; x++) this.pixels[y][x] /= fval; return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#divideInplace(org.openimaj.image.Image) */ @Override public FImage divideInplace(final Image im) { if (im instanceof FImage) return this.divideInplace((FImage) im); else throw new UnsupportedOperationException("Unsupported Type"); } /** * {@inheritDoc} * * @see org.openimaj.image.Image#extractROI(int, int, * org.openimaj.image.Image) */ @Override public FImage extractROI(final int x, final int y, final FImage out) { for (int r = y, rr = 0; rr < out.height; r++, rr++) { for (int c = x, cc = 0; cc < out.width; c++, cc++) { if (r < 0 || r >= this.height || c < 0 || c >= this.width) (out).pixels[rr][cc] = 0; else (out).pixels[rr][cc] = this.pixels[r][c]; } } return out; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#extractROI(int, int, int, int) */ @Override public FImage extractROI(final int x, final int y, final int w, final int h) { final FImage out = new FImage(w, h); for (int r = y, rr = 0; rr < h; r++, rr++) { for (int c = x, cc = 0; cc < w; c++, cc++) { if (r < 0 || r >= this.height || c < 0 || c >= this.width) out.pixels[rr][cc] = 0; else out.pixels[rr][cc] = this.pixels[r][c]; } } return out; } /** * {@inheritDoc} * * @see org.openimaj.image.SingleBandImage#fill(java.lang.Comparable) */ @Override public FImage fill(final Float colour) { for (int r = 0; r < this.height; r++) for (int c = 0; c < this.width; c++) this.pixels[r][c] = colour; return this; } /** * Fill an image with the given colour * * @param colour * the colour * @return the image * @see org.openimaj.image.SingleBandImage#fill(java.lang.Comparable) */ public FImage fill(final float colour) { for (int r = 0; r < this.height; r++) for (int c = 0; c < this.width; c++) this.pixels[r][c] = colour; return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#getContentArea() */ @Override public Rectangle getContentArea() { int minc = this.width, maxc = 0, minr = this.height, maxr = 0; for (int r = 0; r < this.height; r++) { for (int c = 0; c < this.width; c++) { if (this.pixels[r][c] > 0) { if (c < minc) minc = c; if (c > maxc) maxc = c; if (r < minr) minr = r; if (r > maxr) maxr = r; } } } return new Rectangle(minc, minr, maxc - minc, maxr - minr); } /** * Returns the pixels of the image as a vector (array) of doubles. * * @return the pixels of the image as a vector (array) of doubles. */ public double[] getDoublePixelVector() { final double f[] = new double[this.height * this.width]; for (int y = 0; y < this.height; y++) for (int x = 0; x < this.width; x++) f[x + y * this.width] = this.pixels[y][x]; return f; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#getField(org.openimaj.image.Image.Field) */ @Override public FImage getField(final Field f) { final FImage img = new FImage(this.width, this.height / 2); int r, r2, c; final int init = (f.equals(Field.ODD) ? 1 : 0); for (r = init, r2 = 0; r < this.height && r2 < this.height / 2; r += 2, r2++) { for (c = 0; c < this.width; c++) { img.pixels[r2][c] = this.pixels[r][c]; } } return img; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#getFieldCopy(org.openimaj.image.Image.Field) */ @Override public FImage getFieldCopy(final Field f) { final FImage img = new FImage(this.width, this.height); int r, c; for (r = 0; r < this.height; r += 2) { for (c = 0; c < this.width; c++) { if (f.equals(Field.EVEN)) { img.pixels[r][c] = this.pixels[r][c]; img.pixels[r + 1][c] = this.pixels[r][c]; } else { img.pixels[r][c] = this.pixels[r + 1][c]; img.pixels[r + 1][c] = this.pixels[r + 1][c]; } } } return img; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#getFieldInterpolate(org.openimaj.image.Image.Field) */ @Override public FImage getFieldInterpolate(final Field f) { final FImage img = new FImage(this.width, this.height); int r, c; for (r = 0; r < this.height; r += 2) { for (c = 0; c < this.width; c++) { if (f.equals(Field.EVEN)) { img.pixels[r][c] = this.pixels[r][c]; if (r + 2 == this.height) { img.pixels[r + 1][c] = this.pixels[r][c]; } else { img.pixels[r + 1][c] = 0.5F * (this.pixels[r][c] + this.pixels[r + 2][c]); } } else { img.pixels[r + 1][c] = this.pixels[r + 1][c]; if (r == 0) { img.pixels[r][c] = this.pixels[r + 1][c]; } else { img.pixels[r][c] = 0.5F * (this.pixels[r - 1][c] + this.pixels[r + 1][c]); } } } } return img; } /** * Returns the pixels of the image as a vector (array) of floats. * * @return the pixels of the image as a vector (array) of floats. */ public float[] getFloatPixelVector() { final float f[] = new float[this.height * this.width]; for (int y = 0; y < this.height; y++) for (int x = 0; x < this.width; x++) f[x + y * this.width] = this.pixels[y][x]; return f; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#getPixel(int, int) */ @Override public Float getPixel(final int x, final int y) { return this.pixels[y][x]; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#getPixelComparator() */ @Override public Comparator getPixelComparator() { return new Comparator() { @Override public int compare(final Float o1, final Float o2) { return o1.compareTo(o2); } }; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#getPixelInterp(double, double) * @see Interpolation#bilerp(double, double, double, double, double, double) */ @Override public Float getPixelInterp(final double x, final double y) { int x0 = (int) Math.floor(x); int x1 = x0 + 1; int y0 = (int) Math.floor(y); int y1 = y0 + 1; if (x0 < 0) x0 = 0; if (x0 >= this.width) x0 = this.width - 1; if (y0 < 0) y0 = 0; if (y0 >= this.height) y0 = this.height - 1; if (x1 < 0) x1 = 0; if (x1 >= this.width) x1 = this.width - 1; if (y1 < 0) y1 = 0; if (y1 >= this.height) y1 = this.height - 1; final float f00 = this.pixels[y0][x0]; final float f01 = this.pixels[y1][x0]; final float f10 = this.pixels[y0][x1]; final float f11 = this.pixels[y1][x1]; float dx = (float) (x - x0); float dy = (float) (y - y0); if (dx < 0) dx = 1 + dx; if (dy < 0) dy = 1 + dy; return Interpolation.bilerp(dx, dy, f00, f01, f10, f11); } /** * {@inheritDoc} * * @see org.openimaj.image.Image#getPixelInterp(double, double) * @see Interpolation#bilerp(double, double, double, double, double, double) */ @Override public Float getPixelInterp(final double x, final double y, final Float background) { final int x0 = (int) Math.floor(x); final int x1 = x0 + 1; final int y0 = (int) Math.floor(y); final int y1 = y0 + 1; boolean tx0, tx1, ty0, ty1; tx0 = ty0 = tx1 = ty1 = true; if (x0 < 0) tx0 = false; if (x0 >= this.width) tx0 = false; if (y0 < 0) ty0 = false; if (y0 >= this.height) ty0 = false; if (x1 < 0) tx1 = false; if (x1 >= this.width) tx1 = false; if (y1 < 0) ty1 = false; if (y1 >= this.height) ty1 = false; final double f00 = (ty0 && tx0 ? this.pixels[y0][x0] : background.floatValue()); // this.pixels[y0][x0]; final double f01 = (ty1 && tx0 ? this.pixels[y1][x0] : background.floatValue()); // this.pixels[y1][x0]; final double f10 = (ty0 && tx1 ? this.pixels[y0][x1] : background.floatValue()); // this.pixels[y0][x1]; final double f11 = (ty1 && tx1 ? this.pixels[y1][x1] : background.floatValue()); // this.pixels[y1][x1]; double dx = x - x0; double dy = y - y0; if (dx < 0) dx = 1 + dx; if (dy < 0) dy = 1 + dy; final double interpVal = Interpolation.bilerp(dx, dy, f00, f01, f10, f11); return (float) interpVal; } /** * Interpolate the value of a pixel at the given coordinates * * @param x * the x-ordinate * @param y * the y-ordinate * @param background * the background colour * @return the interpolated pixel value * @see org.openimaj.image.Image#getPixelInterp(double, double) * @see Interpolation#bilerp(double, double, double, double, double, double) */ public float getPixelInterpNative(final float x, final float y, final float background) { final int x0 = (int) Math.floor(x); final int x1 = x0 + 1; final int y0 = (int) Math.floor(y); final int y1 = y0 + 1; boolean tx0, tx1, ty0, ty1; tx0 = ty0 = tx1 = ty1 = true; if (x0 < 0) tx0 = false; if (x0 >= this.width) tx0 = false; if (y0 < 0) ty0 = false; if (y0 >= this.height) ty0 = false; if (x1 < 0) tx1 = false; if (x1 >= this.width) tx1 = false; if (y1 < 0) ty1 = false; if (y1 >= this.height) ty1 = false; final float f00 = (ty0 && tx0 ? this.pixels[y0][x0] : background); // this.pixels[y0][x0]; final float f01 = (ty1 && tx0 ? this.pixels[y1][x0] : background); // this.pixels[y1][x0]; final float f10 = (ty0 && tx1 ? this.pixels[y0][x1] : background); // this.pixels[y0][x1]; final float f11 = (ty1 && tx1 ? this.pixels[y1][x1] : background); // this.pixels[y1][x1]; float dx = x - x0; float dy = y - y0; if (dx < 0) dx = 1 + dx; if (dy < 0) dy = 1 + dy; final float interpVal = Interpolation.bilerpf(dx, dy, f00, f01, f10, f11); return interpVal; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#internalAssign(org.openimaj.image.Image) */ @Override public FImage internalCopy(final FImage im) { final int h = im.height; final int w = im.width; final float[][] impixels = im.pixels; for (int r = 0; r < h; r++) System.arraycopy(impixels[r], 0, this.pixels[r], 0, w); return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#internalAssign(org.openimaj.image.Image) */ @Override public FImage internalAssign(final FImage im) { this.pixels = im.pixels; this.height = im.height; this.width = im.width; return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#internalAssign(int [] data, int width, int * height) */ @Override public FImage internalAssign(final int[] data, final int width, final int height) { if (this.height != height || this.width != width) { this.height = height; this.width = width; this.pixels = new float[height][width]; } for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { final int rgb = data[x + width * y]; final int red = ((rgb >> 16) & 0xff); final int green = ((rgb >> 8) & 0xff); final int blue = ((rgb) & 0xff); // NTSC colour conversion: // This improves keypoint detection for some reason! final float fpix = 0.299f * red + 0.587f * green + 0.114f * blue; this.pixels[y][x] = ImageUtilities.BYTE_TO_FLOAT_LUT[(int) fpix]; } } return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#inverse() */ @Override public FImage inverse() { int r, c; final float max = this.max(); for (r = 0; r < this.height; r++) for (c = 0; c < this.width; c++) this.pixels[r][c] = max - this.pixels[r][c]; return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#max() */ @Override public Float max() { int r, c; float max = Float.MIN_VALUE; for (r = 0; r < this.height; r++) for (c = 0; c < this.width; c++) if (max < this.pixels[r][c]) max = this.pixels[r][c]; return max; } /** * Get the pixel with the maximum value. Returns an {@link FValuePixel} * which contains the location and value of the pixel. If there are multiple * pixels with the same value then the first is returned. Note that this * method assumes all pixel values are greater than 0. * * @return the maximum pixel as an {@link FValuePixel}. */ public FValuePixel maxPixel() { final FValuePixel max = new FValuePixel(-1, -1); max.value = -Float.MAX_VALUE; for (int y = 0; y < this.height; y++) { for (int x = 0; x < this.width; x++) { if (max.value < this.pixels[y][x]) { max.value = this.pixels[y][x]; max.x = x; max.y = y; } } } return max; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#min() */ @Override public Float min() { int r, c; float min = Float.MAX_VALUE; for (r = 0; r < this.height; r++) for (c = 0; c < this.width; c++) if (min > this.pixels[r][c]) min = this.pixels[r][c]; return min; } /** * Get the pixel with the minimum value. Returns an {@link FValuePixel} * which contains the location and value of the pixel. If there are multiple * pixels with the same value then the first is returned. Note that this * method assumes all pixel values are greater than 0. * * @return The minimum pixel as an {@link FValuePixel}. */ public FValuePixel minPixel() { final FValuePixel min = new FValuePixel(-1, -1); min.value = Float.MAX_VALUE; for (int y = 0; y < this.height; y++) for (int x = 0; x < this.width; x++) if (min.value > this.pixels[y][x]) { min.value = this.pixels[y][x]; min.x = x; min.y = y; } return min; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#multiply(java.lang.Object) */ @Override public FImage multiply(final Float num) { return super.multiply(num); } /** * Multiplies this image's pixel values by the corresponding pixel values in * the given image side-affecting this image. This is a version of * {@link Image#multiplyInplace(Image)} which takes an {@link FImage}. This * directly accesses the underlying float[][] and is therefore fast. This * function works inplace. * * @see Image#multiplyInplace(Image) * @param im * the {@link FImage} to multiply with this image * @return a reference to this image */ public FImage multiplyInplace(final FImage im) { if (!ImageUtilities.checkSameSize(this, im)) throw new AssertionError("images must be the same size"); for (int r = 0; r < this.height; r++) { for (int c = 0; c < this.width; c++) { this.pixels[r][c] *= im.pixels[r][c]; } } return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#multiplyInplace(java.lang.Object) */ @Override public FImage multiplyInplace(final Float num) { final float fnum = num; for (int r = 0; r < this.height; r++) { for (int c = 0; c < this.width; c++) { this.pixels[r][c] *= fnum; } } return this; } /** * Multiply all pixel values by the given value * * @param fnum * the value * @return this image * @see org.openimaj.image.Image#multiplyInplace(java.lang.Object) */ public FImage multiplyInplace(final float fnum) { for (int r = 0; r < this.height; r++) { for (int c = 0; c < this.width; c++) { this.pixels[r][c] *= fnum; } } return this; } /** * {@inheritDoc} This method will throw an * {@link UnsupportedOperationException} if the input input is not an * {@link FImage}. * * @see org.openimaj.image.Image#multiplyInplace(org.openimaj.image.Image) * @throws UnsupportedOperationException * if the given image is not an {@link FImage} */ @Override public FImage multiplyInplace(final Image im) { if (im instanceof FImage) return this.multiplyInplace((FImage) im); else throw new UnsupportedOperationException("Unsupported Type"); } /** * {@inheritDoc} * * @return A new {@link FImage} * @see org.openimaj.image.Image#newInstance(int, int) */ @Override public FImage newInstance(final int width, final int height) { return new FImage(width, height); } /** * {@inheritDoc} * * @see org.openimaj.image.Image#normalise() */ @Override public FImage normalise() { final float min = this.min(); final float max = this.max(); if (max == min) return this; for (int r = 0; r < this.height; r++) { for (int c = 0; c < this.width; c++) { this.pixels[r][c] = (this.pixels[r][c] - min) / (max - min); } } return this; } /** * {@inheritDoc} * * @see org.openimaj.image.SingleBandImage#process(org.openimaj.image.processor.KernelProcessor) */ @Override public FImage process(final KernelProcessor p) { return this.process(p, false); } /** * {@inheritDoc} This method has been overridden in {@link FImage} for * performance. * * @see org.openimaj.image.SingleBandImage#process(org.openimaj.image.processor.KernelProcessor, * boolean) */ @Override public FImage process(final KernelProcessor p, final boolean pad) { final FImage newImage = new FImage(this.width, this.height); final int kh = p.getKernelHeight(); final int kw = p.getKernelWidth(); final FImage tmp = new FImage(kw, kh); final int hh = kh / 2; final int hw = kw / 2; if (!pad) { for (int y = hh; y < this.height - (kh - hh); y++) { for (int x = hw; x < this.width - (kw - hw); x++) { newImage.pixels[y][x] = p.processKernel(this.extractROI(x - hw, y - hh, tmp)); } } } else { for (int y = 0; y < this.height; y++) { for (int x = 0; x < this.width; x++) { newImage.pixels[y][x] = p.processKernel(this.extractROI(x - hw, y - hh, tmp)); } } } return newImage; } /** * {@inheritDoc} This method has been overridden in {@link FImage} for * performance. * * @see org.openimaj.image.Image#processInplace(org.openimaj.image.processor.PixelProcessor) */ @Override public FImage processInplace(final PixelProcessor p) { for (int y = 0; y < this.height; y++) { for (int x = 0; x < this.width; x++) { this.pixels[y][x] = p.processPixel(this.pixels[y][x]); } } return this; } /** * {@inheritDoc} This method has been overridden in {@link FImage} for * performance. * * @see org.openimaj.image.Image#analyseWith(org.openimaj.image.analyser.PixelAnalyser) */ @Override public void analyseWith(final PixelAnalyser p) { p.reset(); for (int y = 0; y < this.height; y++) { for (int x = 0; x < this.width; x++) { p.analysePixel(this.pixels[y][x]); } } } /** * {@inheritDoc} * * @see org.openimaj.image.Image#setPixel(int, int, java.lang.Object) */ @Override public void setPixel(final int x, final int y, final Float val) { if (x >= 0 && x < this.width && y >= 0 && y < this.height) this.pixels[y][x] = val; } /** * Subtracts the given {@link FImage} from this image returning a new image * containing the result. * * @param im * The image to subtract from this image. * @return A new image containing the result. */ public FImage subtract(final FImage im) { if (!ImageUtilities.checkSameSize(this, im)) throw new AssertionError("images must be the same size"); final FImage newImage = new FImage(im.width, im.height); int r, c; for (r = 0; r < im.height; r++) for (c = 0; c < im.width; c++) newImage.pixels[r][c] = this.pixels[r][c] - im.pixels[r][c]; return newImage; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#subtract(java.lang.Object) */ @Override public FImage subtract(final Float num) { final FImage newImage = new FImage(this.width, this.height); for (int r = 0; r < this.height; r++) { for (int c = 0; c < this.width; c++) { newImage.pixels[r][c] = this.pixels[r][c] - num; } } return newImage; } /** * {@inheritDoc} Throws an {@link UnsupportedOperationException} if the * given image is not an {@link FImage}. * * @see org.openimaj.image.Image#subtract(org.openimaj.image.Image) * @throws UnsupportedOperationException * if the given image is not an {@link FImage}. */ @Override public FImage subtract(final Image input) { if (input instanceof FImage) return this.subtract((FImage) input); else throw new UnsupportedOperationException("Unsupported Type"); } /** * Subtracts (pixel-by-pixel) the given {@link FImage} from this image. * Side-affects this image. * * @param im * The {@link FImage} to subtract from this image. * @return A reference to this image containing the result. */ public FImage subtractInplace(final FImage im) { if (!ImageUtilities.checkSameSize(this, im)) throw new AssertionError("images must be the same size"); float pix1[][], pix2[][]; int r, c; pix1 = this.pixels; pix2 = im.pixels; for (r = 0; r < this.height; r++) for (c = 0; c < this.width; c++) pix1[r][c] -= pix2[r][c]; return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#subtractInplace(java.lang.Object) */ @Override public FImage subtractInplace(final Float num) { final float fnum = num; for (int r = 0; r < this.height; r++) { for (int c = 0; c < this.width; c++) { this.pixels[r][c] -= fnum; } } return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#subtractInplace(org.openimaj.image.Image) */ @Override public FImage subtractInplace(final Image im) { if (im instanceof FImage) return this.subtractInplace((FImage) im); else throw new UnsupportedOperationException("Unsupported Type"); } /** * {@inheritDoc} * * @see org.openimaj.image.Image#threshold(java.lang.Object) */ @Override public FImage threshold(final Float thresh) { final float fthresh = thresh; for (int r = 0; r < this.height; r++) { for (int c = 0; c < this.width; c++) { if (this.pixels[r][c] <= fthresh) this.pixels[r][c] = 0; else this.pixels[r][c] = 1; } } return this; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#toByteImage() */ @Override public byte[] toByteImage() { final byte[] pgmData = new byte[this.height * this.width]; for (int j = 0; j < this.height; j++) { for (int i = 0; i < this.width; i++) { int v = (int) (255.0f * this.pixels[j][i]); v = Math.max(0, Math.min(255, v)); pgmData[i + j * this.width] = (byte) (v & 0xFF); } } return pgmData; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#toPackedARGBPixels() */ @Override public int[] toPackedARGBPixels() { final int[] bimg = new int[this.width * this.height]; for (int r = 0; r < this.height; r++) { for (int c = 0; c < this.width; c++) { final int v = (Math.max(0, Math.min(255, (int) (this.pixels[r][c] * 255)))); final int rgb = 0xff << 24 | v << 16 | v << 8 | v; bimg[c + this.width * r] = rgb; } } return bimg; } /** * {@inheritDoc} * * @see java.lang.Object#toString() */ @Override public String toString() { String imageString = ""; for (int y = 0; y < this.height; y++) { for (int x = 0; x < this.width; x++) { imageString += String.format("%+.3f ", this.pixels[y][x]); if (x == 16) { if (this.width - 16 <= x) continue; imageString += "... "; x = this.width - 16; } } imageString += "\n"; if (y == 16) { if (this.height - 16 <= y) continue; y = this.height - 16; imageString += "... \n"; } } return imageString; } /** * Returns a string representation of every pixel in this image using the * format string (see {@link String#format(String, Object...)}) to format * each pixel value. * * @param format * The format string to use for each pixel output * @return A string representation of the image * @see String#format(String, Object...) */ public String toString(final String format) { String imageString = ""; for (int y = 0; y < this.height; y++) { for (int x = 0; x < this.width; x++) { imageString += String.format(format, this.pixels[y][x]); } imageString += "\n"; } return imageString; } /** * {@inheritDoc} * * @see org.openimaj.image.Image#transform(Jama.Matrix) */ @Override public FImage transform(final Matrix transform) { return super.transform(transform); } /** * {@inheritDoc} * * @see org.openimaj.image.Image#zero() */ @Override public FImage zero() { for (int r = 0; r < this.height; r++) { for (int c = 0; c < this.width; c++) { this.pixels[r][c] = 0; } } return this; } @Override public boolean equals(final Object o) { if (!(o instanceof FImage)) { return false; } return this.equalsThresh((FImage) o, 0); } /** * Compare this image against another using a threshold on the absolute * difference between pixel values in order to determine equality. * * @param o * the image to compare against * @param thresh * the threshold for determining equality * @return true images are the same size and if all pixel values have a * difference less than threshold; false otherwise. */ public boolean equalsThresh(final FImage o, final float thresh) { final FImage that = o; if (that.height != this.height || that.width != this.width) return false; for (int i = 0; i < this.height; i++) { for (int j = 0; j < this.width; j++) { if (Math.abs(that.pixels[i][j] - this.pixels[i][j]) > thresh) { return false; } } } return true; } /** * Get the value of the pixel at coordinate p * * @param p * The coordinate to get * * @return The pixel value at (x, y) */ public float getPixelNative(final Pixel p) { return this.getPixelNative(p.x, p.y); } /** * Get the value of the pixel at coordinate (x, y). * * @param x * The x-coordinate to get * @param y * The y-coordinate to get * * @return The pixel value at (x, y) */ public float getPixelNative(final int x, final int y) { return this.pixels[y][x]; } /** * Returns the pixels in this image as a vector (an array of the pixel * type). * * @param f * The array into which to place the data * @return The pixels in the image as a vector (a reference to the given * array). */ public float[] getPixelVectorNative(final float[] f) { for (int y = 0; y < this.getHeight(); y++) for (int x = 0; x < this.getWidth(); x++) f[x + y * this.getWidth()] = this.pixels[y][x]; return f; } /** * Sets the pixel at (x,y) to the given value. Side-affects * this image. * * @param x * The x-coordinate of the pixel to set * @param y * The y-coordinate of the pixel to set * @param val * The value to set the pixel to. */ public void setPixelNative(final int x, final int y, final float val) { this.pixels[y][x] = val; } /** * Convenience method to initialise an array of FImages * * @param num * array length * @param width * width of images * @param height * height of images * @return array of newly initialised images */ public static FImage[] createArray(final int num, final int width, final int height) { final FImage[] array = new FImage[num]; for (int i = 0; i < num; i++) { array[i] = new FImage(width, height); } return array; } /** * @return The sum of all the pixels in the image */ public float sum() { float sum = 0; for (final float[] row : this.pixels) { for (int i = 0; i < row.length; i++) { sum += row[i]; } } return sum; } /** * Convert this {@link FImage} to an RGB {@link MBFImage}. * * @return a new RGB colour image. */ public MBFImage toRGB() { return new MBFImage(ColourSpace.RGB, this.clone(), this.clone(), this.clone()); } @Override public FImage flipX() { final int hwidth = this.width / 2; for (int y = 0; y < this.height; y++) { for (int x = 0; x < hwidth; x++) { final int xx = this.width - x - 1; final float tmp = this.pixels[y][x]; this.pixels[y][x] = this.pixels[y][xx]; this.pixels[y][xx] = tmp; } } return this; } @Override public FImage flipY() { final int hheight = this.height / 2; for (int y = 0; y < hheight; y++) { final int yy = this.height - y - 1; for (int x = 0; x < this.width; x++) { final float tmp = this.pixels[y][x]; this.pixels[y][x] = this.pixels[yy][x]; this.pixels[yy][x] = tmp; } } return this; } /** * Overlay the given image on this image with the given alpha channel at the * given location. * * @param img * The image to overlay * @param alpha * The alpha channel to use * @param x * The location to draw the image * @param y * The location to draw the image * @return This image with the overlay on it */ public FImage overlayInplace(final FImage img, final FImage alpha, final int x, final int y) { final int sx = Math.max(x, 0); final int sy = Math.max(y, 0); final int ex = Math.min(this.width, x + img.getWidth()); final int ey = Math.min(this.height, y + img.getHeight()); for (int yc = sy; yc < ey; yc++) { for (int xc = sx; xc < ex; xc++) { final float a = alpha.pixels[yc - sy][xc - sx]; this.pixels[yc][xc] = (a * img.pixels[yc - sy][xc - sx] + (1 - a) * this.pixels[yc][xc]); } } return this; } /** * {@inheritDoc} *

* This method will overlay the given image at the given location with full * opacity. * * @see org.openimaj.image.Image#overlayInplace(org.openimaj.image.Image, * int, int) */ @Override public FImage overlayInplace(final FImage image, final int x, final int y) { return this.overlayInplace(image, this.clone().fill(1f), x, y); } /** * Create a random image of the given size. * * @param width * the width * @param height * the height * @return the image */ public static FImage randomImage(final int width, final int height) { final FImage img = new FImage(width, height); for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) img.pixels[y][x] = (float) Math.random(); return img; } @Override public FImage replace(Float target, Float replacement) { return replace((float) target, (float) replacement); } /** * Replace pixels of a certain colour with another colour. Side-affects this * image. * * @param target * the colour to fill the image with * @param replacement * the colour to fill the image with * @return A reference to this image. */ public FImage replace(float target, float replacement) { for (int r = 0; r < this.height; r++) for (int c = 0; c < this.width; c++) if (this.pixels[r][c] == target) this.pixels[r][c] = replacement; return this; } @Override public FImage extractCentreSubPix(float cx, float cy, FImage out) { final int width = out.width; final int height = out.height; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { final float ix = (float) (x + cx - (width - 1) * 0.5); final float iy = (float) (y + cy - (height - 1) * 0.5); out.pixels[y][x] = this.getPixelInterpNative(ix, iy, 0f); } } return out; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy