您好,登錄后才能下訂單哦!
本文小編為大家詳細(xì)介紹“Python中怎么使用OpenCV庫(kù)對(duì)圖像進(jìn)行分割和提取”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“Python中怎么使用OpenCV庫(kù)對(duì)圖像進(jìn)行分割和提取”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。
圖像中將前景對(duì)象作為目標(biāo)圖像分割或者提取出來(lái)。對(duì)背景本身并無(wú)興趣分水嶺算法及GrabCut算法對(duì)圖像進(jìn)行分割及提取。
分水嶺算法將圖像形象地比喻為地理學(xué)上的地形表面,實(shí)現(xiàn)圖像分割,該算法非常有效。
任何一幅灰度圖像,都可以被看作是地理學(xué)上的地形表面,灰度值高的區(qū)域可以被看成是山峰,灰度值低的區(qū)域可以被看成是山谷。
左圖是原始圖像,右圖是其對(duì)應(yīng)的“地形表面”。
該過(guò)程將圖像分成兩個(gè)不同的集合:集水盆地和分水嶺線。我們構(gòu)建的堤壩就是分水嶺線,也即對(duì)原始圖像的分割。這就是分水嶺算法。
由于噪聲等因素的影響,采用上述基礎(chǔ)分水嶺算法經(jīng)常會(huì)得到過(guò)度分割的結(jié)果。過(guò)度分割會(huì)將圖像劃分為一個(gè)個(gè)稠密的獨(dú)立小塊,讓分割失去了意義。為了改善圖像分割效果,人們提出了基于掩模的改進(jìn)的分水嶺算法。改進(jìn)的分水嶺算法允許用戶將他認(rèn)為是同一個(gè)分割區(qū)域的部分標(biāo)注出來(lái)(被標(biāo)注的部分就稱為掩模)。分水嶺算法在處理時(shí),就會(huì)將標(biāo)注的部分處理為同一個(gè)分割區(qū)域。
例如:
原始圖像,對(duì)其做標(biāo)注處理,其中被標(biāo)注為深色的三個(gè)小色塊表示,在使用掩模分水嶺算法時(shí),這些部分所包含的顏色都會(huì)被分割在同一個(gè)區(qū)域內(nèi)。
在OpenCV中,可以使用函數(shù)cv2.watershed()實(shí)現(xiàn)分水嶺算法。
在具體的實(shí)現(xiàn)過(guò)程中,還需要借助于形態(tài)學(xué)函數(shù)、距離變換函數(shù)cv2.distanceTransform()、cv2.connectedComponents()來(lái)完成圖像分割。
形態(tài)學(xué)函數(shù)
在使用分水嶺算法對(duì)圖像進(jìn)行分割前,需要對(duì)圖像進(jìn)行簡(jiǎn)單的形態(tài)學(xué)處理。
開運(yùn)算
開運(yùn)算是先腐蝕、后膨脹的操作,開運(yùn)算能夠去除圖像內(nèi)的噪聲
在用分水嶺算法處理圖像前,要先使用開運(yùn)算去除圖像內(nèi)的噪聲,以避免噪聲對(duì)圖像分割可能造成的干擾。
獲取圖像邊界
通過(guò)形態(tài)學(xué)操作和減法運(yùn)算能夠獲取圖像的邊界。
使用形態(tài)學(xué)變換,獲取一幅圖像的邊界信息
import cv2 import numpy as np import matplotlib.pyplot as plt o=cv2.imread("my.bmp", cv2.IMREAD_UNCHANGED) k=np.ones((5,5), np.uint8) e=cv2.erode(o, k) b=cv2.subtract(o, e) plt.subplot(131) plt.imshow(o) plt.axis('off') plt.subplot(132) plt.imshow(e) plt.axis('off') plt.subplot(133) plt.imshow(b) plt.axis('off') plt.show()
使用形態(tài)學(xué)操作和減法運(yùn)算能夠獲取圖像的邊界信息。但是,形態(tài)學(xué)操作僅適用于比較簡(jiǎn)單的圖像。如果圖像內(nèi)的前景對(duì)象存在連接的情況,使用形態(tài)學(xué)操作就無(wú)法準(zhǔn)確獲取各個(gè)子圖像的邊界了。
距離變換函數(shù)distanceTransform
當(dāng)圖像內(nèi)的各個(gè)子圖沒(méi)有連接時(shí),可以直接使用形態(tài)學(xué)的腐蝕操作確定前景對(duì)象,但是如果圖像內(nèi)的子圖連接在一起時(shí),就很難確定前景對(duì)象了
此時(shí),借助于距離變換函數(shù)cv2.distanceTransform()可以方便地將前景對(duì)象提取出來(lái)。
函數(shù)cv2.distanceTransform()計(jì)算二值圖像內(nèi)任意點(diǎn)到最近背景點(diǎn)的距離。
一般情況下,該函數(shù)計(jì)算的是圖像內(nèi)非零值像素點(diǎn)到最近的零值像素點(diǎn)的距離,即計(jì)算二值圖像中所有像素點(diǎn)距離其最近的值為0的像素點(diǎn)的距離。
如果像素點(diǎn)本身的值為0,則這個(gè)距離也為0。
cv2.distanceTransform()的計(jì)算結(jié)果反映了各個(gè)像素與背景(值為0的像素點(diǎn))的距離關(guān)系。
通常情況下:
如果前景對(duì)象的中心(質(zhì)心)距離值為0的像素點(diǎn)距離較遠(yuǎn),會(huì)得到一個(gè)較大的值。
如果前景對(duì)象的邊緣距離值為0的像素點(diǎn)較近,會(huì)得到一個(gè)較小的值。
如果對(duì)上述計(jì)算結(jié)果進(jìn)行閾值化,就可以得到圖像內(nèi)子圖的中心、骨架等信息。距離變換函數(shù)cv2.distanceTransform()可以用于計(jì)算對(duì)象的中心,還能細(xì)化輪廓、獲取圖像前景等
函數(shù)cv2.distanceTransform()的語(yǔ)法格式為:
dst=cv2.distanceTransform(src, distanceType, maskSize[, dstType]])
src是8位單通道的二值圖像。
distanceType為距離類型參數(shù)
maskSize為掩模的尺寸
distanceType=cv2.DIST_L1或cv2.DIST_C時(shí),maskSize強(qiáng)制為3(因?yàn)樵O(shè)置為3和設(shè)置為5及更大值沒(méi)有什么區(qū)別)。
dstType為目標(biāo)圖像的類型,默認(rèn)值為CV_32F。
dst表示計(jì)算得到的目標(biāo)圖像,可以是8位或32位浮點(diǎn)數(shù),尺寸和src相同。
使用距離變換函數(shù)cv2.distanceTransform(),計(jì)算一幅圖像的確定前景
import numpy as np import cv2 import matplotlib.pyplot as plt img = cv2.imread('water_coins.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img=cv2.cvtColor(img, cv2.COLOR_BGR2RGB) ishow=img.copy() ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) kernel = np.ones((3,3), np.uint8) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations = 2) # 進(jìn)行開運(yùn)算 dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2,5) ret, fore = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0) plt.subplot(131) plt.imshow(ishow) plt.axis('off') plt.subplot(132) plt.imshow(dist_transform) plt.axis('off') plt.subplot(133) plt.imshow(fore) plt.axis('off') plt.show()
fore圖像中: 比較準(zhǔn)確地顯示出左圖內(nèi)的“確定前景”。確定前景,通常是指前景對(duì)象的中心。之所以認(rèn)為這些點(diǎn)是確定前景,是因?yàn)樗鼈兙嚯x背景點(diǎn)的距離足夠遠(yuǎn),都是距離大于足夠大的固定閾值(0.7*dist_transform.max())的點(diǎn)。
確定未知區(qū)域
使用形態(tài)學(xué)的膨脹操作能夠?qū)D像內(nèi)的前景“膨脹放大”。
當(dāng)圖像內(nèi)的前景被放大后,背景就會(huì)被“壓縮”,所以此時(shí)得到的背景信息一定小于實(shí)際背景的,不包含前景的“確定背景”。
為了方便說(shuō)明將確定背景稱為B。
距離變換函數(shù)cv2.distanceTransform()能夠獲取圖像的“中心”,得到“確定前景”。
圖像中有了確定前景F和確定背景B,剩下區(qū)域的就是未知區(qū)域UN了。這部分區(qū)域正是分水嶺算法要進(jìn)一步明確的區(qū)域。
針對(duì)一幅圖像O,通過(guò)以下關(guān)系能夠得到未知區(qū)域UN:
未知區(qū)域UN=圖像O-確定背景B-確定前景F
未知區(qū)域UN=(圖像O-確定背景B)- 確定前景F
“圖像O-確定背景B”,可以通過(guò)對(duì)圖像進(jìn)行形態(tài)學(xué)的膨脹操作得到。
標(biāo)注一幅圖像的確定前景、確定背景及未知區(qū)域。
import numpy as np import cv2 import matplotlib.pyplot as plt img = cv2.imread('water_coins.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img=cv2.cvtColor(img, cv2.COLOR_BGR2RGB) ishow=img.copy() ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) kernel = np.ones((3,3), np.uint8) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations = 2) bg = cv2.dilate(opening, kernel, iterations=3) dist = cv2.distanceTransform(opening, cv2.DIST_L2,5) ret, fore = cv2.threshold(dist,0.7*dist.max(),255,0) fore = np.uint8(fore) un = cv2.subtract(bg, fore) plt.subplot(221) plt.imshow(ishow) plt.axis('off') plt.subplot(222) plt.imshow(bg) plt.axis('off') plt.subplot(223) plt.imshow(fore) plt.axis('off') plt.subplot(224) plt.imshow(un) plt.axis('off') plt.show()
函數(shù)connectedComponents
明確了確定前景后,就可以對(duì)確定前景圖像進(jìn)行標(biāo)注了。
在OpenCV中,可以使用函數(shù)cv2.connectedComponents()進(jìn)行標(biāo)注。該函數(shù)會(huì)將背景標(biāo)注為0,將其他的對(duì)象使用從1開始的正整數(shù)標(biāo)注。
函數(shù)cv2.connectedComponents()的語(yǔ)法格式為:
retval, labels = cv2.connectedComponents( image )
image為8位單通道的待標(biāo)注圖像。
retval為返回的標(biāo)注的數(shù)量。
labels為標(biāo)注的結(jié)果圖像。
使用函數(shù)cv2.connectedComponents()標(biāo)注一幅圖像
import numpy as np import cv2 import matplotlib.pyplot as plt img = cv2.imread('water_coins.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img=cv2.cvtColor(img, cv2.COLOR_BGR2RGB) ishow=img.copy() ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) kernel = np.ones((3,3), np.uint8) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations = 2) sure_bg = cv2.dilate(opening, kernel, iterations=3) dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2,5) ret, fore = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0) fore = np.uint8(fore) ret, markers = cv2.connectedComponents(fore) print(ret) plt.subplot(131) plt.imshow(ishow) plt.axis('off') plt.subplot(132) plt.imshow(fore) plt.axis('off') plt.subplot(133) plt.imshow(markers) plt.axis('off') plt.show()
前景圖像的中心點(diǎn)被做了不同的標(biāo)注(用不同顏色區(qū)分)
函數(shù)cv2.connectedComponents()在標(biāo)注圖像時(shí),會(huì)將背景標(biāo)注為0,將其他的對(duì)象用從1開始的正整數(shù)標(biāo)注。具體的對(duì)應(yīng)關(guān)系為:
數(shù)值0代表背景區(qū)域。
從數(shù)值1開始的值,代表不同的前景區(qū)域。
在分水嶺算法中,標(biāo)注值0代表未知區(qū)域。所以,我們要對(duì)函數(shù)cv2.connectedComponents()標(biāo)注的結(jié)果進(jìn)行調(diào)整:將標(biāo)注的結(jié)果都加上數(shù)值1。經(jīng)過(guò)上述處理后,在標(biāo)注結(jié)果中:
數(shù)值1代表背景區(qū)域。
從數(shù)值2開始的值,代表不同的前景區(qū)域。
為了能夠使用分水嶺算法,還需要對(duì)原始圖像內(nèi)的未知區(qū)域進(jìn)行標(biāo)注,將已經(jīng)計(jì)算出來(lái)的未知區(qū)域標(biāo)注為0即可。
關(guān)鍵代碼:
ret, markers = cv2.connectedComponents(fore) markers = markers+1 markers[未知區(qū)域] = 0
使用函數(shù)cv2.connectedComponents()標(biāo)注一幅圖像,并對(duì)其進(jìn)行修正,使未知區(qū)域被標(biāo)注為0
import numpy as np import cv2 import matplotlib.pyplot as plt img = cv2.imread('water_coins.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img=cv2.cvtColor(img, cv2.COLOR_BGR2RGB) ishow=img.copy() ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) kernel = np.ones((3,3), np.uint8) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations = 2) sure_bg = cv2.dilate(opening, kernel, iterations=3) dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2,5) ret, fore = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0) fore = np.uint8(fore) ret, markers1 = cv2.connectedComponents(fore) foreAdv=fore.copy() unknown = cv2.subtract(sure_bg, foreAdv) ret, markers2 = cv2.connectedComponents(foreAdv) markers2 = markers2+1 markers2[unknown==255] = 0 plt.subplot(121) plt.imshow(markers1) plt.axis('off') plt.subplot(122) plt.imshow(markers2) plt.axis('off') plt.show()
前景都有一個(gè)黑色的邊緣,這個(gè)邊緣是被標(biāo)注的未知區(qū)域。
函數(shù)cv2.watershed()
完成上述處理后,就可以使用分水嶺算法對(duì)預(yù)處理結(jié)果圖像進(jìn)行分割了。
在OpenCV中,實(shí)現(xiàn)分水嶺算法的函數(shù)是cv2.watershed(),其語(yǔ)法格式為:
markers = cv2.watershed( image, markers )
image是輸入圖像,必須是8位三通道的圖像。在對(duì)圖像使用
cv2.watershed()函數(shù)處理之前,必須先用正數(shù)大致勾畫出圖像中的期望分割區(qū)域。每一個(gè)分割的區(qū)域會(huì)被標(biāo)注為1、2、3等。對(duì)于尚未確定的區(qū)域,需要將它們標(biāo)注為0。我們可以將標(biāo)注區(qū)域理解為進(jìn)行分水嶺算法分割的“種子”區(qū)域。
markers是32位單通道的標(biāo)注結(jié)果,它應(yīng)該和image具有相等大小。在markers中,每一個(gè)像素要么被設(shè)置為初期的“種子值”,要么被設(shè)置為**“-1”表示邊界**。
使用分水嶺算法進(jìn)行圖像分割時(shí),基本的步驟為:
通過(guò)形態(tài)學(xué)開運(yùn)算對(duì)原始圖像O去噪。
通過(guò)腐蝕操作獲取“確定背景B”。
需要注意,這里得到“原始圖像-確定背景”即可。
利用距離變換函數(shù)cv2.distanceTransform()對(duì)原始圖像進(jìn)行運(yùn)算,并對(duì)其進(jìn)行閾值處理,得到“確定前景F”。
計(jì)算未知區(qū)域UN(UN=O -B - F)
利用函數(shù)cv2.connectedComponents()對(duì)原始圖像O進(jìn)行標(biāo)注。
對(duì)函數(shù)cv2.connectedComponents()的標(biāo)注結(jié)果進(jìn)行修正。
使用分水嶺函數(shù)完成對(duì)圖像的分割。
使用分水嶺算法對(duì)一幅圖像進(jìn)行分割:
import numpy as np import cv2 import matplotlib.pyplot as plt img = cv2.imread('water_coins.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img=cv2.cvtColor(img, cv2.COLOR_BGR2RGB) ishow=img.copy() ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) kernel = np.ones((3,3), np.uint8) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations = 2) sure_bg = cv2.dilate(opening, kernel, iterations=3) dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2,5) ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0) sure_fg = np.uint8(sure_fg) unknown = cv2.subtract(sure_bg, sure_fg) ret, markers = cv2.connectedComponents(sure_fg) markers = markers+1 markers[unknown==255] = 0 markers = cv2.watershed(img, markers) img[markers == -1] = [0,255,0] # 邊界 plt.subplot(121) plt.imshow(ishow) plt.axis('off') plt.subplot(122) plt.imshow(img) plt.axis('off') plt.show()
經(jīng)典的前景提取技術(shù)主要使用紋理(顏色)信息,如魔術(shù)棒工具,或根據(jù)邊緣(對(duì)比度)信息,如智能剪刀等。在開始提取前景時(shí),先用一個(gè)矩形框指定前景區(qū)域所在的大致位置范圍,然后不斷迭代地分割,直到達(dá)到最好的效果。經(jīng)過(guò)上述處理后,提取前景的效果可能并不理想,存在前景沒(méi)有提取出來(lái),或者將背景提取為前景的情況,此時(shí)需要用戶干預(yù)提取過(guò)程。
用戶在原始圖像的副本中(也可以是與原始圖像大小相等的任意一幅圖像),用白色標(biāo)注要提取為前景的區(qū)域,用黑色標(biāo)注要作為背景的區(qū)域。然后,將標(biāo)注后的圖像作為掩模,讓算法繼續(xù)迭代提取前景從而得到最終結(jié)果。
GrabCut算法的具體實(shí)施過(guò)程。
將前景所在的大致位置使用矩形框標(biāo)注出來(lái)。
此時(shí)矩形框框出的僅僅是前景的大致位置,其中既包含前景又包含背景,所以該區(qū)域?qū)嶋H上是未確定區(qū)域。但是,該區(qū)域以外的區(qū)域被認(rèn)為是“確定背景”。
根據(jù)矩形框外部的“確定背景”數(shù)據(jù)來(lái)區(qū)分矩形框區(qū)域內(nèi)的前景和背景。
用高斯混合模型(Gaussians Mixture Model, GMM)對(duì)前景和背景建模。
GMM會(huì)根據(jù)用戶的輸入 學(xué)習(xí)并創(chuàng)建新的像素分布。對(duì)未分類的像素(可能是背景也可能是前景),根據(jù)其與已知分類像素(前景和背景)的關(guān)系進(jìn)行分類。
根據(jù)像素分布情況生成一幅圖,圖中的節(jié)點(diǎn)就是各個(gè)像素點(diǎn)。
除了像素點(diǎn)之外,還有兩個(gè)節(jié)點(diǎn):前景節(jié)點(diǎn)和背景節(jié)點(diǎn)。所有的前景像素都和前景節(jié)點(diǎn)相連,所有的背景像素都和背景節(jié)點(diǎn)相連。每個(gè)像素連接到前景節(jié)點(diǎn)或背景節(jié)點(diǎn)的邊的權(quán)重由像素是前景或背景的概率來(lái)決定。
圖中的每個(gè)像素除了與前景節(jié)點(diǎn)或背景節(jié)點(diǎn)相連外,彼此之間還存在著連接。兩個(gè)像素連接的邊的權(quán)重值由它們的相似性決定,兩個(gè)像素的顏色越接近,邊的權(quán)重值越大。
完成節(jié)點(diǎn)連接后,需要解決的問(wèn)題變成了一幅連通的圖。在該圖上根據(jù)各自邊的權(quán)重關(guān)系進(jìn)行切割,將不同的點(diǎn)劃分為前景節(jié)點(diǎn)和背景節(jié)點(diǎn)。
不斷重復(fù)上述過(guò)程,直至分類收斂為止。
在OpenCV中,實(shí)現(xiàn)交互式前景提取的函數(shù)是cv2.grabCut(),其語(yǔ)法格式為:
mask, bgdModel, fgdModel =cv2.grabCut(img, mask, rect, bgdModel, fgdModel, iterCount[, mode] )
img為輸入圖像,要求是8位3通道的。
mask為掩模圖像,要求是8位單通道的。該參數(shù)用于確定前景區(qū)域、背景區(qū)域和不確定區(qū)域,可以設(shè)置為4種形式。
cv2.GC_BGD:表示確定背景,也可以用數(shù)值0表示。
cv2.GC_FGD:表示確定前景,也可以用數(shù)值1表示。
cv2.GC_PR_BGD:表示可能的背景,也可以用數(shù)值2表示。
cv2.GC_PR_FGD:表示可能的前景,也可以用數(shù)值3表示。
在最后使用模板提取前景時(shí),會(huì)將參數(shù)值0和2合并為背景(均當(dāng)作0處理),將參數(shù)值1和3合并為前景(均當(dāng)作1處理)。
在通常情況下,我們可以使用白色筆刷和黑色筆刷在掩模圖像上做標(biāo)記,再通過(guò)轉(zhuǎn)換將其中的白色像素設(shè)置為0,黑色像素設(shè)置為1。
rect指包含前景對(duì)象的區(qū)域,該區(qū)域外的部分被認(rèn)為是“確定背景”。因此,在選取時(shí)務(wù)必確保讓前景包含在rect指定的范圍內(nèi);否則,rect外的前景部分是不會(huì)被提取出來(lái)的。
只有當(dāng)參數(shù)mode的值被設(shè)置為矩形模式cv2.GC_INIT_WITH_RECT時(shí),參數(shù)rect才有意義。
其格式為(x, y, w, h),分別表示區(qū)域左上角像素的x軸和y軸坐標(biāo)以及區(qū)域的寬度和高度。
如果前景位于右下方,又不想判斷原始圖像的大小,對(duì)于w 和h可以直接用一個(gè)很大的值。
使用掩模模式時(shí),將該值設(shè)置為none即可。
bgdModel為算法內(nèi)部使用的數(shù)組,只需要?jiǎng)?chuàng)建大小為(1, 65)的numpy.float64數(shù)組。
fgdModel為算法內(nèi)部使用的數(shù)組,只需要?jiǎng)?chuàng)建大小為(1, 65)的numpy.float64數(shù)組。
iterCount表示迭代的次數(shù)。
mode表示迭代模式。其可能的值與含義如下:
RECT 和MASK可以組合使用( 并的關(guān)系 )
使用GrabCut算法提取圖像的前景
import numpy as np import cv2 import matplotlib.pyplot as plt o = cv2.imread('lenacolor.png') orgb=cv2.cvtColor(o, cv2.COLOR_BGR2RGB) mask = np.zeros(o.shape[:2], np.uint8) bgdModel = np.zeros((1,65), np.float64) fgdModel = np.zeros((1,65), np.float64) rect = (50,50,400,500) cv2.grabCut(o, mask, rect, bgdModel, fgdModel,5, cv2.GC_INIT_WITH_RECT) mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8') ogc = o*mask2[:, :, np.newaxis] ogc=cv2.cvtColor(ogc, cv2.COLOR_BGR2RGB) plt.subplot(121) plt.imshow(orgb) plt.axis('off') plt.subplot(122) plt.imshow(ogc) plt.axis('off') plt.show()
為了得到完整的前景對(duì)象,需要做一些改進(jìn)。
這里對(duì)原始圖像進(jìn)行標(biāo)注,將需要保留的部分設(shè)置為白色,將需要?jiǎng)h除的背景設(shè)置為黑色。以標(biāo)記好的圖像作為模板,使用函數(shù)cv2.grabCut()完成前景的提取。
這個(gè)過(guò)程主要包含以下步驟:
利用函數(shù)cv2.grabCut()在cv2.GC_INIT_WITH_RECT 模式下對(duì)圖像進(jìn)行初步的前景提取,得到初步提取的結(jié)果圖像og。
使用Windows系統(tǒng)自帶的筆刷工具,打開要提取前景的圖像,比如lena。
使用白色筆刷在希望提取的前景區(qū)域做標(biāo)記。
使用黑色筆刷在希望刪除的背景區(qū)域做標(biāo)記。
將當(dāng)前設(shè)置好的lena圖像另存為模板圖像m0。
將模板圖像m0中的白色值和黑色值映射到模板m中。將模板圖像m0中的白色值(像素值為255)映射為模板圖像m中的確定前景(像素值為1),將模板圖像m0中的黑色值(像素值為0)映射為模板圖像m中的確定背景(像素值為0)。
以模板圖像m作為函數(shù)cv2.grabCut()的模板參數(shù)(mask),對(duì)圖像og完成前景提取。
使用畫筆標(biāo)記的模板圖像m0不能直接作為模板(即參數(shù)mask)使用
函數(shù)cv2.grabCut()要求,參數(shù)mask的值必須是cv2.GC_BGD(確定背景)、cv2.GC_FGD(確定前景)、cv2.GC_PR_BGD(可能的背景)、cv2.GC_PR_FGD(可能的前景),或者是0、1、2、3之中的值。
必須先將模板圖像m0中的白色值和黑色值映射到模板m上,再將模板圖像m作為函數(shù)cv2.grabCut()的模板參數(shù)。
在GrabCut算法中使用模板提取圖像的前景:
import numpy as np import cv2 import matplotlib.pyplot as plt o= cv2.imread('lenacolor.png') orgb=cv2.cvtColor(o, cv2.COLOR_BGR2RGB) mask = np.zeros(o.shape[:2], np.uint8) bgd = np.zeros((1,65), np.float64) fgd = np.zeros((1,65), np.float64) rect = (50,50,400,500) cv2.grabCut(o, mask, rect, bgd, fgd,5, cv2.GC_INIT_WITH_RECT) mask2 = cv2.imread('mask.png',0) mask2Show = cv2.imread('mask.png', -1) m2rgb=cv2.cvtColor(mask2Show, cv2.COLOR_BGR2RGB) mask[mask2 == 0] = 0 mask[mask2 == 255] = 1 mask, bgd, fgd = cv2.grabCut(o, mask, None, bgd, fgd,5, cv2.GC_INIT_WITH_MASK) mask = np.where((mask==2)|(mask==0),0,1).astype('uint8') ogc = o*mask[:, :, np.newaxis] ogc=cv2.cvtColor(ogc, cv2.COLOR_BGR2RGB) plt.subplot(121) plt.imshow(m2rgb) plt.axis('off') plt.subplot(122) plt.imshow(ogc) plt.axis('off') plt.show()
在函數(shù)cv2.grabCut()的實(shí)際使用中,也可以不使用矩形初始化,直接使用模板模式。構(gòu)造一個(gè)模板圖像,其中:
使用像素值0標(biāo)注確定背景。
使用像素值1標(biāo)注確定前景。
使用像素值2標(biāo)注可能的背景。
使用像素值3標(biāo)注可能的前景。
構(gòu)造完模板后,直接將該模板用于函數(shù)cv2.grabCut()處理原始圖像,即可完成前景的提取。
一般情況下,自定義模板的步驟為:
先使用numpy.zeros構(gòu)造一個(gè)內(nèi)部像素值都是0(表示確定背景)的圖像mask,以便在后續(xù)步驟中逐步對(duì)該模板圖像進(jìn)行細(xì)化。
.使用mask[30:512, 50:400]=3,將模板圖像中第30行到第512行,第50列到400列的區(qū)域劃分為可能的前景(像素值為3,對(duì)應(yīng)參數(shù)mask的含義為“可能的前景”)。
使用mask[50:300, 150:200]=1,將模板圖像中第50行到第300行,第150列到第200列的區(qū)域劃分為確定前景(像素值為1,對(duì)應(yīng)參數(shù)mask的含義為“確定前景”)。
在GrabCut算法中直接使用自定義模板提取圖像的前景
import numpy as np import cv2 import matplotlib.pyplot as plt o= cv2.imread('lenacolor.png') orgb=cv2.cvtColor(o, cv2.COLOR_BGR2RGB) bgd = np.zeros((1,65), np.float64) fgd = np.zeros((1,65), np.float64) mask2 = np.zeros(o.shape[:2], np.uint8) #先將掩模的值全部構(gòu)造為0(確定背景),在后續(xù)步驟中,再根據(jù)需要修改其中的部分值 mask2[30:512,50:400]=3 #lena頭像的可能區(qū)域 mask2[50:300,150:200]=1 #lena頭像的確定區(qū)域,如果不設(shè)置這個(gè)區(qū)域,頭像的提取不完整 cv2.grabCut(o, mask2, None, bgd, fgd,5, cv2.GC_INIT_WITH_MASK) mask2 = np.where((mask2==2)|(mask2==0),0,1).astype('uint8') ogc = o*mask2[:, :, np.newaxis] ogc=cv2.cvtColor(ogc, cv2.COLOR_BGR2RGB) plt.subplot(121) plt.imshow(orgb) plt.axis('off') plt.subplot(122) plt.imshow(ogc) plt.axis('off') plt.show()
對(duì)于不同的圖像,要構(gòu)造不同的模板來(lái)劃分它們的確定前景、確定背景、可能的前景與可能的背景。
讀到這里,這篇“Python中怎么使用OpenCV庫(kù)對(duì)圖像進(jìn)行分割和提取”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。