###### tags: `OpenCV`,`形態學` # OpenCV 基礎篇-形態學(用於邊界抽取、輪廓處理) **腐蝕(Erosion)** * 腐蝕是最基本的形態學操作之一,它能夠將影像的邊界點消除,使影像沿邊界內收縮。 * 腐蝕用來「收縮」或「細化」二值影像的前景,借此實現去除雜訊、元素分割等功能。 Dst = cv2.erode(src,kernel[,anchor[,iterations[,borderType[,borderValue]]]]) src : 參數表示待處理的輸入圖像 kernel:結構元素的形狀 **腐蝕示意圖** 腐蝕會把物體的邊界腐蝕掉,卷積核沿著圖像滑動,如果卷積核對應的原圖的所有像素值為1,那麼中心元素就保持原來的值,否則變為零。主要應用在去除白噪聲,也可以斷開連在一起的物體。 藉由設定的kernel去滾動每個框格,有完全符合kernel的,才會留下中間值的部分數字1(最後剩下綠色方框) ![](https://i.imgur.com/JJUswGe.jpg) ```python= import cv2 import numpy as np img=np.zeros((5,5),np.uint8) img[1:4,1:4]=1 kernel = np.ones((3,1),np.uint8) erosion = cv2.erode(img,kernel) print("img=\n",img) print("kernel=\n",kernel) print("erosion=\n",erosion) ``` ![](https://i.imgur.com/sTzTdMF.jpg) **膨脹(Dilation)** * 膨脹操作是形態學的另外一種操作。 * 膨脹操作是腐蝕操作的作用是相反的,膨脹操作能對影像的邊界進行擴張。 * 膨脹的作用: 圖像大小會往外增加一個像素(3*3) 平滑對象邊緣 減少或者填充對象之間的距離 Dst = cv2.erode(src,kernel[,anchor[,iterations[,borderType[,bordeValue]]]]) src : 參數表示待處理的輸入圖像 kernel:結構元素的形狀 ![](https://i.imgur.com/8N8xWGZ.jpg) ```python= import cv2 import numpy as np img=np.zeros((5,5),np.uint8) img[2:3,1:4]=1 kernel = np.ones((3,1),np.uint8) dilation = cv2.dilate(img,kernel) print("img=\n",img) print("kernel=\n",kernel) print("dilation\n",dilation) ``` ![](https://i.imgur.com/i2zdLnH.jpg) **結構元素的形狀 getStructuringElement** getStructuringElement(shape, ksize, anchor=None) shape 表示內核的形狀,有三種形狀可以選擇: * 十字形:cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5)) * 橢圓:cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5)) * 矩形:cv2.getStructuringElement(cv2.MORPH_RECT,(5,5)) ksize 表示內核的尺寸(n, n) anchor 錨點的位置,只對十字形有用 ```python= import cv2 element_cross = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5)) print(element_cross) print("------------") element_ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) print(element_ellipse) print("------------") element_rect = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) print(element_rect) ``` ![](https://i.imgur.com/rvRDZDc.jpg) ```python= import cv2 #十字形取錨點 element_cross = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5),(1,3)) print(element_cross) #橢圓尺寸取單數比較好 element_ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (13, 13)) print(element_ellipse) ``` ![](https://i.imgur.com/EWMQSBj.jpg) check 差異 ```python= import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread('origin.jpg') grayimage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) eroded = cv2.erode(grayimage, kernel) dilated = cv2.dilate(grayimage, kernel) plt.rcParams['figure.figsize'] = [200, 100] plt.rcParams['font.size'] = 150 img2 = cv2.cvtColor(img , cv2.COLOR_BGR2RGB) plt.subplot(1,3,1) plt.imshow(img2) plt.xticks([]), plt.yticks([]) plt.title('origin image BGR') eroded2 = cv2.cvtColor(eroded , cv2.COLOR_GRAY2RGB) plt.subplot(1,3,2) plt.imshow(eroded2) plt.xticks([]), plt.yticks([]) plt.title('eroded image ') dilated2 = cv2.cvtColor(dilated , cv2.COLOR_GRAY2RGB) plt.subplot(1,3,3) plt.imshow(dilated2) plt.xticks([]), plt.yticks([]) plt.title('dilated image') plt.show() ``` ![](https://i.imgur.com/Fk7uLVi.jpg) **開運算** * 開運算的操作是先將影像腐蝕,再對腐蝕結果進行膨脹。 * 開運算可以用於去噪、計數等 * Opening = cv2.morphologyEx(img, cv2.MORPH_OPEN,kernel) * 使用cv2.morthologyEx()實現開運算 **閉運算** * 先膨脹、後腐蝕的運算,有助於關閉前景物體的小孔,或去除物體上的小黑點,還可以將不同的前景影像進行連接(看上去將兩個細微連接的圖封閉在一起) ```python= import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread('opening.bmp') grayimage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) closed = cv2.morphologyEx(grayimage, cv2.MORPH_CLOSE, kernel) Open = cv2.morphologyEx(grayimage, cv2.MORPH_OPEN, kernel) plt.rcParams['figure.figsize'] = [200, 200] plt.rcParams['font.size'] = 150 img2 = cv2.cvtColor(img , cv2.COLOR_BGR2RGB) plt.subplot(1,3,1) plt.imshow(img2) plt.xticks([]), plt.yticks([]) plt.title('origin image BGR') closed2 = cv2.cvtColor(closed , cv2.COLOR_GRAY2RGB) plt.subplot(1,3,2) plt.imshow(closed2) plt.xticks([]), plt.yticks([]) plt.title('closed image ') open2 = cv2.cvtColor(Open , cv2.COLOR_GRAY2RGB) plt.subplot(1,3,3) plt.imshow(open2) plt.xticks([]), plt.yticks([]) plt.title('open image') plt.show() ``` ![](https://i.imgur.com/UFhWFpA.jpg) :star:開運算總結: (1)開運算能夠除去孤立的小點,毛刺和小橋,而總的位置和形狀不變。 (2)開運算是一個基於幾何運算的filter(你會看到有人翻成濾波器或是濾片) (3)結構元素大小的不同將導致濾波效果的不同 (4)不同的結構元素的選擇導致了不同的分割,即提取出不同的特徵 :star:閉運算總結: (1)閉運算能夠填平小湖(即小孔),彌合小裂縫,而總的位置和形狀不變。 (2)閉運算是通過填充圖像的凹角來濾波圖像的。 (3)結構元素大小的不同將導致濾波效果的不同。 (4)不同結構元素的選擇導致了不同的分割。 **為什麼需要膨脹和腐蝕?** * 形態學檢測邊緣的原理很簡單,在膨脹時,圖像中的物體會向周圍“擴張”;腐蝕時,圖像的物體會“收縮”。 * 比較兩幅圖像,由於其變化的區域只發生在邊緣。所以這時將這兩幅圖像相減,得到的就是圖像中的邊緣。 ```python= import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread('ja.jpg') element = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) dilate = cv2.dilate(img, element) erode = cv2.erode(img, element) # 將兩幅圖像相減獲得邊,第一個參數是膨脹後的圖像,第二個參數是腐蝕後的圖像 # cv2.absdiff:(膨脹後的圖像,腐蝕後的圖像) result = cv2.absdiff(dilate, erode)#絕對值的差異 # 上面得到的結果是灰度圖,將其二值化以便更清楚的觀察結果 retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY) # 反色,即對二值圖每個像素取反 result = cv2.bitwise_not(result) plt.rcParams['figure.figsize'] = [200, 100] plt.rcParams['font.size'] = 150 img2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) plt.subplot(1,2,1) plt.imshow(img2) plt.xticks([]), plt.yticks([]) plt.title('origin image BGR') result2 = cv2.cvtColor(result , cv2.COLOR_BGR2RGB) plt.subplot(1,2,2) plt.imshow(result2,'gray') plt.xticks([]), plt.yticks([]) plt.title('edge') ``` ![](https://i.imgur.com/l2MFm2o.jpg) **形態學梯度運算** * 形態學梯度運算是用影像的膨脹影像減腐蝕影像的操作,該操作可以取得原始影像中前景影像的邊緣 * 語法結構如下: * gradient = cv2.morphologyEx(img,cv2.MORPH_GRADIENT,kernel) ```python= import cv2 import numpy as np original=cv2.imread("gradient.bmp",cv2.IMREAD_UNCHANGED) kernel=np.ones((5,5),np.uint8) gardient=cv2.morphologyEx(original,cv2.MORPH_GRADIENT,kernel) plt.rcParams['figure.figsize'] = [200, 100] plt.rcParams['font.size'] = 150 img_original = cv2.cvtColor(original, cv2.COLOR_BGR2RGB) plt.subplot(1,2,1) plt.imshow(img_original) plt.xticks([]), plt.yticks([]) plt.title('origin image BGR') img_gardien = cv2.cvtColor(gardient, cv2.COLOR_BGR2RGB) plt.subplot(1,2,2) plt.imshow(img_gardien) plt.xticks([]), plt.yticks([]) plt.title('edge') ``` ![](https://i.imgur.com/ru7Xt1o.jpg) **禮帽運算** * 禮帽運算是用原始影像減去**開**運算影像操作 * 禮帽運算能夠取得影像的雜訊資訊,或獲得比原始影像的邊緣更亮的邊緣資訊 * 語法結構如下: result = cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel) ```python= #禮帽運算(tophat) import cv2 import numpy as np original=cv2.imread("tophat.bmp",cv2.IMREAD_UNCHANGED) kernel=np.ones((5,5),np.uint8) tophat=cv2.morphologyEx(original,cv2.MORPH_TOPHAT,kernel) #cv2.MORPH_TOPHAT原始影像減去開運算影像操作 plt.rcParams['figure.figsize'] = [200, 100] plt.rcParams['font.size'] = 150 img_original = cv2.cvtColor(original, cv2.COLOR_BGR2RGB) plt.subplot(1,2,1) plt.imshow(original) plt.xticks([]), plt.yticks([]) plt.title('origin image BGR') img_tophat = cv2.cvtColor(tophat, cv2.COLOR_BGR2RGB) plt.subplot(1,2,2) plt.imshow(tophat) plt.xticks([]), plt.yticks([]) plt.title('tophat') ``` ![](https://i.imgur.com/ym8ANB5.jpg) **黑帽運算** * 黑帽運算是用原始影像減去**閉**運算影像操作。 * 黑帽運算能夠取得影像內部的小孔,或前景色中的小黑點,或獲得比原始影像的邊緣更暗的邊緣資訊 * 語法結構如下: result = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel) ```python= #黑帽運算(blackhat) import cv2 import numpy as np original=cv2.imread("blackhat.bmp",cv2.IMREAD_UNCHANGED) kernel=np.ones((5,5),np.uint8) black=cv2.morphologyEx(original,cv2.MORPH_BLACKHAT,kernel)#cv2.MORPH_TOPHAT原始影像減去閉運算影像操作 plt.rcParams['figure.figsize'] = [200, 100] plt.rcParams['font.size'] = 150 img_original = cv2.cvtColor(original, cv2.COLOR_BGR2RGB) plt.subplot(1,2,1) plt.imshow(original) plt.xticks([]), plt.yticks([]) plt.title('origin image BGR') img_black = cv2.cvtColor(black, cv2.COLOR_BGR2RGB) plt.subplot(1,2,2) plt.imshow(img_black) plt.xticks([]), plt.yticks([]) plt.title('blackhat') ``` ![](https://i.imgur.com/2vNNhl9.jpg)