Image Processing 9. Blur

3 min read June 24, 2025 #image processing #javascript

Let's return to pixel processing and this time explore image blurring algorithms.

logo

Standard Blur#

Last time, when discussing bilinear image scaling, we sampled 4 pixels in the original image for each new pixel to blend components with specific coefficients for smooth results. For the Lanczos filter, number of pixels increased to 9, 25, etc.

Blurring works on the same principle: sample multiple pixels around the current target pixel, then calculate the arithmetic mean for each color component.

for (let y = 0; y < height; y++) {
  for (let x = 0; x < width; x++) {
    let a = 0, r = 0, g = 0, b = 0, count = 0;
    for (let sy = y - size; sy <= y + size; sy++) {
      const yy = Math.min(height - 1, Math.max(0, sy));
      for (let sx = x - size; sx <= x + size; sx++) {
        const xx = Math.min(width - 1, Math.max(0, sx));
        let pix = src[yy * width + xx];
        r += pix & 0xFF;
        g += (pix >> 8) & 0xFF;
        b += (pix >> 16) & 0xFF;
        a += (pix >> 24) & 0xFF;
        count++;
      }
    }

    a = (a / count) & 0xFF;
    r = (r / count) & 0xFF;
    g = (g / count) & 0xFF;
    b = (b / count) & 0xFF;

    dst[dstIndex++] = (a << 24) | (b << 16) | (g << 8) | r;
  }
}

blur1.jpg

Let's consider a brief calculation. Given a 400×292 image.

That's a lot! And this isn't even Gaussian blur, which might use 25×25 pixels!

Note on Gaussian Blur

Unlike the method above, Gaussian blur applies decreasing coefficients based on distance from the center. Feel free to modify the code above to account for coefficients. For example, for a radius of 1:

0.2 0.6 0.2
0.6  1  0.6
0.2 0.6 0.2

Box Blur#

There're algorithms that approximate Gaussian blur results. They produce slightly lower quality, but operate significantly faster. For example, you can first blur only in the horizontal direction, then blur the resulting image vertically.

boxblur.jpg

You can also reduce the number of repeated pixel reads. For example, we perform horizontal blurring and are currently at point (0,3). The viewing radius is two pixels (2 left + 2 right + center):

boxblur_opt1.png

After calculating the sum and dividing by 5, we stored the result at (0,3). Now, move on to processing (0,4):

boxblur_opt2.png

Last time we already read (0,2), (0,3), (0,4), and (0,5). And these values are already in the sum, so instead of rereading, we subtract the leftmost pixel (0,1) from the sum and add the new right pixel (0,6):

boxblur_opt3.png

The number of reads is reduced to 2 per pixel (plus 2r+1 for the first pixel in the row). You can go further and replace division by reading a precomputed average value.

A single pass gives average result, so this filter is typically applied two or more times. Below a comparison between our box blur implementation with a radius of 10 and Photoshop's Gaussian blur with the same radius:

blur2.jpg

Again, the math. For our 400×292 image the first pass of the optimized algorithm at radius 1 gives 469,276 reads. At radius 2: 470,660, 3: 472,044, 5: 474,812, 10: 481,732, 20: 495,572 reads.

At radius 3, we achieve 12x fewer reads than the unoptimized approach. Even if you perform blurring two or three times, it will still be faster.