Programming Computer Vision with Python (学习笔记七)
數(shù)學(xué)形態(tài)學(xué)(mathematical morphology)關(guān)注的是圖像中的形狀,它提供了一些方法用于檢測(cè)形狀和改變形狀。起初是基于二值圖像提出的,后來(lái)擴(kuò)展到灰度圖像。二值圖像就是:每個(gè)像素的值只能是0或1,1代表描繪圖像的點(diǎn),0代表背景。
基本的形態(tài)學(xué)運(yùn)算包括:腐蝕(erosion)、膨脹(dilation)、開(kāi)(opening)、閉(closing),對(duì)于這些運(yùn)算,都需要用到被稱為結(jié)構(gòu)元素(Structuring element)的模板,一般為方形,以小矩陣的形式表示,但它的元素的值只能是0或1,它代表的是一個(gè)集合,這個(gè)集合罩在原圖像上,可以跟原圖像的形狀進(jìn)行集合運(yùn)算。
腐蝕(erosion)
要講清楚去處過(guò)程不容易,直接上圖看效果:
圖中(a)為原圖像,(b)為腐蝕運(yùn)算后結(jié)果,可以看出除了字母筆刷變細(xì)了之外,黑色背景的噪點(diǎn)也都不見(jiàn)了,(c)是膨脹運(yùn)算結(jié)果,字母筆刷比原圖像粗。
ok,現(xiàn)在看腐蝕是怎么實(shí)現(xiàn)的,還是先看圖:
如圖所示,(a)是3×3結(jié)構(gòu)元素,相當(dāng)于:
array([[ 1., 1., 1.],[ 1., 1., 1.],[ 1., 1., 1.]])圖中標(biāo)識(shí)出了它的中心點(diǎn)。
結(jié)構(gòu)元素的設(shè)置也可以是其它大小,也不一定全是1(黑點(diǎn)),比如是一個(gè)3×3十字形:
[[0,1,0],[1,1,1],[0,1,0]](b)為待處理的原圖像,我們把其中由所有黑點(diǎn)組成的集合設(shè)為X
(c)為腐蝕后的結(jié)果,黑色點(diǎn)就是經(jīng)過(guò)腐蝕之后保留下來(lái)的點(diǎn),灰色的點(diǎn)表示被排除出去的點(diǎn),我們看到的效果是X變小了一圈,這也之所以叫腐蝕的原因吧。
可以這樣來(lái)形象理解腐蝕運(yùn)算過(guò)程:將結(jié)構(gòu)元素平移到原圖像上某個(gè)位置,如果結(jié)構(gòu)元素中所有的黑點(diǎn)(值為1)都落在X里,就把結(jié)構(gòu)元素中心點(diǎn)對(duì)應(yīng)的原圖像的像素點(diǎn)保留下來(lái),否則就排除出去,如(c)所示,假設(shè)結(jié)構(gòu)元素蓋在這個(gè)位置,這時(shí)結(jié)構(gòu)元素下半部還有幾個(gè)點(diǎn)沒(méi)落在原圖X中,所以將中心點(diǎn)對(duì)應(yīng)的像素點(diǎn)排除出去,從黑色標(biāo)記為灰色。將結(jié)構(gòu)元素在原圖像上進(jìn)行平移,直到原圖像的每一個(gè)像素都被處理過(guò)。
所以這個(gè)結(jié)果也會(huì)把形狀以外的噪點(diǎn)排除掉。
腐蝕函數(shù)說(shuō)明
scipy.ndimage.morphology.binary_erosion(input, structure=None, iterations=1,...)input: 原圖像二值圖 structure: 即結(jié)構(gòu)元素,默認(rèn)為3×3十字形 iterations: 表示要連續(xù)應(yīng)用腐蝕多少次返回腐蝕后二值圖結(jié)果,ndarray類型示例:
>>> a = np.zeros((7,7), dtype=np.int) >>> a[1:6, 2:5] = 1 >>> a #原圖像二值圖,注意中間由1組成的矩形形狀 array([[0, 0, 0, 0, 0, 0, 0],[0, 0, 1, 1, 1, 0, 0],[0, 0, 1, 1, 1, 0, 0],[0, 0, 1, 1, 1, 0, 0],[0, 0, 1, 1, 1, 0, 0],[0, 0, 1, 1, 1, 0, 0],[0, 0, 0, 0, 0, 0, 0]])>>> ndimage.binary_erosion(a).astype(a.dtype) #可以看出矩形形狀被"腐蝕"了一圈 array([[0, 0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 0, 0, 0],[0, 0, 0, 1, 0, 0, 0],[0, 0, 0, 1, 0, 0, 0],[0, 0, 0, 1, 0, 0, 0],[0, 0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 0, 0, 0]])膨脹(dilation)
類似地:
如圖(c)就是膨脹的結(jié)果,運(yùn)算過(guò)程跟腐蝕類似,只不過(guò)對(duì)像素的排除判斷不一樣,膨脹的判斷方式是:只要結(jié)構(gòu)元素中有一個(gè)黑點(diǎn)(值為1)落在X集合里,就把結(jié)構(gòu)元素中心點(diǎn)對(duì)應(yīng)的原圖像的像素點(diǎn)保留下來(lái),否則就排除出去。
膨脹函數(shù)scipy.ndimage.morphology.binary_dilation與腐蝕類似,使用示例:
>>> a = np.zeros((5, 5)) >>> a[2, 2] = 1 >>> a array([ [ 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0.], [ 0., 0., 1., 0., 0.], [ 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0.]]) >>> ndimage.binary_dilation(a).astype(a. dtype) #binary_dilation第二個(gè)參數(shù)可指定結(jié)構(gòu)元素,默認(rèn)為3×3十字形 array([ [ 0., 0., 0., 0., 0.], [ 0., 0., 1., 0., 0.], [ 0., 1., 1., 1., 0.], [ 0., 0., 1., 0., 0.], [ 0., 0., 0., 0., 0.]]) 我們從以上的效果圖可以看到,腐蝕和膨脹可以改變形狀,同時(shí)也可以去背景噪點(diǎn)。
另外,把形狀的膨脹結(jié)果減去它的腐蝕結(jié)果,可以得到形狀的粗略邊緣以及角點(diǎn)。
開(kāi)(opening)
先對(duì)原圖像進(jìn)行腐蝕,再膨脹,就是開(kāi)運(yùn)算。有什么用呢?簡(jiǎn)單點(diǎn)說(shuō)它可以去除與結(jié)構(gòu)元素大小相當(dāng)?shù)目锥春退槠H绻惶巿D像中有多個(gè)形狀,開(kāi)運(yùn)算可以把那些只有一點(diǎn)點(diǎn)粘連的形狀分開(kāi)。因?yàn)槟屈c(diǎn)粘連的地方被去除了。
簡(jiǎn)單示例:
閉(closing)
與開(kāi)運(yùn)算相反,先對(duì)原圖進(jìn)行膨脹,再腐蝕,就是閉運(yùn)算。閉運(yùn)算可以填充圖像中的孔洞,連接一些缺口和碎片,變成塊狀。舉個(gè)應(yīng)用場(chǎng)景——車牌定位,如下圖:
右圖是使用通過(guò)簡(jiǎn)單的算法得到車的粗略邊角,車牌位置像是一堆散點(diǎn),如果對(duì)這個(gè)邊角圖運(yùn)用閉運(yùn)算可以得到這樣的效果:
車牌的位置變成一個(gè)接近車牌形狀的矩形,為下一步檢測(cè)提供了便利。
閉運(yùn)算函數(shù)ndimage.binary_closing的用法:
>>> a = np.zeros((5,5), dtype=np.int) >>> a[1:-1, 1:-1] = 1; a[2,2] = 0 >>> a #原圖像,注意中間有個(gè)0,表示形狀里面有個(gè)空洞 array([[0, 0, 0, 0, 0],[0, 1, 1, 1, 0],[0, 1, 0, 1, 0],[0, 1, 1, 1, 0],[0, 0, 0, 0, 0]])>>> ndimage.binary_closing(a).astype(np.int) array([[0, 0, 0, 0, 0],[0, 1, 1, 1, 0],[0, 1, 1, 1, 0],[0, 1, 1, 1, 0],[0, 0, 0, 0, 0]]) #應(yīng)用閉運(yùn)算之后,空洞被填充了開(kāi)閉運(yùn)算原理看似簡(jiǎn)單,但很強(qiáng)大,只要結(jié)構(gòu)元素選取得當(dāng),可以做很多事情。
對(duì)象計(jì)數(shù)(Counting Objects)
這里說(shuō)的對(duì)象是指圖像中與周圍沒(méi)有連通的單獨(dú)的形狀,我們的目標(biāo)是要計(jì)算這些對(duì)象的個(gè)數(shù),計(jì)算對(duì)象個(gè)數(shù)可以使用函數(shù):
label, num_features = scipy.ndimage.measurements.label(input, structure=None, output=None) 參數(shù) input: 數(shù)組類型,其中元素非0值表示對(duì)象組成的點(diǎn),0表示圖像背景 structure: 結(jié)構(gòu)元素,用于檢測(cè)對(duì)象的連通特征,默認(rèn)是3×3十字形返回值 label: 返回與input一樣的大小,但是把對(duì)象標(biāo)記出來(lái) num_features:對(duì)象的個(gè)數(shù)用法簡(jiǎn)單示例:
>>> a = np.array([[0,0,1,1,0,0], ... [0,0,0,1,0,0], ... [1,1,0,0,1,0], ... [0,0,0,1,0,0]]) >>> labeled_array, num_features = measurements.label(a) #使用默認(rèn)3×3十字形結(jié)構(gòu)元素 >>> print(num_features) 4 >>> print(labeled_array) #打印被識(shí)別出來(lái)的對(duì)象的位置,分別用1,2,3...遞增的下標(biāo)標(biāo)記出來(lái),所以labeled_array可以當(dāng)成灰度圖打印出來(lái),被標(biāo)識(shí)的對(duì)象的灰度從黑到白變化 array([[0, 0, 1, 1, 0, 0],[0, 0, 0, 1, 0, 0],[2, 2, 0, 0, 3, 0],[0, 0, 0, 4, 0, 0]])從上面例子看出,使用默認(rèn)3×3十字形結(jié)構(gòu)元素,檢測(cè)時(shí),只有水平和垂直連通才認(rèn)為像素屬于同一個(gè)對(duì)象,對(duì)角連通不算,如果要把對(duì)角連通當(dāng)作是同一個(gè)對(duì)象來(lái)計(jì)算,可以指定結(jié)構(gòu)元素為:
[[1,1,1],[1,1,1],[1,1,1]]有時(shí)候,因受噪聲影響,對(duì)象之間有一點(diǎn)邊角的粘連,人眼可以很容易分辨出是兩個(gè)對(duì)象,但要讓label函數(shù)理解這一點(diǎn),可以使用前面提到的開(kāi)運(yùn)算先對(duì)把對(duì)象稍微分開(kāi),再把結(jié)果傳給label函數(shù)進(jìn)行計(jì)數(shù),下面給出一個(gè)具體的圖像進(jìn)行示例:
from PIL import Image import numpy as np from scipy.ndimage import measurements,morphology import matplotlib.pyplot as pltim = np.array(Image.open('house.png').convert('L')) im = 1 * (im < 128) #把灰度圖像轉(zhuǎn)為二值圖,即灰度少于128的當(dāng)成圖像黑點(diǎn),否則當(dāng)作背景label_from_origin, num_from_origin = measurements.label(im)im_open = morphology.binary_opening(im, np.ones((9, 5)), iterations=2) #運(yùn)用了一個(gè)9×5全1的結(jié)構(gòu)元素,并連續(xù)應(yīng)用兩次開(kāi)運(yùn)算 label_from_open, num_from_open = measurements.label(im_open)#以下是畫(huà)圖 index = 221 plt.subplot(index) plt.imshow(im) plt.title('original') plt.axis('off')plt.subplot(index + 1) plt.imshow(label_from_origin) plt.title('%d objects' % num_from_origin) plt.axis('off')plt.subplot(index + 2) plt.imshow(im_open) plt.title('apply opening') plt.axis('off')plt.subplot(index + 3) plt.imshow(label_from_open) plt.title('%d objects' % num_from_open) plt.axis('off')#plt.gray() #為了更好的看出對(duì)象的分離,故意不用灰度顯示 plt.show() 效果圖如下,第二組(即第二行)是應(yīng)用開(kāi)運(yùn)算之后的圖像及計(jì)算結(jié)果,跟第一組相比,對(duì)象計(jì)數(shù)增加了,我在第二組圖中圈出了應(yīng)用開(kāi)運(yùn)算之后的主要變化之處:
小結(jié)
上面介紹的用于二值圖的一些函數(shù),也有其對(duì)應(yīng)的用于灰度圖像的函數(shù),包括:
-
grey_erosion()
-
grey_dilation()
-
grey_opening()
-
grey_closing()
下一節(jié)學(xué)習(xí)圖像去噪。
你還可以查看其它筆記。
總結(jié)
以上是生活随笔為你收集整理的Programming Computer Vision with Python (学习笔记七)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Programming Computer
- 下一篇: Programming Computer