作者:曾成訓(CH.Tseng)
影像處理(image processing)與電腦視覺(computer vision)是兩個讓人常常聯想在一起的名詞,同樣都是接收影像資訊,兩者的差異反映在最終結果:影像處理輸出的是「處理後的影像」,電腦視覺輸出的是「圖像中的資訊」。這兩種不同的技術運用,實際上卻是唇齒相依息息相關,電腦視覺需要影像處理來解析更多的資訊;同樣的,影像處理為了滿足電腦視覺的需求,也發展出各種更深更廣的技術,以期能深入剖析影像,取得更多的特徵與資訊。
今天要帶大家來了解,如何使用 OpenCV 進行捲積(Convolution)的影像處理技術。但在開始之前,我們需要先了解以下幾個電腦術語:
- Image Patch:指的是影像中以某一點為中心的一小塊區域(大小如 3×3、5×5 等)。
- Low Frequency Information:指平坦、單調、無太多紋理、邊緣、邊角等特徵,例如:「An image patch is said to have low frequency information」。
- High Frequency Information:與 Low Frequency 相反,有很多的紋理、邊緣、邊角等特徵。
- Low Pass Filtering:Low Pass 指的是 blurring(模糊)、smoothing(平滑)等動作;Low Pass Filtering 指讓影像中 Low Frequency 的資訊通過,阻隔 High Frequency 的部份。
- High Pass Filtering:High Pass 指的是 sharpening(銳化)或 edge enhancement(邊緣強化)等動作;High Pass Filtering 指阻卻影像中 Low Pass 的部份,讓 High Pass 的資訊通過。
認識 Image Filtering
當我們需要強化影像中的某些特徵並消除其他不想要的特徵,所採用的方法便是使用特定 kernel,針對整張進行捲積(convolution)操作。舉例來說,模糊(blur)、邊緣偵測(edge detection)、邊緣強化(edge enhancement)、噪點去除(noise removal)等,都是使用 kernel 針對影像進行捲積的結果。
kernel 指的是一個固定尺寸的窗格(如 3×3),在影像處理中,我們稱該窗格為 kernel 或 filter。若由左上到右下移動,使用該 kernel 針對影像重疊區域進行運算,最終便會得出一幅經過 filtered 的新影像,這樣的動作稱為捲積(convolution)。目前最流行的 CNN 進行的就是 image filtering 的工作。
如下圖所示,藍色 grid 為定義的 3×3 kernel,在捲積過程進行中,底下覆蓋區域(圖中的紅色的 grid)與 kernel 進行交乘加總後(下方的 output pixel),將作為輸出新圖中該 kernel 區域的中心點;若由左上至右下重複進行上述步驟,輪巡整張圖片後就會得到右邊的圖形。

使用 kernel 針對影像進行捲積的結果(圖片來源:曾成訓提供)
執行 convolution 後,會發現有一個很明顯的特性,就是輸出的圖片尺寸會比原來的小一圈,一般我們會採取四種方式來處理此特性:
- Ignore the boundary pixels:忽略消失的邊界影像,直接使用輸出的圖片。
- Zero padding:先在原圖周圍填補一圈為 0 的像素,再進行捲積,使輸出的圖片尺寸不變。
- Replicate border:直接複製原圖最邊界的 pixels 到輸出的圖周圍,例如:aaaaaa 🡨 abcdefgh 🡪 hhhhhhh
- Reflect border:與 Replicate border 類似,但複製的方式是對稱方式 copy,例如:fedcba 🡨 abcdefgh 🡪 hgfedcb
使用 OpenCV 進行捲積操作
OpenCV 內建了非常方便的捲積指令 filter 2D,只要先定義好使用的 kernel,便可直接進行捲積,我們來試看看。(後文中所指的 kernel 與 filter 都是指用於捲積的過濾窗格)
import numpy as np import cv2 import imutils import sys # cv2.IMREAD_COLOR為imread的預設值,此參數亦可不加。 imageName = "DSCF3454.jpg" image = cv2.imread(imageName, cv2.IMREAD_COLOR) #若無指定圖片則結束程式。 if image is None: print("Could not open or find the image") sys.exit() #縮小圖片到較適當尺寸。 image = imutils.resize(image, height=450) # 設定kernel size為5x5 kernel_size = 5 # 使用numpy建立 5*5且值為1/(5**2)的矩陣作為kernel。 kernel = np.ones((kernel_size, kernel_size), dtype=np.float32) / kernel_size**2 # 顯示矩陣內容,所有值皆為0.04的5x5矩陣 print (kernel) # 使用cv2.filter2D進行convolute, result = cv2.filter2D(image, dst=-1, kernel=kernel, anchor=(-1, -1), delta=0, borderType=cv2.BORDER_DEFAULT) cv2.imshow("Filter", result) cv2.imshow("Original", image) cv2.waitKey(0)
- 程式中定義的 kernel
程式中所使用的 filter 有「模糊化圖片」的功能 ,所產生的 kernel 如下圖所示:

