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 on the Picture class: height(), width(), get(i,j), set(i,j,color), etc.
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 //paint the frame frame.repaint(); }Note that we are doing things differently than you are used to. So far, when we wanted to display an object, we made the object extend JFrame. This time, Picture does not extend JFrame. Instead, its show method creates a JFrame on the fly, adds the image to it, and renders it.
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 object, 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. A color in Jave is represented as a triplet of values in 0..255 that represent the values of R,G and B (red, green, blue). (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 do your own research 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)Again, note that scale returns a Picture, so you'll be able to show it. Test it.
//morph p1 and p2 static void morph(Picture p1, Picture p2, int k)or better
//morph this with p void morph(Picture p, 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. In our case, 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').
Again, note that rotate returns a Picture object. You can show it, and test whether your rotates works correctly (your image should look...rotated).
a = normalized distance of (i,j) to center * 180Experiment with other values than 180.
By normalized distance here we mean that we want a number d in [0,1]. The center of the image should have d=0 and correspondingly a=0 ----in other words, the center does not rotate. The point of the image furthest away from the center should have d=1 and correspondingly a=180; in other words these points rotate 180 degrees. Points in between will rotate by an angle between 0 and 180. The effect will be of a "swirl".
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.