影像處理易混淆概念整理
1. 空間域 vs 頻率域
空間域(Spatial Domain)
- 看的是:每個位置的像素值
- 操作方式:直接修改像素值
- 例子:調整亮度、對比度、直接塗改
頻率域(Frequency Domain)
- 看的是:影像變化的快慢
- 操作方式:透過傅立葉轉換處理
🎵 聲音類比
- 空間域 = 看波形圖(每個時間點的振幅)
- 頻率域 = 看頻譜圖(有哪些音高/頻率)
影像的頻率分類
低頻 = 變化慢 = 平滑區域
- 例子:藍天、牆壁、臉部膚色
- 相鄰像素值相近
高頻 = 變化快 = 細節/邊緣
- 例子:頭髮絲、文字邊緣、紋理
- 相鄰像素值差異大
實際應用
-
模糊/銳化
- 低通濾波器(保留低頻)→ 模糊,去雜訊
- 高通濾波器(保留高頻)→ 銳化,強調邊緣
-
壓縮(JPEG)
- 人眼對高頻不敏感 → 可以丟棄部分高頻 → 節省空間
-
去摩爾紋
- 摩爾紋是特定頻率干涉 → 在頻率域直接濾除
關鍵理解
傅立葉轉換就像「把影像拆解成不同頻率的波的組合」
任何影像 = 一堆不同頻率、不同方向的正弦波疊加
2. 卷積(Convolution) vs 相關(Correlation)
容易搞混的點
- 看起來很像,但數學上有差異
- 在影像處理中,很多人混用這兩個詞
差異
卷積(Convolution)
卷積需要「翻轉」核心(180度旋轉)
結果 = Σ 影像(x,y) × 核心(翻轉後的位置)
相關(Correlation)
相關「不翻轉」核心
結果 = Σ 影像(x,y) × 核心(相同位置)
實務上
- 對稱核心(如高斯濾波器):兩者結果相同
- 非對稱核心(如某些邊緣檢測):結果不同
- OpenCV 的
filter2D()實際上是「相關」,不是卷積 - 深度學習框架說的「卷積層」實際上也是「相關」操作
記憶訣竅
卷積 = 核心要翻轉,相關 = 核心不翻轉
3. 濾波器(Filter) vs 核心(Kernel) vs 模板(Mask)
容易搞混的原因
這三個詞在影像處理中「幾乎是同義詞」!
細微差異
- Kernel(核心):一個小矩陣,用來做卷積/相關運算
- Filter(濾波器):整個濾波操作的概念(包含核心 + 運算方式)
- Mask(模板/遮罩):有時指核心,有時指二值遮罩
例子
# 3×3 的高斯模糊核心
kernel = [[1, 2, 1],
[2, 4, 2],
[1, 2, 1]] / 16
# 使用這個 kernel 進行濾波
filtered_image = apply_filter(image, kernel)
實務建議
不用太糾結術語,理解概念即可。在不同文獻中這些詞經常互換使用。
4. 侵蝕(Erosion) vs 膨脹(Dilation)
形態學操作的兩大基礎
膨脹(Dilation)
- 效果:白色區域「變胖」、物體變大
- 用途:
- 填補小洞
- 連接斷裂的線條
- 放大物體
侵蝕(Erosion)
- 效果:白色區域「變瘦」、物體變小
- 用途:
- 去除小雜點
- 分離黏連物體
- 縮小物體
記憶訣竅
膨脹 = 塗白色油漆 → 白色擴散
侵蝕 = 用橡皮擦 → 白色縮小
組合操作
開運算(Opening)= 先侵蝕,後膨脹
- 效果:去除小白點(雜訊)
- 保持:大物體的大小基本不變
閉運算(Closing)= 先膨脹,後侵蝕
- 效果:填補小黑洞
- 保持:大物體的大小基本不變
5. RGB vs HSV vs HSL
容易搞混的顏色空間
RGB(Red, Green, Blue)
- 特性:電腦最常用,直覺但不符合人類感知
- 範圍:R, G, B ∈ [0, 255]
- 缺點:要調整「顏色」很難(需同時調三個通道)
HSV(Hue, Saturation, Value)
- H(色相):顏色種類(0-360度,紅橙黃綠藍靛紫)
- S(飽和度):顏色的鮮豔程度(0=灰,100=純色)
- V(明度):顏色的明暗(0=黑,100=亮)
- 優點:更符合人類感知,容易選取特定顏色
HSL(Hue, Saturation, Lightness)
- 和 HSV 類似,但 L(亮度)的定義不同
- L=50% 是純色,L=0% 是黑,L=100% 是白
實際應用
# 在 HSV 中選取紅色物體超簡單!
lower_red = (0, 100, 100) # H: 0-10度
upper_red = (10, 255, 255)
mask = cv2.inRange(hsv_image, lower_red, upper_red)
# 如果用 RGB 選紅色會很複雜
# 因為紅色可以是 (255,0,0), (200,50,50), (180,20,30)...
選擇建議
- RGB:顯示影像、儲存影像
- HSV/HSL:顏色偵測、色彩調整、特效處理
6. 邊緣檢測:Sobel vs Canny vs Laplacian
Sobel 算子
- 原理:計算梯度(像素值變化率)
- 輸出:梯度強度(變化大的地方 = 邊緣)
- 特點:
- 可以檢測水平或垂直邊緣
- 對雜訊敏感
- 邊緣較粗
Laplacian 算子
- 原理:二階導數(變化的變化)
- 特點:
- 對雜訊非常敏感
- 會檢測到所有方向的邊緣
- 較少單獨使用
Canny 邊緣檢測
- 原理:多階段算法(最強大)
- 高斯模糊去雜訊
- 計算梯度(類似 Sobel)
- 非極大值抑制(讓邊緣變細)
- 雙閾值 + 邊緣追蹤
- 優點:
- 邊緣細且準確
- 抗雜訊能力強
- 有完整的閉合邊緣
選擇建議
| 需求 | 推薦方法 |
|---|---|
| 快速簡單的邊緣 | Sobel |
| 需要方向資訊 | Sobel |
| 最準確的邊緣 | Canny |
| 學術研究 | Canny |
7. 直方圖均衡化(Histogram Equalization)
容易誤解的概念
❌ 錯誤理解
「讓影像變得更平均」
✅ 正確理解
「讓直方圖變得更平均(拉伸對比度)」
原理
- 計算影像的灰度直方圖
- 計算累積分佈函數(CDF)
- 用 CDF 重新映射像素值
- 結果:暗的變更暗,亮的變更亮,拉開對比
效果
- 原圖:灰灰的、對比低
- 均衡化後:黑白分明、細節清晰
注意事項
- 可能過度增強雜訊
- 不適合已經對比度良好的影像
- 彩色影像要在 HSV 的 V 通道做,不要直接在 RGB 做
8. 二值化(Binarization)的閾值選擇
全局閾值(Global Thresholding)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 整張圖用同一個閾值
- 優點:簡單快速
- 缺點:光照不均時效果差
自適應閾值(Adaptive Thresholding)
binary = cv2.adaptiveThreshold(gray, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
# 每個區域計算不同閾值
- 優點:適應光照變化
- 缺點:計算較慢
Otsu's 方法
- 自動找最佳閾值(最大化類間變異數)
- 適用:雙峰分佈的直方圖(前景+背景)
選擇建議
- 光照均勻 → 全局閾值
- 光照不均 → 自適應閾值
- 不知道閾值該設多少 → Otsu
9. 影像金字塔(Image Pyramid)
高斯金字塔 vs 拉普拉斯金字塔
高斯金字塔(Gaussian Pyramid)
- 向下採樣:模糊 + 縮小
- 用途:多尺度特徵檢測、影像混合
拉普拉斯金字塔(Laplacian Pyramid)
- 儲存細節:原圖 - 高斯金字塔的放大版
- 用途:影像重建、影像融合
混淆點
拉普拉斯金字塔「不是」用 Laplacian 算子做的,而是用減法!
10. 常見的混淆總結
| 概念 A | 概念 B | 關鍵差異 |
|---|---|---|
| 卷積 | 相關 | 卷積要翻轉核心 |
| 侵蝕 | 膨脹 | 侵蝕縮小白色,膨脹放大白色 |
| 開運算 | 閉運算 | 開運算去白點,閉運算填黑洞 |
| RGB | HSV | RGB 是加法混色,HSV 更直覺 |
| Sobel | Canny | Sobel 簡單快速,Canny 準確但慢 |
| 全局閾值 | 自適應閾值 | 全局用一個值,自適應用多個值 |
| 空間域 | 頻率域 | 空間看位置,頻率看變化快慢 |
| 濾波器 | 核心 | 幾乎同義,濾波器是整個操作 |
| 高通濾波 | 低通濾波 | 高通保留邊緣,低通保留平滑 |
| 形態學 | 濾波 | 形態學處理形狀,濾波處理強度 |
學習建議
- 多動手實作:理論再多次不如寫一次程式
- 視覺化結果:每個操作都把結果顯示出來看
- 對比實驗:同一張圖試不同方法,觀察差異
- 從經典開始:掌握基礎後再碰深度學習
- 理解再記憶:知道「為什麼」比「是什麼」重要
推薦練習
import cv2
import numpy as np
# 1. 試試看空間域 vs 頻率域的模糊
img = cv2.imread('test.jpg', 0)
# 空間域:直接用高斯核
spatial_blur = cv2.GaussianBlur(img, (15,15), 0)
# 頻率域:FFT → 低通濾波 → IFFT
dft = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
# ... 低通濾波器 ...
frequency_blur = cv2.idft(filtered)
# 2. 試試看侵蝕和膨脹
kernel = np.ones((5,5), np.uint8)
erosion = cv2.erode(img, kernel, iterations=1)
dilation = cv2.dilate(img, kernel, iterations=1)
# 3. 比較 Sobel vs Canny
sobel_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
canny = cv2.Canny(img, 100, 200)
持續練習,這些概念就會越來越清晰!