理論
GranCut算法是Carsten Rother, Vladimir Kolmogorov & Andrew Blake from Microsoft Research Cambridge, UK在他們的論文“GrabCut”: interactive foreground extraction using iterated graph cuts里設(shè)計的。使用最小程度的用戶交互來分解前景姑原。
從用戶角度來看是怎么工作的呢?開始用戶畫一個矩形方塊把前景圖圈起來蜗巧,前景區(qū)域應(yīng)該完全在矩形內(nèi)二驰,然后算法反復(fù)進(jìn)行分割以達(dá)到最好效果召调。但是有些情況下,分割的不是很好支子,比如把前景給標(biāo)稱背景了等。在這種情況下用戶需要再潤色达舒,就在圖像上有缺陷的點給幾筆值朋。這幾筆的意思是說“嘿,這個區(qū)域應(yīng)該是前景巩搏,你把它標(biāo)成背景了昨登,下次迭代改過來”或者是反過來。那么在下次迭代贯底,結(jié)果會更好丰辣。
看下面的圖像撒强,首先球員和足球杯包在藍(lán)色矩形框里,然后用白色筆(指出前景)和黑色筆(指出背景)來做一些潤色
后臺發(fā)生了什么笙什?
·用戶輸入矩形飘哨,矩形外的所有東西都被確認(rèn)是背景。所有矩形內(nèi)的東西都是未知的琐凭,同樣的任何用戶輸入指定前景和背景的也都被認(rèn)為是硬標(biāo)記芽隆,在處理過程中不會變。
·計算機(jī)會根據(jù)我們給的數(shù)據(jù)做初始標(biāo)記统屈,它會標(biāo)記出前景和背景像素胚吁。
·現(xiàn)在回使用高斯混合模型(GMM)來為前景和背景建模
·根據(jù)我們給的數(shù)據(jù),GMM學(xué)習(xí)和創(chuàng)建新的像素分布愁憔。未知像素被標(biāo)為可能的前景或可能的背景(根據(jù)其他硬標(biāo)記像素的顏色統(tǒng)計和他們之間的關(guān)系)
·根據(jù)這個像素分布創(chuàng)建一個圖腕扶,圖中的節(jié)點是像素,另外還有兩個節(jié)點吨掌,源節(jié)點和匯節(jié)點蕉毯,每個前景像素和源節(jié)點相連,每個背景像素和匯節(jié)點相連思犁。
·源節(jié)點和匯節(jié)點連接的像素的邊的權(quán)重由像素是前景或者背景的概率決定代虾。像素之間的權(quán)重是由邊的信息或者像素的相似度決定。如果像素顏色有很大差異激蹲,他們之間的邊的權(quán)重就比較低棉磨。
·mincut算法是用來分割圖的,它用最小成本函數(shù)把圖切成兩個分開的源點和匯點学辱,成本函數(shù)是被切的邊的權(quán)重之和乘瓤。切完以后,所有連到源節(jié)點的像素稱為前景策泣,所有連到匯節(jié)點的稱為背景衙傀。
·過程持續(xù)直到分類覆蓋。
Demo
現(xiàn)在我們用OpenCV來做grabcut算法萨咕。OpenCV有個函數(shù)cv2.grabCut()來做這個统抬,我們先看看它的參數(shù):
·img - 輸入圖像
·mask - 這是掩圖,我們指定哪個區(qū)域是背景危队,前景以及可能是背景或者前景聪建。由下面的標(biāo)志位:cv2.GC_BGD, cv2.GC_FGD, cv2.PR_BGD, cv2.GC_PR_FGD, 或者簡單傳入0, 1, 2, 3.
·rect - 包含前景對象的矩形的坐標(biāo),格式(x, y, w, h).
·bdgModel, fgdModel - 算法內(nèi)部使用的數(shù)組茫陆, 你創(chuàng)建兩個np.float64 類型的0數(shù)組金麸,大小是(1, 65)
·iterCount - 算法運行的迭代次數(shù)
·mode - 應(yīng)該是cv2.GC_INIT_WITH_RECT 或者 cv2.GC_INIT_WITH_MASK或者兩者合并,決定我們是否畫矩形或者最終的潤色簿盅。
首先讓我們看看矩形模式挥下,我們加載圖片揍魂,創(chuàng)建一個類似的掩圖。我們創(chuàng)建fgdModel和bgdModel棚瘟。 我們給矩形參數(shù)愉烙。這些都很直接。讓算法運行5個迭代解取。Mode應(yīng)該是cv2.GC_INIT_WITH_RECT因為我們用了矩形步责。然后運行g(shù)rabcut。 它修改掩圖禀苦。在新的掩圖里蔓肯,像素會被標(biāo)記為四種標(biāo)志,來指明他們是背景/前景等振乏。所以我們修改 掩圖蔗包,所有的0-像素和2-像素被置0(背景)而所有的1-像素和3-像素被置1(前景像素)。現(xiàn)在我們最終的掩圖就緒慧邮。
import numpy as np
import cv2
from matplotlib import pyplot as pltimg = 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)
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()
可以看到梅西的頭發(fā)沒了调限,所以我們潤色一下,用1-像素確認(rèn)是前景误澳,同時有些地皮成了前景耻矮,還有l(wèi)ogo,我們需要移除他們忆谓,我們給一些0-像素潤色(確認(rèn)是背景)裆装。我們修改我們的結(jié)果掩圖。
實際上我做的是倡缠,我用繪圖軟件打開輸入圖片哨免,添加了另外一層,使用畫刷工具昙沦,把丟失的前景(頭發(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] = 1mask, 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-像素(可能是背景/前景)標(biāo)記矩形區(qū)域,然后把我們確認(rèn)前景的標(biāo)為1-像素定庵,然后直接應(yīng)用grabCut函數(shù)吏饿,用mask 模式踪危。