Introduction to Image Processing

There are tons of built-in methods to handle the processing of images. This is a very brief introduction.


The Image Class

As you know, an image is just rows and columns of red green blue pixels. File formats such as jpeg and gif store that data differently. Because storing RGB values for every pixel takes too much space (3 bytes for each of tens of thousands of pixels), jpeg and gif compress the data. Fortunately, we don't have to decompress the files to read all the pixel data into memory. There are pre-built methods for that.

The basic image class is Image. Here is a good reference page for the Image class. As you can see, the image class is very basic. Getting to pixels, changing pixels, loading pixels from a file, etc. are a huge pain. So, a much more useful class is the BufferedImage class.

picture stolen from http://java.sun.com/developer/technicalArticles/GUI/java2d/java2dpart2.html

The buffered image class has two big parts: the pixel data (raster data), and the color information (color model).

The color model tracks how your device handles color. Remember that Java is machine independent, so your color system on an old cellphone might be greyscale, while your laptop is RGB-based.

The Raster Data is divided into two parts: Sample Model and Data Buffer. The data buffer is the actual pixel values. The sample model describes the meaning of those values.

Here is a good reference page for BufferedImage.


Loading a Image

Here is some sample code to load the file me.jpg into memory, then print its height and width.
// Image test Zero

import java.awt.*;

public class imgtest1a
{
   public static void main ( String[] args )
   {
     System.out.println("Starting Image Test");

     Image my_img = null;
     my_img = Toolkit.getDefaultToolkit().getImage("me.jpg");

     System.out.println ("width  = " + my_img.getWidth(null) );
     System.out.println ("height = " + my_img.getHeight(null) );

     System.out.println("Done with Image Test");
   }
}
But, the code does not work as we might expect. While it does compile and run, it prints height and width of -1. The problem is that getImage is non-blocking. Hence, the println statements execute before the image is finished loading.

Here is an improved version. It adds a media tracker that will monitor the progress of the image loading and wait till the image is in memory.
// Image test One

import java.awt.*;

public class imgtest1
{
   public static Image Read_Image (String file)
   {
    Image image = Toolkit.getDefaultToolkit().getImage(file);
    MediaTracker tracker = new MediaTracker(new Component() {});
    tracker.addImage ( image, 0 );
    try
      {
       tracker.waitForID (0);
      }
    catch (InterruptedException e) {}
    return image;
   }

   public static void main ( String[] args )
   {
     System.out.println("Starting Image Test");

     Image my_img = null;

     my_img = Read_Image ("me.jpg");

     System.out.println ("width  = " + my_img.getWidth(null) );
     System.out.println ("height = " + my_img.getHeight(null) );

     System.out.println("Done with Image Test");
   }
}


Reading Pixels in a BufferedImage

The following program loads a BufferedImage from the file "me.jpg", then prints a sampling of the pixel values. The getRGB methods returns an RGB value for the pixel indicated. Using the image "me.jpg" (see bottom of this page), the program creates this output.

About Pixels: Individuals pixels in BufferedImages are stored in an array of integers, one integer per pixel. A Java integer is four bytes. Each pixel integer uses the highest order byte for the alpha component, then next highest is red, then green, then blue. So, to pull out each part we have to shift bits and AND bits.

About the Coordinate System: The top left of the image is location 0,0. So, an increase in X goes right. And an increase in Y value moves down.
// Image test Two
// retrieve RGB value

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

public class imgtest2
{

   /* ******************************************************* */
   /* *********  Method to load an image from a file ******** */
   /* ******************************************************* */
   public static BufferedImage Read_Image (String file)
   {
    BufferedImage image = null;            // declare the image

    // go get the image data from the file
    try
      {
       image = ImageIO.read(new File(file));
      }
    catch (IOException e)
      {
       System.out.println ("Error Opening image file");
      }

    // track the loading of the image
    MediaTracker tracker = new MediaTracker(new Component() {});
    tracker.addImage ( image, 0 );
    try
      {
       tracker.waitForID (0);
      }
    catch (InterruptedException e) {}

    // done, return the image
    return image;
   }


