grabcut演算法是微軟的一個研究院提出的。演算法在提取前景的操作中需要很少的人機互動,結果非常好。
通俗的說,一開始使用者使用者需要用一個矩形將前景地區框住。然後使用演算法迭代分割。但有時分割的結構不夠理想,會把前景和背景弄錯,這時需要我們人為的修正了。
具體的原理 使用者輸入一個矩形,矩形外的地區一定是背景,矩形內的東西是未知的 電腦會對我們資料的映像做一個初始化的標記,她會標記前景和背景像素 使用高斯混合模型(GMM)進行前景和背景的建模 根據我們的資料,GMM會學習並建立新的像素分布對未知的像素可以根據他們與已知分類像素的關係進行分類 這樣就會根據像素的分布建立一幅圖,圖中的節點就是像素點。除了像素點之外還有兩個節點:source和sink。所有的前景像素和source相連,背景像素和sink相連。 像素連結到source/end(邊)的權重有他們屬於同一類的機率決定。兩個像素之間的權重由邊的資訊或者是兩個像素的相似性決定。如果兩個像素的顏色有很大不同,他們之間邊的權重就會很小 使用mincut演算法對上面的映像進行分割(這個演算法沒聽說過)。她會根據最低的成本方程將圖象分位source和sink。成本方程就是減掉邊的權重之和。剪裁完成之後source被認為是前景,sink被認為是背景。
繼續上述過程知道分類收斂。
下面介紹一下用到的主要函數grabcut
cv2.grabCut(img, mask, rect, bgdModel, fgdModel, iterCount[, mode]) img是數的映像 mask是掩模映像,用來確定哪些地區是背景,哪些地區是前景。可以設定為cv2.GC_BGD定義了一個明顯的背景像素。
cv2.GC_FGD定義了一個明顯的前景(對象)像素。
cv2.GC_PR_BGD定義一個可能的背景像素。
cv2.GC_PR_FGD定義了一個可能的前景像素。(具體的作用可以自己試一下) rect是包含背景的矩形,該參數僅在使用時使用 mode==GC_INIT_WITH_RECT bdgModel,fdgModel是演算法內部使用的數組,需要自己建立兩個大小為(1,65)資料類型為np.float64的數組。 iterCount是演算法的迭代次數 mode可以設定為cv2.Gc_INIT_WITH_RECT或者cv2.GC_INIT_WITH_MASK.這個是來確定我們修改範式,矩陣模式或者掩模模式。
矩陣模式中,演算法會修改掩模映像,在新的掩模映像中,所有的像素被分位四類:背景,前景,可能是背景/前景石油4個不同的標籤標記。下面看一下具體的例子。
import cv2import numpy as npimport matplotlib.pyplot as pltimg=cv2.imread('C:/Users/dell/Desktop/01.jpg')mask=np.zeros((img.shape[:2]),np.uint8)bgdModel=np.zeros((1,65),np.float64)fgdModel=np.zeros((1,65),np.float64)rect=(81,189,587,1041)#這裡計算了5次cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)#關於where函數第一個參數是條件,滿足條件的話賦值為0,否則是1。如果只有第一個參數的話返回滿足條件元素的座標。mask2=np.where((mask==2)|(mask==0),0,1).astype('uint8')#mask2就是這樣固定的plt.subplot(1,2,1)plt.imshow(img)plt.title('original image ')plt.xticks([])plt.yticks([])plt.subplot(1,2,2)#這裡的img也是固定的。img=img*mask2[:,:,np.newaxis]plt.imshow(img)plt.title('target image')plt.xticks([])plt.yticks([])plt.show()
演算法的總體想過還是不錯的,但是有點小瑕疵。原始映像中有個手模型也被誤認為是前景了。不能完全怪演算法,那個東西離人物的手很近而且顏色比較像,判斷錯誤也是情理之中的。要解決這個錯誤就要認為的委任標記了。文檔的做法是在目的地區域用ps強加白色,背景部分強加黑色。