跳轉到

4.3 Laplacian與LoG#

概念說明#

Laplacian 是二階微分的各向同性組合:∇²I = ∂²I/∂x² + ∂²I/∂y²。沿一維剖面看,一階導數(梯度)在邊緣中心達到極值,二階導數則在邊緣中心過零 (zero-crossing)。因此 Laplacian 找邊緣的方式不是找極大值,而是找正負變號的位置。

常用離散核心:

[[0, 1, 0], [1, −4, 1], [0, 1, 0]](四鄰域) [[1, 1, 1], [1, −8, 1], [1, 1, 1]](八鄰域)

Laplacian 的優點:單一核心、各向同性(對所有方向邊緣同樣敏感)、可直接偵測「斑點 (blob)」(暗/亮區塊會產生極值響應)。缺點非常致命:對雜訊極度敏感——二階微分把雜訊放大兩次。理論上雜訊響應的變異數與微分階數成正比。

LoG (Laplacian of Gaussian, Marr–Hildreth, 1980) 的解法:先高斯平滑再做 Laplacian。由於兩個線性運算可交換,LoG 也是一個可以預先計算好的單一核心:

LoG(x, y) = −1/(πσ⁴) · [1 − (x²+y²)/(2σ²)] · exp(−(x²+y²)/(2σ²))

形狀像「墨西哥帽」:中心為正、外環為負。演算法流程:(1) 用 LoG 核心卷積;(2) 找過零點——符號改變且幅度超過小門檻的像素;(3) 過零點即邊緣。

LoG 更深遠的意義在尺度空間 (scale-space):σ 不同,能偵測的結構尺寸不同。理論結果:一個直徑為 d 的圓形斑點,在 σ ≈ d/(2√2) 時 LoG 響應最大。因此「在不同 σ 下找響應極大值」=「多尺度斑點偵測」——這正是 SIFT 特徵(第 9 章)用 DoG(LoG 的近似)找關鍵點的原理。Marr 的視覺計算理論也主張:人類視覺正是在多個尺度上以類 LoG 的運算(視網膜神經節細胞的感受域形狀酷似墨西哥帽)理解世界。

過零點的實作:對卷積結果 L,檢查水平與垂直相鄰像素是否異號;若異號且 |L 兩值之差| > 門檻,則把該邊標為邊緣。過零點天然形成閉合曲線,這是 Laplacian 家族相對梯度法的獨特性質。

演算法步驟#

  1. 選定 σ,產生 LoG 核心(尺寸約 6σ+1,必須奇數)。
  2. 對灰階影像做 LoG 卷積。
  3. 掃描相鄰像素:異號且差值超過門檻 → 標記過零邊緣。
  4. (可選)去除弱響應的孤立過零點。

Python 程式碼範例#

import numpy as np
from PIL import Image

# 一張有圓形亮斑與雜訊的影像
rng = np.random.default_rng(5)
yy, xx = np.mgrid[0:60, 0:60]
img = 100 + 120 * np.exp(-(((xx-30)**2 + (yy-30)**2) / (2*6.0**2)))
img += rng.normal(0, 3, (60, 60))
print("斑點影像中心值:", img[30, 30].round(1), " 背景值:", img[5, 5].round(1))

def log_kernel(sigma, size):
    r = size // 2
    y, x = np.mgrid[-r:r+1, -r:r+1]
    s2 = sigma ** 2
    k = -1/(np.pi*s2**2) * (1 - (x**2+y**2)/(2*s2)) * np.exp(-(x**2+y**2)/(2*s2))
    return k

def conv(img, K):
    r = K.shape[0]//2
    P = np.pad(img, r, mode="edge")
    out = np.zeros(img.shape)
    for y in range(img.shape[0]):
        for x in range(img.shape[1]):
            out[y, x] = (P[y:y+K.shape[0], x:x+K.shape[0]] * K).sum()
    return out

def zero_crossing(L, T=5.0):
    H, W = L.shape
    edge = np.zeros((H, W), bool)
    for y in range(H-1):
        for x in range(W-1):
            if L[y, x] * L[y, x+1] < 0 and abs(L[y, x] - L[y, x+1]) > T:
                edge[y, x] = True
            if L[y, x] * L[y+1, x] < 0 and abs(L[y, x] - L[y+1, x]) > T:
                edge[y, x] = True
    return edge

K = log_kernel(3.0, 19)
L = conv(img, K)
print("LoG 響應範圍:", L.min().round(1), "~", L.max().round(1))
print("理論最佳 σ = 直徑/(2√2) =", (2*3*np.sqrt(12)/2/ (2*np.sqrt(2))).round(2))

edge = zero_crossing(L, 5.0)
print("過零點數:", edge.sum())
# 驗證斑點邊界半徑約 12:統計過零點到中心的距離
ys, xs = np.where(edge)
d = np.sqrt((ys-30)**2 + (xs-30)**2)
print("過零點距中心距離: min %.1f mean %.1f max %.1f" % (d.min(), d.mean(), d.max()))

執行後:LoG 在斑點內部為強負/正響應、在半徑約 12 的邊界過零,過零點的距離統計正好落在 12 附近形成閉合圓——這證明了「過零點對應結構邊界」與「σ 控制偵測尺度」兩個理論。

複雜度分析#

LoG 卷積:核心 (6σ+1)² 不可分離?實際上 LoG 可分離嗎——不能精確分離,但可用「高斯可分離 + 3×3 Laplacian」兩步近似,複雜度從 O(H·W·k²) 降到 O(H·W·k)。過零點掃描 O(H·W)。總計 O(H·W·k)。空間 O(H·W)。多尺度時對 s 個 σ 各做一次,O(s·H·W·k)。注意過零點檢測對門檻 T 敏感:T=0 會把雜訊過零全數保留,實務需配合小門檻或面積過濾。

練習#

  1. 產生直徑 8、16、24 三種亮斑疊加的影像,用 σ = 2.8、5.7、8.5 的 LoG 分別求中心響應,驗證每個斑點在自己的尺度取得極大值。
  2. 比較同一影像上 Canny 與 LoG 的邊緣:LoG 的邊緣是否閉合?在什麼影像上閉合性是優點(提示:區域分割)?
  3. 證明「高斯 + Laplacian ≈ LoG」:對 3σ 高斯核心套 [[0,1,0],[1,−4,1],[0,1,0]],與解析 LoG 核心逐項比較(輸出兩核心的誤差範數)。