iT邦幫忙

0

[OpenCV 學習筆記] 7. 使用 OpenCV 對黑白圖片進行 Morphological Transformations!

  • 分享至 

  • xImage
  •  

本文將簡單介紹什麼是 Morphological Transformations,並示範如何用 OpenCV 對黑白圖片進行相關的運算。
https://ithelp.ithome.com.tw/upload/images/20260903/20182893A9iEErPaDt.png


Morphological Transformations 基本概念:
首先讓我們講講什麼是 Morphology 吧! Morphology 翻成中文就是 "形態學(Mathematical morphology)",顧名思義,也就是一門專注在圖形幾何形狀上的學問,所以 Morphological Transformations 不難看出就是其相關的運算,而其相關應用有"去除影像雜訊"、"物體輪廓提取"、"填補物品內微小孔洞"和"縮小前景物品的邊界"等...。
https://ithelp.ithome.com.tw/upload/images/20260904/20182893WxeOixTjrL.png
常見的相關運算有ErosionDilationGradientOpeningClosingTop-Hat 等...,而其中最重要的就是 Erosion 和 Dilation (因為上述的其他運算皆由這兩個運算衍生而來),接下來將簡單介紹 Erosion 和 Dilation 的相關概念。

Erosion,顧名思義為侵蝕,而要如何達到讓圖像邊界縮小,像受到某種東西"侵蝕"的效果呢?

概念上是使用 Convolution 去完成,當滑動的 window 區域內有黑色的像素時,將其中心點改成黑色,否則維持原樣,也就是說 window 內所有的像素必須全都是白色的,捲積後產生用來取代中心點的像素值才會是白色的。

https://ithelp.ithome.com.tw/upload/images/20260904/20182893xLfa058HqU.png
同樣類似的概念,當滑動的 window 區域內有白色的像素時,將其中心點改成白色,反之則一樣維持原本的黑色,即可產生膨脹的效果。

https://ithelp.ithome.com.tw/upload/images/20260904/20182893wAMsrXL8oT.png

而將稍微膨脹的圖形(dilation)拿去扣掉稍微縮小的圖形(erosion),則可以得到輪廓,這樣的運算就是 "gradient"。

https://ithelp.ithome.com.tw/upload/images/20260904/201828936mWa84Teuc.png


底下為使用 OpenCV 套件對黑白圖片進行 erosion、dilation 和 gradient 的簡單範例。

程式碼1(Python):

import cv2
import numpy as np
import matplotlib.pyplot as plt

dog_pic = '../images/dog.png'  # 圖片位址

try:
    dog_img = cv2.imread(dog_pic) 

    dog_img_gray = cv2.cvtColor(dog_img, cv2.COLOR_BGR2GRAY)
    # 取得 mask
    ret, mask = cv2.threshold(dog_img_gray, 220, 255, cv2.THRESH_BINARY)
    mask_inv = cv2.bitwise_not(mask) 

    kernel = np.ones((5,5),np.uint8)

    erosion = cv2.erode(mask_inv, kernel, iterations = 1)
    dilation = cv2.dilate(mask_inv, kernel, iterations = 1)
    gradient = cv2.morphologyEx(mask_inv, cv2.MORPH_GRADIENT, kernel)

    pic_titles = ['origin','erosion', 'dilation', 'gradient']
    morphological_images= [mask_inv, erosion, dilation, gradient]

    for i in range(len(pic_titles)):

        plt.subplot(1, len(pic_titles) ,i+1), plt.imshow(morphological_images[i],  cmap='gray')
        plt.title(pic_titles[i])
        plt.xticks([]),plt.yticks([]) # 拿掉 X座標軸 和 y座標軸 刻度文字

    plt.show()

except Exception as ex:
    print(f"An unexpected error occurred: {ex}")

執行結果:
https://ithelp.ithome.com.tw/upload/images/20260903/20182893fIW2yU2wHF.png

Observation:

  • 不同的 Kernel: 可以觀察到 kernel 的 size 越大, erosion, dilation 和 gradient 的效果越明顯。
    (以 erosion 為例,當 kernel 的 size 變大時,"滿足 kernel 內整個都是白色的中心點"數量會變少,因此有更多白色的點被改成黑色,造成侵蝕的效果更明顯。)

https://ithelp.ithome.com.tw/upload/images/20260903/20182893hUQhL7G9j4.png
https://ithelp.ithome.com.tw/upload/images/20260903/20182893sdAu4xX9Mq.png
https://ithelp.ithome.com.tw/upload/images/20260903/20182893EIailVPtRL.png

  • 不同的 iteration 次數: 可以發現隨著 iteration 次數變多,效果漸漸跟著增強。

https://ithelp.ithome.com.tw/upload/images/20260903/20182893l72N4kBz85.png

  • 圖片黑白倒轉: 圖片黑白顛倒時,我們可以觀察到相對來說 erode 就是讓黑色膨脹(白色消退),dilate 會讓黑色消退(白色膨脹),我們可以觀察到兩者間的對偶性(duality),而不管圖片有沒有倒轉,兩者的 gradient 運算後得到的輪廓都是一樣的。

