Interactive Foreground Extraction using GrabCut Algorithm:
目標
在本節中我們將要學習:
? GrabCut 算法原理,使用 GrabCut 算法提取圖像的前景
? 創建一個交互是程序完成前景提取?
原理
GrabCut 算法是由微軟劍橋研究院的 Carsten_Rother, Vladimir_Kolmogorov和 Andrew_Blake 在文章《GrabCut”: interactive foreground extraction using iterated graph cuts》中共同提出的。此算法在提取前景的操作過程中需要很少的人機交互,結果非常好。?
從用戶的角度來看它到底是如何工作的呢?開始時用戶需要用一個矩形將前景區域框?。ㄇ熬皡^域應該完全被包括在矩形框內部)。然后算法進行迭代式分割直達達到最好結果。但是有時分割的結果不夠理想,比如把前景當成了背景,或者把背景當成了前景。在這種情況下,就需要用戶來進行修改了。用戶只需要在不理想的部位畫一筆(點一下鼠標)就可以了。畫一筆就等于在告訴計算機:“嗨,老兄,你把這里弄反了,下次迭代的時候記得改過來呀!”。然后,在下一輪迭代的時候你就會得到一個更好的結果了。
如下圖所示。運動員和足球被藍色矩形包圍在一起。其中有我做的幾個修正,白色畫筆表明這里是前景,黑色畫筆表明這里是背景。最后我得到了一個很好的結果。??
在整個過程中到底發生了什么呢?
? 用戶輸入一個矩形。矩形外的所有區域肯定都是背景(我們在前面已經提到,所有的對象都要包含在矩形框內)。矩形框內的東西是未知的。同樣用戶確定前景和背景的任何操作都不會被程序改變。
? 計算機會對我們的輸入圖像做一個初始化標記。它會標記前景和背景像素。?
? 使用一個高斯混合模型(GMM)對前景和背景建模。
? 根據我們的輸入, GMM 會學習并創建新的像素分布。對那些分類未知的像素(可能是前景也可能是背景),可以根據它們與已知分類(如背景)的像素的關系來進行分類(就像是在做聚類操作)。
? 這樣就會根據像素的分布創建一副圖。圖中的節點就是像素點。除了像素點做節點之外還有兩個節點: Source_node 和 Sink_node。所有的前景像素都和 Source_node 相連。所有的背景像素都和 Sink_node 相連。?
? 將像素連接到 Source_node/end_node 的邊的權值由像素作為前景/背景的概率來定義。像素之間的權重由邊緣信息或像素相似度定義。如果像素顏色有較大的差異,它們之間的邊緣會得到較低的權重。
? 然后利用mincut算法對圖像進行分割。利用最小代價函數將圖分割為兩個分離的 Source_node 和 Sink_node。代價函數是所有被切割邊的權值之和。切割后,所有連接到 Source_node 的像素被認為是前景,所有連接到 Sink_node 的像素被認為是背景。
? 繼續這個過程直到分類收斂。?
下圖演示了這個過程(Image Courtesy: http://www.cs.ru.ac.za/research/g02m1682/):?
示例
現在我們進入 OpenCV 中的 grabcut 算法。 OpenCV 提供了函數: cv2.grabCut() 。我們來先看看它的參數:
? img - 輸入圖像
? mask-掩模圖像,用來確定那些區域是背景,前景,可能是前景/背景等??梢栽O置為: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 - 算法內部使用的數組. 你只需要創建兩個大小為 (1,65),數據類型為 np.float64 的數組。
? iterCount - 算法的迭代次數
? mode 可以設置為 cv2.GC_INIT_WITH_RECT 或 cv2.GC_INIT_WITH_MASK,也可以聯合使用。這是用來確定我們進行修改的方式,矩形模式或者掩模模式。
首先,我們來看使用矩形模式。加載圖片,創建掩模圖像,構建 bdgModel和 fgdModel。傳入矩形參數。都是這么直接。讓算法迭代 5 次。由于我們在使用矩形模式所以修改模式設置為 cv2.GC_INIT_WITH_RECT。運行grabcut。算法會修改掩模圖像,在新的掩模圖像中,所有的像素被分為四類:背景,前景,可能是背景/前景使用 4 個不同的標簽標記(前面參數中提到過)。然后我們來修改掩模圖像,所有的 0 像素和 2?像素都被歸為 0(背景),所有的 1 像素和 3 像素都被歸為 1(前景)。我們最終的掩模圖像就這樣準備好了。用它和輸入圖像相乘就得到了分割好的圖像。?
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)
# 函數的返回值是更新的 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()
?結果如下:
哎呀,梅西的頭發被我們弄沒了!讓我們來幫他找回頭發。所以我們要在那里畫一筆(設置像素為 1,肯定是前景)。同時還有一些我們并不需要的草地。我們需要去除它們,我們再在這個區域畫一筆(設置像素為 0,肯定是背景)?,F在可以象前面提到的那樣來修改掩模圖像了。
實際上我是怎么做的呢?我們使用圖像編輯軟件打開輸入圖像,添加一個圖層,使用筆刷工具在需要的地方(比如頭發,鞋子,球等)使用白色繪制;使用黑色筆刷在不需要的地方繪制(比如, logo,草地等)。然后將其他地方用灰色填充,保存成新的掩碼圖像。在 OpenCV 中導入這個掩模圖像,根據新的掩碼圖像對原來的掩模圖像進行編輯。代碼如下:
# 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()
?結果如下:
就是這樣。你也可以不使用矩形初始化,直接進入掩碼圖像模式。使用 2像素和 3 像素(可能是背景/前景)對矩形區域的像素進行標記。然后象我們在第二個例子中那樣對肯定是前景的像素標記為 1 像素。然后直接在掩模圖像模式下使用 grabCut 函數。
OpenCV 自帶的示例中有一個使用 grabcut 算法的交互式工具 grabcut.py。
參考資料:
1.?OpenCV官方文檔-Interactive Foreground Extraction using GrabCut Algorithm
2.《OpenCV-Python 中文教程》(段力輝 譯)
?
?
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

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