跳轉到

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):影像視為環面,最右邊接最左邊。

不同的填充策略對小影像影響顯著,對大影像只影響邊緣數個像素寬的區域。

卷積有幾個重要性質:線性(卷積的和等於和的卷積)、平移不變(核心在任何位置行為相同)、可交換與可結合(連續套兩個核心可先合成一個)。高斯核心的性質尤其優美:兩個高斯卷積仍是高斯,σ 變成 √(σ₁²+σ₂²)。

演算法步驟#

  1. 決定核心尺寸(通常奇數,如 3×3、5×5)與填充策略。
  2. 對輸出影像每個位置 (y, x):把核心對齊該位置。
  3. 取出重疊的鄰域區塊,與核心逐元素相乘。
  4. 全部加總,必要時 clip 到 0~255,寫入輸出。
  5. 掃完全部位置即得輸出影像。

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)(輸出影像)。

練習#

  1. 用手算驗證 3×3 核心 [[1,1,1],[1,1,1],[1,1,1]]/9 作用於本章範例影像的中心像素,與程式輸出比對。
  2. 將範例核心改為不可分的自訂核心(隨機 3×3),實作「先水平後垂直」的分解是否失敗,說明可分離的條件。
  3. 實作鏡射 (reflect) 填充,比較三種填充在同一核心下輸出影像的角落值差異。