Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

影像處理:頻率域的直覺理解

核心概念

空間域看的是「每個位置的像素值」
頻率域看的是「影像變化的快慢」


🎵 用聲音類比

想像聲音的兩種呈現方式:

空間域(時域)= 看波形圖

  • 顯示:每個時間點的振幅
  • 特點:看到聲波的起伏
  • 缺點:很難看出「有哪些音高」

頻率域 = 看頻譜圖

  • 顯示:有哪些音高/頻率
  • 特點:可以分辨不同樂器
  • 優點:一目瞭然看出音色組成

實際例子

一首歌的分析:

波形圖(空間域):
複雜的上下震盪,很難理解

頻譜圖(頻率域):
├─ 低頻:有一根很高的柱子 → 低音鼓
├─ 中頻:有連續的波動 → 人聲
└─ 高頻:有一些尖峰 → 鈸聲、沙鈴

關鍵領悟:
同樣的聲音,用不同角度看會有不同的理解方式!


📸 影像的頻率

低頻 = 變化慢 = 平滑區域

特徵:

  • 相鄰像素的值很接近
  • 顏色或亮度緩慢變化
  • 大面積的均勻區域

實際例子:

  • 🌤️ 藍天
  • 🧱 牆壁
  • 👤 臉部膚色
  • 🌊 平靜的海面
  • 📄 白紙背景

視覺效果: 看起來平滑、柔和、沒有明顯邊界


高頻 = 變化快 = 細節/邊緣

特徵:

  • 相鄰像素的值差異很大
  • 顏色或亮度急劇變化
  • 有明顯的輪廓或紋理

實際例子:

  • 💇 頭髮絲
  • 📝 文字邊緣
  • 🧵 布料紋理
  • 🌲 樹葉細節
  • 📐 物體輪廓

視覺效果: 看起來銳利、清晰、有邊界感


💡 具體例子:人臉照片

假設一張照片內容:

空間域看到:
一個人的臉 + 背景牆 + 頭髮細節

頻率域分解:
├─ 低頻成分:
│   ├─ 臉部大致輪廓
│   ├─ 整體光線明暗
│   └─ 背景牆的顏色
│
├─ 中頻成分:
│   ├─ 五官的形狀(眼睛、鼻子、嘴巴)
│   ├─ 臉部明暗過渡
│   └─ 膚色的漸層變化
│
└─ 高頻成分:
    ├─ 皮膚的毛孔
    ├─ 頭髮的細絲
    ├─ 眼睫毛
    └─ 瞳孔邊緣

視覺化理解

頻率保留後的效果
只保留低頻模糊的大致輪廓,像是近視看到的
只保留高頻只剩邊緣線條,像是素描
全部保留完整清晰的照片

🔧 實際應用

1. 模糊與銳化

低通濾波器(Low-pass Filter)

  • 操作:保留低頻,去除高頻
  • 效果:影像變模糊
  • 應用
    • 去除雜訊(高頻雜點)
    • 平滑處理
    • 磨皮美肌
原圖 → [低通濾波] → 模糊、柔和的影像

高通濾波器(High-pass Filter)

  • 操作:保留高頻,去除低頻
  • 效果:強調邊緣和細節
  • 應用
    • 銳化影像
    • 邊緣檢測
    • 增強細節
原圖 → [高通濾波] → 只剩輪廓和邊緣

2. 影像壓縮(JPEG)

原理

人眼對高頻細節不敏感 → 可以丟棄部分高頻資訊

過程

1. 將影像轉換到頻率域(DCT 變換)
2. 量化高頻係數(允許較大誤差)
3. 壓縮編碼
4. 轉回空間域

結果

  • 檔案大小:縮小到原本的 1/10 或更小
  • 視覺效果:人眼幾乎看不出差異
  • 缺點:過度壓縮會產生「方塊效應」(還是高頻資訊流失)

3. 去摩爾紋

問題

用相機拍螢幕或網狀物時出現的波紋圖案

原因

摩爾紋是「特定頻率的干涉」現象

解決方法

1. 轉換到頻率域
2. 找出摩爾紋對應的頻率(會在頻譜中出現異常的峰值)
3. 用帶阻濾波器消除那個特定頻率
4. 轉回空間域

在頻率域處理比在空間域容易得多!


4. 影像浮水印

隱藏浮水印

  • 在頻率域的中頻區域嵌入資訊
  • 人眼看不出來,但頻譜分析能找到
  • 抗壓縮、抗裁切

為什麼用中頻?

  • 低頻:太容易被修改
  • 高頻:容易在壓縮時流失
  • 中頻:穩定且不顯眼

🎯 關鍵概念:傅立葉轉換

核心思想

任何影像都可以看成「一堆不同頻率、不同方向的正弦波疊加而成」

