java left logo
java middle logo
java right logo
 

Home arrow Java SE Tips arrow java.awt.image arrow Styling digital images with CONVOLVEOP
 
 
Main Menu
Home
Java Tutorials
Book Reviews
Java SE Tips
Java ME Tips
Java EE Tips
Other API Tips
Java Applications
Java Libraries
Java Games
Java Network
Java Forums
Java Blog




Most Visited Tips
Java SE Tips
Java ME Tips
Java EE Tips
Other API Tips
Java Applications
Java Libraries
Java Games
Book Reviews
Top Rated Tips
Java SE Tips
Java ME Tips
Java EE Tips
Other API Tips
Java Applications
Java Libraries
Java Games
Book Reviews


Statistics
Registered Users: 3942
Java SE Tips: 614
Java ME Tips: 202
Java EE Tips: 183
Other API Tips: 779
Java Applications: 298
Java Libraries: 209
Java Games: 16
Book Reviews:
 
 
 
Styling digital images with CONVOLVEOP E-mail
User Rating: / 13
PoorBest 

This Tech Tip reprinted with permission by java.sun.com

Sometimes the images produced by your digital camera might not be exactly what you want. For example, you might want to blur them or sharpen them. Or you might want to brighten them or tone them down. The java.awt.image package contains utilities for making these kinds of changes to digital images. Using these utilities, you can achieve some of the same effects that you find in high-end commercial imaging packages.

In this tip, you will use ConvolveOp, a class in the java.awt.image package, to alter a digital image. ConvolveOp implements a convolution from a source to the destination. You can think of a convolution an operation that replaces each pixel in an image with some combination of the original pixel and its neighbors. You specify the combination by using an array of floats. The floats are multiples of the pixels. From this array you create an instance of a Kernel, and use this instance to perform the convolution.

For example, if the array is a three-by-three array like this:

   a b c
   d e f
   g h i

Then the resulting convolution replaces the center pixel with the sum of:

  • a times the value of the pixel above and to the left of center
  • b times the value of the pixel above the center
  • c times the pixel above and to the right of the center
  • and so on

In other words, you are multiplying the value of the pixel by the value in the kernel at the corresponding position, and adding them up. If the sum of the entries in the array is 1, then the resulting image will have the same dynamic range as the original image.

Here's a more concrete. Consider the following three-by-three array:

     0   0.2   0
     0.2  0.2  0.2
     0   0.2   0

This array corresponds to replacing each pixel with the average of its value together with the pixels to the right, left, above, and below. The effect is to blur the picture a bit.

Now consider the following three-by-three array. It corresponds to an identity operation. Applying it as part of a convolution changes nothing. In other words, it replaces each pixel with its previous value.

   0 0 0
   0 1 0
   0 0 0

However, if the 1 in the previous array is replaced with a 2, the resulting image is brighter and a bit washed out. Replacing the 1 with a .5 results in a darker image.

There is nothing magic about a three-by-three array. If your goal is to perform a convolution that is symmetric about the center and which depends on the center pixel, you need a square array with an odd dimension. A five-by-five, seven-by-seven, and any other odd-dimension array would work just as well as a three-by-three array. The previous example could even be implemented using a one-by-one array. For instance, in the following method, you create an array of floats containing a single element. You then create a new one-by-one Kernel object from this element, and pass the object to the ConvolveOp constructor. Finally you apply the BufferedImageOp to your original BufferedImage to obtain the altered image.

   private void setBrightnessFactor(float multiple) {
     float[] brightKernel = {multiple};
     BufferedImageOp bright
       new ConvolveOp(new Kernel(11, brightKernel));
     convolvedImage 
       = bright.filter(originalImage, null);
     repaint();

   }

This method highlights two requirements. One requirement is that you need to override the paintComponent() method to redraw the new image every time it changes.

   public void paintComponent(Graphics g) {
     g.drawImage(convolvedImage, 00this);
   }

The second requirement is that you need to first create a BufferedImage to which you can apply these effects. In this case, read in test.jpg and create an Image from it. Then create a BufferedImage with the same dimensions as the original image. As a last step, you need to draw the image onto the BufferedImage.

   Image image = new ImageIcon("test.jpg").getImage();
   originalImage 
     new BufferedImage(image.getWidth(null),
       image.getHeight(null)
       BufferedImage.TYPE_INT_RGB);
   Graphics g = originalImage.createGraphics();
   g.drawImage(image, 00null);
   g.dispose();

Instead of using a one-dimensional kernel, you can also use a three-by-three kernel through the following version of setBrightnessFactor.

   private void setBrightnessFactor(float multiple) {
      float[] brightKernel = {0,    0,     0,
                              0, multiple, 0,
                              0,    0,     0};
      BufferedImageOp bright
        new ConvolveOp(new Kernel(33, brightKernel));
      convolvedImage 
        = bright.filter(originalImage, null);
      repaint();
   }

Here is a sample program, BrightnessChanger, that creates a viewer for an image. The viewer includes a slider that you can use to control the brightness by altering the value of the multiple being used. You will need a test image. In this case, the image name is set to test.jpg. The image should be in the directory in which you run the BrightnessChanger program. You can find a fun sample image to use.

    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.ImageIcon;
    import javax.swing.JSlider;
    import javax.swing.event.ChangeListener;
    import javax.swing.event.ChangeEvent;
    import java.awt.Graphics;
    import java.awt.Image;
    import java.awt.BorderLayout;
    import java.awt.image.Kernel;
    import java.awt.image.ConvolveOp;
    import java.awt.image.BufferedImageOp;
    import java.awt.image.BufferedImage;

    public class BrightnessChanger extends JPanel {

       private BufferedImage originalImage;
       private BufferedImage convolvedImage;
       private JSlider slide = new JSlider(1,50,10);

       BrightnessChanger() {
         createBufferedImages();
         setUpJFrame();
       }

       private void createBufferedImages() {
         Image image
           new ImageIcon("test.jpg").getImage();
         originalImage
           new BufferedImage(image.getWidth(null),
             image.getHeight(null),
             BufferedImage.TYPE_INT_RGB);
         convolvedImage
           new BufferedImage(image.getWidth(null),
             image.getHeight(null),
             BufferedImage.TYPE_INT_RGB);
         Graphics g = originalImage.createGraphics();
         g.drawImage(image, 00null);
         g.dispose();
         setBrightnessFactor(1);
       }

       private void setUpJFrame() {
         JFrame myFrame = new JFrame("Image Brightness");
         myFrame.setSize(convolvedImage.getWidth(),
                         convolvedImage.getHeight());
         myFrame.getContentPane().setLayout(
           new BorderLayout());
         myFrame.getContentPane().add(
           this, BorderLayout.CENTER);
         slide.addChangeListener(
           new BrightnessListener());
         myFrame.getContentPane().add(
           slide,BorderLayout.SOUTH);
         myFrame.setDefaultCloseOperation(
           JFrame.EXIT_ON_CLOSE);
         myFrame.setVisible(true);
       }

       private void setBrightnessFactor(float multiple) {
         float[] brightKernel = {multiple};
         BufferedImageOp bright
           new ConvolveOp(
               new Kernel(11, brightKernel));
         bright.filter(originalImage, convolvedImage);
         repaint();

       }

        public void paintComponent(Graphics g) {
         g.drawImage(convolvedImage, 00this);
       }

       class BrightnessListener implements ChangeListener{
         public void stateChanged(
           ChangeEvent changeEvent) {
             setBrightnessFactor(
               (float)(slide.getValue())/10);
         }
       }

       public static void main(String[] args) {
           new BrightnessChanger();
         }

    }
Image

The BrightnessChanger example serves as an introduction to convolutions, and not as a recommended way to control the brightness of your images. In a future tech tip, you will see how to use some of the controls targeted at color and brightness.

In addition to using square, odd-dimensional arrays for the kernel of the convolution, you need to use (for the most part) symmetric arrays. The next example uses three-by-three arrays that are built from the following three components:

             0  0  0         0  1  0              1  0  1
   IDENTITY  0  1  0   EDGE  1  0  1      CORNER  0  0  0
             0  0  0         0  1  0              1  0  1

The resulting array is built from linear combinations of these building blocks. As long as the sum of the entries is not zero, you normalize the kernel by dividing by this sum. That way, you get an image that has the same intensity as the original image. For example, if you add the EDGE and the IDENTITY you get the following:

   0  1  0  
   1  1  1  
   0  1  0

The sum of the entries is 5. You then normalize by dividing each entry by 5 to obtain the following "blur" array:

    0  0.2  0
   .2  0.2 0.2
    0  0.2  0

