您好,登錄后才能下訂單哦!
小編給大家分享一下OpenCV-Python怎么使用分水嶺算法實現(xiàn)圖像分割與提取功能,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
隨著當(dāng)今世界的發(fā)展,計算機視覺技術(shù)的應(yīng)用越來越廣泛。伴隨著硬件設(shè)備的不斷升級,構(gòu)造復(fù)雜的計算機視覺應(yīng)用變得越來越容易了。OpenCV像是一個黑盒,讓我們專注于視覺應(yīng)用的開發(fā),而不必過多的關(guān)注基礎(chǔ)圖象處理的具體細(xì)節(jié)。
了解分水嶺算法之前,我們需要了解什么是圖像的分割。
在圖像的處理過程中,經(jīng)常需要從圖像中將前景對象作為目標(biāo)圖像分割或者提取出來。例如,在視頻監(jiān)控中,觀測到的是固定背景下的視頻內(nèi)容,而我們對背景本身并無興趣,感興趣的是背景中出現(xiàn)的車輛,行人或者其他對象。我們希望將這些對象從視頻中提取出來,而忽略那些沒有對象進入背景的視頻內(nèi)容。
圖像分割是圖像處理過程中一種非常重要的操作。分水嶺算法將圖像形象地比喻為地理學(xué)上的地形表面,實現(xiàn)圖像分割,該算法非常有用。
下面,博主對分水嶺算法的相關(guān)內(nèi)容做簡單的介紹。(詳細(xì)可以參考岡薩雷斯的《數(shù)字圖像處理》一書)
任何一副灰度圖像,都可以被看作是地理學(xué)上的地形表面,灰度值越高的區(qū)域可以被看成是山峰,灰度值越低的區(qū)域可以被看成是山谷。
如果我們向每個山谷中灌注不同顏色的水。那么隨著水位的不斷升高,不同山谷的水就匯聚到一起。在這個過程中,為了防止不同山谷的水交匯,我們需要在水流可能匯合的地方構(gòu)建堤壩。該過程將圖像分為兩個不同的集合:集水盆地和分水嶺線。我們構(gòu)建的堤壩就是分水嶺線,也即對原始圖像的分割。這就是分水嶺算法的原理。
不過,一般的圖像都存在著噪聲,采用分水嶺算法時,會經(jīng)常得到過度分割的結(jié)果。為了改善圖像分割的效果,人們提出了基于掩摸的改進的分水嶺算法。改進的分水嶺算法允許用戶將它認(rèn)為是同一個分割區(qū)域的部分標(biāo)注出來。這樣,分水嶺算法在處理時,就會將標(biāo)注的部分處理為同一個分割區(qū)域。
如果對于該理論不怎么了解,可以使用軟件PowerPoint中的“刪除背景”功能進行觀察配合理解。
在OpenCV中,可以使用函數(shù)cv2.watershed()函數(shù)實現(xiàn)分水嶺算法。不過,具體實現(xiàn)的過程,還需要借助形態(tài)學(xué)函數(shù),距離變換函數(shù)cv2.distanceTransform(),cv2.connectedComponents()來完成圖像分割。
在使用分水嶺算法之前,我們需要對圖像進行簡單的形態(tài)學(xué)處理。一般情況下,我們都是使用形態(tài)學(xué)中的開運算,因為開運算是先腐蝕后膨脹的操作,能夠去除圖像內(nèi)的噪聲。
import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread("36.jpg") k=np.ones((5,5),dtype=np.uint8) e=cv2.erode(img,k) result=cv2.subtract(img,e) plt.subplot(131) plt.imshow(img, cmap="gray") plt.axis('off') plt.subplot(132) plt.imshow(e, cmap="gray") plt.axis('off') plt.subplot(133) plt.imshow(result, cmap="gray") plt.axis('off') plt.show()
回顧一下,我們前面的開運算函數(shù)為cv2.erode(),這里我們首先經(jīng)過開運算去除噪聲。然后減法運算cv2.subtract()獲取圖像邊界。運行之后,效果如下:
當(dāng)圖像內(nèi)的各個子圖沒有連接時,可以直接使用形態(tài)學(xué)的腐蝕操作確定前景對象,但是如果圖像內(nèi)的子圖連接在一起時,就很難確定前景對象了。這個時候,就需要借助變換函數(shù)cv2.distanceTransform()方便地將前景對象提取出來。
cv2.distanceTransform()反應(yīng)了各個像素點與背景(值為0的像素點)的距離關(guān)系。通常情況下:
如果前景對象的中心距離值為0的像素點距離較遠,會得到一個較大的值。
如果前景對象的邊緣距離值為0的像素點較近,會得到一個較小的值。
下面,我們來使用該函數(shù)確定一副圖像的前景,并觀察效果。
import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread("36.jpg") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) k = np.ones((5, 5), dtype=np.uint8) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, k, iterations=2) distTransform = cv2.distanceTransform(opening, cv2.DIST_L2, 5) ret, fore = cv2.threshold(distTransform, 0.7 * distTransform.max(), 255, 0) plt.subplot(131) plt.imshow(img, cmap="gray") plt.axis('off') plt.subplot(132) plt.imshow(distTransform, cmap="gray") plt.axis('off') plt.subplot(133) plt.imshow(fore, cmap="gray") plt.axis('off') plt.show()
這里,我們使用cv2.morphologyEx函數(shù)進行開運算,同時使用cv2.distanceTransform得到距離圖像,最后在通過cv2.threshold對距離圖像進行閾值處理,確定前景。運行之后,效果如下:
通過距離函數(shù),我們獲取到了圖像的“中心”,也就是“確定前景”。為了方便后續(xù)的講解,我們將確定前景稱為F。
圖像中有了確定前景F和確定背景B,剩下的區(qū)域就是未知區(qū)域UN了。這部分區(qū)域正是分水嶺算法要進一步明確的區(qū)域。
針對一副圖像0,通過以下關(guān)系能夠得到未知區(qū)域UN:
未知區(qū)域UN=圖像0-確定背景B-確定前景F
由上述公式變換得到:
未知區(qū)域UN=(圖像0-確定背景B)-確定前景F
其中(圖像0-確定背景B)就是我們開始的減法操作,通過形態(tài)學(xué)膨脹得到。也只需要將上面的代碼添加4行并更改顯示的代碼內(nèi)容:
bg=cv2.dilate(opening,k,iterations=3) fore=np.uint8(fore) un=cv2.subtract(bg,fore) plt.subplot(221) plt.imshow(img, cmap="gray") plt.axis('off') plt.subplot(222) plt.imshow(bg, cmap="gray") plt.axis('off') plt.subplot(223) plt.imshow(fore, cmap="gray") plt.axis('off') plt.subplot(224) plt.imshow(un, cmap="gray") plt.axis('off') plt.show()
運行之后,效果如下:
左上為原圖
右上為原圖膨脹后得到的圖像bg,其背景圖像是確定背景B。前景圖像是“原始圖像0-確定背景B”
左下為確定前景圖像fore
右下為未知區(qū)域圖像UN
明確了確定前景后,就可以對確定前景進行標(biāo)注了。在OpenCV中,它提供了cv2.ConnectedComponents()函數(shù)進行標(biāo)注。
該函數(shù)會將背景標(biāo)注為0,將其他的對象使用從1開始的正整數(shù)標(biāo)注。它只有一個參數(shù)8位單通道的待標(biāo)注圖像。
返回值有兩個:retval為返回的標(biāo)注數(shù)量,labels為標(biāo)注的結(jié)果圖像。
下面,我們來使用該函數(shù)進行標(biāo)注。代碼如下(同樣更改上面bg下面代碼就行):
bg = cv2.dilate(opening, k, iterations=3) fore = np.uint8(fore) ret, markets = cv2.connectedComponents(fore) unknown=cv2.subtract(bg,fore) markets=markets+1 markets[unknown==255]=0 plt.subplot(131) plt.imshow(img, cmap="gray") plt.axis('off') plt.subplot(132) plt.imshow(fore, cmap="gray") plt.axis('off') plt.subplot(133) plt.imshow(markets, cmap="gray") plt.axis('off') plt.show()
修改上面fore = np.uint8(fore)的代碼,并修改輸出內(nèi)容。運行之后,我們會得到原圖,前景圖像的中心點圖像fore以及標(biāo)注后的結(jié)果圖像markets。效果如下:
經(jīng)過前文的介紹,我們了解了使用分水嶺算法進行圖像分割的基本步驟:
通過形態(tài)學(xué)開運算對原始圖像0進行去噪
通過腐蝕操作獲取“確定背景B”。需要注意,這里得到“原始圖像-確定背景”即可
利用距離變換函數(shù)對原始圖像進行運算,并對其進行閾值處理,得到“確定前景F”
計算未知區(qū)域UN(UN=0-B-F)
利用函數(shù)cv2.connectedComponents()對原始圖像0進行標(biāo)注
對函數(shù)cv2.connectedComponents()的標(biāo)注結(jié)果進行修正
使用分水嶺函數(shù)完成圖像分割
完整代碼如下:
import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread("36.jpg") plt.subplot(121) plt.imshow(img, cmap="gray") plt.axis('off') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) k = np.ones((5, 5), dtype=np.uint8) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, k, iterations=2) distTransform = cv2.distanceTransform(opening, cv2.DIST_L2, 5) ret, fore = cv2.threshold(distTransform, 0.2 * distTransform.max(), 255, 0) bg = cv2.dilate(opening, k, iterations=3) fore = np.uint8(fore) ret, markets = cv2.connectedComponents(fore) unknown = cv2.subtract(bg, fore) markets = markets + 1 markets[unknown == 255] = 0 markets = cv2.watershed(img, markets) img[markets == -1] = [255, 0, 0] plt.subplot(122) plt.imshow(img, cmap="gray") plt.axis('off') plt.show()
運行之后,我們就可以得到分割的圖像:
當(dāng)然,參數(shù)可以調(diào)整,可以看到大致的硬幣被完整的分割出來了。
以上是“OpenCV-Python怎么使用分水嶺算法實現(xiàn)圖像分割與提取功能”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。