   /* ************************************************ */
   /* *********  M A I N  **************************** */
   /* ************************************************ */
   public static void main ( String[] args )
   {
     System.out.println("Starting Image Test");

     // declare the image and go get it
     BufferedImage my_img = null;
     my_img = Read_Image ("me.jpg");

     // print some info about the image
     System.out.println ("width  = " + my_img.getWidth(null) );
     System.out.println ("height = " + my_img.getHeight(null) );

     for (int x=0; x<150; x+=10)
       {
        int rgb = my_img.getRGB(x,150);

        int alpha = ((rgb >> 24) & 0xff);
        int red   = ((rgb >> 16) & 0xff);
        int green = ((rgb >>  8) & 0xff);
        int blue  = ((rgb ) & 0xff);

        System.out.println ("at location " + x + ", 150" );
        System.out.println ("    red = " + red );
        System.out.println ("    green = " + green );
        System.out.println ("    blue = " + blue );
       }

     System.out.println("Done with Image Test");
   }
}


Writing onto a BufferedImage

The final example reads the image file args[0], changes it slightly, then writes the new image to args[1].

There are many many many graphics functions that can be used to draw onto images. The example below fills a rectange, then draws a border arount the rectangle, then writes my name into the rectangle. Size of the rectangle is based on the size of the text.

The first step in using such graphics functions is to get the image's graphics via the createGraphics method. The program then sets the font attributes and determines the size of the needed rectangle via getFontMetrics. The setColor and setFont determine what the output looks like.

Given the input file me.jpg, the program creates the output seen below in me2.jpg.

// Image Test Four
// write a box and a string onto the image

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.*;
import java.awt.Color;
import java.awt.Font;

public class imgtest4
{
   /* ****************************************************** */
   /* *********  Method to load an image from a file ******* */
   /* ****************************************************** */
   public static BufferedImage Read_Image (String file)
   {
    BufferedImage image = null;            // declare the image

    // go get the image data from the file
    try
      {
       image = ImageIO.read(new File(file));
      }
    catch (IOException e)
      {
       System.out.println ("Error Opening image file");
      }

    // track the loading of the image
    MediaTracker tracker = new MediaTracker(new Component() {});
    tracker.addImage ( image, 0 );
    try
      {
       tracker.waitForID (0);
      }
    catch (InterruptedException e) {}

    // done, return the image
    return image;
   }


   /* ************************************************ */
   /* *********  M A I N  **************************** */
   /* ************************************************ */
   public static void main ( String[] args )
   {
     System.out.println("Starting Image Test");

     // check the command line args
     if (args.length != 2)
       {
        System.out.println ("Usage Error: imgtest infilename oufile");
        System.exit (1);
       }
     System.out.println ("filename  = " + args[0] );

     // declare the image and go get it
     BufferedImage my_img = null;
     my_img = Read_Image (args[0]);

     // create the graphics and fonts   
     Graphics2D  img_graphics  =  my_img.createGraphics();   
     img_graphics.setFont(new Font( "SansSerif", Font.BOLD, 12 ));
     FontMetrics  fm  =  img_graphics.getFontMetrics();

     // where will this box be drawn
     String       msg         = "Steve Dannelly";
     int          msg_width   = fm.stringWidth ( msg );
     int          msg_height  = fm.getHeight() + 5;
     int          msg_x       = (my_img.getWidth() - msg_width) / 2;
     int          msg_y       = my_img.getHeight() - msg_height;

     // draw a rectangle
     img_graphics.setColor(Color.white);
     img_graphics.fillRect (msg_x, msg_y,  msg_width, msg_height);
     img_graphics.setColor(Color.magenta);
     img_graphics.drawRect (msg_x, msg_y,  msg_width, msg_height);

     // write text in the rectangle
     img_graphics.setColor(Color.red);
     img_graphics.drawString(msg, msg_x, msg_y+msg_height-5);

     // write the image to a new file
     File f = new File(args[1]);
     try
       {
        ImageIO.write(my_img, "jpg", f);
       }
     catch (Exception e)
       { System.out.println("Error writing new image"); }

     System.out.println("Done with Image Test");
   }
}


me.jpg

          me2.jpg