Denoising is the first step before thresholding, edges or features, and each filter trades detail for noise differently.
A filter replaces each pixel with a function of its neighbourhood. Linear filters compute a weighted sum defined by a kernel.
cv2.blur(img, (5, 5)): box filter, the plain averagecv2.GaussianBlur(img, (5, 5), sigmaX): weights fall off with distance; the go-to smoother. Kernel sizes must be odd; pass(0, 0)with a sigma to let OpenCV pick the size.cv2.medianBlur(img, 5): takes the median, which removes salt-and-pepper noise completely while keeping edges sharper than averaging. Not linear.cv2.bilateralFilter(img, d, sigmaColor, sigmaSpace): averages only pixels that are close and similar in colour, so it smooths flat areas but preserves edges. Much slower.
cv2.filter2D(img, ddepth, kernel) applies any kernel you define, such as sharpening or embossing. ddepth=-1 keeps the input dtype; use cv2.CV_32F when results can go negative or above 255. Strictly it computes correlation, not convolution, which only matters for asymmetric kernels.
At the borders the kernel overhangs the image. The borderType argument decides how missing pixels are invented; the default BORDER_REFLECT_101 mirrors the image without repeating the edge pixel.
Separable kernels like the Gaussian are computed as a row pass then a column pass, so even large blurs are cheap.
Example
import cv2
import numpy as np
img = np.full((50, 50), 128, np.uint8)
noisy = img.copy()
rng = np.random.default_rng(0)
ys, xs = rng.integers(0, 50, (2, 100))
noisy[ys, xs] = 255 # salt noise
box = cv2.blur(noisy, (5, 5))
gauss = cv2.GaussianBlur(noisy, (5, 5), 0)
median = cv2.medianBlur(noisy, 5)
print(box.max(), gauss.max()) # still above 128: spikes smeared
print(median.max()) # 128 -- median removed them
sharpen = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32)
step = np.zeros((5, 5), np.uint8); step[:, 2:] = 100
print(cv2.filter2D(step, -1, sharpen)[2]) # [ 0 0 200 100 100] clipped
print(cv2.filter2D(step, cv2.CV_32F, sharpen)[2]) # [ 0. -100. 200. 100. 100.]Remember
- Gaussian blur is the default smoother; kernel sizes are odd
- Median blur removes salt-and-pepper noise and keeps edges
- Bilateral filter preserves edges at a high cost in speed
- filter2D applies custom kernels; use a float ddepth for signed results
Watch out for
- Using filter2D with ddepth=-1 on uint8 when the result can be negative, silently clipping it to 0.