Drawing imagesProgramming Practice TutorialsReading and writing text filesImage processing

Image processing

To process images such as photos, we need to be able to load photo files into an image object, read and set pixels values of the image, and save the image back into a photo file.

We use the class java.awt.image.BufferedImage to store image data. You can create such an object by saying:

  import java.awt.image.BufferedImage

  val img = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)

Or you can load an image from a file using:

  import javax.imageio.ImageIO

  val photo1 = ImageIO.read(java.io.File("photo.jpg"))

The result of this call is a again a BufferedImage object. You can find the width and height of this image using the following methods:

  println("Photo size is ${photo1.width}, ${photo1.height}")

You can save an image object into a file using:

  ImageIO.write(photo1, "jpg", java.io.File("test.jpg"))
or, if you prefer PNG format:
  ImageIO.write(photo1, "png", java.io.File("test.png"))

The pixels of a BufferedImage object can be read and changed using the getRGB and setRGB methods, see below.

Example script

The following small script reads a file photo.jpg, get its dimensions, creates a new empty image of the same size, copies the pixels from the old image img to the new image out (by mirroring them horizontally), draws a red line diagonally across the photo, and finally saves the new image in a file test.jpg (image.kts):

import java.io.File
import javax.imageio.ImageIO
import java.awt.image.BufferedImage

fun phototest(img: BufferedImage): BufferedImage {
  // obtain width and height of image
  val w = img.width
  val h = img.height

  // create new image of the same size
  val out = BufferedImage(w, h, BufferedImage.TYPE_INT_RGB)

  // copy pixels (mirror horizontally)
  for (x in 0 until w)
    for (y in 0 until h)
      out.setRGB(x, y, img.getRGB(w - x - 1, y) and 0xffffff)
  
  // draw red diagonal line
  for (x in 0 until Math.min(h, w))
    out.setRGB(x, x, 0xff0000)

  return out
}
  
fun test() {
  // read original image, and obtain width and height
  val photo1 = ImageIO.read(File("photo.jpg"))
  
  val photo2 = phototest(photo1) 

  // save image to file "test.jpg"
  ImageIO.write(photo2, "jpg", File("test.jpg"))
}

test()

Colors

The method getRGB(x: Int, y: Int): Int returns the color of the pixel at position (x, y), the method setRGB(x: Int, y: Int, color: Int) sets the color of this pixel.

Colors are represented as Int objects. The three components red, green, and blue are "packed" together into one integer. Each component has 8 bits, and therefore can have a value between 0 and 255. The packed color is a 32-bit integer, whose bits look like this:

  tttt tttt rrrr rrrr gggg gggg bbbb bbbb
The top 8 bits are either zero, or represent the "transparency" of the pixel. We will not use these transparency bits. The next 8 bits represent red, the next 8 bits represent green, and the last 8 bits represent blue. This is why this representation is called "RGB".

Given red, green, and blue components with values in the range 0 to 255, we can pack them together like this:

  val color = (red * 65536) + (green * 256) + blue
Given a packed integer color, we can extract the three components as follows:
  val red = (color and 0xff0000) / 65536
  val green = (color and 0xff00) / 256
  val blue = (color and 0xff)
(The and operator here is the bitwise-and operator. It makes sure that only the bits we are interested in are used.)
Drawing imagesProgramming Practice TutorialsReading and writing text filesImage processing