前幾篇講到了關於圖形特徵萃取方面的方法,
今天則是要介紹在處理圖形之前,
常用的一些預處理,
在OPENCV的圖片處理方法上稱之為形態學操作,
簡單來說,形態學操作的目的也是為了要找出圖形中的特徵,
可能是圖片中某個特徵的骨幹架構,又或者是將雜訊濾除等等,
在操作上,主要包含了腐蝕、膨脹、開運算、閉運算、梯度運算、禮帽運算、黑帽運算等,
以下跟大家一一做介紹:
腐蝕與膨脹都是圖形基本的形態學操作方法,
腐蝕能夠將影像的邊界點消除,使圖像特徵由邊界往內縮,
也能夠將小於指定範圍的部分消除,能夠用於各種圖像,
但一般而言會用於去除二值化影像中的雜訊,
舉例來說,我們這裡有一顆被咬一口而且長毛的蘋果,
看起來很不美味,對吧
那些白色的毛是我們不想要的區域,
而我們希望可以保留蘋果的原型,
這個時候就能藉由腐蝕來進行有效的處理,
程式碼如下:
#image_morphology.py
import cv2 as cv
import numpy as np
img = cv.imread('apple2.jpg')
kernel = np.ones((3,3),dtype=np.uint8)
erosion = cv.erode(img,kernel)
cv.imwrite('./erosion.jpg',erosion)
如此一來即可將蘋果旁邊的毛消除,
但是其實我們的蘋果也有稍微小一號,
如果看不出來的話,我們可以調整kernel的參數,
就能夠明顯看出蘋果被吃掉更多了,
#image_morphology.py
import cv2 as cv
import numpy as np
img = cv.imread('apple2.jpg')
kernel = np.ones((6,6),dtype=np.uint8)
erosion = cv.erode(img,kernel)
cv.imwrite('./erosion.jpg',erosion)
這樣一來蘋果就明顯更往內縮了一些,
如果我們希望蘋果可以在縮了之後又放大,
那就可以使用膨脹來進行處理,
其程式碼操作方法如下:
#image_morphology.py
import cv2 as cv
import numpy as np
img = cv.imread('apple2.jpg')
kernel = np.ones((4,4),dtype=np.uint8)
erosion = cv.erode(img,kernel)
dilation = cv.dilate(erosion,kernel)
cv.imwrite('./erosion.jpg',erosion)
cv.imwrite('./dilation.jpg',dilation)
結果圖如下:
原圖
腐蝕
膨脹
回過頭來說,為什麼會有這樣的效果呢?
腐蝕的處理過程是這樣的,我們的cv2.erode()及cv2.dilate()函數會放入兩個參數,
分別為待處理圖片以及用以進行腐蝕的核結構(kernel),
這邊說明關於核結構(kernel),
如下圖所示,核是一個自定義的圖形結構,每個像素值為1,
在腐蝕處理的過程中,會將核放至在圖形的每個位置進行判定,
若是核能夠完全置於圖形特徵中的時候,核所對應的中心位置值計算為1,
反之,若核不完全置於圖形特徵中,則核所對應的中心點計算為0,
因此便可以透過核的結構,來調整進行腐蝕的判定條件,
在cv2.erode()及cv2.dilate()函數中還有能夠調整的參數,
其中包括下列幾個:
1.anchor:調整核當中哪個對應的位置當作定錨點,預設為中心點(-1,-1),
2.iterations:要幾次迭代的次數,預設為1
上面說到的是基礎的形態學操作,腐蝕與膨脹,
下面介紹關於OPENCV當中所包含的"通用形態學函數"-cv2.morphologyEx(),
通用形態學運算提供了更簡便的形態學處理,
其語法結構如下:
cv2.morphologyEx(src,op,kernel)
使用方法跟腐蝕、膨脹一樣,
src是待處理圖片,中間的op所代表的是操作類別,kernel是自定義核結構,
其中op的類型描述如下表,可以看出開運算、閉運算、梯度運算、禮帽運算及黑帽運算都是由腐蝕、膨脹所延伸的,
OP類型 | 說明 | 處理過程 |
---|---|---|
cv2.MORPH_ERODE | 腐蝕 | 腐蝕(src) |
cv2.MORPH_DILATE | 膨脹 | 膨脹(src) |
cv2.MORPH_OPEN | 開運算 | 膨脹(腐蝕(src)) |
cv2.MORPH_CLOSE | 閉運算 | 腐蝕(膨脹(src)) |
cv2.MORPH_GRADIENT | 梯度運算 | 膨脹(src)-腐蝕(src) |
cv2.MORPH_TOPHAT | 禮帽運算 | src-開運算(src) |
cv2.MORPH_BLACKHAT | 黑帽運算 | close(src)-src |
為了能夠比較清楚的看出上述形態學操作的差異,
特別找了一個有部分連結又有分開特徵的葡萄圖片,
在進行形態學特徵操作之前,
而事先進行了二值化處理,其中cv2.threshold()的type選擇cv2.THRESH_BINARY_INV,
目的是為了將白色底反轉為黑色,使葡萄變成白色的圖形特徵以進行相關處理,
同時,為了更明顯的看出禮帽運算與黑帽運算的差異,這邊將兩張處理後的圖進行了合成,
實際程式碼如下:
import cv2 as cv
import numpy as np
#讀取待處理圖片
img = cv.imread('grape.jpg')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret,binary = cv.threshold(gray,200,255,cv.THRESH_BINARY_INV)
#建立核結構
kernel = np.ones((8,8),dtype=np.uint8)
#開運算處理
opening = cv.morphologyEx(binary,cv.MORPH_OPEN,kernel)
#閉運算處理
closing = cv.morphologyEx(binary,cv.MORPH_CLOSE,kernel)
#梯度運算處理
gradient = cv.morphologyEx(binary,cv.MORPH_GRADIENT,kernel)
#禮帽運算處理
tophat = cv.morphologyEx(binary,cv.MORPH_TOPHAT,kernel)
#黑帽運算處理
blackhat = cv.morphologyEx(binary,cv.MORPH_BLACKHAT,kernel)
#禮帽+黑帽
twohat = cv.add(tophat,blackhat)
cv.imwrite('./binary.jpg',binary)
cv.imwrite('./opening.jpg',opening)
cv.imwrite('./closing.jpg',closing)
cv.imwrite('./gradient.jpg',gradient)
cv.imwrite('./tophat.jpg',tophat)
cv.imwrite('./blackhat.jpg',blackhat)
cv.imwrite('./twohat.jpg',twohat)
結果圖:
原圖
二值化
開運算
開運算圖片中,可以看到原來連在一起的枝梗被分開了,這是腐蝕後再膨脹的結果
閉運算
閉運算的圖片結果很明顯,原先分開的部分都被連在一起了,
梯度運算
梯度運算是將膨脹的原圖減去腐蝕後的原圖,因此邊緣就被強調出來了,
禮帽運算
禮帽運算是針對邊緣偵測的一種方法,將保留較亮的邊緣部分,
黑帽運算
黑帽運算結果圖中所取得的結果,是影像內部的小孔,或者是較暗的邊緣部分,
禮帽+黑帽
從禮帽運算與黑帽運算的結果相加可以發現能夠描繪出一個較完整的邊緣外觀,
由此可見兩者在影像處理上算是互補的處理方法,
在上述的形態學操作中,必須自定義一個核結構,
因此OPENCV當中也有內建的核函數,其語法為
cv2.getStructuringElement(shape,ksize[, anchor])
其中shape是kernel的形狀類型,如下圖所示,
類型 | 說明 |
---|---|
cv2.MORPH_RECT | 矩形結構類型,所有值為1 |
cv2.MORPH_CROSS | 十字形結構類型,對角線數值為1 |
cv2.MORPH_ELLIPSE | 橢圓形結構類型 |
除此之外,當然也還是有其他邊緣偵測的方式囉,
不過那就是另一個故事啦,
有人敲碗的話再提了,