Convolution & Image Filtering

Image ProcessingBeginner2 minOct 4, 2026▶ Open in Colab

Needs: Images as Arrays

What you’ll learn

  • What a kernel is and how it slides over an image
  • Why a box blur and a Gaussian blur look different
  • How the Sobel kernel turns brightness changes into edges

Intuition

Every output pixel is a weighted sum of its neighbors. The weights form a small grid called a kernel. Change those nine numbers and the same operation can blur, sharpen or highlight edges.

The math

For an input image II and a (2k+1)×(2k+1)(2k+1) \times (2k+1) kernel KK, the output GG is

G(x,y)=∑i=−kk∑j=−kkK(i,j) I(x+i,  y+j)G(x, y) = \sum_{i=-k}^{k} \sum_{j=-k}^{k} K(i, j)\, I(x + i,\; y + j)

Strictly, this is cross-correlation. True convolution flips the kernel first, K(−i,−j)K(-i, -j), which makes no difference for symmetric kernels such as a blur.

A blur kernel’s weights sum to 11, so flat regions keep their brightness. An edge kernel’s weights sum to 00, so flat regions become 00 and only changes survive.

Code

From scratch with NumPy:

import numpy as np

def conv2d(img, K):
    """img: (H, W) float32, K: (3, 3). Edge-replicated borders."""
    H, W = img.shape
    p = np.pad(img, 1, mode="edge")
    out = np.zeros_like(img)
    for i in range(3):
        for j in range(3):
            out += K[i, j] * p[i:i + H, j:j + W]
    return out

The same thing with OpenCV:

import cv2
import numpy as np

img = cv2.imread("cameraman.png", cv2.IMREAD_GRAYSCALE).astype(np.float32) / 255
sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], np.float32)
edges = cv2.filter2D(img, -1, sobel_x)

Try it

Pick a preset, then edit individual cells. Try making the Sobel kernel detect horizontal edges instead of vertical ones.

Kernel playgroundEdit any cell or pick a preset. The output updates as you type.
Input I
Output G = K ⋆ I

Results and pitfalls

Drag the slider to compare the input with a 5×55 \times 5 Gaussian blur (σ=1.4\sigma = 1.4). The fine stripes in the bottom right disappear first, because blurring is a low-pass filter.

InputGaussian σ=1.4

Common bugs:

  • Integer overflow. Filtering a uint8 image with a sharpening kernel clips negative values to 0. Work in float32.
  • Border handling. Zero padding darkens the edges of a blurred image. Use mode="edge" or cv2.BORDER_REFLECT.

Exercises

  1. Design a kernel that detects only horizontal edges.
  2. Why must a blur kernel’s weights sum to 1? What happens if they sum to 2?
  3. Show that applying a 3×33 \times 3 box blur twice equals one 5×55 \times 5 kernel. What does that kernel look like?