Here is the code for creating these kernels from parameters for multiples of the three base arrays.

   private Kernel getKernel(
     int corner, int edge, int identity) {
      float[] kernel = new float[9];
      int sum = corner * + edge * + identity;
      if (sum == 0sum = 1;
      for (int i = 0; i < 9; i++) {
        kernel[i(corner * CORNER[i]
          + edge * EDGE[i]
          + identity * IDENTITY[i]) / sum;
    }
    return new Kernel(33, kernel);
 }

After you have the kernel, you perform the convolution much as before.

   void convolveImage(Kernel kernel) {
      BufferedImageOp convolve
        new ConvolveOp(kernel);
      buffImage = convolve.filter(buffImage, null);
      repaint();
   }

Now let's run a test program, named Convolve. When you run Convolve, you need to enter three command-line parameters. These are are int values corresponding to the multiples of CORNER, EDGE, and IDENTITY. For example, if you enter:

   java Convolve 1 0 0 

you should see a somewhat blurred image. Again, you need an image named test.jpg.

   import javax.swing.ImageIcon;
   import javax.swing.JFrame;
   import javax.swing.JPanel;
   import java.awt.image.BufferedImage;
   import java.awt.image.BufferedImageOp;
   import java.awt.image.ConvolveOp;
   import java.awt.image.Kernel;
   import java.awt.Image;
   import java.awt.Graphics;

   public class Convolve extends JPanel {
      private BufferedImage buffImage;

      private final float[] IDENTITY = {000,
                                        010,
                                        000};

      private final float[] EDGE = {010,
                                    101,
                                    010};

      private final float[] CORNER = {101,
                                      000,
                                      101};


      Convolve(int corner, int edge, int identity) {
        createBufferedImages();
        setUpJFrame();
        convolveImage(getKernel(corner, edge, identity));
      }

      private void createBufferedImages() {
        Image image 
          new ImageIcon("test.jpg").getImage();
        buffImage 
          new BufferedImage(image.getWidth(null),
            image.getHeight(null)
            BufferedImage.TYPE_INT_RGB);
        Graphics g = buffImage.createGraphics();
        g.drawImage(image, 00null);
        g.dispose();
      }

      private void setUpJFrame() {
        JFrame myFrame = new JFrame("Image Brightness");
        myFrame.setSize(buffImage.getWidth(),
          buffImage.getHeight());
        myFrame.getContentPane().add(this);
        myFrame.setDefaultCloseOperation(
          JFrame.EXIT_ON_CLOSE);
        myFrame.setVisible(true);
      }

      void convolveImage(Kernel kernel) {
        BufferedImageOp convolve
          new ConvolveOp(kernel);
        buffImage = convolve.filter(buffImage, null);
        repaint();

      }

      private Kernel getKernel(
        int corner, int edge, int identity) {
         float[] kernel = new float[9];
         int sum = corner * + edge * + identity;
         if (sum == 0sum = 1;
         for (int i = 0; i < 9; i++) {
           kernel[i(corner * CORNER[i]
             + edge * EDGE[i]
             + identity * IDENTITY[i]) / sum;
         }
        return new Kernel(33, kernel);
      }

      public void paintComponent(Graphics g) {
        g.drawImage(buffImage, 00this);
      }


      public static void main(String[] args) {
        if (args.length != 3) {
          System.out.println("Usage: java Convolve" +
                             " corner edge identity");
          System.out.println("where corner, edge, " +
                             "and identity are ints");
          System.exit(0);
        }
        int corner = Integer.parseInt(args[0]);
        int edge = Integer.parseInt(args[1]);
        int identity = Integer.parseInt(args[2]);
        new Convolve(corner, edge, identity);

      }

   }
Image

Try other combinations of the three command-line parameters. For example, enter -1 -1 8 to see highlights of the edges of objects. This cannot be normalized as the sum is zero. Or enter 0 -1 5 for a sharper view of the original image. You can easily extend this example to five-by-five arrays and higher, with other symmetric primitives.

For more information on using ConvoleOp, see Chapter 5: "Imaging" in the Programmer's Guide to the Java 2D API.

Copyright (c) 2004-2005 Sun Microsystems, Inc.
All Rights Reserved.


 Related Tips

 
< Prev

Page 1 of 0 ( 0 comments )

You can share your information about this topic using the form below!

Please do not post your questions with this form! Thanks.


Name (required)


E-Mail (required)

Your email will not be displayed on the site - only to our administrator
Homepage(optional)



Comment Enable HTML code : Yes No



 
       
         
     
 
 
 
   
 
 
java bottom left
java bottom middle
java bottom right
RSS 0.91 FeedRSS 1.0 FeedRSS 2.0 FeedATOM FeedOPML Feed

Home - About Us - Privacy Policy
Copyright 2005 - 2008 www.java-tips.org
Java is a trademark of Sun Microsystems, Inc.