數學表達(簡化版)

影像(x,y) = Σ [振幅 × cos(頻率×位置 + 相位)]
           不同頻率

傅立葉轉換就是找出:
- 每個頻率的「振幅」有多大
- 每個頻率的「相位」是多少

直覺理解

樂高積木比喻

空間域:

  • 看到完成的樂高作品(城堡)
  • 描述每個位置有什麼顏色的積木

頻率域:

  • 看到用了哪些種類的積木
    • 1×1 小積木(高頻):用來做細節
    • 2×4 中積木(中頻):用來做主結構
    • 大底板(低頻):用來做基礎

轉換的意義:

  • 正向轉換:從「作品」分析「用了哪些材料」
  • 反向轉換:從「材料清單」組裝出「作品」

波的疊加原理

一維例子(聲音)

簡單的聲音 = 單一頻率的波
  ~~~~~~~~~

複雜的聲音 = 多個頻率疊加
  ~~~~~~~~~  (低頻:基音)
+ ~~~~  (高頻:泛音1)
+ ~~  (更高頻:泛音2)
= ~∼~∼~∼~  (複雜波形)

二維例子(影像)

簡單的影像 = 單一方向、單一頻率的條紋
  ||||||||||||

複雜的影像 = 多個方向、多個頻率疊加
  |||||||||||| (垂直低頻)
+ ━━━━━━━━━━ (水平低頻)
+ ////// (斜向高頻)
= 實際的照片

頻譜圖的閱讀

轉換到頻率域後,會得到一張「頻譜圖」:

頻譜圖特徵:

┌─────────────────┐
│    ★            │  ← 中心:低頻(DC 分量,平均亮度)
│  ● ★ ●          │  ← 中間:中頻
│● ● ★ ● ●        │  ← 邊緣:高頻
│  ● ★ ●          │
│    ★            │
└─────────────────┘

★ = 最亮的點(能量最集中的頻率)
● = 較亮的點(次要頻率)
  = 較暗的區域(很少能量)

判讀方法

  • 中心很亮:影像有大片平坦區域
  • 中心周圍有環:影像有週期性紋理(如柵欄)
  • 十字形亮線:影像有水平或垂直的強烈邊緣
  • 分散的亮點:影像紋理複雜且多變

🧮 數學補充(選讀)

一維離散傅立葉轉換(DFT)

正向轉換(時域 → 頻域):
F(k) = Σ f(n) × e^(-2πikn/N)
       n=0 到 N-1

反向轉換(頻域 → 時域):
f(n) = (1/N) × Σ F(k) × e^(2πikn/N)
              k=0 到 N-1

二維離散傅立葉轉換(2D-DFT)

F(u,v) = Σ Σ f(x,y) × e^(-2πi(ux/M + vy/N))
         x y

f(x,y) = (1/MN) × Σ Σ F(u,v) × e^(2πi(ux/M + vy/N))
                  u v

不用怕複雜! 實務上都是呼叫函式庫(如 NumPy 的 fft.fft2


🐍 Python 實作範例

基礎範例:頻域濾波

import cv2
import numpy as np
from matplotlib import pyplot as plt

# 讀取灰階影像
img = cv2.imread('photo.jpg', 0)

# === 1. 轉換到頻率域 ===
# 進行傅立葉轉換
dft = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)

# 將零頻率移到中心(方便觀察)
dft_shift = np.fft.fftshift(dft)

# 計算頻譜(用於顯示)
magnitude_spectrum = 20 * np.log(cv2.magnitude(dft_shift[:,:,0], dft_shift[:,:,1]))

# === 2. 創建濾波器遮罩 ===
rows, cols = img.shape
crow, ccol = rows // 2, cols // 2  # 中心點

# 低通濾波器(保留低頻)
mask_low = np.zeros((rows, cols, 2), np.uint8)
r = 30  # 半徑
mask_low[crow-r:crow+r, ccol-r:ccol+r] = 1

# 高通濾波器(保留高頻)
mask_high = np.ones((rows, cols, 2), np.uint8)
mask_high[crow-r:crow+r, ccol-r:ccol+r] = 0

# === 3. 應用濾波器 ===
# 低通濾波
fshift_low = dft_shift * mask_low
f_ishift_low = np.fft.ifftshift(fshift_low)
img_low = cv2.idft(f_ishift_low)
img_low = cv2.magnitude(img_low[:,:,0], img_low[:,:,1])

# 高通濾波
fshift_high = dft_shift * mask_high
f_ishift_high = np.fft.ifftshift(fshift_high)
img_high = cv2.idft(f_ishift_high)
img_high = cv2.magnitude(img_high[:,:,0], img_high[:,:,1])

# === 4. 顯示結果 ===
plt.figure(figsize=(15, 10))

