dcsimg
December 7, 2016
Hot Topics:

Working with Images in Java

  • November 16, 2016
  • By Manoj Debnath
  • Send Email »
  • More Articles »

Java provides extensive support in its core API for working with images. Image processing is a vast area in its own right. Serious programmers in this domain use more than one tool/library, such as OpenCV, to work with images. Java, being a general purpose language, supports the essential features for common programming needs. However, the OpenCV library has its Java counterpart that can be effectively used for implementing more crude operations with the images. This article explores the key aspects of working with images using the core API library with a brief introduction on some background information.

Images

As we talk of images in computing, they are of different types; the term colloquially mean graphics, pictures, images, logos, and so forth. This article makes no distinction and designates one and all of them as images; in fact, they are same from the programming point of view. The basic property of an image is defined by its dimension, resolution measured in pixels, and the coordinate system independent of the drawing surface. An image in Java is primarily an object of the Image class. It is a part of the java.awt package, along with many other auxiliary imaging classes and interfaces. These core APIs are used mainly for;

  • Loading images
  • Drawing images
  • Manipulating images
  • Saving images

But, before delving into these processes, let's get an idea of image file formats.

File Format

The information about the combination of color, pixels, and dimensions make an image file. They need to be stored in memory. As a result, the information has to be represented in an array of bits. An image file becomes quite large when represented in an simple array of bits. So, a mechanism is devised to compress them so that it not only reduces the file size but also retains visual quality. This is exactly what the encoding technique does. It arranges the bits in a manner that can be stored in a digital medium or transmitted easily in a network. Each encoding technique defines an image file format.

File formats are the data structure on how image information is encoded and stored in a repository. Understandably, these encoding techniques are quite complex and heavily depend upon complex mathematical formulation. Many of them are standardized and commonly used on the Web. The three most common file formats that we always encounter are JPEG (Joint Photographer Experts Group), GIF (Graphics Interchange Format), and PNG (Portable Network Graphics). There are many others, such as TIFF, Exif, BMP, and so on. Let's stick to only three of them for now:

  • JPEG files have an extension jpeg or jpg. They use lossy compression based on DCT (Discrete Cosine Transformation) to encode an image. The JPEG encoding technique discards some image information in favor of compression. This makes the file size considerably smaller. The information is discarded in such a manner that the loss of quality is almost unrecognizable to the human eye. Also, there is another lossless JPEG standard, which is not that popular, possibly because there is not much to complain about the quality of images produced by lossy compression of JPEG.
  • PNG files are used for storing bit-mapped images using lossless data compression. The extension of this file is png. It is a raster graphics file format, often used in image processing as a raw data. PNG images are not suitable for print and meant to be used for transferring images through network and display. In fact PNG is an improved substitute of GIF images. While GIF supports animation; the corresponding PNG file format that supports animation is MNG.
  • GIF files are bitmap images with the extension gif. They also use palette-based, lossless data compression. GIF files represent graphics in a manner that can be used to create a type of rudimentary animation. Back in the 1980s, GIF files rocked Web pages with animation. Later, in 1985, when this file format was patented, controversies peaked over its licensing. This led to the emergence of a non-patented PNG file format. GIF files are particularly used for graphics, logos, and small images, much like PNG files, and are actually a predecessor of PNG.

Java API for Images

The most common class in the Java API that deals with images is java.awt.Image. Image is an abstract class, sub classed by BufferedImage and VolatileImage. Among these two, BufferedImage is most frequently used.

Images1
Figure 1: Classes of images

Images are displayed using drawImage methods of the Graphics class.

abstract boolean

drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer)

Draws as much of the specified image as is currently available.

abstract boolean

drawImage(Image img, int x, int y, ImageObserver observer)

Draws as much of the specified image as is currently available.

abstract boolean

drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer)

Draws as much of the specified image as has already been scaled to fit inside the specified rectangle.

abstract boolean

drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)

Draws as much of the specified image as has already been scaled to fit inside the specified rectangle.

abstract boolean

drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer)

Draws as much of the specified area of the specified image as is currently available, scaling it on the fly to fit inside the specified area of the destination drawable surface.

abstract boolean

drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer)

Draws as much of the specified area of the specified image as is currently available, scaling it on the fly to fit inside the specified area of the destination drawable surface.

** Extracted from Java API documentation

This method displays the images passed as a reference. Apart from the coordinates passed as a parameters, the method drawing an image is associated with the ImageObserver interface and Color class. The Color class encapsulates the colors in the RGB color space and an implicit alpha value that specifies the transparency. The ImageObserver interface receives notification as the image is being updated. This is particularly useful to show the progress of image update of a large image or while downloading image from a slow network connection.

BufferedImage vs VolatileImage

As per the Java API documentation and Chet's VolatileImage Q&A, the concrete subclass BufferedImage uses an accessible data buffer and relies on the image manipulation techniques defined by the methods of java.awt.image.Raster and the color characterization methods of java.awt.image.ColorModel. BufferedImage objects are allocated in an area that is in full control of the application. VolatileImage objects, on the other hand, are created in the limited system resource area, such as VRAM. Such limited resource areas are not in control of the application. As a result, the objects may be garbage collected as a result of optimizing limited resources or due to a proactive call of the flush method. So, the use of the VolatileImages object is unreliable or should be used cautiously. However, when the question of rendering performance arises, VolatileImage has the leverage of hardware rendering, whereas BufferedImage has to remain satisfied with the software rendering performance.

Images that we'll be using in the code below are as follows.

Name: starburst.jpg

Size: 350x200

Revised2
Figure 2: Starburst image

Name: blob.jpg

Size: 50x50

Images3
Figure 3: A blob

Fundamental Operations

As with working with any basic file, images are loaded from an external file stored in a digital medium or an URL from the network; then, an image is created and finally saved. The following code will show how to do it in a simple manner.

Images need to loaded or created in a GUI component. We'll be using java.swing.JPanel as a container for our images. The paint method of this component can be overloaded to create the image. The paint method is automatically invoked to render the component on-screen. It takes a Graphics object as a parameter. This object reference is used to create the image.

Here, we'll be loading two images superimposing one on other, write some text, and finally save the image as a jpg file onto the disk.

package org.mano.example3;

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

import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class ImagePanel extends JPanel {
   private static final long serialVersionUID = 1L;
   private BufferedImage image;

   public ImagePanel(BufferedImage image) {
      this.image = image;
   }

   public BufferedImage getImage() {
      return image;
   }

   public void paint(Graphics g) {
      g.drawImage(image, 0, 0, this);
   }

   public void loadOriginalImage(File file) {
      try {
         image = ImageIO.read(file);
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   public void createArtWork() {
      if (image == null)
         return;
      try {
         Graphics g = image.getGraphics();
         g.setColor(Color.red);
         g.drawString("Picture speaks thousand words", 50, 50);
         g.drawImage(ImageIO.read(new File("/home/mano/Pictures/
            cartoons/blob.jpg")), 120, 100, null);
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   public void convertToGrayscale() {
      if (image == null)
         return;
      for (int i = 0; i < image.getHeight(); i++) {
         for (int j = 0; j < image.getWidth(); j++) {
            Color imageColor = new Color(image.getRGB(j, i));
            int rgb = (int) (imageColor.getRed() * 0.299)
               + (int) (imageColor.getGreen() * 0.587)
               + (int) (imageColor.getBlue() * 0.114);
            Color newColor = new Color(rgb, rgb, rgb);
            image.setRGB(j, i, newColor.getRGB());
         }
      }
   }
}


package org.mano.example3;

import java.awt.*;
import java.io.*;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;

public class ImageProcessing extends JFrame {
   private static final long serialVersionUID = 1L;
   private final JPanel buttonPanel;
   private ImagePanel imagePanel;

   private final JButton artButton;
   private final JButton gsButton;
   private final JButton loadButton;
   private final JButton saveButton;

   public ImageProcessing() {
      try {
         imagePanel = new ImagePanel(ImageIO.read(new File
            ("/home/mano/Pictures/cartoons/tangled.jpg")));
      } catch (IOException e) {
         e.printStackTrace();
      }
      buttonPanel = new JPanel();
      buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
      artButton = new JButton("Create Art");
      artButton.addActionListener(new ButtonHandler());
      gsButton = new JButton("Grascale");
      gsButton.addActionListener(new ButtonHandler());
      loadButton = new JButton("Load Image");
      loadButton.addActionListener(new ButtonHandler());
      saveButton = new JButton("Save");
      saveButton.addActionListener(new ButtonHandler());

      buttonPanel.add(artButton);
      buttonPanel.add(gsButton);
      buttonPanel.add(loadButton);
      buttonPanel.add(saveButton);

      getContentPane().add(imagePanel, BorderLayout.CENTER);
      getContentPane().add(buttonPanel, BorderLayout.NORTH);
   }

   private class ButtonHandler implements ActionListener {

      @Override
      public void actionPerformed(ActionEvent e) {
         if (e.getSource() == artButton) {
            imagePanel.createArtWork();
            imagePanel.repaint();
         } else if (e.getSource() == gsButton) {
            imagePanel.convertToGrayscale();
            imagePanel.repaint();
         } else if (e.getSource() == loadButton) 
            File file = null;
            JFileChooser fc = new JFileChooser("/home");
            FileNameExtensionFilter filter = new
               FileNameExtensionFilter("JPG, PNG & GIF Images",
                                       "jpg", "gif", "png");
            fc.setFileFilter(filter);
            int ret = fc.showOpenDialog(null);
            if (ret == JFileChooser.APPROVE_OPTION) {
               file = fc.getSelectedFile();
            }
            if (file != null) {
               imagePanel.loadOriginalImage(file);
               imagePanel.repaint();
            }
         } else if (e.getSource() == saveButton) {
            if (imagePanel.getImage() != null) {
               JFileChooser fc = new JFileChooser("/home");
               fc.setDialogTitle("Save file as JPG");
               int ret = fc.showSaveDialog(null);
               if (ret == JFileChooser.APPROVE_OPTION) {
                  File file = fc.getSelectedFile();
                  try {
                     ImageIO.write(imagePanel.getImage(), "jpg", file);
                  } catch (IOException e1) {
                     e1.printStackTrace();
                  }
               }
            }
         }
      }
   }

   public static void main(String[] args) {
      // start application
      ImageProcessing ip = new ImageProcessing();
      ip.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      ip.setSize(450, 300);
      ip.setVisible(true);
   }
}

Output

Revised4
Figure 4: Starburst image with control buttons and the blob

Conclusion

The imaging classes provided in this are article are just tip of the iceberg. The core API library has several other classes that can be used to have a better control over imaging process and support advanced imaging techniques. Some of the classes are described in package javax.imageio. Other classes such as MediaTracker which enables checking the status of arbitrary number of images in parallel, interfaces such as ImageProducer, ImageConsumer to work with image data, ImageFilter objects can be used to manipulate pixels in the images. In a nutshell, the core API library along with a third party library such as OpenCV can provide almost every need of an image processing programmer.


Tags: Java, jpeg, gif, png, image files, OpenCV library, core API library, DCT, lossy compression, lossless compression, GUI component




Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date
Rocket Fuel