ENGR 131: Image File Input/Output in Java

M.S. Branicky (Copyright 2006, 2007)


Overview

This document gives some example programs demonstrating the reading and writing of image files.

The programs use the following Java classes:

The programs and .jpg files are available for download in the ENGR 131 Code Repository. Specifically, the programs are

Example 1: Converting a jpg picture to a png picture

import java.awt.image.*;
import javax.imageio.*;
import java.io.*;

public class jpg2pngHC
{
  //                       Needed b/c I didn't use try-catch; see D&D, p. 647
  //                                      vvvvvvvvvvvvvvvvvv
  public static void main (String args[]) throws IOException {
    // open and read the input image
    File inputFile = new File("bakeonceeatanywhere.jpg");
    BufferedImage input = ImageIO.read(inputFile);

    // write the output image
    File outputFile = new File("bakeonceeatanywhere.png");
    ImageIO.write(input, "png", outputFile);
  }
}

This assumes that the file bakeonceeatanywhere.jpg exists in the same directory as the Java program jpg2pngHC

Here are the two images (they look nearly identical because they are just the same image stored in different formats):

Example 2: Command-line version of the foregoing

import java.awt.image.*;
import javax.imageio.*;
import java.io.*;

public class jpg2png
{
  public static void main (String args[]) throws IOException {
    System.out.println("Converting "+args[0]+".jpg to "+args[0]+".png ...");
    File inputFile = new File(args[0]+".jpg");
    BufferedImage input = ImageIO.read(inputFile);
    File outputFile = new File(args[0]+".png");
    ImageIO.write(input, "png", outputFile);
  }
}

Example usage (at DrJava Interactions Console or MS-DOS or MacOSX Terminal prompt):

java jpg2png bakeonceeatanywhere
java jpg2png cooljava
java jpg2png javaiscool

This assumes that the files bakeonceeatanywhere.jpg, cooljava.jpg, and javaiscool.jpg exist in the same directory as the Java program jpg2png

Here are the input and output images for the latter two cases (again looking expectedly identical):

Example 3: Creating a New Picture and Writing it to a File

import java.awt.image.*;
import javax.imageio.*;
import java.io.*;

public class NewImage
{
  // 0xNUM is how to input HEXadecimal numbers (base 16: digits 0-F) in Java
  public static final int BLACK = 0, WHITE = 0xFFFFFF;

  public static void main (String args[]) throws IOException {
    // create a new, "blank" 300 by 300 image
    BufferedImage myimage = new BufferedImage(300,300,BufferedImage.TYPE_INT_RGB);

    // set the values for the image
    for (int i=0; i<300; i++) {
      for (int j=0; j<300; j++) {
        if (i%2==0) myimage.setRGB(i,j,WHITE);
        else myimage.setRGB(i,j,BLACK);
      }
    }

    // store the image in a file
    File outImage = new File("myimage.jpg");
    ImageIO.write(myimage, "jpg", outImage);
  }
}
The output of this file looks like:

We can add some color to the above using the following program:

import java.awt.image.*;
import javax.imageio.*;
import java.io.*;

public class NewImage2
{
  // See http://cloford.com/resources/colours/500col.htm for HEX color codes
  public static final int BLACK = 0, WHITE = 0xFFFFFF;
  public static final int BLUE = 0xFF, GREEN = 0xFF00, RED=0xFF0000;
  public static final int CYAN = GREEN+BLUE, MAGENTA = RED+BLUE, YELLOW = RED+GREEN;

  // Converts R-G-B values to a single integer
  // See http://www.tayloredmktg.com/rgb/ for RGB color codes
  public static int RGBint (int R, int G, int B) {
    return (R*256*256+G*256+B);
  }
  
  public static void main (String args[]) throws IOException {
    // create a new, "blank" 500 by 300 image
    BufferedImage myimage = new BufferedImage(500,300,BufferedImage.TYPE_INT_RGB);
    
    // See http://www.tayloredmktg.com/rgb/ for RGB color codes
    int goldenrod = RGBint(184, 134, 11);
    int thistle = RGBint(216, 191, 216);
    
    // set the values for the image
    for (int i=0; i<500; i++) {
      for (int j=0; j<300; j++) {
        if (i<50) myimage.setRGB(i,j,BLACK);
        else if (i<100) myimage.setRGB(i,j,WHITE);
        else if (i<150) myimage.setRGB(i,j,RED);
        else if (i<200) myimage.setRGB(i,j,GREEN);
        else if (i<250) myimage.setRGB(i,j,BLUE);
        else if (i<300) myimage.setRGB(i,j,CYAN);
        else if (i<350) myimage.setRGB(i,j,MAGENTA);
        else if (i<400) myimage.setRGB(i,j,YELLOW);
        else if (i<450) myimage.setRGB(i,j,goldenrod);
        else myimage.setRGB(i,j,thistle);
      }
    }

    // store the image in a file
    File outImage = new File("myimage2.jpg");
    ImageIO.write(myimage, "jpg", outImage);
  }
}
The output image of this program looks like:

