Gaussian Blur: A Numerical Methods Implementation

Stack: Python (numpy)


Overview

An implementation of Gaussian image blurring without OpenCV, scipy.ndimage, or any library that abstracts away the math. Each stage is written directly in NumPy.

The current implementation covers discrete 2D convolution with a fixed binomial kernel and edge padding. Later stages derive the kernel analytically, parameterize sigma and radius, and prove separability.

The Mathematics

A Gaussian blur is a convolution of an image with a kernel. In the discrete case:

\[(f * g)[m, n] = \sum_{j=-\infty}^{\infty} \sum_{k=-\infty}^{\infty} f[j, k] \cdot g[m-j, n-k]\]

where $$f$$ is the image and $$g$$ is the kernel. At each pixel, the kernel slides over a window of neighbors, multiplies each value by the corresponding weight, and sums the products. Each pixel becomes a weighted average of its neighborhood. That's the blur.

The kernel in this implementation is the 3×3 binomial matrix:

\[\frac{1}{16}\begin{bmatrix}1&2&1\\2&4&2\\1&2&1\end{bmatrix}\]

It comes from taking the outer product of [1, 2, 1] (the second row of Pascal's triangle) with itself, giving a discrete approximation of the 2D Gaussian. The kernel is center-weighted and tapers toward the corners. Dividing by 16 normalizes the weights to sum to 1, which preserves overall image brightness. Skip that step and the output gets brighter or darker, not just blurrier.

When the kernel window extends past the image boundary, the behavior is undefined without padding. Four options are common: reflect, wrap, zero, and edge. Edge padding (replicating the nearest border pixel outward) was the right call here. The source image has no strong border content, doesn't tile, and the 3×3 kernel means only a one-pixel-wide strip is affected. The surface area for artifacts is small.

The naive convolution loop runs at $$O(n \cdot k^2)$$, where $$n$$ is the number of pixels and $$k$$ is the kernel side length. Fine for a 256×256 image with a 3×3 kernel. It doesn't scale. Part 3 covers separability, which brings this down to $$O(n \cdot 2k)$$.

Writing

The posts behind each stage: