Learning/IVP/Session 06
Session 06

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.

Ex6_1 — Sharpening filtersPython
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.

Ex6_2 — Simulate blur and noisePython
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.

Ex6_2 — Butterworth low-pass filterPython
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.

Ex6_2 — Wiener filter restorationPython
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}')