plt.subplot(2, 3, 1), plt.imshow(img, cmap='gray')
plt.title('原始影像'), plt.axis('off')

plt.subplot(2, 3, 2), plt.imshow(magnitude_spectrum, cmap='gray')
plt.title('頻譜'), plt.axis('off')

plt.subplot(2, 3, 3), plt.imshow(mask_low[:,:,0], cmap='gray')
plt.title('低通濾波器遮罩'), plt.axis('off')

plt.subplot(2, 3, 5), plt.imshow(img_low, cmap='gray')
plt.title('低通濾波結果(模糊)'), plt.axis('off')

plt.subplot(2, 3, 6), plt.imshow(img_high, cmap='gray')
plt.title('高通濾波結果(邊緣)'), plt.axis('off')

plt.tight_layout()
plt.show()

進階範例:去除週期性雜訊

import cv2
import numpy as np

def remove_periodic_noise(image_path):
    """去除影像中的週期性雜訊(如掃描線)"""
    
    # 讀取影像
    img = cv2.imread(image_path, 0)
    
    # 傅立葉轉換
    dft = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
    dft_shift = np.fft.fftshift(dft)
    
    # 計算頻譜大小(用於找雜訊峰值)
    magnitude = cv2.magnitude(dft_shift[:,:,0], dft_shift[:,:,1])
    
    # 找出異常高的頻率峰值(雜訊)
    # 這裡簡化處理:手動標記要去除的頻率位置
    rows, cols = img.shape
    crow, ccol = rows // 2, cols // 2
    
    # 假設雜訊在 (crow-100, ccol) 和 (crow+100, ccol) 這兩個位置
    # 創建帶阻濾波器
    mask = np.ones((rows, cols, 2), np.uint8)
    r = 10  # 阻擋半徑
    
    # 在雜訊位置創建凹陷
    mask[crow-100-r:crow-100+r, ccol-r:ccol+r] = 0
    mask[crow+100-r:crow+100+r, ccol-r:ccol+r] = 0
    
    # 應用濾波器
    fshift = dft_shift * mask
    
    # 反轉換回空間域
    f_ishift = np.fft.ifftshift(fshift)
    img_back = cv2.idft(f_ishift)
    img_back = cv2.magnitude(img_back[:,:,0], img_back[:,:,1])
    
    return img, img_back

# 使用範例
original, filtered = remove_periodic_noise('noisy_image.jpg')

# 顯示結果
cv2.imshow('Original', original)
cv2.imshow('Filtered', filtered)
cv2.waitKey(0)
cv2.destroyAllWindows()

實用函式:分析影像頻率特性

def analyze_frequency_content(img):
    """分析影像的頻率內容分佈"""
    
    # 轉換到頻率域
    dft = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
    dft_shift = np.fft.fftshift(dft)
    
    # 計算幅度譜
    magnitude = cv2.magnitude(dft_shift[:,:,0], dft_shift[:,:,1])
    
    rows, cols = img.shape
    crow, ccol = rows // 2, cols // 2
    
    # 定義頻率環
    def get_ring_energy(inner_r, outer_r):
        """計算特定頻率環的能量"""
        y, x = np.ogrid[:rows, :cols]
        dist = np.sqrt((x - ccol)**2 + (y - crow)**2)
        ring_mask = (dist >= inner_r) & (dist < outer_r)
        return np.sum(magnitude[ring_mask])
    
    # 計算不同頻率範圍的能量
    low_freq = get_ring_energy(0, 30)
    mid_freq = get_ring_energy(30, 100)
    high_freq = get_ring_energy(100, min(rows, cols)//2)
    
    total = low_freq + mid_freq + high_freq
    
    print(f"低頻能量: {low_freq/total*100:.1f}%")
    print(f"中頻能量: {mid_freq/total*100:.1f}%")
    print(f"高頻能量: {high_freq/total*100:.1f}%")
    
    # 判斷影像特性
    if low_freq/total > 0.8:
        print("→ 這是一張平滑的影像(少細節)")
    elif high_freq/total > 0.3:
        print("→ 這是一張細節豐富的影像")
    else:
        print("→ 這是一張平衡的影像")

# 使用範例
img = cv2.imread('test.jpg', 0)
analyze_frequency_content(img)

🎓 深入理解:常見問題

Q1: 為什麼頻譜圖看起來是對稱的?

答: 因為實數影像的傅立葉轉換具有「共軛對稱性」

F(u, v) = F*(-u, -v)

意思是:頻譜的左半邊和右半邊是鏡像關係
所以我們只需要看一半的頻譜就夠了

Q2: DC 分量是什麼?

答: DC (Direct Current) 分量是頻率為 0 的成分

  • 位置:頻譜圖的正中心
  • 意義:整張影像的「平均亮度」
  • 特點:最大的值,通常要取 log 才能看清其他頻率
# 取得 DC 分量
dc_value = dft_shift[crow, ccol]

# DC 分量越大 → 影像整體越亮
# DC 分量越小 → 影像整體越暗

Q3: 為什麼要用 log 尺度顯示頻譜?

答: 因為頻率分佈的動態範圍太大

DC 分量的值可能是 10^6
高頻成分的值可能只有 10^1

如果不取 log,高頻成分會完全看不見(都是黑色)

magnitude_spectrum = 20 * np.log(cv2.magnitude(...) + 1)
                           ↑
                     取 log 壓縮動態範圍

Q4: 快速傅立葉轉換(FFT)比 DFT 快多少?

答: 快非常多!

影像大小DFT 複雜度FFT 複雜度速度提升
64×64O(N⁴)O(N² log N)~100倍
512×512O(N⁴)O(N² log N)~1000倍
1024×1024O(N⁴)O(N² log N)~5000倍

實務建議: 永遠使用 FFT,不要用 DFT


Q5: 彩色影像怎麼做頻域處理?

答: 有兩種方法

方法 1:分別處理三個通道

b, g, r = cv2.split(img)

# 對每個通道分別做 FFT
dft_b = cv2.dft(np.float32(b), flags=cv2.DFT_COMPLEX_OUTPUT)
dft_g = cv2.dft(np.float32(g), flags=cv2.DFT_COMPLEX_OUTPUT)
dft_r = cv2.dft(np.float32(r), flags=cv2.DFT_COMPLEX_OUTPUT)

# ... 濾波處理 ...

# 合併回彩色影像
result = cv2.merge([b_filtered, g_filtered, r_filtered])

方法 2:轉到 HSV,只處理 V 通道(推薦)

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)