(圖片來源:曾成訓提供)
當該 filter 一步步捲積整張圖片時,kernel 下方所遮蓋的圖片像素會除以對應數值後,全體取平均作為指定點 anchor 的新值,參數中的(-1, -1)為預設值,代表該 anchor 位於 kernel 的中心點,以這樣的 kernel 進行 convolution 可模糊化相片。
透過捲積進行邊緣檢測
接著我們來試試下面這個 3×3 filter,它可具有 edge detection 的能力哦!使用上方一樣的程式,僅將kernel 變數的內容更改為:
kernel = np.array([[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]])
執行後的圖片將只留下邊緣,相當神奇,如下圖所示:
為什麼這樣的 filter 捲積能讓影像僅留下邊緣作為 edge detection 使用呢?

(圖片來源:曾成訓提供)
仔細看一下這個 3×3 kernel,有沒有注意到該 kernel 的九個值全部加起來為 0(-1×8+8=0)?也就是說,當影像區域的色彩一致時(可能為背景),kernel 計算出的平均值為 0,代表輸出值(kernel anchor)等於 0(黑色),而倘若影像區域的中間顏色比周圍亮(代表可能為物體的交界處),此時 kernel 中間的數值 8 便會強化加大該交界值而變得更亮,-1 則淡化了周圍非物件邊緣的像素強度,使得整體算出的輸出值(kernel anchor)更大。
其實這就是大名鼎鼎的 Sobel filter,最早是由美國計算機科學家艾爾文·索伯(Irwin Sobel)及蓋瑞·費德曼(Gary Feldman)於 1968 年在史丹佛大學的人工智慧實驗室(SAIL)所提出,專門用於邊緣檢測(Edge Detector),而為了表揚他們的貢獻,才用他們的名字命名。
旋轉後可形成四種 Sobel filters:left、right、top、bottom,分別用於檢測水平與垂直的變化。下圖中,1 與 3 的 Sobel filter 可檢測垂直邊緣、2 與 4 則檢測水平邊緣。

四種 Sobel filter 可分別用於檢測水平與垂直的變化(圖片來源:曾成訓提供)
結果分別如下:
另外還有一種相當知名的邊緣檢測稱為 Laplacian Edge Detector,不同於 Sobel 需要至少兩種 kernels 來分別檢測水平與垂直邊緣,它僅用一種 kernel 就可以偵測兩種方向的邊緣。

Laplacian Edge Detector 的 kernel (圖片來源:曾成訓提供)
上圖左側 kernel 可偵測水平與垂直邊緣,右側 kernel 則包含了對角線(斜的邊緣),下方分別為其檢測結果。

5×5 kernel (圖片來源:曾成訓提供)
不過 Laplacian 有個缺點,是對於噪點較為敏感,因此若能在進行 Laplacian 前先作模糊化處理,效果會更好,這個部分可透過上方的 5×5 kernel 一次完成(blur+Laplacian)。
透過捲積進行銳化處理
圖形的銳化與模糊化同樣能透過 filter 捲積進行,例如下方的二種 filter 會將整張圖片進行銳化處理,其中右方的 filter 處理得更為極端。如果您將上面提到用於 edge detection 的 Sobel filter,與下方銳化的 kernel 進行比較,會發現下方銳化 kernel 其實是輕微的 Sobel filter,讓 kernel 數值加總後的 anchor 稍大於 0。

可用於銳化的 kernel(圖片來源:曾成訓提供)
不過,此種利用 filter 捲積進行銳化處理的效果並不太好,因為會造成影像中的噪點大量增加,因此一般是以強化邊緣(Edge Enhancement)的方式來進行圖像銳化。例如下列的 kernel,它與上述的 Sobel filter 差異在於 size 較大,且從四周以漸進的方式往中間 anchor 增大。

用於強化邊緣的 kernel(圖片來源:曾成訓提供)
小結
透過本文,我們瞭解到如何在 OpenCV 透過 Convolution 的方式搭配不同的 kernel 偵測影像中不同的邊緣及特徵,此作法也就是深度學習中的 CNN 透過 filter 擷取特徵的觀念及基礎,希望大家有幫助。
(本文經作者同意轉載自CH.TSENG部落格、原文連結;責任編輯:賴佩萱)
- 【模型訓練】訓練馬賽克消除器 - 2020/04/27
- 【AI模型訓練】真假分不清!訓練假臉產生器 - 2020/04/13
- 【AI防疫DIY】臉部辨識+口罩偵測+紅外線測溫 - 2020/03/23
訂閱MakerPRO知識充電報
與40000位開發者一同掌握科技創新的技術資訊!