Image Enhancement & Restoration
Sharpening degraded images and restoring blurry/noisy images with Butterworth and Wiener filters.
Learning Objectives
- ✓Sharpen images using high-pass filters
- ✓Understand image degradation (blur + noise)
- ✓Apply Butterworth low-pass filter in frequency domain
- ✓Restore images using Wiener filter
Image Sharpening
Sharpening enhances edges by subtracting a smoothed version from the original (unsharp masking) or using a high-pass kernel. A Laplacian kernel highlights rapid intensity changes.
import cv2
import numpy as np
img = cv2.imread('input.jpg', cv2.IMREAD_GRAYSCALE)
# Laplacian sharpening kernel
kernel_1 = np.array([[0,-1,0],[-1,5,-1],[0,-1,0]], dtype=np.float64)
# Stronger sharpening
kernel_2 = np.array([[-1,-1,-1],[-1,9,-1],[-1,-1,-1]], dtype=np.float64)
sharp_1 = cv2.filter2D(img, -1, kernel_1)
sharp_2 = cv2.filter2D(img, -1, kernel_2)Image Degradation Model
A degraded image g = h * f + η, where f is the original, h is the blur kernel (PSF), and η is additive noise. The Fourier transform of this is: G(u,v) = H(u,v)·F(u,v) + N(u,v). Restoration tries to recover F from G.
from scipy.signal import convolve2d
import numpy as np
img_f = img.astype(np.float64) / 255.0
# Create motion blur kernel (LENGTH=21, ANGLE=10)
LENGTH, ANGLE = 21, 10
kernel = np.zeros((LENGTH, LENGTH))
kernel[LENGTH // 2, :] = 1
kernel = kernel / LENGTH
# Stage 1: blur
blurry = convolve2d(img_f, kernel, mode='same')
# Stage 2: add noise
noise = np.random.normal(0, 0.01, img_f.shape)
noisy_blurry = blurry + noise
mse = np.mean((img_f - noisy_blurry) ** 2)
print(f'MSE: {mse:.6f}')Butterworth Filter
The Butterworth low-pass filter attenuates high frequencies (where noise concentrates) smoothly without a sharp cutoff. Its response: H(f) = 1 / √(1 + (f/f₀)^(2n)), where f₀ is cutoff and n is filter order.
from scipy.signal import butter, lfilter
def butterworth_1d(cutoff, fs, order=5):
nyq = 0.5 * fs
normal_cutoff = cutoff / nyq
b, a = butter(order, normal_cutoff, btype='low', analog=False)
return b, a
b, a = butterworth_1d(cutoff=0.1, fs=1.0, order=5)
# Apply row by row then column by column (separable 2-D)
filtered = lfilter(b, a, lfilter(b, a, noisy_blurry, axis=0), axis=1)Wiener Filter
The Wiener filter is the optimal linear filter for restoration in the presence of additive noise. It minimizes MSE: F̂(u,v) = [H*(u,v) / (|H(u,v)|² + K)] · G(u,v), where K = noise-to-signal power ratio.
from scipy.signal import wiener
# Apply Wiener filter (mysize=kernel window, noise=estimated noise power)
restored = wiener(noisy_blurry, mysize=(21, 21), noise=0.01)
restored = np.clip(restored, 0, 1)
mse_wiener = np.mean((img_f - restored) ** 2)
print(f'Wiener MSE: {mse_wiener:.6f}')