# 只對明度(V)做頻域處理
v_filtered = frequency_filter(v)

# 合併回去
hsv_filtered = cv2.merge([h, s, v_filtered])
result = cv2.cvtColor(hsv_filtered, cv2.COLOR_HSV2BGR)

🚀 延伸學習

相關主題

  1. 小波轉換(Wavelet Transform)

    • 比傅立葉轉換更靈活
    • 同時提供時間和頻率資訊
    • 用於 JPEG2000 壓縮
  2. 離散餘弦轉換(DCT)

    • JPEG 壓縮的核心
    • 只用實數(比 FFT 簡單)
    • 能量集中度更好
  3. 加伯濾波器(Gabor Filter)

    • 模擬人類視覺系統
    • 同時具有空間和頻率選擇性
    • 用於紋理分析和人臉識別
  4. 短時傅立葉轉換(STFT)

    • 分段做傅立葉轉換
    • 可以看到頻率隨時間的變化
    • 用於語音處理

📚 學習資源推薦

書籍

  • 《數位影像處理》(Rafael C. Gonzalez) - 第 4 章頻域處理
  • 《Fundamentals of Digital Image Processing》(Chris Solomon)

線上課程

  • Coursera: Image and Video Processing (Duke University)
  • YouTube: 3Blue1Brown 的傅立葉級數視覺化

實作練習

# 練習 1: 觀察不同影像的頻譜差異
# - 拍一張天空的照片(低頻為主)
# - 拍一張樹葉的照片(高頻豐富)
# - 比較兩者的頻譜

# 練習 2: 實作理想低通濾波器
# - 嘗試不同的截止頻率
# - 觀察「振鈴效應」(Ringing)

# 練習 3: 影像壓縮實驗
# - 手動設定頻率閾值
# - 看看要丟掉多少高頻才會明顯失真

🎁 總結

核心記憶點

  1. 空間域 vs 頻率域

    • 空間域 = 每個像素的值
    • 頻率域 = 變化的快慢
  2. 頻率的意義

    • 低頻 = 平滑、輪廓、整體
    • 高頻 = 細節、邊緣、紋理
  3. 轉換的目的

    • 某些操作在頻域更容易(如濾波、壓縮)
    • 頻域看到空間域看不到的資訊(如週期性)
  4. 實務應用

    • 模糊/銳化 → 濾波器
    • 壓縮 → 丟棄高頻
    • 去雜訊 → 帶阻濾波器
    • 浮水印 → 中頻嵌入

最後的比喻

影像處理就像聽音樂:

  • 空間域 = 聽歌詞(一個字一個字聽)
  • 頻率域 = 聽旋律(整體感受和聲、節奏)

兩種方式都重要,但有時候頻域能讓你「聽出」空間域聽不到的東西!


記住:理解比記憶重要,實作比理解重要! 🚀

馬上打開 Python,試試看把你的照片轉到頻率域吧!