Your program will consist of one class, the Picture class, and a couple of helper classes and tester programs. The Picture class is responsible creating, modifying and rendering pictures. We'll describe it below.
Class BufferedImage is a Java class that can handle images in many of the standard formats and implements handy functions like loading an image from a file.
BufferedImage image; //assume file is the image file try { image = ImageIO.read(file); } catch (IOException e) { System.out.println("Invalid image file: " + filename); System.exit(0); }The BufferedImage stores the image as a matrix of pixels, but you do not need to know the details as you should only communicate with a BufferedImage through its interface. Some useful methods of a BufferedImage are:
Implement the needed getters and setters to make your code easier (height(), width(), get(i,j), set(i,j,color)).
The first thing you'll do with the Picture is to write a show() method that displays the image on the screen. To display the image you first need to create the JFrame object that will hold the image, set its title and whatever other defaults you usually set for a frame, add a menubar and the image to it, and then call repaint() on the frame.
public void show() { frame = new JFrame(); //set frame title, set it visible, etc //add a menubar on the frame with a single option: saving the image //add the image to the frame //pait the frame frame.repaint(); }To render the image in the frame we'll do a trick: we'll create a JLabel from the image, and set the JLabel as the ContentPane of the frame. Something like this:
ImageIcon icon = new ImageIcon(image); frame.setContentPane(new JLabel(icon));
Picture grayscale()So I could do something like this:
Picture p1, p2; p1= new Picture("mypic.jpg"); p1.show(); p2 = p1.grayscale(); p2.show();Since grayscale() needs to return a new Picture, this means that inside grayscale() you need to create an empty picture of the same size and set it with values. To do this you'll create another constructor for the Picture class that creates an empty image of specified size.
public Picture(int w, int h) { //filename=null; image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); }Once you have an empty image of the right size, you simply loop through it and set the pixels:
// convert to grayscale for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { Color color = pic.get(i, j); Color gray = toGray(color); pic.set(i, j, gray); } }Now the only thing left is how to convert a Color to a gray shade. You should know by now that an RGB Color is represented as a triplet of values in 0..255 that represent the values of R,G and B. (0,0,0) represents White, and (255, 255, 255) represents Black. Any gray shade has the same values for R,G and B, so teh triplet looks like this (x, x, x). To transform an (r,g,b) value to a gray value you need to map the triplet to a value x between 0 and 255. Think of x as a function/combination of r, g and b. You can read and search about this, but here is one way to do it:
int r = color.getRed(); int g = color.getGreen(); int b = color.getBlue(); //the monochrome luminance of this color double l = .299*r + .587*g + .114*b; Color gray = new Color((int)l, (int)l, (int)l);Test your function.
Now you want to generalize this to scaling from w-by-h to x-by-y, and you can do both scaling up and down with the same code. Hint: think of the (i,j) pixel in the target image; what pixel does it correspond to in the source image?
Write a method that takes a targer height and width and returns a new Picture that is the scaled version of the original one.
Picture scale(int th, int tw)
void morph(Picture p1, Picture p2, int k)or
void morph(Picture p2, int k)This method does not return anything. When called with two picture arguments p1 and p2, it simply computes and shows a set of k intermediate pictures P0, P1, P2, ..., Pk that are a combination of p1 and p2.
The picture at the i-th step has each pixel (i,j) colored with a color c as a linear function of the color c1 of pixel (i,j) in p1, and c2, the color of pixel (i,j) in p2: c = f(c1, c2, i). For i=0, c is p1, and for i=k c is p2.
In order to avoid a large number of frames popping up on the screen, I suggest that you keep all intermediate pictures in the same variable, and you render it every time. In other words you do not create k picture objects, but you create only one and you modify it and show it k times.
//angle a is in degrees Picture rotate(int a)To rotate, copy the color of a pixel (i,j) into the rotated position of (i,j), which we denote by (i', j'). Assume that i represents the x coordinate and j represents the y coordinate.
It can be shown using elementary geometry and trigonometry, that a rotation by a around the origin maps point (x,y) to point (x', y') as follows:
x' = x cos a - y sin a y' = x sin a + y cos aThis is the rotation of a point around the origin. We want to rotate around the center of the image. Let (Xc, Yc) be the coordinates of the center of the picture. We first need to translate each point (i,j) by (-Xc, -Yc) so that we bring the center of the image to coordinates (0,0). Then we rotate using the formula, then we add (Xc, Yc) to the rotated coordinates (x',y').
a = normalized distance of (i,j) to center * 180Experiment with other values than 180.
enter your command: show stars.jpg enter your command: scale stars.jpg 100 200 enter your command: rotate stars.jpg 90 enter your command: morph 50 stars.jpg peguin.jpg enter your command: swirl stars.jpg 45 enter your command: quit Good bye.Each of these commands should show the transformed image in a separate frame.
java Rotate [path-to-image] [angle]
As usual, email me the folder (include the images that you use), and bring a hard copy to class.