OpenCV 中的圖像處理 005_形態變換

本文主要內容來自於 OpenCV-Python 教程OpenCV 中的圖像處理 部分,這部分的全部主要內容如下:

目標

在本章中:

理論

形態變換是基於圖像形狀的一些簡單操作。它通常在二值圖像上操作。它需要兩個輸入,一個是我們的原始圖像,第二個稱爲 結構元素內核,其決定了操作的性質。 兩個基本的形態學操作是侵蝕和膨脹。然後它的變體形式,如 Opening,Closing,Gradient 等也開始發揮作用。我們將在下圖的幫助下一一看看它們: image

1. 侵蝕

蝕的基本思想就像土壤侵蝕一樣,它侵蝕了前景物體的邊界(總是儘量保持前景爲白色)。那麼它有什麼作用呢?內核在圖像中滑動(如在 2D 卷積中)。只有當內核下的所有像素都爲 1 時,原始圖像中的一個像素(1 或 0)纔會被認爲是 1,否則它會被侵蝕(變爲 0)。

所以發生的情況是,邊界附近的所有像素都將根據內核的大小被丟棄。因此,前景物體的厚度或大小會減小,或者只是圖像中的白色區域減小。它對於去除小的白噪聲(正如我們在色彩空間章節中所見)、分離兩個連接的對象等很有用。

在這裏,作爲一個例子,我們使用一個 5x5 的內核,其中全是 1。讓我們看看它是如何工作的:

import cv2 as cv
import numpy as np

def erosion():
    img = cv.imread("/home/zhangsan/j.png", 0)
    kernel = np.ones((5, 5), np.uint8)
    erosion = cv.erode(img, kezhangsanrnel, iterations=1)

    edge = np.full((img.shape[0], 3, 1), 255, np.uint8);

    images = [img, edge, erosion]
    dest = cv.hconcat(images)

    cv.imshow("Image", dest)
    cv.waitKey(-1)


if __name__ == "__main__":
    erosion()

結果如下: Image

2. 膨脹

它與侵蝕完全相反。在這裏,如果內核下的元素中至少有一個值爲 '1',則像素元素值爲 '1'。因此它增加圖像中的白色區域,或者增加前景對象的大小。通常,在去除噪聲等情況下,侵蝕之後是膨脹。因爲,侵蝕消除白噪聲,但它也縮小了我們的對象。所以我們擴張它。由於噪音消失了,它們不會回來,但我們的對象面積增加了。它也可用於連接對象的損壞部分。

    dilation = cv.dilate(img, kernel, iterations=1)

結果如下: Image

3. 開

開只是 侵蝕後膨脹 的另一個名稱。正如我們上面解釋的那樣,它在消除噪音方面很有用。這裏我們使用函數 cv.morphologyEx()

def opening():
    img = cv.imread("/home/hanpfei/j.png", 0)

    for i in range(50):
        row = random.randint(0, img.shape[0] - 1)
        col = random.randint(0, img.shape[1] - 1)
        img[row][col] = 255

    kernel = np.ones((5, 5), np.uint8)
    opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)

    edge = np.full((img.shape[0], 3, 1), 255, np.uint8);

    images = [img, edge, opening]
    dest = cv.hconcat(images)

    cv.imshow("Image", dest)
    cv.waitKey(-1)

要處理的圖像中如果包含一些白色的噪聲像素點,效果會比較明顯。這裏先給輸入圖像增加了一些白色的噪聲像素點。結果如下:

Image

4. 閉

閉是開的逆操作,膨脹後侵蝕。它對於閉合前景對象內的小孔,或對象上的小黑點很有用。

def closing():
    img = cv.imread("/home/hanpfei/j.png", 0)

    for i in range(5000):
        row = random.randint(0, img.shape[0] - 1)
        col = random.randint(0, img.shape[1] - 1)
        img[row][col] = 0

    kernel = np.ones((5, 5), np.uint8)
    closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)

    edge = np.full((img.shape[0], 3, 1), 255, np.uint8);

    images = [img, edge, closing]
    dest = cv.hconcat(images)

    cv.imshow("Image", dest)
    cv.waitKey(-1)


if __name__ == "__main__":
    closing()

這裏同樣先在輸入圖像上製造一些噪聲點,不過這次是黑色噪聲像素點。由於大多數黑色噪聲點會落在黑色的背景區域內而看不出效果,所以這裏製造更多的噪聲點。最終的結果如下: Image

5. 形態梯度

它是一幅圖像的膨脹和侵蝕之間的差值。

結果將看起來像是對象的輪廓。

    gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)

結果如下: Image

6. 禮帽

它是輸入圖像和圖像的開的插值。下面的示例是針對 9x9 內核完成的。

def top_hat():
    img = cv.imread("/home/hanpfei/j.png", 0)

    kernel = np.ones((9, 9), np.uint8)
    tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)

    edge = np.full((img.shape[0], 3, 1), 255, np.uint8);

    images = [img, edge, tophat]
    dest = cv.hconcat(images)

    cv.imshow("Image", dest)
    cv.waitKey(-1)

結果如下: Image

7. 黑帽

它是輸入圖像和圖像的閉的插值。下面的示例是針對 9x9 內核完成的。

    tophat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)

結果如下: Image

結構元素

我們在 Numpy 的幫助下手動創建前面的示例中的結構元素。它是矩形的。但在某些情況下,你可能需要 橢圓形/圓形 的內核。所以爲了這個目的,OpenCV 有一個函數,cv.getStructuringElement()。我們只需傳入內核的形狀和大小,就可以得到想要的內核。

# Rectangular Kernel
>>> cv.getStructuringElement(cv.MORPH_RECT,(5,5))
array([[1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1]], dtype=uint8)
# Elliptical Kernel
>>> cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
array([[0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [0, 0, 1, 0, 0]], dtype=uint8)
# Cross-shaped Kernel
>>> cv.getStructuringElement(cv.MORPH_CROSS,(5,5))
array([[0, 0, 1, 0, 0],
       [0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1],
       [0, 0, 1, 0, 0],
       [0, 0, 1, 0, 0]], dtype=uint8)

其它資源

  1. HIPR2 的 Morphological Operations

練習

參考文檔

Morphological Transformations

Done.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章