org.openimaj.image.ImageUtilities Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core-image Show documentation
Show all versions of core-image Show documentation
Core definitions of images, pixels and connected components. Also contains interfaces for processors for these basic types.
Includes loading, saving and displaying images.
The 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.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.ComponentSampleModel;
import java.awt.image.DataBufferByte;
import java.awt.image.WritableRaster;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.util.Map;
import java.util.StringTokenizer;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import org.apache.commons.lang.ArrayUtils;
import org.apache.sanselan.ImageFormat;
import org.apache.sanselan.Sanselan;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.byteSources.ByteSourceInputStream;
import org.openimaj.image.colour.ColourSpace;
import org.openimaj.io.InputStreamObjectReader;
/**
* A static utility class with methods for dealing with images.
*
* @author Jonathon Hare ([email protected])
*/
public class ImageUtilities {
/**
* An {@link InputStreamObjectReader} for reading {@link FImage}s.
*/
public static final InputStreamObjectReader FIMAGE_READER = new InputStreamObjectReader() {
@Override
public FImage read(final InputStream stream) throws IOException {
return ImageUtilities.readF(stream);
}
@Override
public boolean canRead(final InputStream stream, final String name) {
try {
final ByteSource src = new ByteSourceInputStream(stream, name);
return Sanselan.guessFormat(src) != ImageFormat.IMAGE_FORMAT_UNKNOWN;
} catch (final Exception e) {
return false;
}
}
};
/**
* An {@link InputStreamObjectReader} for reading {@link MBFImage}s.
*/
public static final InputStreamObjectReader MBFIMAGE_READER = new InputStreamObjectReader() {
@Override
public MBFImage read(final InputStream stream) throws IOException {
return ImageUtilities.readMBF(stream);
}
@Override
public boolean canRead(final InputStream stream, final String name) {
try {
final ByteSource src = new ByteSourceInputStream(stream, name);
return Sanselan.guessFormat(src) != ImageFormat.IMAGE_FORMAT_UNKNOWN;
} catch (final Exception e) {
return false;
}
}
};
/** Lookup table for byte->float conversion */
public final static float[] BYTE_TO_FLOAT_LUT;
// Static initialisation
static {
BYTE_TO_FLOAT_LUT = new float[256];
for (int i = 0; i < ImageUtilities.BYTE_TO_FLOAT_LUT.length; i++)
ImageUtilities.BYTE_TO_FLOAT_LUT[i] = i / 255f;
}
private ImageUtilities() {
// don't allow instances to be created
}
/**
* Calculate normalised RGB planes. Extracts the planes from the given RGB
* BufferedImage and returns an array of FImage of length 3. The images are
* ordered Red, Green and Blue.
*
* @param bimg
* A {@link BufferedImage} from which the planes are extracted.
* @return An array of {@link FImage}.
*/
public static FImage[] getNormalisedColourPlanes(final BufferedImage bimg) {
final FImage[] images = new FImage[3];
final BufferedImage workingImage = ImageUtilities.createWorkingImage(bimg);
final int[] data = workingImage.getRGB(0, 0, workingImage.getWidth(), workingImage.getHeight(), null, 0,
workingImage.getWidth());
images[0] = new FImage(data, bimg.getWidth(), bimg.getHeight(), ARGBPlane.RED);
images[1] = new FImage(data, bimg.getWidth(), bimg.getHeight(), ARGBPlane.GREEN);
images[2] = new FImage(data, bimg.getWidth(), bimg.getHeight(), ARGBPlane.BLUE);
int r, c;
for (r = 0; r < images[0].height; r++) {
for (c = 0; c < images[0].width; c++) {
final float norm = (float) Math.sqrt(
(images[0].pixels[r][c] * images[0].pixels[r][c]) +
(images[1].pixels[r][c] * images[1].pixels[r][c]) +
(images[2].pixels[r][c] * images[2].pixels[r][c])
);
if (norm == 0 && images[0].pixels[r][c] == 0)
images[0].pixels[r][c] /= (1.0 / Math.sqrt(3.0));
else
images[0].pixels[r][c] /= norm;
if (norm == 0 && images[1].pixels[r][c] == 0)
images[1].pixels[r][c] /= (1.0 / Math.sqrt(3.0));
else
images[1].pixels[r][c] /= norm;
if (norm == 0 && images[2].pixels[r][c] == 0)
images[2].pixels[r][c] /= (1.0 / Math.sqrt(3.0));
else
images[2].pixels[r][c] /= norm;
}
}
return images;
}
/**
* Returns a ARGB BufferedImage, even if the input BufferedImage is not ARGB
* format.
*
* @param bimg
* The {@link BufferedImage} to normalise to ARGB
* @return An ARGB {@link BufferedImage}
*/
public static BufferedImage createWorkingImage(final BufferedImage bimg) {
// to avoid performance complications in the getRGB method, we
// pre-calculate the RGB rep of the image
BufferedImage workingImage;
if (bimg.getType() == BufferedImage.TYPE_INT_ARGB) {
workingImage = bimg;
} else {
workingImage = new BufferedImage(bimg.getWidth(), bimg.getHeight(), BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = workingImage.createGraphics();
g2d.drawImage(bimg, null, 0, 0);
}
return workingImage;
}
/**
* Write the given image to the given file with the given format name.
* Format names are the same as used by
* {@link ImageIO#write(java.awt.image.RenderedImage, String, File)}.
*
* @param image
* The image to write.
* @param formatName
* a {@link String} containing the informal name of the format.
* @param output
* The {@link File} to write the image to.
* @throws IOException
* If the image cannot be written to the file.
*/
public static void write(final Image, ?> image, final String formatName, final File output) throws IOException {
ImageIO.write(ImageUtilities.createBufferedImageForDisplay(image), formatName, output);
}
/**
* Write the given image to the given file with the given format name.
* Format names are the same as used by
* {@link ImageIO#write(java.awt.image.RenderedImage, String, OutputStream)}
* .
*
* @param image
* The image to write.
* @param formatName
* a {@link String} containing the informal name of the format.
* @param output
* The {@link OutputStream} to write the image to.
* @throws IOException
* If the image cannot be written to the file.
*/
public static void write(final Image, ?> image, final String formatName, final OutputStream output)
throws IOException
{
ImageIO.write(ImageUtilities.createBufferedImageForDisplay(image), formatName, output);
}
/**
* Write the given image to the given file with the given format name.
* Format names are the same as used by
* {@link ImageIO#write(java.awt.image.RenderedImage, String, ImageOutputStream)}
* .
*
* @param image
* The image to write.
* @param formatName
* a {@link String} containing the informal name of the format.
* @param output
* The {@link ImageOutputStream} to write the image to.
* @throws IOException
* If the image cannot be written to the file.
*/
public static void write(final Image, ?> image, final String formatName, final ImageOutputStream output)
throws IOException
{
ImageIO.write(ImageUtilities.createBufferedImageForDisplay(image), formatName, output);
}
/**
* Write the given image to the given file, guessing the format name from
* the extension. Format names are the same as used by
* {@link ImageIO#write(java.awt.image.RenderedImage, String, File)}.
*
* @param image
* The image to write.
* @param output
* The {@link File} to write the image to.
* @throws IOException
* If the image cannot be written to the file.
*/
public static void write(final Image, ?> image, final File output) throws IOException {
final String name = output.getName();
String format = name.substring(name.lastIndexOf(".") + 1);
format = format.toLowerCase().trim();
ImageIO.write(ImageUtilities.createBufferedImageForDisplay(image), format, output);
}
/**
* Create an FImage from a buffered image.
*
* @param image
* the image
* @return an FImage representation of the input image
*/
public static FImage createFImage(final BufferedImage image) {
final BufferedImage bimg = ImageUtilities.createWorkingImage(image);
final int[] data = bimg.getRGB(0, 0, bimg.getWidth(), bimg.getHeight(), null, 0, bimg.getWidth());
return new FImage(data, bimg.getWidth(), bimg.getHeight());
}
/**
* Create an MBFImage from a buffered image.
*
* @param image
* the image
* @param alpha
* should the resultant MBFImage have an alpha channel
* @return an MBFImage representation of the input image
*/
public static MBFImage createMBFImage(final BufferedImage image, final boolean alpha) {
final BufferedImage bimg = ImageUtilities.createWorkingImage(image);
final int[] data = bimg.getRGB(0, 0, bimg.getWidth(), bimg.getHeight(), null, 0, bimg.getWidth());
return new MBFImage(data, bimg.getWidth(), bimg.getHeight(), alpha);
}
/**
* Reads an {@link FImage} from the given file.
*
* @param input
* The file to read the {@link FImage} from.
* @return An {@link FImage}
* @throws IOException
* if the file cannot be read
*/
public static FImage readF(final File input) throws IOException {
return ImageUtilities.createFImage(ExtendedImageIO.read(input));
}
/**
* Reads an {@link FImage} from the given input stream.
*
* @param input
* The input stream to read the {@link FImage} from.
* @return An {@link FImage}
* @throws IOException
* if the stream cannot be read
*/
public static FImage readF(final InputStream input) throws IOException {
return ImageUtilities.createFImage(ExtendedImageIO.read(input));
}
/**
* Reads an {@link FImage} from the given URL.
*
* @param input
* The URL to read the {@link FImage} from.
* @return An {@link FImage}
* @throws IOException
* if the URL stream cannot be read
*/
public static FImage readF(final URL input) throws IOException {
return ImageUtilities.createFImage(ExtendedImageIO.read(input));
}
/**
* Reads an {@link MBFImage} from the given file.
*
* @param input
* The file to read the {@link MBFImage} from.
* @return An {@link MBFImage}
* @throws IOException
* if the file cannot be read
*/
public static MBFImage readMBF(final File input) throws IOException {
return ImageUtilities.createMBFImage(ExtendedImageIO.read(input), false);
}
/**
* Reads an {@link MBFImage} from the given input stream.
*
* @param input
* The input stream to read the {@link MBFImage} from.
* @return An {@link MBFImage}
* @throws IOException
* if the stream cannot be read
*/
public static MBFImage readMBF(final InputStream input) throws IOException {
return ImageUtilities.createMBFImage(ExtendedImageIO.read(input), false);
}
/**
* Reads an {@link MBFImage} from the given URL.
*
* @param input
* The URL to read the {@link MBFImage} from.
* @return An {@link MBFImage}
* @throws IOException
* if the URL stream cannot be read
*/
public static MBFImage readMBF(final URL input) throws IOException {
return ImageUtilities.createMBFImage(ExtendedImageIO.read(input), false);
}
/**
* Reads an {@link MBFImage} from the given file. The resultant MBImage will
* contain an alpha channel
*
* @param input
* The file to read the {@link MBFImage} from.
* @return An {@link MBFImage}
* @throws IOException
* if the file cannot be read
*/
public static MBFImage readMBFAlpha(final File input) throws IOException {
return ImageUtilities.createMBFImage(ExtendedImageIO.read(input), true);
}
/**
* Reads an {@link MBFImage} from the given input stream. The resultant
* MBImage will contain an alpha channel
*
* @param input
* The input stream to read the {@link MBFImage} from.
* @return An {@link MBFImage}
* @throws IOException
* if the stream cannot be read
*/
public static MBFImage readMBFAlpha(final InputStream input) throws IOException {
return ImageUtilities.createMBFImage(ExtendedImageIO.read(input), true);
}
/**
* Reads an {@link MBFImage} from the given URL. The resultant MBImage will
* contain an alpha channel
*
* @param input
* The URL to read the {@link MBFImage} from.
* @return An {@link MBFImage}
* @throws IOException
* if the URL stream cannot be read
*/
public static MBFImage readMBFAlpha(final URL input) throws IOException {
return ImageUtilities.createMBFImage(ExtendedImageIO.read(input), true);
}
/**
* Checks whether the width and height of all the given images match.
*
* @param images
* The images to compare sizes.
* @return TRUE if all the images are the same size; FALSE otherwise
*/
protected static boolean checkSameSize(final Image, ?>... images) {
if (images == null || images.length == 0)
return true;
final Image, ?> image = images[0];
final int w = image.getWidth();
final int h = image.getHeight();
return ImageUtilities.checkSize(h, w, images);
}
/**
* Checks whether the width and height of all the given images match the
* given width and height.
*
* @param h
* The height to match against all the images
* @param w
* The width to match against all the images
* @param images
* The images to compare sizes.
* @return TRUE if all the images are wxh
in size; FALSE
* otherwise
*/
protected static boolean checkSize(final int h, final int w, final Image, ?>... images) {
for (final Image, ?> image : images)
if (image.getHeight() != h || image.getWidth() != w)
return false;
return true;
}
/**
* Checks whether the width and height of all the given images match the
* given width and height.
*
* @param h
* The height to match against all the images
* @param w
* The width to match against all the images
* @param images
* The images to compare sizes.
* @return TRUE if all the images are wxh
in size; FALSE
* otherwise
*/
protected static boolean checkSize(final int h, final int w, final Iterable extends Image, ?>> images) {
for (final Image, ?> image : images)
if (image.getHeight() != h || image.getWidth() != w)
return false;
return true;
}
/**
* Reads a PNM header from the byte array containing the PNM binary data.
* The headerData
variable will be populated with the header
* information. Returns the number of bytes read from the array.
*
* @param data
* The PNM binary data.
* @param headerData
* A {@link Map} to populate with header information.
* @return The number of bytes read from the array.
* @throws IOException
* if the byte array does not contain PNM information.
*/
protected static int pnmReadHeader(final byte[] data, final Map headerData) throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(data);
final InputStreamReader isr = new InputStreamReader(bais);
final BufferedReader br = new BufferedReader(isr);
int count = 0, bytesRead = 0;
while (count < 4) {
final String line = br.readLine();
final StringTokenizer st = new StringTokenizer(line);
while (st.hasMoreTokens()) {
final String tok = st.nextToken();
if (tok.startsWith("#"))
break;
else {
switch (count) {
case 0: // magic
headerData.put("magic", Integer.decode(tok.substring(1)));
break;
case 1: // width
headerData.put("width", Integer.decode(tok));
break;
case 2: // height
headerData.put("height", Integer.decode(tok));
break;
case 3: // maxval
headerData.put("maxval", Integer.decode(tok));
break;
}
count++;
}
}
bytesRead += (line.length() + 1); // +1 for the newlines
}
return bytesRead;
}
/**
* Returns the contents of a file in a byte array.
*
* @param file
* The file to read
* @return A byte array representation of the file.
* @throws IOException
* if the file cannot be read fully.
*/
protected static byte[] getBytes(final File file) throws IOException {
InputStream is = null;
try {
is = new FileInputStream(file);
// Get the size of the file
final long length = file.length();
// You cannot create an array using a long type.
// It needs to be an int type.
// Before converting to an int type, check
// to ensure that file is not larger than Integer.MAX_VALUE.
if (length > Integer.MAX_VALUE) {
// File is too large
}
// Create the byte array to hold the data
final byte[] bytes = new byte[(int) length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < bytes.length
&& (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0)
{
offset += numRead;
}
// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read file " + file.getName());
}
return bytes;
} finally {
// Close the input stream and return bytes
if (is != null)
is.close();
}
}
/**
* Converts the input stream to a byte array. The input stream is fully
* read.
*
* @param stream
* The {@link InputStream} to convert to byte array
* @return A byte array representation of the {@link InputStream} data.
* @throws IOException
* if the input stream cannot be fully read.
*/
protected static byte[] getBytes(final InputStream stream) throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final byte[] b = new byte[4096];
while (stream.read(b) > 0)
baos.write(b);
return baos.toByteArray();
}
/**
* Convert any image to a {@link BufferedImage}.
*
* @param img
* image to convert
* @return BufferedImage representation
*/
public static BufferedImage createBufferedImage(final Image, ?> img) {
return ImageUtilities.createBufferedImage(img, null);
}
/**
* Convert any image to a {@link BufferedImage}.
*
* @param img
* image to convert
* @param bimg
* BufferedImage to draw into if possible. Can be null.
* @return BufferedImage representation
*/
public static BufferedImage createBufferedImage(final Image, ?> img, BufferedImage bimg) {
if (bimg == null || bimg.getWidth() != img.getWidth() || bimg.getHeight() != img.getHeight()
|| bimg.getType() != BufferedImage.TYPE_INT_ARGB)
bimg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
bimg.setRGB(0, 0, img.getWidth(), img.getHeight(), img.toPackedARGBPixels(), 0, img.getWidth());
return bimg;
}
/**
* Convert any image to a {@link BufferedImage}.
*
* @param img
* image to convert
* @return BufferedImage representation
*/
public static BufferedImage createBufferedImageForDisplay(final Image, ?> img) {
if (img instanceof MBFImage)
return ImageUtilities.createBufferedImageForDisplay((MBFImage) img);
else if (img instanceof FImage)
return ImageUtilities.createBufferedImage((FImage) img);
return ImageUtilities.createBufferedImage(img);
}
/**
* Convert any image to a {@link BufferedImage}.
*
* @param img
* image to convert
* @param bimg
* BufferedImage to draw into if possible. Can be null.
* @return BufferedImage representation
*/
public static BufferedImage createBufferedImageForDisplay(final Image, ?> img, final BufferedImage bimg) {
if (img instanceof MBFImage)
return ImageUtilities.createBufferedImageForDisplay((MBFImage) img, bimg);
else if (img instanceof FImage)
return ImageUtilities.createBufferedImage((FImage) img, bimg);
return ImageUtilities.createBufferedImage(img, bimg);
}
/**
* Efficiently create a TYPE_3BYTE_BGR for display if possible. This is
* typically much faster than to create and display than an ARGB buffered
* image. If the input image is not in RGB format, then the ARGB form will
* be returned instead.
*
* @param img
* the image to convert
* @return the converted image
*/
public static BufferedImage createBufferedImageForDisplay(final MBFImage img) {
return ImageUtilities.createBufferedImageForDisplay(img, null);
}
/**
* Efficiently create a TYPE_3BYTE_BGR for display if possible. This is
* typically much faster than to create and display than an ARGB buffered
* image. If the input image is not in RGB format, then the ARGB form will
* be returned instead.
*
* @param img
* the image to convert
* @param ret
* the image to draw into if possible. Can be null.
* @return the converted image. Might not be the same as the ret parameter.
*/
public static BufferedImage createBufferedImageForDisplay(final MBFImage img, BufferedImage ret) {
if (img.colourSpace != ColourSpace.RGB)
return ImageUtilities.createBufferedImage(img, ret);
final int width = img.getWidth();
final int height = img.getHeight();
if (ret == null || ret.getWidth() != width || ret.getHeight() != height
|| ret.getType() != BufferedImage.TYPE_3BYTE_BGR)
ret = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
final WritableRaster raster = ret.getRaster();
final float[][] r = img.getBand(0).pixels;
final float[][] g = img.getBand(1).pixels;
final float[][] b = img.getBand(2).pixels;
final ComponentSampleModel sm = (ComponentSampleModel) raster.getSampleModel();
final DataBufferByte db = (DataBufferByte) raster.getDataBuffer();
final int scanlineStride = sm.getScanlineStride();
final int pixelStride = sm.getPixelStride();
final byte[] data = db.getData();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
data[y * scanlineStride + x * pixelStride + 2] = (byte)
Math.max(0, Math.min(255, (int) (r[y][x] * 255)));
data[y * scanlineStride + x * pixelStride + 1] = (byte)
Math.max(0, Math.min(255, (int) (g[y][x] * 255)));
data[y * scanlineStride + x * pixelStride] = (byte)
Math.max(0, Math.min(255, (int) (b[y][x] * 255)));
}
}
return ret;
}
/**
* Efficiently create a TYPE_BYTE_GRAY for display. This is typically much
* faster than to create and display than an ARGB buffered image.
*
* @param img
* the image to convert
* @return the converted image
*/
public static BufferedImage createBufferedImage(final FImage img) {
return ImageUtilities.createBufferedImage(img, null);
}
/**
* Efficiently create a TYPE_BYTE_GRAY for display. This is typically much
* faster than to create and display than an ARGB buffered image.
*
* @param img
* the image to convert
* @param ret
* BufferedImage to draw into if possible. Can be null.
* @return the converted image
*/
public static BufferedImage createBufferedImage(final FImage img, BufferedImage ret) {
final int width = img.getWidth();
final int height = img.getHeight();
if (ret == null || ret.getWidth() != width || ret.getHeight() != height
|| ret.getType() != BufferedImage.TYPE_BYTE_GRAY)
ret = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
final WritableRaster raster = ret.getRaster();
final float[][] p = img.pixels;
final ComponentSampleModel sm = (ComponentSampleModel) raster.getSampleModel();
final DataBufferByte db = (DataBufferByte) raster.getDataBuffer();
final int scanlineStride = sm.getScanlineStride();
final int pixelStride = sm.getPixelStride();
final byte[] data = db.getData();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
data[y * scanlineStride + x * pixelStride] = (byte) (Math.max(0, Math.min(255, (int) (p[y][x] * 255))));
}
}
return ret;
}
/**
* Write an image to a {@link DataOutput}.
*
* @param img
* the image
* @param formatName
* the format
* @param out
* the output
* @throws IOException
* if an error occurs
*/
public static void write(final Image, ?> img, final String formatName, final DataOutput out) throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageUtilities.write(img, formatName, baos);
out.writeInt(baos.size());
out.write(baos.toByteArray());
}
/**
* Read an {@link FImage} from a DataInput
*
* @param in
* input
* @return new FImage
* @throws IOException
* if error occurs
*/
public static FImage readF(final DataInput in) throws IOException {
final int sz = in.readInt();
final byte[] bytes = new byte[sz];
in.readFully(bytes);
return ImageUtilities.readF(new ByteArrayInputStream(bytes));
}
/**
* Assign the contents of a {@link BufferedImage} to an {@link Image}.
*
* @param
* the type of {@link Image}
* @param img
* the {@link BufferedImage} to copy
* @param oiImage
* the {@link Image} to fill
* @return the given input image.
*/
public static > I assignBufferedImage(final BufferedImage img, final I oiImage) {
final BufferedImage bimg = ImageUtilities.createWorkingImage(img);
final int[] data = bimg.getRGB(0, 0, bimg.getWidth(), bimg.getHeight(), null, 0, bimg.getWidth());
return oiImage.internalAssign(data, bimg.getWidth(), bimg.getHeight());
}
/**
* Alpha composites the pixel p1 with the pixel p2, returning the value in
* pixel p1
*
* @param p1
* The first pixel
* @param p2
* The second pixel
* @return The updates first pixel p1
*/
public static float[] alphaCompositePixel(final float[] p1, final float[] p2)
{
final float thisR = p1[0];
final float thisG = p1[1];
final float thisB = p1[2];
float thisA = 1f;
if (p1.length == 4)
thisA = p1[3];
float thatA = 1f;
if (p2.length == 4)
thatA = p2[3];
final float thatR = p2[0];
final float thatG = p2[1];
final float thatB = p2[2];
float a = thatA + thisA * (1 - thatA);
a = a > 1.0f ? 1.0f : a;
float r = thatR * thatA + (thisR * thisA) * (1 - thatA);
r = r > 1.0f ? 1.0f : r;
float g = thatG * thatA + (thisG * thisA) * (1 - thatA);
g = g > 1.0f ? 1.0f : g;
float b = thatB * thatA + (thisB * thisA) * (1 - thatA);
b = b > 1.0f ? 1.0f : b;
p1[0] = r;
p1[1] = g;
p1[2] = b;
if (p1.length == 4)
p1[3] = a;
return p1;
}
/**
* @param out
* where the write the composition
* @param thisR
* @param thisG
* @param thisB
* @param thisA
*
* @param thatR
* @param thatG
* @param thatB
* @param thatA
*
* @return returns out
*/
public static float[] alphaCompositePixel(
float[] out,
float thisR, float thisG, float thisB, float thisA,
float thatR, float thatG, float thatB, float thatA
)
{
float a = thatA + thisA * (1 - thatA);
a = a > 1.0f ? 1.0f : a;
float r = thatR * thatA + (thisR * thisA) * (1 - thatA);
r = r > 1.0f ? 1.0f : r;
float g = thatG * thatA + (thisG * thisA) * (1 - thatA);
g = g > 1.0f ? 1.0f : g;
float b = thatB * thatA + (thisB * thisA) * (1 - thatA);
b = b > 1.0f ? 1.0f : b;
out[0] = r;
out[1] = g;
out[2] = b;
out[3] = a;
return out;
}
/**
* Alpha composites the pixel p1 with the pixel p2, returning the value in
* pixel p1
*
* @param p1
* The first pixel
* @param p2
* The second pixel
* @return The updates first pixel p1
*/
public static float[] alphaCompositePixel(final Float[] p1, final Float[] p2)
{
final float[] p1p = new float[p1.length];
for (int b = 0; b < p1.length; b++)
p1p[b] = p1[b];
final float[] p2p = new float[p2.length];
for (int b = 0; b < p2.length; b++)
p2p[b] = p2[b];
return ImageUtilities.alphaCompositePixel(p1p, p2p);
}
/**
* Test if a given image output format name is supported
*
* @param fmt
* the format name
* @return true if supported; false otherwise
*/
public static boolean isWriteFormatSupported(String fmt) {
return ArrayUtils.contains(ImageIO.getWriterFormatNames(), fmt);
}
/**
* Test if the image output format suggested by the extension of the given
* filename is supported
*
* @param file
* the file
* @return true if supported; false otherwise
*/
public static boolean isWriteFormatSupported(File file) {
final String name = file.getName();
String format = name.substring(name.lastIndexOf(".") + 1);
format = format.toLowerCase().trim();
return ArrayUtils.contains(ImageIO.getWriterFormatNames(), format);
}
}