Home

CV

๐Ÿ“˜ Full Tutorial: High-Pass and Low-Pass Filtering with Laplacian, Gaussian Kernels, and Image Sharpening


๐Ÿ”ท 1. Introduction

This tutorial walks through:

  • High-pass and low-pass filtering
  • How Laplacian detects curvature and edge polarity
  • How Gaussian blurs smooth images
  • How to build high-pass filters from low-pass ones
  • How to sharpen images using Laplacian or Gaussian blur

๐Ÿ”ท 2. Kernels Overview

๐ŸŸฉ Low-Pass Filter (Gaussian Kernel)

\[K_{\text{gauss}} = \frac{1}{16} \begin{bmatrix} 1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 2 & 1 \end{bmatrix}\]
  • Smooths the image
  • Preserves low frequencies
  • Kernel sum = 1

๐ŸŸฅ High-Pass Filter (Laplacian Kernel)

\[K_{\text{laplace}} = \begin{bmatrix} -1 & -1 & -1 \\ -1 & 8 & -1 \\ -1 & -1 & -1 \end{bmatrix}\]
  • Enhances edges and fine texture
  • Kernel sum = 0
  • Sensitive to curvature and polarity

๐Ÿ”ถ 3. Laplacian as a Curvature Detector

The Laplacian operator:

\[\Delta I(x, y) = \frac{\partial^2 I}{\partial x^2} + \frac{\partial^2 I}{\partial y^2}\]

Measures local curvature:

Region Laplacian Output
Flat 0
Bright center Positive
Dark center Negative

Used for edge detection and edge polarity.


๐Ÿ”ท 4. Bright vs. Dark Edge Behavior

Type Center Neighbors Output
Bright blob Brighter Darker ๐Ÿ”บ Positive
Dark pit Darker Brighter ๐Ÿ”ป Negative

To visualize both types, take abs(laplacian) or encode sign with color.


๐Ÿ”ถ 5. Blurring with Gaussian (LPF)

Gaussian smoothing:

  • Reduces high-frequency noise
  • Creates a blurred version of the image
  • Used before downsampling or as part of denoising

OpenCV:

blurred = cv2.GaussianBlur(image, (5, 5), sigmaX=1.0)

๐Ÿ”ท 6. High-Pass from Blurring

You can generate a high-pass image by:

\[\text{HPF} = \text{Original} - \text{Blurred}\]

This removes the low-frequency parts (smooth regions) and retains high-frequency details (edges).


โœ… 7. Image Sharpening Techniques

๐Ÿ”น 1. Sharpening via Laplacian (Unsharp Masking)

\[\text{Sharpened} = \text{Original} - \lambda \cdot \text{Laplacian}\]
  • Subtract Laplacian โ†’ enhances edges
  • $\lambda$ controls intensity (e.g., 1.0)
lap = cv2.Laplacian(img, cv2.CV_64F, ksize=3)
sharpened = cv2.addWeighted(img, 1.0, lap, -1.0, 0)

๐Ÿ”น 2. Sharpening via Gaussian Blur (Unsharp Masking)

\[\text{HPF} = \text{Original} - \text{Blurred} \\ \text{Sharpened} = \text{Original} + \lambda \cdot \text{HPF}\]

This gives:

\[\text{Sharpened} = (1 + \lambda) \cdot \text{Original} - \lambda \cdot \text{Blurred}\]
blur = cv2.GaussianBlur(img, (5, 5), 1.0)
hpf = cv2.subtract(img, blur)
sharpened = cv2.addWeighted(img, 1.0, hpf, 1.0, 0)

๐Ÿ”ท 8. Python Implementation

import cv2
import numpy as np

# Load image
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

# Gaussian Blur (LPF)
blur = cv2.GaussianBlur(img, (5, 5), 1.0)

# Laplacian (HPF)
lap = cv2.Laplacian(img, cv2.CV_64F, ksize=3)
lap_abs = np.uint8(np.clip(np.abs(lap), 0, 255))

# HPF from blur
hpf_from_blur = cv2.subtract(img, blur)

# Sharpen using Laplacian
sharpen_lap = cv2.addWeighted(img, 1.0, lap, -1.0, 0)
sharpen_lap = np.clip(sharpen_lap, 0, 255).astype(np.uint8)

# Sharpen using blur subtraction
sharpen_blur = cv2.addWeighted(img, 1.0, hpf_from_blur, 1.0, 0)

# Save outputs
cv2.imwrite('blur.jpg', blur)
cv2.imwrite('laplacian.jpg', lap_abs)
cv2.imwrite('hpf_from_blur.jpg', hpf_from_blur)
cv2.imwrite('sharpen_lap.jpg', sharpen_lap)
cv2.imwrite('sharpen_blur.jpg', sharpen_blur)

โœ… 9. Summary Table

Method Kernel Sum Enhances Removes Notes
Gaussian LPF 1 Smoothness Detail All-positive weights
Laplacian HPF 0 Edges Flat areas Signed curvature
HPF via Blur 0 Edges Blur Easy and fast
Sharpen (Laplacian) โ€” Contrast None Subtracts curvature
Sharpen (Gaussian) โ€” Contrast Blur Unsharp masking variant

๐Ÿง  Tutorial: Laplacian Pyramid Blending โ€” Full Theory, Intuition, and Code


๐Ÿ”ท Overview

Laplacian pyramid blending is a multi-scale image fusion technique designed to seamlessly combine two images. Instead of directly blending pixel values, it fuses image structures at different frequency bands, which avoids visible seams and preserves both low-frequency background and high-frequency details.


๐Ÿ”ถ Problem: Why Naive Blending Fails