https://ithelp.ithome.com.tw/upload/images/20260904/20182893yoSx2a68Og.png


Opening, Closing, Top-hat, Black-hat:
在理解了 Erode 和 Dilation 這兩個基本且重要的原理後,接下來讓我們看看由其衍伸出來的 "Opening" 和 "Closing" 吧~~~

Opening 主要可以用於消除雜訊,其運算方式為先對影像進行 Erode(侵蝕) 後再進行 Dilate(膨脹), Erdoe 可以將外部的雜訊消除,但因為邊界會跟著被侵蝕,所以再用 Dilate 稍微將其補回來。

https://ithelp.ithome.com.tw/upload/images/20260907/20182893CkVvAhCZ2m.png

而 Closing 則是可以用來填補物品的內部小空洞,其運算方式為先對影像進行 Dilate(膨脹) 後再進行 Erode(侵蝕),和上述的 Opening 類似概念,先透過 Dilate 填補內部小空洞,接著因為邊界稍微膨脹,所以在用 Erode 侵蝕將其稍微縮小。

https://ithelp.ithome.com.tw/upload/images/20260907/20182893Pf6egkDudx.png

至於 Top-hat 則是將拿原來的圖像去扣掉 "Opening 運算後的圖像" 的結果,Black-hat 則是將原來的圖像去扣掉 "Closing 運算後的圖像" 的結果。


底下為使用 OpenCV 套件對黑白圖片進行 Opening 和 Closing 的簡單範例。

程式碼2(Python):

import cv2
import numpy as np
import random as rd
import matplotlib.pyplot as plt

dog_pic = '../images/dog.png'  # 圖片位址

try:

    dog_img = cv2.imread(dog_pic) 

    dog_img_gray = cv2.cvtColor(dog_img, cv2.COLOR_BGR2GRAY)
    # 取得 mask
    ret, mask = cv2.threshold(dog_img_gray, 220, 255, cv2.THRESH_BINARY)
    mask_inv = cv2.bitwise_not(mask) 

    height, width = mask_inv.shape

    # 隨機選取格子點加入雜訊
    grid_points = [(a, b) for a in range(height) for b in range(width)]
    num_samples = 1000
    random_points = rd.sample(grid_points, num_samples)

    for pt in random_points:

        if(mask_inv[pt] == 255):
            mask_inv[pt] = 0
        else:
            mask_inv[pt] = 255

    # 使用 opening 和 closing 消除部分雜訊
    kernel = np.ones((3,3),np.uint8)

    opening = cv2.morphologyEx(mask_inv, cv2.MORPH_OPEN, kernel)
    closing = cv2.morphologyEx(mask_inv, cv2.MORPH_CLOSE, kernel)
    top_hat = cv2.morphologyEx(mask_inv, cv2.MORPH_TOPHAT, kernel)
    black_hat = cv2.morphologyEx(mask_inv, cv2.MORPH_BLACKHAT, kernel)

    pic_titles = ['pic with noise','opening', 'closing', 'top_hat', 'black_hat']
    morphological_images= [mask_inv, opening, closing, top_hat, black_hat]
    num_of_pics = len(pic_titles)

    for i in range(num_of_pics):

        plt.subplot(1, num_of_pics ,i+1), plt.imshow(morphological_images[i],  cmap='gray')
        plt.title(pic_titles[i])
        plt.xticks([]),plt.yticks([]) # 拿掉 X座標軸 和 y座標軸 刻度文字

    plt.show()

except Exception as ex:
    print(f"An unexpected error occurred: {ex}")

執行結果:
https://ithelp.ithome.com.tw/upload/images/20260907/20182893FKknhl1Vgf.png

在這張隨機添加雜訊的圖片上,我們可以觀察到 Opening 和 closing 是一體兩面的,使用 Opening 時,內部的空洞跟著變多,而使用 Closing 時,內部的空洞雖然被填補了,但是外部的雜訊卻也被加強了。


Generalization:

  • 實際上,若我們去觀察維基百科 Structing element的原始定義,我們可以知道 Kernel 不見得要是正方形,只要是某個集合就可以了,而 OpenCV 也有提供 "getStructuringElement" 方法讓我們去取得某些不同類型的 Kernel。
  • 另外一個筆者從 Wiki 上看到覺得比較有趣的性質是 Dilate 擁有交換性,這代表說我今天反過來把 image 當作 Structing element,而 kernel 當作 image 去做運算,可以得到一樣的結果 XD

https://ithelp.ithome.com.tw/upload/images/20260907/20182893ycoAD6lF45.png

上述是筆者得一些簡單推論,有興趣的讀者可以自己去參考維基百科的Binary_morphology,了解一下這些 Openration 背後的 Peoperties。


以上就是對黑白圖片的 Morphological Transformations 相關介紹,謝謝~


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言