Interactive Foreground Extraction using GrabCut Algorithm:
目標(biāo)
在本節(jié)中我們將要學(xué)習(xí):
? GrabCut 算法原理,使用 GrabCut 算法提取圖像的前景
? 創(chuàng)建一個(gè)交互是程序完成前景提取?
原理
GrabCut 算法是由微軟劍橋研究院的 Carsten_Rother, Vladimir_Kolmogorov和 Andrew_Blake 在文章《GrabCut”: interactive foreground extraction using iterated graph cuts》中共同提出的。此算法在提取前景的操作過程中需要很少的人機(jī)交互,結(jié)果非常好。?
從用戶的角度來看它到底是如何工作的呢?開始時(shí)用戶需要用一個(gè)矩形將前景區(qū)域框?。ㄇ熬皡^(qū)域應(yīng)該完全被包括在矩形框內(nèi)部)。然后算法進(jìn)行迭代式分割直達(dá)達(dá)到最好結(jié)果。但是有時(shí)分割的結(jié)果不夠理想,比如把前景當(dāng)成了背景,或者把背景當(dāng)成了前景。在這種情況下,就需要用戶來進(jìn)行修改了。用戶只需要在不理想的部位畫一筆(點(diǎn)一下鼠標(biāo))就可以了。畫一筆就等于在告訴計(jì)算機(jī):“嗨,老兄,你把這里弄反了,下次迭代的時(shí)候記得改過來呀!”。然后,在下一輪迭代的時(shí)候你就會(huì)得到一個(gè)更好的結(jié)果了。
如下圖所示。運(yùn)動(dòng)員和足球被藍(lán)色矩形包圍在一起。其中有我做的幾個(gè)修正,白色畫筆表明這里是前景,黑色畫筆表明這里是背景。最后我得到了一個(gè)很好的結(jié)果。??
在整個(gè)過程中到底發(fā)生了什么呢?
? 用戶輸入一個(gè)矩形。矩形外的所有區(qū)域肯定都是背景(我們?cè)谇懊嬉呀?jīng)提到,所有的對(duì)象都要包含在矩形框內(nèi))。矩形框內(nèi)的東西是未知的。同樣用戶確定前景和背景的任何操作都不會(huì)被程序改變。
? 計(jì)算機(jī)會(huì)對(duì)我們的輸入圖像做一個(gè)初始化標(biāo)記。它會(huì)標(biāo)記前景和背景像素。?
? 使用一個(gè)高斯混合模型(GMM)對(duì)前景和背景建模。
? 根據(jù)我們的輸入, GMM 會(huì)學(xué)習(xí)并創(chuàng)建新的像素分布。對(duì)那些分類未知的像素(可能是前景也可能是背景),可以根據(jù)它們與已知分類(如背景)的像素的關(guān)系來進(jìn)行分類(就像是在做聚類操作)。
? 這樣就會(huì)根據(jù)像素的分布創(chuàng)建一副圖。圖中的節(jié)點(diǎn)就是像素點(diǎn)。除了像素點(diǎn)做節(jié)點(diǎn)之外還有兩個(gè)節(jié)點(diǎn): Source_node 和 Sink_node。所有的前景像素都和 Source_node 相連。所有的背景像素都和 Sink_node 相連。?
? 將像素連接到 Source_node/end_node 的邊的權(quán)值由像素作為前景/背景的概率來定義。像素之間的權(quán)重由邊緣信息或像素相似度定義。如果像素顏色有較大的差異,它們之間的邊緣會(huì)得到較低的權(quán)重。
? 然后利用mincut算法對(duì)圖像進(jìn)行分割。利用最小代價(jià)函數(shù)將圖分割為兩個(gè)分離的 Source_node 和 Sink_node。代價(jià)函數(shù)是所有被切割邊的權(quán)值之和。切割后,所有連接到 Source_node 的像素被認(rèn)為是前景,所有連接到 Sink_node 的像素被認(rèn)為是背景。
? 繼續(xù)這個(gè)過程直到分類收斂。?
下圖演示了這個(gè)過程(Image Courtesy: http://www.cs.ru.ac.za/research/g02m1682/):?
示例
現(xiàn)在我們進(jìn)入 OpenCV 中的 grabcut 算法。 OpenCV 提供了函數(shù): cv2.grabCut() 。我們來先看看它的參數(shù):
? img - 輸入圖像
? mask-掩模圖像,用來確定那些區(qū)域是背景,前景,可能是前景/背景等。可以設(shè)置為:cv2.GC_BGD,cv2.GC_FGD,cv2.GC_PR_BGD,cv2.GC_PR_FGD,或者直接輸入 0,1,2,3 也行。
? rect - 包含前景的矩形,格式為 (x,y,w,h)
? bdgModel, fgdModel - 算法內(nèi)部使用的數(shù)組. 你只需要?jiǎng)?chuàng)建兩個(gè)大小為 (1,65),數(shù)據(jù)類型為 np.float64 的數(shù)組。
? iterCount - 算法的迭代次數(shù)
? mode 可以設(shè)置為 cv2.GC_INIT_WITH_RECT 或 cv2.GC_INIT_WITH_MASK,也可以聯(lián)合使用。這是用來確定我們進(jìn)行修改的方式,矩形模式或者掩模模式。
首先,我們來看使用矩形模式。加載圖片,創(chuàng)建掩模圖像,構(gòu)建 bdgModel和 fgdModel。傳入矩形參數(shù)。都是這么直接。讓算法迭代 5 次。由于我們?cè)谑褂镁匦文J剿孕薷哪J皆O(shè)置為 cv2.GC_INIT_WITH_RECT。運(yùn)行g(shù)rabcut。算法會(huì)修改掩模圖像,在新的掩模圖像中,所有的像素被分為四類:背景,前景,可能是背景/前景使用 4 個(gè)不同的標(biāo)簽標(biāo)記(前面參數(shù)中提到過)。然后我們來修改掩模圖像,所有的 0 像素和 2?像素都被歸為 0(背景),所有的 1 像素和 3 像素都被歸為 1(前景)。我們最終的掩模圖像就這樣準(zhǔn)備好了。用它和輸入圖像相乘就得到了分割好的圖像。?
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('messi5.jpg')
mask = np.zeros(img.shape[:2],np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,65),np.float64)
rect = (50,50,450,290)
# 函數(shù)的返回值是更新的 mask, bgdModel, fgdModel
cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask2[:,:,np.newaxis]
plt.imshow(img),plt.colorbar(),plt.show()
?結(jié)果如下:
哎呀,梅西的頭發(fā)被我們弄沒了!讓我們來幫他找回頭發(fā)。所以我們要在那里畫一筆(設(shè)置像素為 1,肯定是前景)。同時(shí)還有一些我們并不需要的草地。我們需要去除它們,我們?cè)僭谶@個(gè)區(qū)域畫一筆(設(shè)置像素為 0,肯定是背景)。現(xiàn)在可以象前面提到的那樣來修改掩模圖像了。
實(shí)際上我是怎么做的呢?我們使用圖像編輯軟件打開輸入圖像,添加一個(gè)圖層,使用筆刷工具在需要的地方(比如頭發(fā),鞋子,球等)使用白色繪制;使用黑色筆刷在不需要的地方繪制(比如, logo,草地等)。然后將其他地方用灰色填充,保存成新的掩碼圖像。在 OpenCV 中導(dǎo)入這個(gè)掩模圖像,根據(jù)新的掩碼圖像對(duì)原來的掩模圖像進(jìn)行編輯。代碼如下:
# newmask is the mask image I manually labelled
newmask = cv2.imread('newmask.png',0)
# whereever it is marked white (sure foreground), change mask=1
# whereever it is marked black (sure background), change mask=0
mask[newmask == 0] = 0
mask[newmask == 255] = 1
mask, bgdModel, fgdModel = cv2.grabCut(img,mask,None,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK)
mask = np.where((mask==2)|(mask==0),0,1).astype('uint8')
img = img*mask[:,:,np.newaxis]
plt.imshow(img),plt.colorbar(),plt.show()
?結(jié)果如下:
就是這樣。你也可以不使用矩形初始化,直接進(jìn)入掩碼圖像模式。使用 2像素和 3 像素(可能是背景/前景)對(duì)矩形區(qū)域的像素進(jìn)行標(biāo)記。然后象我們?cè)诘诙€(gè)例子中那樣對(duì)肯定是前景的像素標(biāo)記為 1 像素。然后直接在掩模圖像模式下使用 grabCut 函數(shù)。
OpenCV 自帶的示例中有一個(gè)使用 grabcut 算法的交互式工具 grabcut.py。
參考資料:
1.?OpenCV官方文檔-Interactive Foreground Extraction using GrabCut Algorithm
2.《OpenCV-Python 中文教程》(段力輝 譯)
?
?
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