Given:

  • Image A
  • Image B
  • Mask M (defines blending region: 1 = A, 0 = B)

โŒ Naive blending:

blend = M * A + (1 - M) * B
  • Produces sharp seams if M is binary.
  • Fails at preserving texture, edges.

โœ… Solution: Multi-scale Blending with Laplacian Pyramids

๐ŸŽฏ Core Idea:

Blend low frequencies over broad regions and high frequencies only near transitions. This creates a smooth blend in global color and sharpness.


๐Ÿ”ท Background: Gaussian and Laplacian Pyramids

๐ŸŸฉ Gaussian Pyramid (G):

  • Sequence of blurred and downsampled images.
  • Each level is a lower resolution approximation.
  • Used to extract low-frequency (smooth) content.

๐ŸŸฅ Laplacian Pyramid (L):

  • Stores difference between levels in Gaussian pyramid:

$L_i = G_i - \text{Upsample}(G_{i+1})$

  • Captures details (high-frequency residuals)
  • Final level $L_N = G_N$: lowest-res base image

๐Ÿ”ท Algorithm Steps

Input:

  • Image A, Image B (same size)
  • Mask M (values $\in [0, 1]$)
  • Pyramid depth $N$

Step-by-step:

  1. Build Gaussian pyramids:

    • $G_A, G_B, G_M$
  2. Build Laplacian pyramids:

    • $L_A, L_B$
  3. Blend pyramids:

    • For each level $i$: $L_{blend}^i = G_M^i \cdot L_A^i + (1 - G_M^i) \cdot L_B^i$
  4. Collapse blended Laplacian pyramid to reconstruct final image.


๐Ÿ”ถ Why Multi-scale Works (Intuition)

Frequency What is blended Result
Low freq Global structure Smooth transitions
High freq Edges, texture Sharp seams are avoided

โš  Problem with hard masks:

Without pyramid mask smoothing, high-res levels will sharply cut edges. That leads to ghosting or seams.

โœ… Pyramid mask = frequency-aware blending:

Blending at each scale with a smoothed mask ensures transitions are aligned to scale.


๐ŸŽจ Analogies

  • Audio Equalizer: Each Laplacian level = frequency band
  • Painterโ€™s Brush:

    • Broad brush for backgrounds (low-freq)
    • Fine brush for detail (high-freq)
  • Focal blending: coarse vision blends wide zones, fine vision blends contours

๐Ÿงฎ Mathematical Formulation

Let:

  • $G_A^i$: Gaussian pyramid of A
  • $G_B^i$: Gaussian pyramid of B
  • $G_M^i$: Gaussian pyramid of mask M
  • $L_A^i, L_B^i$: Laplacian pyramids
  • $L_{blend}^i$: blended pyramid

Then:

\[L_A^i = G_A^i - \text{Upsample}(G_A^{i+1}) \quad \text{(same for B)}\] \[L_{blend}^i = G_M^i \cdot L_A^i + (1 - G_M^i) \cdot L_B^i\] \[\text{Reconstruct: } R^{i} = L_{blend}^{i} + \text{Upsample}(R^{i+1})\]

๐Ÿ” Flowchart

Image A         Image B         Mask
   |               |              |
   v               v              v
GaussianPyrA   GaussianPyrB   GaussianPyrMask
   |               |              |
   v               v              v
LaplacianPyrA  LaplacianPyrB      |
   \_________   ________/         |
             \ /                  |
         Blend at each level <----
                |
         Collapse pyramid
                |
             Output

๐Ÿ’ป Code (Python + OpenCV)

import cv2
import numpy as np

def build_gaussian_pyramid(img, levels):
    gp = [img.copy()]
    for _ in range(levels):
        img = cv2.pyrDown(img)
        gp.append(img)
    return gp

def build_laplacian_pyramid(gp):
    lp = []
    for i in range(len(gp) - 1):
        up = cv2.pyrUp(gp[i + 1], dstsize=(gp[i].shape[1], gp[i].shape[0]))
        lp.append(cv2.subtract(gp[i], up))
    lp.append(gp[-1])
    return lp

def blend_pyramids(lpA, lpB, gpM):
    return [gm * la + (1 - gm) * lb for la, lb, gm in zip(lpA, lpB, gpM)]

def reconstruct_from_laplacian(lp):
    img = lp[-1]
    for i in range(len(lp) - 2, -1, -1):
        img = cv2.pyrUp(img, dstsize=(lp[i].shape[1], lp[i].shape[0]))
        img = cv2.add(img, lp[i])
    return img

# Load and normalize
A = cv2.imread('A.jpg').astype(np.float32) / 255
B = cv2.imread('B.jpg').astype(np.float32) / 255
M = cv2.imread('mask.jpg', 0).astype(np.float32) / 255
M = cv2.merge([M, M, M])

levels = 5

gpA = build_gaussian_pyramid(A, levels)
gpB = build_gaussian_pyramid(B, levels)
gpM = build_gaussian_pyramid(M, levels)

lpA = build_laplacian_pyramid(gpA)
lpB = build_laplacian_pyramid(gpB)

blended_lp = blend_pyramids(lpA, lpB, gpM)
blended_img = reconstruct_from_laplacian(blended_lp)

cv2.imwrite('blended.jpg', (np.clip(blended_img, 0, 1) * 255).astype(np.uint8))

โœ… Conclusion

Laplacian pyramid blending is a powerful method for seamless image fusion. By decomposing and recomposing images across multiple resolutions, it allows fine control of transition zones and detail preservation โ€” solving the fundamental challenge of edge artifacts in image compositing.