3.1 卷積的觀念#
概念說明#
卷積 (convolution) 讓一個小矩陣(稱為核心 kernel 或濾波器)在影像上滑動,每個位置計算核心覆蓋區域的加權總和,作為輸出像素的值。數學上,離散二維卷積定義為:
(I ⋆ K)[y, x] = Σᵢ Σⱼ I[y−i, x−j] · K[i, j]
直觀的計算口訣是三步:翻轉、滑動、加權求和。先把核心上下左右翻轉 180°,再放到影像每個位置,把重疊的值對應相乘後加總。若不做翻轉而直接滑動相乘,稱為相關 (correlation)。對對稱核心(如高斯、盒狀)兩者結果相同;實務上多數影像函式庫的「卷積」其實是相關,本書也從寬。
以 3×3 核心 K 對像素 I[y,x] 計算時:
out[y,x] = Σₐ₌₋₁¹ Σ_b₌₋₁¹ I[y+a, x+b] · K[a+1, b+1]
邊界處理:核心滑到影像邊緣時會覆蓋到影像外,常見策略有四種:
- 零填充 (zero padding):影像外視為 0,結果影像邊緣會出現暗框。
- 複製邊界 (replicate):複製最近的邊界像素,較自然。
- 鏡射 (reflect):以邊界為鏡子反射像素。
- 循環 (wrap):影像視為環面,最右邊接最左邊。
不同的填充策略對小影像影響顯著,對大影像只影響邊緣數個像素寬的區域。
卷積有幾個重要性質:線性(卷積的和等於和的卷積)、平移不變(核心在任何位置行為相同)、可交換與可結合(連續套兩個核心可先合成一個)。高斯核心的性質尤其優美:兩個高斯卷積仍是高斯,σ 變成 √(σ₁²+σ₂²)。
演算法步驟#
- 決定核心尺寸(通常奇數,如 3×3、5×5)與填充策略。
- 對輸出影像每個位置 (y, x):把核心對齊該位置。
- 取出重疊的鄰域區塊,與核心逐元素相乘。
- 全部加總,必要時 clip 到 0~255,寫入輸出。
- 掃完全部位置即得輸出影像。
Python 程式碼範例#
import numpy as np
from PIL import Image
img = np.array([
[ 10, 20, 30, 40, 50],
[ 15, 25, 35, 45, 55],
[ 20, 30, 40, 50, 60],
[ 25, 35, 45, 55, 65],
[ 30, 40, 50, 60, 70]
], dtype=np.float64)
K = np.array([[0, 1, 0],
[1, 4, 1],
[0, 1, 0]], dtype=np.float64) / 8.0 # 權重總和為 1
def convolve2d(img, K, pad="replicate"):
kh, kw = K.shape
py, px = kh // 2, kw // 2
if pad == "zero":
P = np.pad(img, ((py, py), (px, px)))
elif pad == "replicate":
P = np.pad(img, ((py, py), (px, px)), mode="edge")
H, W = img.shape
out = np.zeros((H, W))
for y in range(H):
for x in range(W):
out[y, x] = (P[y:y+kh, x:x+kw] * K).sum()
return out
out = convolve2d(img, K)
print("輸入中心值 40 → 輸出中心值:", out[2, 2])
print("手算驗證:", (4*40 + 30 + 50 + 35 + 45) / 8)
print("輸出:\n", out.round(1))
執行後,中心像素 40 經核心加權(自己佔 4/8,四鄰居各 1/8)變成 40,而角落像素被邊界條件影響。可再換成 pad="zero" 觀察邊緣變暗,體會填充策略的影響。這個樸素的雙層迴圈實作就是所有濾波的本質;實務上 numpy 可用滑動視窗向量化加速,或交給 OpenCV 的 C 實作。
複雜度分析#
核心 k×k 對 H×W 影像:每個輸出像素做 k² 次乘加,總複雜度 O(H·W·k²)。對 3×3 核心約等於 9 次乘加/像素,完全可接受;但 51×51 的大核心就會很慢。兩個加速方向:(1) 可分離核心——若 K 能寫成一維核心的外積 K = k_y·k_xᵀ(高斯、盒狀都可),先做水平卷積再做垂直卷積,複雜度降到 O(H·W·k);(2) 對極大核心改用 FFT 在頻域做(見第 5 章),複雜度 O(H·W·log(HW))。空間複雜度 O(H·W)(輸出影像)。
練習#
- 用手算驗證 3×3 核心 [[1,1,1],[1,1,1],[1,1,1]]/9 作用於本章範例影像的中心像素,與程式輸出比對。
- 將範例核心改為不可分的自訂核心(隨機 3×3),實作「先水平後垂直」的分解是否失敗,說明可分離的條件。
- 實作鏡射 (reflect) 填充,比較三種填充在同一核心下輸出影像的角落值差異。