Linear Filtering and Convolution
A photograph fresh off a camera sensor is never quite clean. It carries speckled noise from photon counting, faint blur from lens imperfections, and edges that a later algorithm needs to find precisely. All of these problems share a structural feature: fixing them requires looking at a pixel and its neighbors, not the pixel alone. Point Operations can remap brightness or contrast. But they cannot smooth noise or detect an edge. This is because they process each pixel in isolation. Linear filtering removes that limitation. It defines a new value at each pixel as a weighted combination of the values in a small surrounding neighborhood. This idea is the workhorse underneath nearly every classical vision pipeline. It also underlies much of modern deep learning. Camera image signal processors denoise and sharpen using linear filters before you ever see a photo. Edge detectors, corner detectors, and feature descriptors begin with a handful of small convolutions. Convolutional neural networks generalize the exact same sliding-window, weighted-sum operation. They learn the kernel weights from data instead of designing them by hand. If you understand convolution here, you already understand the core arithmetic of a CNN layer. The big picture is deceptively simple: pick a small grid of numbers (the kernel), slide it over the image. At each position, compute a weighted sum of the pixels underneath it. The entire richness of the topic lies in two questions. What weights should you choose? And how do you handle the mathematics precisely (correlation versus true convolution, and what happens at the image border)? Get these right and you unlock blurring, sharpening, edge detection. You also unlock the mathematical language (the frequency domain) used to reason about all of them. This article builds linear filtering from first principles. It covers the definitions of correlation and convolution, the algorithm and its complexity, and a worked numerical trace. It also covers the classic implementation pitfalls, and the historical path from analog signal processing to today's learned convolutional layers.
- A linear filter replaces each pixel with a weighted sum of its neighborhood, using weights given by a small kernel.
- Correlation slides the kernel as-is; convolution flips the kernel first — they coincide for symmetric kernels.
- Filtering is linear and shift-invariant: scale inputs, scale outputs; shift the image, shift the output identically.
- Naive filtering costs O(N · k²) for an N-pixel image and a k×k kernel; separable kernels cut this to O(N · k).
- Border handling (zero-pad, replicate, reflect, wrap) is a real design decision, not a footnote — it changes pixel values near edges.
- Convolution is the exact arithmetic operation inside a convolutional neural network layer, just with learned kernels.
Linear filtering computes each output pixel as a fixed weighted sum of an input neighborhood, and that single sliding-window operation — convolution — is the shared mathematical core of classical image processing and modern CNNs.
The idea
A linear filter (also called a neighborhood operator or linear shift-invariant system) produces an output image whose value at each pixel is a linear combination of input pixel values in a surrounding window. The pattern of weights is called the kernel or filter mask, written as a small array , typically odd-sized (, , …) so it has a well-defined center pixel. There are two closely related, frequently confused operations. Correlation places the kernel directly over the neighborhood and computes
Convolution first flips the kernel by 180° (negating both indices) and then does the same sliding sum:
For a kernel that is symmetric under 180° rotation (true of a Gaussian kernel and of a uniform box kernel), correlation and convolution give identical results. For an asymmetric kernel — a derivative kernel used for Image Gradients, for instance — they differ, and the distinction matters: convolution is the operation with the clean mathematical properties (associativity, commutativity, and a direct correspondence to multiplication in the frequency domain, previewed in The Fourier View of Images), while correlation is what most image-processing libraries actually implement by default because it is more intuitive to specify ("the kernel is the pattern you're matching"). Two properties make this operation worth an entire formal theory rather than being a mere trick. Linearity means filtering a sum of images equals the sum of filtering each image, and scaling an image before filtering scales the output identically: . Shift invariance means sliding the input by before filtering produces exactly the same output slid by — the filter has no notion of "special" image locations. A system with both properties is called an LSI (linear shift-invariant) system, and a classical result of signal processing states that every LSI system is expressible as a convolution with some kernel — the kernel is the system's complete description, called its impulse response.
How it works
The mechanical procedure is the same for correlation and convolution up to the kernel flip, so the pseudocode below implements correlation and notes the one-line change needed for true convolution.
Step 1 — choose the kernel. A array of weights, e.g. a box-blur kernel of all , or a Sobel derivative kernel.
Step 2 — decide the border policy. The kernel needs pixels outside the image whenever its center sits within pixels of an edge. Common choices: zero-padding (treat outside pixels as 0), replicate (clamp to nearest edge pixel), reflect (mirror across the edge), and wrap (treat the image as periodic/toroidal). Each yields a different — sometimes very different — output near the border.
Step 3 — slide and accumulate. For every output pixel, center the kernel there, multiply kernel weights by the (possibly border-extended) underlying pixels, and sum.
Step 4 — (optional) normalize / clip. If the kernel weights do not sum to 1 (e.g. a derivative kernel), the output may need rescaling for display, and values may need clipping into the valid intensity range. ``` function FILTER(image F, kernel H, mode): # mode ∈ {correlate, convolve} (W, H_img) = size(F) k = (size(H) - 1) / 2 # assumes H is (2k+1) x (2k+1) G = new image of size (W, H_img) for y in 0 .. H_img - 1: for x in 0 .. W - 1: sum = 0 for j in -k .. k: for i in -k .. k: if mode == convolve: (hi, hj) = (-i, -j) # flip kernel indices else: (hi, hj) = (i, j) (sx, sy) = BORDER(x - i, y - j, W, H_img) # or x+i,y+j; see note sum += H[hi + k][hj + k] * F[sx][sy] G[x][y] = sum return G function BORDER(sx, sy, W, H_img):
# zero-pad: caller instead returns 0 directly when sx,sy out of range
# replicate: sx = clamp(sx, 0, W - 1) sy = clamp(sy, 0, H_img - 1) return (sx, sy) ``` A production implementation exploits separability: if a 2-D kernel can be written as an outer product of two 1-D kernels, (true of the box filter and the Gaussian filter), then filtering with is identical to filtering with along rows and then along columns: ``` function SEPARABLE_FILTER(image F, vector u, vector v):
# H = v * u^T (rank-1 kernel) Temp = FILTER_1D_HORIZONTAL(F, u) # convolve each row with u G = FILTER_1D_VERTICAL(Temp, v) # convolve each column with v return G ``` This is not an approximation — it is algebraically exact, because the 2-D sum factors:
Complexity & correctness
Time complexity. For an image with pixels and a kernel, the direct (naive) algorithm visits every output pixel once and does multiply-adds per pixel, giving
This is tight for the direct method: every one of the kernel taps genuinely must be read and multiplied for a general (non-separable) kernel, so no direct algorithm can do fewer operations without extra structure. If the kernel is separable (, rank 1), filtering decomposes into two 1-D passes, each doing work per pixel, for a total of
This is a genuine asymptotic win — for a kernel, versus , roughly a 7.5× speedup — and it is exact, not approximate, because of the algebraic factoring shown above. A third route uses the convolution theorem: multiplying in the frequency domain via the FFT costs regardless of , which wins asymptotically once is large enough relative to ; the crossover in practice is typically around in the dozens, since the FFT carries constant-factor and memory overhead that direct or separable filtering avoids for small kernels.
Space complexity. The direct and separable algorithms need space for the output image (plus for the intermediate pass in the separable case, or in-place with care), and or for the kernel itself — negligible compared to the image. FFT-based filtering needs working space for the transformed arrays, with a larger constant due to complex-valued intermediate storage and (for linear, not circular, convolution) zero-padding to at least by .
Correctness argument. The claim to justify is not an algorithmic subtlety but a mathematical identity: the sliding-window sum, as defined, is correlation or convolution by construction, so correctness reduces to showing the loop faithfully implements the sum. Three invariants guarantee this: 1. Index coverage. For each output location , the inner double loop over visits every kernel tap exactly once, matching the summation range in the definition. 2. Correct addressing. For correlation the sample read is ; for convolution it is — equivalently, correlation with the 180°-rotated kernel. Implementing the flip as `hi, hj = -i, -j` while still reading `F[x - i][y - j]` reproduces exactly the convolution sum; swapping only one of the two (kernel index or image index) but not both is the classic bug that silently turns convolution into correlation or vice versa. 3. Border consistency. Whatever border policy is chosen, it must be applied consistently to every out-of-range access, otherwise the boundary handling itself is not shift-invariant (a pixel near the top edge and an identical pixel pattern near the left edge should be treated by the same rule, just reflected in orientation). Given these invariants hold, the output at every pixel equals the defined sum exactly (up to floating-point rounding), so the algorithm is correct. Separability's correctness follows from the distributive-law factoring shown earlier — it is not a heuristic approximation, so the separable and direct algorithms are provably identical (up to floating-point associativity effects), not merely similar.
Honest tradeoffs. Larger kernels blur more but cost proportionally more time () unless separable. Zero-padding introduces a dark halo near edges (output pixels near the border are biased toward zero); replicate and reflect avoid that bias but can create artifacts of their own (replicate can smear an edge feature; reflect assumes local symmetry that may not exist). FFT-based convolution is asymptotically superior for large kernels but adds implementation complexity, a periodic-wraparound artifact if padding is insufficient, and higher constant overhead unsuitable for the small – kernels dominant in classical vision.
Worked example
Take a grayscale image (values 0–9, purely illustrative) and correlate it with the non-symmetric kernel
(the horizontal Sobel kernel, foreshadowing Image Gradients), using zero-padding, and compute one interior output pixel by hand to make the flip/no-flip distinction concrete. Image (rows top to bottom, columns left to right, 0-indexed):
| c0 | c1 | c2 | c3 | c4 | |
|---|---|---|---|---|---|
| r0 | 2 | 2 | 2 | 8 | 8 |
| r1 | 2 | 2 | 2 | 8 | 8 |
| r2 | 2 | 2 | 2 | 8 | 8 |
| r3 | 2 | 2 | 2 | 8 | 8 |
| r4 | 2 | 2 | 2 | 8 | 8 |
This is a vertical step edge (dark on the left, bright on the right) — exactly the pattern Sobel-x is designed to detect.
Correlation at pixel . The neighborhood centered there (rows 1–3, cols 1–3):
Correlation multiplies elementwise with as written and sums:
Convolution at the same pixel. First flip by 180° (reverse both rows and columns):
Now correlate with :
So at this pixel correlation gives and convolution gives — exact sign flips, as expected for a kernel that is antisymmetric under 180° rotation. Both correctly signal "there is a strong vertical edge here"; the sign merely encodes which side is brighter, and a consistent choice (always convolution, or always correlation) must be used throughout a pipeline so signs stay comparable across the whole image.
Border pixel check. Now compute correlation at , which needs zero-padded neighbors above and to the left:
Compare this to the true (unpadded) local pattern, which is uniformly in the visible region — an interior pixel deep inside the left constant region gives exactly (no edge), while this corner pixel gives purely because zero-padding manufactured a fake dark region outside the image. This is precisely the border artifact discussed in Complexity & Correctness: it is not a bug in the loop, but a consequence of the chosen border policy, and a replicate border would have returned here instead, matching the interior behavior.
Common pitfalls & traps
| Pitfall | What goes wrong | Fix |
|---|---|---|
| Confusing correlation and convolution | Asymmetric kernels (Sobel, derivative masks) produce sign-flipped or mirrored results if the kernel isn't flipped when "convolution" is claimed | Decide once which operation the pipeline uses and flip the kernel explicitly if convolution is required |
| Off-by-one in kernel indexing | Using `size(H)/2` with integer truncation on an even-sized kernel breaks the "center pixel" assumption | Always require odd kernel dimensions, or handle even kernels with an explicit anchor point |
| Ignoring border policy | Comparing a filtered image's edge pixels to interior pixels as if both were equally reliable | Document the border mode; consider cropping the outer pixels before downstream analysis |
| Integer overflow / clipping | Sharpening or derivative kernels with negative weights produce negative or out-of-range sums, silently wrapping in fixed-point arithmetic | Accumulate in floating point or wide integers, then clip/rescale for display |
| Treating separability as approximate | Re-deriving a rank-1 kernel decomposition by trial and error, assuming loss of accuracy | Recognize separability is an exact algebraic factoring — verify via SVD that the kernel matrix has rank 1 |
| Forgetting kernel normalization | A blur kernel whose weights sum to more than 1 brightens the image; less than 1 darkens it | Normalize kernel weights to sum to 1 for blur/averaging filters (not for derivative filters, whose weights should sum to 0) |
Pioneers & history
The mathematics of convolution predates computer vision by roughly two centuries, developed for analyzing linear systems in physics and later electrical engineering; image filtering imported this apparatus wholesale once digital images and Digital Images and Sampling made it computable.
Origins · 1 minPierre-Simon Laplace and how this was discovered
Laplace's work on integral transforms and linear operators, alongside contemporaries studying differential equations, laid mathematical groundwork later recognized as convolution — the same operator that, two centuries later, would slide across pixel grids to blur and sharpen digital photographs.
The throughline from Fourier's series to Sobel's edge kernel to LeCun's learned filters is the same sliding-window sum: only the source of the kernel weights changes, from analytic derivation, to manual engineering, to gradient-based learning.
Where this matters
Camera image signal processors (ISPs) apply small linear filters at every stage of the pipeline: demosaicing interpolation, noise reduction, and sharpening (an "unsharp mask," itself a linear filter combination) all run before an image is even displayed. Medical imaging systems filter CT and MRI slices to suppress acquisition noise while preserving diagnostic edges. Classical computer vision feature detectors — Sobel and Prewitt gradients, the Laplacian of Gaussian, the Harris corner detector — are built from a handful of small, fixed convolution kernels applied in sequence. In deep learning, the convolutional layer at the heart of every CNN (from LeNet to ResNet to modern vision transformer hybrids that still use convolutional stems) performs exactly the sliding weighted-sum operation described here, differing only in that the kernel weights are learned by gradient descent rather than hand-designed, and that many kernels (channels) are learned and combined at once. GPUs and specialized accelerators are built around fast convolution primitives (im2col + matrix multiply, Winograd convolution, FFT-based convolution) precisely because this operation is the dominant cost in both classical and learned vision pipelines. Video codecs and graphics pipelines use separable filters for fast resizing and blurring at real-time frame rates, directly exploiting the separable complexity argued above.
Ask the tutor: How does the convolution theorem let us compute large-kernel filtering faster using the FFT, and why does zero-padding size matter there?
Why do most software libraries call their filtering function "convolve" when it actually computes correlation?
Historically, many image-processing APIs prioritized the intuitive interpretation of a kernel as a literal pattern being matched against the image, which is exactly what correlation computes — no flip needed for the kernel to "look like" the feature you're searching for. True convolution's flip matters mathematically because it makes the operation commutative and associative and gives it the clean multiplication property in the frequency domain via the convolution theorem. For symmetric kernels the distinction is invisible, so many libraries never bothered to enforce the flip. The practical fix is to always check documentation and test with an asymmetric kernel (like Sobel) before trusting sign conventions in a new library.
Is a convolutional neural network layer literally the same operation described here?
Almost exactly, with two extensions. First, a CNN layer typically operates over multiple input channels simultaneously, summing contributions from a small 3-D kernel (height × width × input channels) rather than a single 2-D kernel. Second, most deep learning frameworks implement the correlation form (no kernel flip) despite calling it "convolution," which is mathematically harmless during training because the network simply learns whichever kernel orientation minimizes the loss. The sliding-window, weighted-sum arithmetic — and its complexity profile — is identical to classical linear filtering; only the source and dimensionality of the weights differ.
Why must the kernel usually have odd dimensions?
An odd-sized kernel, like 3×3 or 5×5, has a single unambiguous center pixel, so every output location aligns exactly with one input pixel. An even-sized kernel like 2×2 has no true center; its "center" sits between four pixels, forcing an arbitrary anchor convention (often rounding toward one corner) that can subtly shift the entire output image by half a pixel. Some specialized applications (certain downsampling filters) do use even kernels deliberately, but the anchor point must then be tracked explicitly, and most general-purpose filtering code simply requires odd sizes to avoid the ambiguity entirely.
How does separability relate to the rank of the kernel matrix?
A 2-D kernel can be viewed as a matrix; it is separable exactly when that matrix has rank 1, meaning it can be written as an outer product of a column vector and a row vector. This can be checked via singular value decomposition: a rank-1 matrix has exactly one nonzero singular value. The Gaussian and box (mean) filters are separable by construction because their 2-D formula is itself a product of two independent 1-D formulas (one per axis). Non-separable kernels, such as many hand-tuned sharpening masks, require the full per-pixel cost, though higher-rank kernels can sometimes be approximated by a sum of a few rank-1 terms for partial speedup.
What's the difference between "filtering" and the earlier point operations covered in prerequisite material?
A point operation computes each output pixel from that same single input pixel, using a fixed function such as a brightness curve or gamma correction — no neighborhood information is used at all. Linear filtering fundamentally requires a neighborhood: the output at a pixel depends on several nearby input pixels weighted by the kernel. This is why filtering can perform blurring, sharpening, and edge detection, none of which are achievable by any per-pixel remapping, since those tasks intrinsically require comparing a pixel to its surroundings.
Does the choice of border-handling policy actually change downstream results in a real pipeline?
Yes, measurably, especially for tasks reliant on edge or gradient information near image boundaries. Zero-padding manufactures a fake dark (or bright, depending on normalization) region outside the image, which can produce spurious gradient responses right at the border, as shown in the worked example. Replicate and reflect padding avoid manufacturing new intensities but can smear or falsely mirror real content. Many pipelines simply crop the outer pixels after filtering to sidestep the issue entirely, accepting a slightly smaller valid output region in exchange for guaranteed uncontaminated values everywhere that remains.
Why is the FFT-based approach not always used, given it's asymptotically faster for large kernels?
The FFT approach computes convolution in time independent of kernel size, which wins decisively for large kernels, but it carries real overhead: forward and inverse transforms, complex number arithmetic, and careful zero-padding to avoid circular-wraparound artifacts that would corrupt a linear convolution computed via a nominally periodic transform. For the small 3×3–7×7 kernels dominant in classical vision and early CNN layers, the constant factors of the direct or separable method are smaller in practice, so FFT convolution is reserved for genuinely large kernels or for signal-processing contexts (e.g., large blur radii, deconvolution) where its asymptotic advantage actually pays off.