Java · Course navigation

Java: filtering an image with a convolution kernel

Slide a 3×3 averaging kernel over a small grayscale image to smooth it.

On this page
Before you start

Java two-dimensional arrays.

  • Slide a 3×3 averaging kernel over a small grayscale image to smooth it.
  • Predict the sample output and explain the state changes.
  • Complete the practice task and check the boundary cases.

The concept

A grayscale image is a grid of numbers where each pixel stores brightness. Convolution slides a small grid of weights, the kernel, over the image. At each position the pixels under the kernel are multiplied by the weights and summed to give one output pixel.

A kernel of all ones divided by 9 replaces each pixel by the average of its 3×3 neighbourhood, which blurs the image. This example computes only positions where the kernel fits entirely inside the image, so a 5×5 image gives a 3×3 result. Real libraries also pad the border so the output keeps the input size.

Code example

Java
class Main {
  public static void main(String[] args) {
    int[][] image = {
      {10, 10, 10, 10, 10},
      {10, 10, 10, 10, 10},
      {10, 10, 80, 80, 80},
      {10, 10, 80, 80, 80},
      {10, 10, 80, 80, 80},
    };
    int size = image.length - 2;
    int[][] out = new int[size][size];
    for (int r = 0; r < size; r++) {
      for (int c = 0; c < size; c++) {
        int sum = 0;
        for (int kr = 0; kr < 3; kr++) {
          for (int kc = 0; kc < 3; kc++) {
            sum += image[r + kr][c + kc];
          }
        }
        out[r][c] = sum / 9;
      }
    }
    for (int[] row : out) {
      System.out.println(row[0] + " " + row[1] + " " + row[2]);
    }
  }
}
Output
17 25 33
25 41 56
33 56 80

Follow the code

Open the walkthrough to inspect the example one step at a time.

Show guided walkthrough
Guided walkthroughReady
1class Main {
2 public static void main(String[] args) {
3 int[][] image = {
4 {10, 10, 10, 10, 10},
5 {10, 10, 10, 10, 10},
6 {10, 10, 80, 80, 80},
7 {10, 10, 80, 80, 80},
8 {10, 10, 80, 80, 80},
9 };
10 int size = image.length - 2;
11 int[][] out = new int[size][size];
12 for (int r = 0; r < size; r++) {
13 for (int c = 0; c < size; c++) {
14 int sum = 0;
15 for (int kr = 0; kr < 3; kr++) {
16 for (int kc = 0; kc < 3; kc++) {
17 sum += image[r + kr][c + kc];
18 }
19 }
20 out[r][c] = sum / 9;
21 }
22 }
23 for (int[] row : out) {
24 System.out.println(row[0] + " " + row[1] + " " + row[2]);
25 }
26 }
27}

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
A 3×3 kernel fits in 3 positions along each side of a 5×5 image.output is 3×3
—
Add the nine pixels under the kernel; every weight is 1.top-left window sum = 160
—
Divide by 9 to average them: 160 / 9 is 17 with integer division.out[0][0] = 17
—
The sharp step between 10 and 80 has become a gradual ramp.out[0][0] = 17
17 25 33
25 41 56
33 56 80

Behind the scenes

How Java processes this code

The Java compiler produces bytecode. The JVM loads the class and invokes its main method.

Cost and common mistakes

O(R·C·k²) for an R×C image and a k×k kernel.

  • Integer division drops fractions. Use double when the exact average matters.
  • Strictly, convolution flips the kernel before sliding it; most libraries actually compute correlation. For a symmetric kernel such as this one the two are identical.
  • Reading outside the array throws ArrayIndexOutOfBoundsException, which is why the loops stop before the border.

Try it yourself

What is out[2][2]?

Reveal answer and reasoning

All nine pixels under that window are 80, so the average is 80.

Further reading