###### tags: `OpenCV`,`形態學`
# OpenCV 基礎篇-形態學(用於邊界抽取、輪廓處理)
**腐蝕(Erosion)**
* 腐蝕是最基本的形態學操作之一,它能夠將影像的邊界點消除,使影像沿邊界內收縮。
* 腐蝕用來「收縮」或「細化」二值影像的前景,借此實現去除雜訊、元素分割等功能。
Dst = cv2.erode(src,kernel[,anchor[,iterations[,borderType[,borderValue]]]])
src : 參數表示待處理的輸入圖像
kernel:結構元素的形狀
**腐蝕示意圖**
腐蝕會把物體的邊界腐蝕掉,卷積核沿著圖像滑動,如果卷積核對應的原圖的所有像素值為1,那麼中心元素就保持原來的值,否則變為零。主要應用在去除白噪聲,也可以斷開連在一起的物體。
藉由設定的kernel去滾動每個框格,有完全符合kernel的,才會留下中間值的部分數字1(最後剩下綠色方框)

```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)
```

**膨脹(Dilation)**
* 膨脹操作是形態學的另外一種操作。
* 膨脹操作是腐蝕操作的作用是相反的,膨脹操作能對影像的邊界進行擴張。
* 膨脹的作用:
圖像大小會往外增加一個像素(3*3)
平滑對象邊緣
減少或者填充對象之間的距離
Dst = cv2.erode(src,kernel[,anchor[,iterations[,borderType[,bordeValue]]]])
src : 參數表示待處理的輸入圖像
kernel:結構元素的形狀

```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)
```

**結構元素的形狀 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)
```

```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)
```

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()
```

**開運算**
* 開運算的操作是先將影像腐蝕,再對腐蝕結果進行膨脹。
* 開運算可以用於去噪、計數等
* 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()
```

: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')
```

**形態學梯度運算**
* 形態學梯度運算是用影像的膨脹影像減腐蝕影像的操作,該操作可以取得原始影像中前景影像的邊緣
* 語法結構如下:
* 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')
```

**禮帽運算**
* 禮帽運算是用原始影像減去**開**運算影像操作
* 禮帽運算能夠取得影像的雜訊資訊,或獲得比原始影像的邊緣更亮的邊緣資訊
* 語法結構如下:
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')
```

**黑帽運算**
* 黑帽運算是用原始影像減去**閉**運算影像操作。
* 黑帽運算能夠取得影像內部的小孔,或前景色中的小黑點,或獲得比原始影像的邊緣更暗的邊緣資訊
* 語法結構如下:
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')
```
