Computer Vision Chapter 5

Image filtering

Filtering combines each pixel with its neighbors using a kernel (small weight grid). Smoothing reduces noise and detail; sharpening emphasizes edges. Understanding convolution is the gateway to edge detection and many deep layers.

Convolution in one minute

At each location, place the kernel over the image, multiply overlapping values, sum (often normalize)—that sum becomes the new center pixel. Correlation is the same idea without flipping the kernel; in OpenCV’s filter2D the kernel is used as given. Borders need a policy: extend edges, reflect, wrap, or constant padding.

Low-pass (blur)

Weights positive and localized—averages neighbors, suppresses high frequencies (noise, fine texture).

High-pass (sharpen)

Emphasizes differences from neighbors—edges pop; also amplifies noise if overdone.

Box and Gaussian blur

Box blur (cv2.blur) uses equal weights—fast but can look “blocky.” Gaussian blur weights fall off with distance from the center; sigma controls spread. Larger kernels or sigmas mean more smoothing and softer edges.

import cv2

img = cv2.imread("photo.jpg")
box = cv2.blur(img, (5, 5))
gauss = cv2.GaussianBlur(img, (0, 0), sigmaX=1.5)  # ksize (0,0) → from sigma
gauss_k = cv2.GaussianBlur(img, (7, 7), 0)

Gaussian kernels are separable (apply 1D horizontal then vertical)—that is why large Gaussian blurs stay efficient.

Median and edge-aware smoothing

Median blur replaces the center with the neighborhood median—excellent for salt-and-pepper noise while preserving step edges better than linear blur. Kernel size must be odd.

import cv2

img = cv2.imread("noisy.jpg")
med = cv2.medianBlur(img, 5)

# Optional: bilateral filter — smooths flat regions, keeps strong edges (slower)
bilateral = cv2.bilateralFilter(img, d=9, sigmaColor=75, sigmaSpace=75)

Sharpening with filter2D

A simple 3×3 sharpen kernel adds a multiple of the Laplacian-like response (center positive, neighbors negative). Tune strength for your noise level.

import cv2
import numpy as np

img = cv2.imread("photo.jpg")
kernel = np.array([[ 0, -1,  0],
                   [-1,  5, -1],
                   [ 0, -1,  0]], dtype=np.float32)
sharp = cv2.filter2D(img, -1, kernel)

Depth -1 means output matches source depth; for float experiments, convert to float32, filter, clip, then back to uint8.

Borders and channels

Low-level filters need pixels outside the image. OpenCV defaults often use replication or reflection internally per function. Color images: most blur APIs run per-channel the same way; separable Gaussian is standard.

Takeaways

  • Gaussian blur for general-purpose smoothing before subsampling or noise reduction.
  • Median for impulse noise; bilateral when you need edge-preserving smoothing.
  • filter2D is the Swiss Army knife for custom kernels (sharpen, emboss, small conv nets).

Quick FAQ

Edges are high-frequency structure. Too much low-pass smoothing rounds off gradients, so peaks in the derivative shrink or vanish—tune blur to noise, not more than needed.

Same local sliding-window idea; CNNs learn kernels from data. Classical filters use fixed, hand-designed weights for interpretable preprocessing.