Example 4: Image Fun

This section documents some steps in my attempt to learn how to change an image, with the goal of digitally erasing Duke (Java's mascot) from the original image of the Java's 10th Birthday Cake:

Here was my first piece of code to understand some BufferedImage class methods

import java.awt.image.*;
import javax.imageio.*;
import java.io.*;

public class ImageFun
{
  // 0xNUM is how to input HEXadecimal numbers (base 16: digits 0-F) in Java
  // See http://cloford.com/resources/colours/500col.htm for HEX color codes
  public static final int BLACK = 0, WHITE = 0xFFFFFF;

  public static void main (String args[]) throws IOException {
    // open and read the input image
    File inputFile = new File("bakeonceeatanywhere.jpg");
    BufferedImage input = ImageIO.read(inputFile);

    ///////// BEGIN COMMANDS THAT CHANGED FROM VERSION TO VERSION ////////
    // test some methods that told me ranges of pixel indices
    System.out.println("MinX: "+input.getMinX());
    System.out.println("MinY: "+input.getMinY());
    System.out.println("Height: "+input.getHeight());
    System.out.println("Width: "+input.getWidth());

    // my commands to do a simple mod to the picture
    for (int i=0; i<100; i++) {
      for (int j=0; j<100; j++) {
        input.setRGB(i,j,BLACK);
      }
    }
    ///////// END COMMANDS THAT CHANGED FROM VERSION TO VERSION ////////

    File outImage = new File("imagemod.jpg");
    ImageIO.write(input, "jpg", outImage);
  }
}
After compiling, I ran the code, which gave the following output on the screen:
MinX: 0
MinY: 0
Height: 333
Width: 500
It also produced a new file called "imagemod.jpg". I viewed this new image file using an application on my computer (Preview on MacOSX). It looked like this:

Notice that there is a 100x100 black square in the top-left corner (which is a little hard to see because the picture itself is black in that corner). So (0,0) is in the top-left corner, and apparently the bottom-right is (499, 332) [counting starts from 0]. In any case, I wanted to see my square a little better, so I modified a small piece of the code above as follows, with the following result:
    for (int i=100; i<200; i++) {
      for (int j=100; j<200; j++) {
        input.setRGB(i,j,BLACK);
      }
    }

I then noticed that Duke was nearly covered and decided to try to cover him up a little more.

    for (int i=150; i<250; i++) {
      for (int j=100; j<200; j++) {
        input.setRGB(i,j,BLACK);
      }
    }

Cool, he's pretty much covered. But it would be neater if he was covered in white, so it looks like he was never there.

    for (int i=150; i<250; i++) {
      for (int j=100; j<200; j++) {
        input.setRGB(i,j,WHITE);
      }
    }

But he's not fully covered, so let's tweak it a bit:

    for (int i=150; i<250; i++) {
      for (int j=95; j<208; j++) {
        input.setRGB(i,j,WHITE);
      }
    }

Almost! But we really should be covering him with a trapezoid, not a square, so we don't mess up that orange border. For that, we need to have the horizontal position getting smaller as the vertical one gets larger. I did this by swapping the for loops and trying some linear function (slope).

   for (int j=95; j<208; j++) {
      for (int i=150-(j-95)/2; i<250; i++) {
        input.setRGB(i,j,WHITE);
      }
    }

Hmm. Slope is too big, and the white portion begins too far left and ends just a bit too soon.

   for (int j=95; j<208; j++) {
      for (int i=165-(j-95)/3; i<255; i++) {
        input.setRGB(i,j,WHITE);
      }
    }

Nailed it! Now it would be nice if the square wasn't so obviously white. Instead of pure white, I'll sample the color of a nearby cake-colored pixel and use that:

   for (int j=95; j<208; j++) {
      for (int i=165-(j-95)/3; i<255; i++) {
        int cakecolor = input.getRGB(256,100);
        input.setRGB(i,j,cakecolor);
      }
    }

That matches nicely at the top, but is too dark at the bottom. Let's take a sample that's a little lower vertically:

   for (int j=95; j<208; j++) {
      for (int i=165-(j-95)/3; i<255; i++) {
        int cakecolor = input.getRGB(256,200);
        input.setRGB(i,j,cakecolor);
      }
    }

Now that matches nicely at the bottom, but is too light at the top! Let's take samples from all along vertically to achieve a nice blending all the way down:

   for (int j=95; j<208; j++) {
      for (int i=165-(j-95)/3; i<255; i++) {
        int cakecolor = input.getRGB(256,j);
        input.setRGB(i,j,cakecolor);
      }
    }

I declare success! My Scrat-like persistence paid off!



Created: 2006-10-11. Last Modified: 2007-10-10.