文章目錄
QQ:3020889729 小蔡
形態變換的實現過程
形態變換是一些基於圖像形狀的簡單操作。通常在二進制圖像上執行,當然也可以在多通道圖像上執行。
它有兩個輸入,一個是我們的原始圖像,而另外的一個是決定操作性質的結構元素或者說內核。
正是基於這樣的內核,才使得我們通過指定的方法實現關於內核下的形態變化。
其中,兩種基本的形態學算子是侵蝕和膨脹——除此之外,這裏還會講解部分其它形態變換,以及常用內核的創建方法。
形態變換實例(以灰度圖像爲例)
多通道圖像也可以發生形態變換,這一篇博客未涉及,後邊會作爲補充講解補上。
(其實現本質與灰度圖像無二,只是內核的設置上要改動一下元素值)
1.侵蝕(Erosion)——內核區域存在的像素值全爲1,整個區域才置1,否則置爲0
涉及的方法——erode()
其作用是,傳入內核,實現對原輸入圖像的侵蝕——本質是侵蝕前景物體的邊界。
其侵蝕的形式是——內核在圖像中滑動(如2D卷積),僅在當內核下的所有像素均爲1時,原始圖像中的像素(1或0)才被視爲1,否則它將被侵蝕(設爲零)。
方法參數如下:
- 輸入圖像(src )
- 輸入內核(kernel )
- 錨點(anchor )——默認爲元素中心,一般不修改
- 迭代數(iterations)——也就是侵蝕次數
- 邊界像素處理類型(borderType )——一般不設置
- 邊界不變時的邊界值(borderValue )——默認值
代碼實例
通過numpy創建一個內核,實現侵蝕
import cv2 as cv
import numpy as np
if __name__ == "__main__":
img = cv.imread('../imag_in_save/images1.jfif', 0)
kernel = np.ones((5, 5), np.uint8) # 創建一個uint8的5*5全一矩陣
img_ex = cv.erode(img, kernel, iterations=1) # 侵蝕1次
cv.imshow('imag1', img)
cv.imshow('imag2', img_ex)
cv.waitKey(0)
cv.destroyAllWindows()
效果:
2.擴張(Dilation)——內核區域存在像素值爲1,整個區域就置1
涉及的方法——dilate()
其作用是,傳入內核,實現對原輸入圖像的擴張。
它與侵蝕正好相反。如果內核下的至少一個像素爲“ 1”,則像素元素爲“ 1”。
通常,在消除噪音的情況下,腐蝕後會膨脹。因爲腐蝕會消除白噪聲,但也會縮小物體。因此,我們需要對其進行擴展。由於噪音消失了,它們是不會回來的,使得我們的目標區域增加。擴張,在連接對象的損壞部分時也很有用。
方法參數如下:
- 輸入圖像(src )
- 輸入內核(kernel )
- 錨點(anchor )——默認爲元素中心,一般不修改
- 迭代數(iterations)——也就是擴張次數
- 邊界像素處理類型(borderType )——一般不設置
- 邊界不變時的邊界值(borderValue )——默認值
代碼實例
import cv2 as cv
import numpy as np
if __name__ == "__main__":
img = cv.imread('../imag_in_save/images1.jfif', 0)
kernel = np.ones((5, 5), np.uint8) # 創建一個uint8的5*5全一矩陣
img_ex = cv.dilate(img, kernel, iterations=1) # 擴張1次
cv.imshow('imag1', img)
cv.imshow('imag2', img_ex)
cv.waitKey(0)
cv.destroyAllWindows()
效果:(可以看出,在內核掃過圖形區域時,只要有1就擴張(範圍內全部置1),否則保留爲0)
3.開場(Opening)
涉及的方法——morphologyEx()
該方法使用侵蝕和膨脹作爲基本操作來執行高級形態轉換——開場。它同樣對於消除噪聲很有用。
方法參數如下:
- 輸入圖像(src )
- 形態學運算類型(MorphTypes)
- 輸入內核(kernel )
- 錨點(anchor )——默認爲元素中心,一般不修改
- 迭代數(iterations)——也就是擴張次數
- 邊界像素處理類型(borderType )——一般不設置
- 邊界不變時的邊界值(borderValue )——默認值
形態學運算類型(MorphTypes)選項:也對應着執行效果
- cv.MORPH_ERODE——侵蝕運算
- cv.MORPH_DILATE——擴張運算
- cv.MORPH_OPEN——開張運算
- cv.MORPH_CLOSE——閉幕運算
- cv.MORPH_GRADIENT——形態梯度運算
- cv.MORPH_TOPHAT——高頂禮帽運算
- cv.MORPH_BLACKHAT——黑帽運算
- cv.MORPH_HITMISS——命中或未命中——本次不涉及,它僅僅操作對CV_8UC1二進制映像運算。
代碼實例(包含前邊兩種變換的實現)
import cv2 as cv
import numpy as np
if __name__ == "__main__":
img = cv.imread('../imag_in_save/images1.jfif', 0)
kernel = np.ones((5, 5), np.uint8) # 創建一個uint8的5*5全一矩陣
img_erode = cv.morphologyEx(img, cv.MORPH_ERODE, kernel) # 形態學侵蝕運算
img_dilate = cv.morphologyEx(img, cv.MORPH_DILATE, kernel) # 擴張運算
img_open = cv.morphologyEx(img, cv.MORPH_OPEN, kernel) # 開場運算
cv.imshow('imag1', img)
cv.imshow('erode', img_erode)
cv.imshow('dilate', img_dilate)
cv.imshow('open', img_open)
cv.waitKey(0)
cv.destroyAllWindows()
效果:開場的形態學運算類型爲——cv.MORPH_OPEN實現過程:——先執行侵蝕然後擴張
其它實例的效果:
4.閉幕(Closing)
涉及的方法——morphologyEx()
實現閉幕,採用形態學運算類型爲——cv.MORPH_CLOSE
代碼實例
import cv2 as cv
import numpy as np
if __name__ == "__main__":
img = cv.imread('../imag_in_save/images1.jfif', 0)
kernel = np.ones((5, 5), np.uint8) # 創建一個uint8的5*5全一矩陣
img_close = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel) # 形態學侵蝕運算
cv.imshow('imag1', img)
cv.imshow('close', img_close)
cv.waitKey(0)
cv.destroyAllWindows()
效果:——先執行擴張然後侵蝕
其它實例的效果:
5.形態梯度(Morphological Gradient)
涉及的方法——morphologyEx()
實現閉幕,採用形態學運算類型爲——cv.MORPH_GRADIENT
代碼實例
import cv2 as cv
import numpy as np
if __name__ == "__main__":
img = cv.imread('../imag_in_save/images1.jfif', 0)
kernel = np.ones((5, 5), np.uint8) # 創建一個uint8的5*5全一矩陣
img_gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel) # 形態學侵蝕運算
cv.imshow('imag1', img)
cv.imshow('gradient', img_gradient)
cv.waitKey(0)
cv.destroyAllWindows()
效果:左方爲變化效果
其它實例的效果:
6.高頂禮帽(Top Hat)
涉及的方法——morphologyEx()
實現閉幕,採用形態學運算類型爲——cv.MORPH_TOPHAT
代碼實例
使用高頂禮帽——內核大小需要配置一下,不同的內核,對於圖形變換的效果影響不同。
import cv2 as cv
import numpy as np
if __name__ == "__main__":
img = cv.imread('../imag_in_save/images1.jfif', 0)
kernel = np.ones((9, 9), np.uint8) # 創建一個uint8的5*5全一矩陣
img_tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel) # 高頂禮帽運算
cv.imshow('imag1', img)
cv.imshow('tophat', img_tophat)
cv.waitKey(0)
cv.destroyAllWindows()
效果:這是圖像的擴張和腐蝕之間的區別——這張實例圖不是很清楚,可以看一下後邊的那個實例圖像。
其它實例的效果:
7.黑帽(Black Hat)
涉及的方法——morphologyEx()
實現閉幕,採用形態學運算類型爲——cv.MORPH_BLACKHAT
代碼實例
import cv2 as cv
import numpy as np
if __name__ == "__main__":
img = cv.imread('../imag_in_save/images1.jfif', 0)
kernel = np.ones((5, 5), np.uint8) # 創建一個uint8的5*5全一矩陣
img_blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel) # 黑帽運算
cv.imshow('imag1', img)
cv.imshow('blackhat', img_blackhat)
cv.waitKey(0)
cv.destroyAllWindows()
效果:
其它實例的效果:
常見結構元素(內核)的創建
我們常用的內核有矩形,橢圓形,十字形等。而通過numpy創建的ones矩陣僅爲矩形內核,當然也可以手動設置矩陣元素使得其爲一些其他形狀。正因如此,多餘內核的多樣創建,opencv提供了一個getStructuringElement()方法——實現不同形狀內核的創建。
該getStructuringElement()函數構造並返回可進一步傳遞給erode,dilate或morphologyEx的結構元素。但是您也可以自己構造一個任意的二進制掩碼,並將其用作結構元素。
getStructuringElement方法的講解
參數如下:
- 內核形狀
- 內核大小
- 錨點——默認元素中心
內核形狀類型:
- cv.MORPH_RECT——矩形
- cv.MORPH_CROSS——十字形
- cv.MORPH_ELLIPSE——橢圓形
代碼實例——三種常用內核類型的效果輪播
import cv2 as cv
import numpy as np
if __name__ == "__main__":
img = cv.imread('../imag_in_save/images1.jfif', 0)
cv.namedWindow('Showing', cv.WINDOW_NORMAL)
choice = [cv.MORPH_RECT, cv.MORPH_CROSS, cv.MORPH_ELLIPSE]
title = ['RECT', 'CROSS', 'ELLIPSE']
# 依次對應內核類型的矩形,十字形, 橢圓形
counts = 0 # 遍歷內核類型
while True:
kernel = cv.getStructuringElement(choice[counts], (5, 5)) # 創建一個矩形內核
img_blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel) # 黑帽運算
img_blackhat = cv.putText(img_blackhat, title[counts], (20, 40), cv.FONT_ITALIC, 1, 160, 2, cv.LINE_AA)
cv.imshow('Showing', img_blackhat)
k = cv.waitKey(1600) & 0xFF
if k == 27:
break
counts += 1
if counts == 3:
counts = 0
cv.destroyAllWindows()
效果: