目標
這一節
- 我們將學習不同的形態學操作,如腐蝕、膨脹、開、閉......
- 我們將看到不同的函數,如:cv2.erode()、cv2.dilate()、cv2.morphology()
理論
形態變換是基於圖像形狀的一些簡單操作。它通常在二進制圖像上執行。它需要兩個輸入,一個是我們的原始圖像,第二個是稱為結構元素或內核,它決定了操作的本質。兩個基本的形態學運算符是侵蝕和膨脹。然后它的變體形式如Opening,Closing,Gradient等也發揮作用。我們將在以下圖片的幫助下逐一看到它們:
1、腐蝕(Erosion)
腐蝕的基本思想就像土壤侵蝕一樣,它會侵蝕前景物體的邊界(總是試圖保持前景為白色)。那它是做什么的?內核在圖像中滑動(如在2D卷積中)。只有當內核下的所有像素都是1時,原始圖像中的像素(1或0)才會被視為1,否則它將被侵蝕(變為零)。
所以發生的事情是,邊界附近的所有像素都將被丟棄,具體取決於內核的大小。因此,前景對象的厚度或大小減小,或者圖像中的白色區域減小。它有助於消除小的白噪聲(正如我們在色彩空間章節中看到的那樣),分離兩個連接的對象等。
在這里,作為一個例子,我將使用一個全1的5x5內核,其中包含完整的內核。讓我們看看它是如何工作的:
import cv2 import numpy as np img = cv2.imread('j.png',0) kernel = np.ones((5,5),np.uint8) erosion = cv2.erode(img,kernel,iterations = 1)
結果:
2、膨脹(Dilation)
它恰好與腐蝕相反。這里,如果內核下的至少一個像素為“1”,則像素元素為“1”。因此它增加了圖像中的白色區域或前景對象的大小增加。通常,在去除噪音的情況下,腐蝕之后再膨脹。因為,腐蝕會消除白噪聲,但它也會縮小我們的物體,所以我們需要再擴大它。由於噪音消失了,它們不會再回來,但我們的物體區域會增加。它也可用於連接對象的破碎部分。
dilation = cv2.dilate(img,kernel,iterations = 1)
結果:
3、開運算(Opening)
開運算是腐蝕再膨脹的另一種說法。如上所述,它有助於消除噪音。這里我們使用函數cv2.morphologyEx().
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
結果:
4. 閉運算(Closing)
開運算與閉運算,腐蝕和膨脹是相反的。閉運算就是先膨脹再腐蝕,它可用於關閉前景對象內的小孔或對象上的小黑點。
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
結果:
我們可以通過以下代碼觀察之間的關系:
import cv2 import numpy as np img = cv2.imread('./Pictures/j.png',0) kernel = np.ones((5,5),np.uint8) erosion = cv2.erode(img,kernel,iterations = 1) img2 = ~img dilation = cv2.dilate(img2,kernel,iterations = 1) dilation = ~dilation htich = np.hstack((img, erosion, dilation)) cv2.imshow("erosion", htich) cv2.waitKey(0)
結果:
可見,img開運算等同於反轉圖閉運算再反轉。
5、形態梯度(Morphological Gradient)
它是一張圖像膨脹和腐蝕之間的差異,結果看起來像對象的輪廓。
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
結果:
其效果等同於膨脹減去腐蝕。
import cv2 import numpy as np img = cv2.imread('./Pictures/j.png',0) kernel = np.ones((5,5),np.uint8) erosion = cv2.erode(img,kernel,iterations = 1) dilation = cv2.dilate(img,kernel,iterations = 1) diff = dilation - erosion gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel) htich = np.hstack((img, gradient, diff)) cv2.imwrite("./Pictures/i.png", htich) cv2.imshow("erosion", htich) cv2.waitKey(0)
效果:
6、高帽變換(Top Hat/White Top-Hot)
它是輸入圖像和圖像開運算之間的區別。下面的示例是針對9x9內核完成的。
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
結果:
其等同於膨脹后減去原圖,例如,該圖在使用2x2內核時,兩者效果比較接近。
import cv2 import numpy as np img = cv2.imread('./Pictures/j.png',0) kernel = np.ones((2,2),np.uint8) erosion = cv2.erode(img,kernel,iterations = 1) dilation = cv2.dilate(img,kernel,iterations = 1) diff = img - dilation diff2 = dilation - img tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel) htich = np.hstack((img, tophat, diff2)) cv2.imwrite("./Pictures/i.png", htich) cv2.imshow("erosion", htich) cv2.waitKey(0)
結果:
7、黑帽變換(Black Hat/Black Top-Hot)
它是閉運算與輸入圖像的差異。
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
結果:
其等同於原圖減去閉運算,顯然這兩個都能用來提取輪廓,有什么區別呢?WTH能使較暗背景中較亮的像素聚集,BTH能使較亮背景中較暗像素的聚集。前者使“峰”更尖,后者使“谷”更深。兩者結合$THE(f)=f+WTH(f,b)-BTH(f,b)$,對比更加明顯。
結構元素(Structuring Element)
我們在Numpy的幫助下手動創建了前面示例中的結構元素。它是矩形。但在某些情況下,您可能需要橢圓/圓形內核。因此,為此,OpenCV有一個函數cv2.getStructuringElement()。您只需傳遞內核的形狀和大小,即可獲得所需的內核。
# Rectangular Kernel >>> cv2.getStructuringElement(cv2.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 >>> cv2.getStructuringElement(cv2.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 >>> cv2.getStructuringElement(cv2.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)
參考鏈接:OpenCV-Python Tutorials https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html#dilation