OpenCV-平滑化與模糊化

將粗糙不平的衣物表面進行平滑處理,或是透過模糊效果來模擬窗外朦朧的景物。這種視覺處理方式已成為生活中隨處可見的技巧,同樣時常應用於資料分析的前處理過程。其中每種運算方式都有其獨特的特性和適用場景,需要根據實際需求來選擇最適合的處理方式,就讓我們用 OpenCV 來一探究竟。

素材取用

圖片:<雲端下載> | 清明 by DaylightAllure

概念釐清

模糊化的本質概念是減少影像中的高頻成分,這樣的處理過程能夠達到平滑影像、抹除雜訊的效果。在數位影像處理中,高頻成分通常代表了影像中的細節和邊緣資訊。然而因為過高的數值影響反而會導致柔邊,使得影像的細節和輪廓變得不夠銳利。

因此普遍觀念上,平滑化之技術則包括於模糊化之中,而兩者事實上是相近的概念。但是人們主觀的設計一套分類方式,其中平滑化的特點是處理後仍舊保持邊線的清晰度,而模糊化則會使整體影像變得較為混和模糊。

實際演練

針對 OpenCV 套件討論,其已經內建四種功能達到模糊化的效果。單就分類來說,我們可以依照運算是否具有「線性」特質。但保有分類之外,我也依據這些功能的用途性質以及其原理難易來進行依序討論,讓各位更容易循序漸進地理解這些技術。

線性處理

線性處理的優點主要在於依賴簡便的社學運算,因此處理速度相對快速,在需要快速處理大量影像資料的場景中特別實用。但也也存在一些限制,在需要保留細節的場景中,我們需要謹慎使用線性模糊,或考慮稍後提到的非線性運算。

線性模糊

平均模糊的概念在處理方式上較為暴力,其運作原理是透過對捲積核內所有像素值進行簡單的算術平均計算,並將得出的平均值賦予給中心像素點。這種方法雖然在實作上相當直觀且容易理解,但由於其處理過程過於簡化,降噪的功力並不突出,還往往導致整體影像在細節上的大量損失。

然而基於這樣的特性,此種模糊方式在製作柔和背景效果時特別實用,藉由降低影像中的視覺細節,能讓觀者更容易聚焦在主體上,或者呈現一種朦朧美。

# 平均模糊
avgBlur = cv2.blur(img, (ksize, ksize))

# :: avgBlur = cv2.blur(img, (31, 31))
高斯模糊

高斯模糊使用高斯函數來計算權重,類似於鐘形曲線,使得中心像素的權重最高,周圍像素的權重逐漸減小。透過這個方式可以減小像素點被重複運算導致的變形,獲得更自然的變化。

其捲積的寬高必須為奇數,而在後方還有 x 與 y 軸的標準差,設置越大會導致更模糊,而當 sigmaY=0 則會自動同步 sigmaX 之數值。

# 高斯模糊
gaussianBlur = cv2.GaussianBlur(img, (ksize, ksize), sigmaX, sigmaY=0)

# :: gaussianBlur = cv2.GaussianBlur(img, (31, 31), 0)

非線性運算

由於複雜的技術混和,非線性運算通常需要更多的時間,尤其用來判斷遮罩的權重與形狀。但可以根據影像內容進行自適應調整,提供更靈活的處理方式,針對需要精細處理的應用中更為常見。

雙邊濾波

雙邊濾波是一種能夠在平滑影像的同時,保留影像邊緣的濾波技術。其透過高斯捲積核來計算空間域,並額外施加值域比對像素之間的色彩差異,以此減少處理色彩變化較大的外圍區域,等同預期邊線不會受到過多處理。

這邊的參數比較複雜一些,d 所代表的是像素臨域的直徑 (影響的色彩範圍),接者 sigmaColour 則是顏色差值的範圍,一般偏大,代表越多的顏色能參與運算,如果 d=0 則會從這邊推算出空間範圍。最後的 sigmaSpace 則是座標空間的距離差異,一般偏小,避免過多的資料處理量,但注意當 d>0 時這個參數就不會被使用。

# 雙邊濾波
bilBlur = cv2.bilateralFilter(img, d, sigmaColour, sigmaSpace)

# :: bilBlur = cv2.bilateralFilter(img, 0, 50, 10)
comparison-blur-common
中值模糊

雖然沒有使用任何捲積,透過替代周圍像素的中值,可以想像成同化周遭的顏色。這樣做可以有效地消除孤立的雜訊點,可以很好的去除 椒鹽雜訊微小斑點。而由於沒有進行捲積的加權平均,因此一定程度上也能保留邊線。

而為了實作這個案例,我們首先需要先設計一個方法來添加雜訊。下方範例我們則使用 numpy 進行隨機的噪點添加,並追求泛用而分別針對單頻道與多頻道進行處理。

# 添加椒鹽雜訊
def addSaltPepperNoise(image, prob):
    output = np.copy(image)
    h, w = image.shape[:2]  

    # 白色數量
    num_salt = int(np.ceil(prob * h * w * 0.5))
    # 像素改造
    for i in range(num_salt):
        x = random.randint(0, w - 1)
        y = random.randint(0, h - 1)

        # 單通道
        if len(image.shape) == 2: 
            output[y, x] = 255
        # 多通道
        else: 
            output[y, x] = [255] * image.shape[2]

    # 黑色數量
    num_pepper = int(np.ceil(prob * h * w * 0.5))
    # 像素改造
    for i in range(num_pepper):
        x = random.randint(0, w - 1)
        y = random.randint(0, h - 1)

        # 單通道
        if len(image.shape) == 2: 
            output[y, x] = 0
        # 多通道
        else: 
            output[y, x] = [0] * image.shape[2]

    return output

這邊為了讓效果明顯一些,我刻意設 prod 為 0.2 多灑一些白鹽與胡椒,讓圖片看起來更好吃 (?) 一些。此時跟先前的平均模糊相比一下,整體的潔淨感和清晰度立即變得更加突出和明顯。

# 中值模糊
mdnBlur = cv2.medianBlur(img, ksize)

# :: mdnBlur = cv2.medianBlur(img, 31)
comparison-blur-median

後話

從社交媒體的照片修圖,到醫學影像的雜訊處理,影像的平滑化技術實際上已深深地融入我們的日常生活之中。無論是美化自拍照片,還是提升醫療診斷的準確度,這些技術都扮演著重要的角色。

而透過實際的實作與比較,並從基本原理出發進行深入思考,我們不僅能更好地理解各種平滑化方法的特點,還能更準確地運用這些技術來達到我們想要的效果。

參考

[1] Smoothing Images — OpenCV
https://docs.opencv.org/4.x/d4/d13/tutorial_py_filtering.html

[2] Grayscale Salt-and-Pepper Noise — Geeks-for-Geeks
https://www.geeksforgeeks.org/add-a-salt-and-pepper-noise-to-an-image-with-python/

額外讀物

[1] 台大資訊工程學系 — Bilateral Filters
https://www.csie.ntu.edu.tw/~cyy/courses/vfx/10spring/lectures/handouts/lec14_bilateral_4up.pdf

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料