import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.*;
import java.io.*;
import java.awt.*;
import javax.swing.*;
import java.net.*;
/**
* The EzImage class makes accessing and manipulating images straightforward.
* Images can be loaded from files in GIF, JPEG, or PNG format and manipulated
* through a variety of array operations.
* Both gray-scale and color images are supported.
*
* The ideas and much of the code are largely from the class EasyBufferedImage
* by Kenny Hunt at UW-Lacrosse.
*
* @author Kenny Hunt, University of Wisconin - Lacrosse
* @author Dave Musicant, Carleton College
* @author Jeff Ondich, Carleton College
*/
public class EzImage
{
// Assign numeric values for each color. Enumerated types would be a
// safer way to do this, but they make it more difficult for the user:
// instead of being able to say EzImage.ALPHA, the user would have to
// say EzImage.EzColor.ALPHA (or something like it). Alternatively,
// EzColor (the enumerated type) could be represented as a non-nested class
// to avoid this problem, but then the user would have to manipulate two
// files. All in all, the even though enumerated types are safer, using
// static final ints is cleaner for the user in this case. To help
// safety, the constants used are negative and large in magnitude.
// This helps assure that they won't be accidentally confused for color
// band identifiers.
public static final int RED = -1001;
public static final int GREEN = -1002;
public static final int BLUE = -1003;
public static final int GRAY = -1004;
public static final int ALPHA = -1005;
// All image formats that Java knows how to write
private static final String[] WRITER_TYPES = ImageIO.getWriterFormatNames();
private static int windowCount = 0;
private BufferedImage bufferedImage;
/**
* Constructs an EzImage object represented by the specified pixels.
* The dimensions of the pixel array must be [height][width] and is assumed
* to be gray-scale.
*
* @param pixels an array of [height][width] pixels that represents the
* image
* @throws IllegalArgumentException if pixels is null
*/
public EzImage(int[][] pixels)
{
// Create the BufferedImage (doesn't include transparency)
bufferedImage = new BufferedImage(pixels[0].length,pixels.length,
BufferedImage.TYPE_BYTE_GRAY);
if(pixels == null)
throw new IllegalArgumentException("null pixels array");
setPixels(pixels, GRAY);
}
/**
* Constructs an EzImage object represented by the specified pixels.
* The dimensions of the pixel array must be [height][width][3]
* (red-green-blue).
*
* @param pixels an array of [height][width][bands] pixels that
* represents the image.
* @throws IllegalArgumentException if pixels is null
*/
public EzImage(int[][][] pixels)
{
// Create the BufferedImage (doesn't include transparency)
bufferedImage = new BufferedImage(pixels[0].length,pixels.length,
BufferedImage.TYPE_INT_RGB);
if(pixels == null)
throw new IllegalArgumentException("null pixels array");
setPixels(pixels);
}
/**
* Constructs an EzImage object represented by the specified pixels.
* The dimensions of the pixel array must be [height * width] and layed out
* in row-major format. The image is assumed to be gray-scale.
*
* @param pixels an array of [height * width] pixels that represents the
* image.
* @param width the width (in pixels) of the image.
* @param height the height (in pixels) of the image.
* @throws IllegalArgumentException if pixels is null or the
* length is not width * height.
*/
public EzImage(int[] pixels, int height, int width)
{
// Create the BufferedImage (doesn't include transparency)
bufferedImage = new BufferedImage(width,height,
BufferedImage.TYPE_BYTE_GRAY);
if(pixels == null)
{
throw new IllegalArgumentException("null pixels array");
}
else if((width * height) != pixels.length)
{
throw new IllegalArgumentException(
"pixels dimensions doesn't match width/height parameters");
}
setPixels(pixels, GRAY);
}
/**
* Constructs an EzImage object by wrapping around a BufferedImage object.
*
* @param image a BufferedImage object
*/
public EzImage(BufferedImage image)
{
bufferedImage = image;
}
/**
* Constructs an EzImage object by reading the image file
* specified by the filename. The format of the file may be any
* one of the formats that Java has readers for, such as GIF, PNG, or JPEG.
*
* @param filename the name of the file to load
* @throws FileNotFoundException
*/
public EzImage(String filename) throws FileNotFoundException, IOException
{
File file = new File(filename);
if(!file.exists())
throw new FileNotFoundException(filename);
bufferedImage = ImageIO.read(file);
}
/**
* Constructs an EzImage object from the File object indicated.
* The format of the file may be any one of the formats that Java has
* readers for, such as GIF, PNG, or JPEG.
*
* @param file the File object to load
* @throws IOException,FileNotFoundException
*/
public EzImage(File file) throws IOException, FileNotFoundException
{
if(!file.exists())
throw new FileNotFoundException(file.getName());
bufferedImage = ImageIO.read(file);
}
/**
* Constructs an EzImage object by reading the image
* specified by the url. The format of the file may be any
* one of the formats that Java has readers for, such as GIF, PNG, or JPEG.
*
* @param url the URL of an image file to load.
*/
public EzImage(URL url) throws IOException
{
bufferedImage = ImageIO.read(url);
}
/**
* Returns the value of v clamped to the range 0-255.
* This is a convenience method for working with image pixel values.
*
* @param v the value of a pixel to be clamped.
* @return the value v clamped to the range 0-255.
*/
private static int clamp(double v)
{
if(v < 0)
return 0;
else if(v > 255)
return 255;
else
return (int)v;
}
/**
* Returns an EzImage object that is a gray-scale copy.
*
* @return an EzImage that is a gray-scale copy
*/
public EzImage copyToGrayScale() {
BufferedImage result = new BufferedImage(bufferedImage.getWidth(),
bufferedImage.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
WritableRaster input = bufferedImage.getRaster();
WritableRaster output = result.getRaster();
for(int row=0; row < input.getHeight(); row++)
{
for(int col=0; col < input.getWidth(); col++)
{
int red = input.getSample(col, row, getBandId(RED));
int green = input.getSample(col, row, getBandId(BLUE));
int blue = input.getSample(col, row, getBandId(GREEN));
output.setSample(col, row, 0, clamp((red+green+blue)/3.0));
}
}
return new EzImage(result);
}
/**
* Returns a 3D array of pixel values. The dimensions of the array
* correspond to the [height][width][bands] of the EzImage. The number of
* bands will be 1 for a gray-scale or binary image, 3 for a color image
* without transparency, and 4 for a color image with transparency. The
* bands are (in order) RED (or GRAY), GREEN, BLUE, ALPHA. Pixel values
* are in the range 0-255.
*
* @return an array of pixels
*/
public int[][][] getPixels3D()
{
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
int bands = bufferedImage.getSampleModel().getNumBands();
int[][][] pixels = new int[height][width][bands];
WritableRaster raster = bufferedImage.getRaster();
for(int i=0; iall such windows are closed.
* @param title the title of the window
*/
public void show(String title)
{
show(title,0,0);
}
/**
* Creates a window that will display this EzImage. EzImage maintains a
* count of all windows shown and will terminate the application if
* all such windows are closed.
* @param title the title of the window
* @param row the row coordinate of the upper left corner of the
* window
* @param column the column coordinate of the upper left corner of the
* window
*/
public void show(String title, int row, int column)
{
JFrame window = new JFrame(title);
window.getContentPane().add(new ImagePanel(this.copy()));
window.getContentPane().setPreferredSize
(new Dimension(getWidth(),getHeight()));
window.pack();
window.setLocation(column,row);
windowCount++;
window.addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
if(--windowCount == 0)
System.exit(0);
else
e.getWindow().dispose();
}
}
);
window.setVisible( true );
}
/**
* Creates an image file having the specified name and of the specified format.
* @param filename the name of the file to be saved
* @param format String containing one of the supported Java file
* types
* @throws IOException if the file cannot be created,
* IllegalArgumentException if the file type is not
* supported
*/
public void save(String filename, String format) throws IOException {
boolean validType = false;
for (int i=0; i < WRITER_TYPES.length; i++)
if (format.equals(WRITER_TYPES[i]))
{
validType = true;
break;
}
if (!validType)
throw new IllegalArgumentException("File type is not valid.");
ImageIO.write(bufferedImage, format, new File(filename));
}
/**
* The main method for this class. Used for testing purposes only!
* It is highly recommended that you write your own main method in a
* separate class file to use the EzImage class.
*/
public static void main(String[] args) throws IOException
{
// Test 2D array constructor
int[][] test1 = new int[200][255];
int[][] test2 = new int[200][255];
for (int i=0; i < test1.length; i++)
for (int j=0; j < test1[0].length; j++) {
test1[i][j] = j;
test2[i][j] = i;
}
EzImage image1 = new EzImage(test1);
image1.show("Black on left, white on right, shade in between");
EzImage image2 = new EzImage(test2);
image2.show("Black on top, almost white on bottom, shade in between",
0,270);
System.out.println("Image 1 color bands (should be 1) = "
+ image1.numColorBands());
System.out.println("Image 2 color bands (should be 1) = "
+ image2.numColorBands());
// Test 3D array constructor gray
int[][][] test3 = new int[200][255][3];
int[][][] test4 = new int[200][255][3];
int[][][] test5 = new int[200][255][3];
for (int i=0; i < test3.length; i++)
for (int j=0; j < test3[0].length; j++) {
test3[i][j][0] = j;
test4[i][j][1] = i;
test5[i][j][2] = 255-j;
}
EzImage image3 = new EzImage(test3);
image3.show("Black on left, red on right, shade in between",250,0);
EzImage image4 = new EzImage(test4);
image4.show("Black on top, almost green on bottom, shade in between",
250,270);
EzImage image5 = new EzImage(test5);
image5.show("Blue on left, black on right, shade in between",
250,550);
System.out.println("Image 3 color bands (should be 3) = "
+ image3.numColorBands());
System.out.println("Image 4 color bands (should be 3) = "
+ image4.numColorBands());
System.out.println("Image 5 color bands (should be 3) = "
+ image5.numColorBands());
// Test 1D array constructor
int height = 200;
int width = 255;
int[] test6 = new int[height*width];
int[] test7 = new int[height*width];
for (int i=0; i < height; i++)
for (int j=0; j < width; j++) {
test6[i*width+j] = j;
test7[i*width+j] = i;
}
EzImage image6 = new EzImage(test6,height,width);
image6.show("Black on left, white on right, shade in between",100,100);
EzImage image7 = new EzImage(test7,height,width);
image7.show("Black on top, almost white on bottom, shade in between",
100,370);
System.out.println("Image 6 color bands (should be 1) = "
+ image6.numColorBands());
System.out.println("Image 7 color bands (should be 1) = "
+ image7.numColorBands());
// Test String and file constructors
EzImage image8 = new EzImage(new File("jackets.jpg"));
image8.show("Jackets",400,100);
EzImage image9 = new EzImage("jackets.jpg");
image9.show("Jackets",400,500);
// Test URL constructor
EzImage image10 = new EzImage(new URL(
"http://www.mathcs.carleton.edu/faculty/dmusican/dave3.jpg"));
image10.show("Dave",400,900);
// Test copy to gray and copy, verify that change to original doesn't
// mess up copy
EzImage image11 = new EzImage(new File("jackets.jpg"));
EzImage image12 = image11.copyToGrayScale();
EzImage image13 = image11.copy();
EzImage image14 = image12.copy();
int[][][] pixels = image11.getPixels3D();
for (int i=100; i < 200; i++)
for (int j=100; j < 200; j++)
for (int k=0; k < 3; k++)
pixels[i][j][k] = 0;
image11.setPixels(pixels);
image11.show("Jackets with hole",0,0);
image12.show("Pristine gray jackets",0,400);
image13.show("Pristine color jackets",0,800);
System.out.println("Image 11 color bands (should be 3) = " +
image11.numColorBands());
System.out.println("Image 12 color bands (should be 1) = " +
image12.numColorBands());
System.out.println("Image 13 color bands (should be 3) = " +
image13.numColorBands());
System.out.println("Image 14 color bands (should be 1) = " +
image14.numColorBands());
// Get and set 1D pixels
EzImage image15 = new EzImage("jackets.jpg");
EzImage image16 = image15.copyToGrayScale();
int[] pixels1d = image15.getPixels1D(BLUE);
for (int i=0; i < pixels1d.length; i++)
pixels1d[i] = 255;
image15.setPixels(pixels1d,BLUE);
image15.show("Jackets with blue way up",0,0);
pixels1d = image16.getPixels1D();
for (int i=0; i < pixels1d.length; i+=5)
pixels1d[i] = 255;
image16.setPixels(pixels1d);
image16.show("Gray jackets with every fifth pixel white",0,400);
// Get and set 2D pixels
EzImage image17 = new EzImage("jackets.jpg");
EzImage image18 = image15.copyToGrayScale();
int[][] pixels2d = image17.getPixels2D(BLUE);
for (int i=100; i < pixels2d.length; i++)
for (int j=300; j < pixels2d[0].length; j++)
pixels2d[i][j] = 255;
image17.setPixels(pixels2d,BLUE);
image17.show(
"Jackets with blue way up in rectangle more right than down",300,0);
pixels2d = image18.getPixels2D();
for (int i=100; i < pixels2d.length; i++)
for (int j=300; j < pixels2d[0].length; j++)
pixels2d[i][j] = 255;
image18.setPixels(pixels2d);
image18.show("Gray jackets with white rectangle more right than down"
,300,400);
// Get and set 3D pixels
EzImage image19 = new EzImage("jackets.jpg");
EzImage image20 = image19.copyToGrayScale();
int[][][] pixels3d = image19.getPixels3D();
System.out.println("Image 19: num bands returned (should be 3) = " +
pixels3d[0][0].length);
for (int i=100; i < pixels3d.length; i++)
for (int j=300; j < pixels3d[0].length; j++)
pixels3d[i][j][2] = 255;
image19.setPixels(pixels3d);
image19.show(
"Jackets with blue way up in rectangle more right than down",0,0);
pixels3d = image20.getPixels3D();
System.out.println("Image 20: num bands returned (should be 1) = " +
pixels3d[0][0].length);
for (int i=100; i < pixels3d.length; i++)
for (int j=300; j < pixels3d[0].length; j++)
pixels3d[i][j][0] = 255;
image20.setPixels(pixels3d);
image20.show("Gray jackets with white rectangle more right than down"
,0,400);
// Try saving
image20.save("boxjackets.jpg","JPEG");
}
}