在本章中,
GrabCut算法由英國微軟研究院的Carsten Rother,Vladimir Kolmogorov和Andrew Blake設計。在他們的論文「GrabCut」中:使用疊代圖割的交互式前景提取。需要用最少的用戶交互進行前景提取的算法,結果是GrabCut。
從用戶角度來看,它是如何工作的?最初,用戶在前景區域周圍繪製一個矩形(前景區域應完全位於矩形內部)。然後,算法會對其進行疊代分割,以獲得最佳結果。做完了但在某些情況下,分割可能不會很好,例如,可能已將某些前景區域標記為背景,反之亦然。在這種情況下,需要用戶進行精修。只需在圖像錯誤分割區域上畫些筆畫。筆畫基本上說 「嘿,該區域應該是前景,你將其標記為背景,在下一次疊代中對其進行校正」或與背景相反。然後在下一次疊代中,你將獲得更好的結果。
參見下圖。第一名球員和橄欖球被封閉在一個藍色矩形中。然後用白色筆劃(表示前景)和黑色筆劃(表示背景)進行最後的修飾。而且我們得到了不錯的結果。
那麼背景發生了什麼呢?
如下圖所示(圖片提供:http://www.cs.ru.ac.za/research/g02m1682/)
現在我們使用OpenCV進行抓取算法。OpenCV為此具有功能cv.grabCut(),我們將首先看到其參數:
首先讓我們看看矩形模式。我們加載圖像,創建類似的mask圖像。我們創建fgdModel和bgdModel。我們給出矩形參數。一切都是直截了當的。讓算法運行5次疊代。模式應為cv.GCINITWITH_RECT, 因為我們使用的是矩形。然後運行grabcut。修改mask圖像。在新的mask圖像中,像素將被標記有四個標記,分別表示上面指定的背景/前景。因此,我們修改mask,使所有0像素和2像素都置為0(即背景),而所有1像素和3像素均置為1(即前景像素)。現在,我們的最終mask已經準備就緒。只需將其與輸入圖像相乘即可得到分割的圖像。
import numpy as npimport cv2 as cvfrom matplotlib import pyplot as pltimg = cv.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)cv.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv.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像素的修飾(確保背景)。因此,如現在所說,我們在以前的情況下修改生成的mask。
我實際上所做的是,我在paint應用程式中打開了輸入圖像,並在圖像中添加了另一層。使用畫筆中的畫筆工具,我在新圖層上用白色標記了錯過的前景(頭髮,鞋子,球等),而用白色標記了不需要的背景(例如logo,地面等)。然後用灰色填充剩餘的背景。然後將該mask圖像加載到OpenCV中,編輯我們在新添加的mask圖像中具有相應值的原始mask圖像。
檢查以下代碼:
#newmask是我手動標記過的mask圖像newmask = cv.imread('newmask.png',0)# 標記為白色(確保前景)的地方,更改mask = 1# 標記為黑色(確保背景)的地方,更改mask = 0mask[newmask == 0] = 0mask[newmask == 255] = 1mask, bgdModel, fgdModel = cv.grabCut(img,mask,None,bgdModel,fgdModel,5,cv.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()
查看以下結果:
就是這樣了。在這裡,你無需直接在rect模式下初始化,而可以直接進入mask模式。只需用2像素或3像素(可能的背景/前景)標記mask圖像中的矩形區域。然後像在第二個示例中一樣,將我們的sure_foreground標記為1像素。然後直接在mask模式下應用grabCut功能。