package imageprocessing;
import java.util.StringTokenizer;
import java.awt.*;
import java.awt.image.*;
import java.awt.geom.AffineTransform;
import java.net.*;
import javax.swing.*;
import java.io.Serializable;
import java.io.*;
import javax.imageio.ImageIO;
/**
* <p>RGBImage is a class for images which is supposed to be easier to
* use than the classes for image processing in the Java SDK API. The
* RGImage class contains three two-dimensional short arrays
* (matrices) called red green and blue which contains the pixel
* values of the image. Other colours then the three primary colours
* are composed as a combination of red, green and blue.
*
* <p>In order to be viewed properly, the colour values must be set in
* the interval from 0 to 255.
*
* <p>The RGBImage class contains methods for setting and getting the
* color matrices, showing the image, saving the image to a file and
* reading the images from a file or from a web-location.
*
* <p>Here is a <a href="../../RGBImage.java">link</a> to
* the source code.
*
* @see imageprocessing.IntensityImage
* @see imageprocessing.BinaryImage
* @see imageprocessing.HSIImage
* @author Per-Olav Rusås
*/
public class RGBImage implements Serializable
{
/**
* The Matrix with the red component of the image.
*/
protected short[][] red;
/**
* The Matrix with the green component of the image.
*/
protected short[][] green;
/**
* The Matrix with the blue component of the image.
*/
protected short[][] blue;
protected int imageType;
private static final int RED = 3;
private static final int GREEN = 2;
private static final int BLUE = 1;
public static final int NN_INTERPOL = 10;
public static final int BL_INTERPOL = 11;
/**
* Creates an empty RGBImage object.
*/
public RGBImage()
{
setColorImage (true);
}
/**
* A constructor that sets the color matrices.
* @param red The red component of the image.
* @param green The green component of the image.
* @param blue The blue component of the image.
*/
public RGBImage (short[][] red, short[][] green, short[][] blue)
{
this();
this.red = red;
this.green = green;
this.blue = blue;
}
/**
* A copy-constructor which copy another RGBImage.
* @param otherImage Another RGBImage.
*/
public RGBImage (RGBImage otherImage)
{
imageType = otherImage.imageType;
red = copy (otherImage.red);
green = copy (otherImage.green);
blue = copy (otherImage.blue);
}
/**
* Gets the reference to the matrix with the red colour component
* of the image. This method does not make a copy of the the
* matrix, but returns the reference to the RGBImage's colour
* matrix. This means that the returned reference can be used to
* manipulate the colour of the RGBImage.
* @return The red colour matrix.
*/
public short[][] getRed(){ return red; }
/**
* Gets the reference to the matrix with the green colour component
* of the image. This method does not make a copy of the the
* matrix, but returns the reference to the RGBImage's colour
* matrix. This means that the returned reference can be used to
* manipulate the colour of the RGBImage.
* @return The green colour matrix.
*/
public short[][] getGreen(){ return green; }
/**
* Gets the reference to the matrix with the blue colour component
* of the image. This method does not make a copy of the the
* matrix, but returns the reference to the RGBImage's colour
* matrix. This means that the returned reference can be used to
* manipulate the colour of the RGBImage.
* @return The blue colour matrix.
*/
public short[][] getBlue(){ return blue; }
/**
* Sets the red component of the image.
* @param red Matrix with the red colour values.
*/
public void setRed (short[][] red)
{
this.red = red;
}
/**
* Sets the green component of the image.
* @param green Matrix with the green colour values.
*/
public void setGreen (short[][] green)
{
this.green = green;
}
/**
* Sets the blue component of the image.
* @param blue Matrix with the blue colour values.
*/
public void setBlue (short[][] blue)
{
this.blue = blue;
}
/**
* The weights of the colors are usually different when the image
* is shown as a color image, or as a gray image. This method can
* be used to force the image to be shown as a color or gray
* image.
* @param isColorImage true if image is to be shown as a colored image.
*/
public void setColorImage (boolean isColorImage)
{
if (isColorImage)
imageType = BufferedImage.TYPE_INT_RGB;
else
imageType = BufferedImage.TYPE_BYTE_GRAY;
}
/**
* Returns true if the image will be shown as a colored image.
* @return true if image will be shown as a colored image.
*/
public boolean isColorImage()
{
return (imageType < BufferedImage.TYPE_BYTE_GRAY);
}
/**
* The width of the image. If some of the matrixes are missing, the
* method returns -1.
* @return The width of the image if all the matrices exist.
*/
public int getWidth()
{
if (red != null && green != null && blue != null)
return red[0].length;
else
return -1;
}
/**
* The height of the image. If some of the matrixes are missing, the
* method returns -1.
* @return The height of the image if all the matrices exist.
*/
public int getHeight()
{
if (red != null && green != null && blue != null)
return red.length;
else
return -1;
}
/**
* Returns a description of the image.
* @return A description.
*/
public String toString()
{
return "RGBImage-object:" +
"\n\tWidth: " + getWidth() +
"\n\tHeight: " + getHeight() +
"\n\tImage type: " + imageType;
}
/**
* Set the BufferedImage of the RGBImage.
* @param bImage the BufferedImage.
*/
public void setBImage (BufferedImage bImage)
{
updateMatrices (bImage);
}
/**
* Make a BufferedImage of the RGBImage
* @return a BufferedImage representing the RGBImage.
*/
public BufferedImage makeBufferedImage()
{
BufferedImage bImage = new BufferedImage (getWidth(), getHeight(),
imageType);
int shiftR = 8*(RED-1);
int shiftG = 8*(GREEN-1);
int shiftB = 1;
int rows = getHeight();
int cols = getWidth();
int[] data = new int[rows*cols];
int index;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
index = row*cols + col;
data[index] = (red[row][col] << shiftR) |
(green[row][col] << shiftG) |
blue[row][col];
}
}
setRGBData (bImage, data);
return bImage;
}
private void updateMatrices (BufferedImage bImage)
{
int shiftR = 8*(RED-1);
int shiftG = 8*(GREEN-1);
int shiftB = 8*(BLUE-1);
int bR = 255 << shiftR;
int bG = 255 << shiftG;
int bB = 255 << shiftB;
int rows = bImage.getHeight();
int cols = bImage.getWidth();
red = new short[rows][cols];
green = new short[rows][cols];
blue = new short[rows][cols];
int[] data = getRGBData (bImage);
int index;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
index = row*cols + col;
red[row][col] = (short)((data[index] & bR) >> shiftR);
green[row][col] = (short)((data[index] & bG) >> shiftG);
blue[row][col] = (short)((data[index] & bB) >> shiftB);
}
}
}
/**
* Load an image from a URL.
* @param url the URL of the web-location containing an image file
*/
public void loadURL (String url) throws IOException
{
URL u;
try
{
u = new URL (url);
}
catch (MalformedURLException mfu)
{
mfu.printStackTrace();
return;
}
updateMatrices (ImageIO.read (u));
}
/**
* Load an image from a file.
* @param filename The name of the image file.
*/
public void loadFile (String filename)
throws IOException
{
BufferedImage bImage = ImageIO.read (new File (filename));
setColorImage (bImage.getType() < BufferedImage.TYPE_BYTE_GRAY);
updateMatrices(bImage);
}
private static int[] getRGBData (BufferedImage bImage)
{
return bImage.getRGB (0, 0,
bImage.getWidth(), bImage.getHeight(),
null, 0, bImage.getWidth());
}
private static void setRGBData (BufferedImage bImage, int[] data)
{
bImage.setRGB (0, 0, bImage.getWidth(), bImage.getHeight(), data,
0, bImage.getWidth());
}
/**
* Save the image to a file. The format of the file is determined
* by the extension of the filename (jpg, gif or png).
* @param filename the name of the file.
*/
public void save (String filename) throws IOException
{
StringTokenizer st = new StringTokenizer (filename, ".");
st.nextToken();
String format = st.nextToken();
ImageIO.write (makeBufferedImage(), format, new File (filename));
}
/**
* Save the image to a JPEG-file.
* @param filename the name of the file.
* @param quality the quality of the image, between 0.0 and 1.0
*/
public void saveAsJPEG (String filename, double quality)
throws IOException
{
BufferedImage bImage = makeBufferedImage();
ImageIOTools.saveAsJPEG (bImage, filename, (float)quality);
}
/**
* Draw the image to a Graphics object at a given position.
* @param g The Graphics object to draw on.
* @param x The horizontal position of the image.
* @param y The vertical position of the image.
*/
public void draw (Graphics g, int x, int y)
{
BufferedImage bImage = makeBufferedImage();
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(bImage, new AffineTransform(1f,0f,0f,1f,x,y), null);
}
/**
* Draw the image to a Graphics object at a given position with a
* given scale and rotation.
* @param g The Graphics object to draw on.
* @param x The horizontal position of the image.
* @param y The vertical position of the image.
* @param scaleX The scale in x-direction. 1.0 means unscaled.
* @param scaleY The scale in y-direction. 1.0 means unscaled.
* @param rotateAngle Rotation of the image in degrees (0-360).
*/
public void draw (Graphics g, int x, int y, double scaleX, double scaleY,
double rotateAngle)
{
BufferedImage bImage = makeBufferedImage();
double rotateRadians = rotateAngle*Math.PI/180.0;
AffineTransform transformer = new AffineTransform();
transformer.scale (scaleX, scaleY);
transformer.rotate (rotateRadians);
AffineTransformOp transOp = new AffineTransformOp (transformer,
AffineTransformOp.TYPE_BILINEAR );
Graphics2D g2 = (Graphics2D) g;
g2.drawImage (bImage, transOp, x, y);
}
/**
* Show a JOptionPane-window including the image. The window is
* modal, which means that the program waits until it is closed.
* @param title A text placed on the bar on top of the window.
*/
public void show (String title)
{
BufferedImage bImage = makeBufferedImage();
if (bImage.getWidth() != 0)
{
ImageIcon icon = new ImageIcon (bImage);
JOptionPane.showMessageDialog (null, "", title,
JOptionPane.INFORMATION_MESSAGE,
icon);
}
else
{
JOptionPane.showMessageDialog (null,
"Error: The RGBImage object is empty",
"No image",
JOptionPane.ERROR_MESSAGE);
}
}
/**
* Shows a JOptionPane-window including the image. The window is
* modal, which means that the program waits until it is closed.
*/
public void show()
{
show ("");
}
/**
* Shows the RGBImage scaled with use of nearest neighbor
* interpolation. This method utilizes an ImageScaler to perform
* the scaling.
* @param scale The scale.
* @param title The title on top of the window.
*/
public void show (double scale, String title)
{
createScaledImage (scale, NN_INTERPOL).show (title);
}
/**
* Shows the RGBImage scaled with use of bilinear
* interpolation. This method utilizes an ImageScaler to perform
* the scaling.
* @param scale The scale.
* @param title The title on top of the window.
*/
public void showBL (double scale, String title)
{
createScaledImage (scale, BL_INTERPOL).show (title);
}
/**
* Creates a scaled image. This method utilizes an ImageScaler to
* perform the scaling. The parameter interpolType may be one of
* the constants RGBImage.NN_INTERPOL for nearest neighbor
* interpolation or RGBImage.BL_INTERPOL for bilinear interpolation.
* @param scale The scale.
* @param interpolType The type of interpolation
*/
public RGBImage createScaledImage (double scale, int interpolType)
{
ImageScaler redScaler = new ImageScaler (red);
ImageScaler greenScaler = new ImageScaler (green);
ImageScaler blueScaler = new ImageScaler (blue);
if (interpolType == NN_INTERPOL)
{
redScaler.scaleNN (scale);
greenScaler.scaleNN (scale);
blueScaler.scaleNN (scale);
}
else if (interpolType == BL_INTERPOL)
{
redScaler.scaleBL (scale);
greenScaler.scaleBL (scale);
blueScaler.scaleBL (scale);
}
else
{
throw new RuntimeException ("\n----------------\n"
+ "Error in HSIImage.createScaledImage."
+ "\nImpossible value of parameter "
+ "interpolType,\nshould be either "
+ "RGBImage.NN_INTERPOL or "
+ "RGBImage.BL_INTERPOL"
+ "\n----------------\n");
}
return new RGBImage (redScaler.getProcessedData(),
greenScaler.getProcessedData(),
blueScaler.getProcessedData());
}
/**
* A method for copying of two-dimensional arrays (matrices).
* @param array The array to be copied.
* @return A copy of the array.
*/
public static short[][] copy (short[][] array)
{
short[][] copiedArray = new short[array.length][array[0].length];
for (int row = 0; row < array.length; row++)
{
for (int col = 0; col < array[0].length; col++)
{
copiedArray[row][col] = array[row][col];
}
}
return copiedArray;
}
}