卷積與影像濾波
先備知識: 影像就是陣列
你將學到
- 什麼是卷積核,以及它如何在影像上滑動
- 為什麼方框模糊和高斯模糊看起來不一樣
- Sobel 卷積核如何把亮度變化轉成邊緣
直覺
每一個輸出像素,都是它周圍鄰居的加權總和。這些權重排成一個小網格,稱為卷積核(kernel)。只要改變這九個數字,同一個運算就能做到模糊、銳化或強調邊緣。
數學
對輸入影像 與大小為 的卷積核 ,輸出 為
嚴格來說這是互相關(cross-correlation)。真正的卷積會先把卷積核翻轉成 ;對於模糊這類對稱的卷積核,兩者沒有差別。
模糊卷積核的權重總和為 ,所以平坦區域的亮度不變;邊緣卷積核的權重總和為 ,平坦區域會變成 ,只留下變化的地方。
程式碼
用 NumPy 從頭實作:
import numpy as np
def conv2d(img, K):
"""img: (H, W) float32,K: (3, 3),邊界以複製邊緣像素處理。"""
H, W = img.shape
p = np.pad(img, 1, mode="edge")
out = np.zeros_like(img)
for i in range(3):
for j in range(3):
out += K[i, j] * p[i:i + H, j:j + W]
return out
用 OpenCV 做同樣的事:
import cv2
import numpy as np
img = cv2.imread("cameraman.png", cv2.IMREAD_GRAYSCALE).astype(np.float32) / 255
sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], np.float32)
edges = cv2.filter2D(img, -1, sobel_x)
動手試試
選一個預設,再修改個別格子。試著讓 Sobel 卷積核改成偵測水平邊緣。
卷積核實驗室修改任一格或選擇預設,輸出會即時更新。
結果與常見陷阱
拖曳滑桿,比較輸入影像與 高斯模糊()的結果。右下角的細條紋最先消失,因為模糊是一種低通濾波器。
輸入高斯 σ=1.4
常見錯誤:
- 整數溢位。 用銳化卷積核處理
uint8影像時,負值會被截成 0。請改用float32計算。 - 邊界處理。 補零會讓模糊後的影像邊緣變暗。請使用
mode="edge"或cv2.BORDER_REFLECT。
練習
- 設計一個只偵測水平邊緣的卷積核。
- 為什麼模糊卷積核的權重總和必須是 1?如果總和是 2 會發生什麼事?
- 證明連續套用兩次 方框模糊,等同於一個 卷積核。這個卷積核長什麼樣子?