http://www.tuicool.com/articles/63aANv
一、序言
陸陸續續的如果累計起來,我估計至少有二十來位左右的朋友加我QQ,向我咨詢有關摳圖方面的算法,可惜的是,我對這方面之前一直是沒有研究過的。除了利用和Photoshop中的魔棒一樣的技術或者Photoshop中的選區菜單中的色彩范圍類似的算法(這兩個我有何PS至少90%一致的代碼)是實現簡單的摳圖外,現在一些state of art 方面的算法我都不了解。因此,也浪費了不少的將知識轉換為資產的機會。年30那天,偶然的一個機會,有位朋友推薦我看了一篇關於摳圖的文章,並有配套的實現代碼,於是我就決定從這篇文章開始我的摳圖算法研究之旅。
這篇文章就是Shared Sampling for Real-Time Alpha Matting,關於這篇文章的一些信息,可以在這個網站里找到很多:http://www.inf.ufrgs.br/~eslgastal/SharedMatting/ ,配套的一個代碼在CSDN中可以下載,具體見: http://download.csdn.net/detail/jlwyc/4676516
這篇文章的標題很具有吸引力,發表日期為2010,也算是比較新的。在大家繼續看下去之前,我要提醒的是,這里的Real - Time有比較多的限制:主要是(1)必須依賴於強勁的GPU;(2)應用的摳圖場合的背景應該比較簡單。
不管如何,因為有配套的實現代碼,作為起步的研究來說,該文還是算不錯的。
從目前流行的摳圖技術來看,這篇文章的思路算是比較落伍的一種。
二、技術細節
好了,不管那么多,我先貼些論文中的公式及一些說明將文章的主體細路描述一下。
簡單的說,摳圖問題就是要解決如下的一個超級病態的方程:

式中:C p 是我們觀察到的圖像的顏色,F P、 B P、 α p 均是未知量,可分別稱之為前景、背景及透明度。
要解決這樣的一個病態的方程,就必須給其增加一些附加的約束,通常,這種約束可以是和待scribbles分割圖像同等大小的TriMap或者是用戶收工划定的scribbles的形式存在,如下兩圖所示(如未特別說明,一般白色部分表示前景,黑色表示背景,灰色表示待識別的部分):

TriMap scribbles
這樣的約束條件使得我們知道了那一部分是明確屬於前景(α p =1),而那一部分是屬於背景(α p =0),那么下面的主要任務就是搞定那些未知區域的α p 值 。
按照論文的說法,在2010年前后解決matting問題的主要方法是基於 sampling, pixel affinities 或者兩者的結合,特別是后兩種是主流的方式。但是這兩種都需要求解一個大型的線性系統,這個系統的大小和未知點的個數成正比(我簡單看了下closed form那篇摳圖文檔的代碼,就用到了一個龐大的稀疏矩陣),因此對於1MB左右大小的圖,求解時間在幾秒到幾分鍾不等。這篇論文提出的算法應該說是基於sampling技術的,他充分利用了相鄰像素之間的相似性,並利用了算法內在的並行性,結合GPU編程,實現摳圖的實時展示。
總的來說,論文提出的算法可以分成4個步驟:
第一步:Expansion,針對用戶的輸入,對已知區域(前景或背景)進行小規模的擴展;
第二步:Sample and Gather,對剩余的未知區域內的每個點按一定的規則取樣,並選擇出最佳的一對前景和背景取樣點;
第三步:Refinement,在一定的領域范圍內,對未知區域內的每個點的最佳配對重新進行組合。
第四步:Local Smoothing,對得到的前景和背景對以及透明度值進行局部平滑,以減少噪音。
2.1 Expansion
這一步,按照我的經驗,可以不做,他唯一的作用就是減少未知點的個數,可能在一定程度上減小后期的計算量,原理也很簡單,就是對一個未知點,在其一定的鄰域半徑內(文中推薦值10,
並且是圓形半徑),如果有已知的背景點或前景點,則計算其顏色和這些已知點顏色的距離,然后把這個未知點歸屬於和其顏色距離小於某個值並且最靠近該點的對象(前景或背景)。
在CSDN提供的參考代碼中,這一部分的編碼其實寫的還是很有特色的,他的循環方式不同於我們普通的鄰域編碼,他是從像素點逐漸向外部循環開來,有點類似左圖的這種循環方式(實際上還是有點區別的,實際是上下兩行一起處理,在左右兩列處理,然后再向外層擴散),這種處理方式的明顯好處就是,只要找到某個點顏色距離小於設定的值,就可以停止循環了,因為這個點肯定是第一個符合顏色距離條件又同時符合物理距離最小的要求的。
這一步做不做,最最終的結果又一定的影響,但是他不具有質的影響。
2.2 Sample and Gather
總的來說,這一步是算法的核心部分,也是對結果影響最大的,他的步驟說起來其實也很簡單,我們先看下圖。
在這個圖中,P和q點都是未知區域,我們需要通過一定的原則在已知區域為其取得一定的樣本對,論文中提出的提取方法是:
設定一個參數Kg,其意義為一個點最多可能取樣的前景點和背景點的個數,也就意味着最多的取樣對為Kg*Kg組,通常這個值可以取為4或者更多,論文建議取4就可以了,越大則程序越耗時。
這樣對於每個未知點,從該點出發,引出Kg條路徑,每個路徑之間成360/Kg的夾角,記錄下每條路徑經過的路線中首次遇到的前景或背景點,直到超出圖像的邊緣。
為了算法的穩定性,每3*3的矩形區域內(4*4或者5*5也沒說不可以的),路徑的起始角度周期性的改變,這樣相鄰像素的Kg條路徑經過的區域就有着較大的不同能得到更為有效的結果集。
由上圖可以看到,在不少情況下,未知點的前景和背景取樣數並不能達到Kg個,甚至極端情況下,找不到任何一個取樣點,這樣該點就無法進行透明度的計算了,這就要靠后面的過程了。

前景取樣點數量分布 背景取樣點數量分布 前景+背景取樣點數量分布
上圖繪制了前面列舉的TriMap圖中未知區域每個部位的取樣點數量分布情況,顏色越靠近白色,表明取樣點的數量越大,從圖中可以明顯看出,處於圖像角落的一些未知點取樣情況並不是特別理想,但基本上未出現沒有取到樣的情況,那我們在來看看scribbles那張圖的結果。

前景取樣點數量分布 背景取樣點數量分布 前景+背景取樣點數量分布
特別是前景取樣分布的結果似乎不太令人滿意,有些部分取樣數為0了,這個問題下面還會談到。
在完成取樣計算后,我們就需要找出這些取樣點中那些是最佳的組合,這個時候就涉及到一般優化時常談到的目標函數了,在這篇論文中,對目標函數用了四個小函數的乘積來計算,分別如下:
1:
其中 
為了全面,我們將上式中α p 的計算公式列出: 
公式(2)的道理很為明顯,用一對F/B算出的α值如果很合理的話,那么用α結合F/B重新計算出的顏色應該和原始顏色的差距很小。公式(3)在表明在一定的領域內,由於像素一般不會有突變,差值的平均值也應該很小。
為方便理解,我貼出計算α的部分代碼:
/// <summary> /// 通過當前點、前景點以及背景點的顏色值計算對應的Alpha值,對應論文的公式(12)。 /// </summary> /// <param name="BC、GC、RC">當前點的BGR顏色分量值。</param> /// <param name="BF、GF、RF">前景點的BGR顏色分量值。</param> /// <param name="BF、GF、RF">背景點的BGR顏色分量值。</param> /// <remarks>Alpha會出現不在[0,1]區間的情況,因此需要抑制。</remarks> double CalcAlpha(int BC, int GC, int RC, int BF, int GF, int RF, int BB, int GB, int RB) { double Alpha =(double) ((BC - BB) * (BF - BB) + (GC - GB) * (GF - GB) + (RC - RB) * (RF - RB)) / ((BF - BB) * (BF - BB) + (GF - GB) * (GF - GB) + (RF - RB) * (RF - RB) + 0.0000001); // 這里0.0000001換成Eps在LocalSmooth階段似乎就不對了,有反常的噪點產生 if (Alpha > 1) Alpha = 1; else if (Alpha < 0) Alpha = 0; return Alpha; }
2: 作者考慮在未知點到取樣的前景和背景點之間的直線路徑上,應該盡量要少有像素的突變,比如如果這條路徑需要經過圖像的邊緣區域,則應該設計一個函數使得該函數的返回值較大,於是作者使用了下面的公式:

上式即沿着路徑對像素顏色進行積分,離散化后也就是一些累加,CSDN的提供的代碼在這個函數的處理過程中是有錯誤的,因為他最后一個判斷條件使得循環只會進行一次,有興趣的朋友可以自己去改改。
按照公式(4)的意義,一個未知點屬於前景的可能性可由下式表示:
、
而一個好的組合也應該最小化下式:

3、未知點和前景點之間的物理距離,一個好的組合中的前景點應該要盡量靠近未知點;
4、未知點和背景點之間的物理距離,一個好的組合中的背景點也應該要盡量靠近未知點;
將這四個條件組合起來,最終得到如下的目標函數:

各子項的指數數據可詳見論文本身。
按照這個要求,對前面進行取樣得到數據進行處理,並記錄下使上式最小的那一對組合,就初步確定了最佳的取樣點。
其實,這個時候我們也就可以初步獲得處理后的α值了,比如對於我們前面所說的Trimap,其原始圖像及經過sample和gather處理后的結果如下圖:

從處理結果看,已經可以粗略的得到處理的效果了。
2.3、Refinement
初步的gather處理后,正如前文所說,得到的結果還不夠細膩,並且有些未知點由於采樣的過程未收集到有效的前景和背景數據,造成該點無法進行處理,因此,在Refinement階段需要進一步解決這個問題。
論文提出,首先,在一定的鄰域內,比如半徑為5的領域內,首先統計出公式(2)對應的MP值最小的3個點相關顏色數據,並對這些數據進行加權平均,得到數據對:

然后按照下面這些公式計算新的前景、背景、透明度及可信度的計算。

可信度的計算是為下一步的局部平滑做准備的,他反應了我們在這一步確定的取樣點是否合理程度的一個度量,經由此步驟,我們可得到的透明度和合成圖如下所示:

可見在這一步得到的結果對於上圖來說已經相當完美了。
2.4 Local Smoothing
這一步說實在的我沒有花太多的精力去看,他的實現過程大概有點類似於高斯模糊,但里面多了很多其他方面的處理,一個很好的事情就是在CSDN提供的代碼中對這部分每個公式的實現都是正確的,也是完整的,因此,有興趣的朋友需要自己多看下論文和對應的代碼了。
三、算法的效果
按照論文提供的相關資料集我自己搜集的一些圖及配套的Trimap測試了該算法的一些結果,現貼出如下所示:







原圖 Trimap 合成后的效果圖
可見,對於這些Trimap圖,在很多情況下是能獲得較為滿意的效果的。
我還找了一些簡單的圖,使用scribble的方式進行處理,效果如下所示:





原圖 操作界面 結果圖
我選的這些都是背景比較簡單的圖,因此還能獲得較為理想的效果,如果是比較復雜的圖,使用scribble是基本上獲取不到很理想的效果的,除非人工仔細的划分邊界。
由於CSDN提供了代碼,我這里不提供我自己寫的了。畢竟那個代碼只是個原型。
提供了三個示例程序,給有興趣的朋友測試下效果: http://files.cnblogs.com/Imageshop/SharedMatting.rar
*****************************基本上我不提供源代碼,但是我會盡量用文字把對應的算法描述清楚或提供參考文檔*******************************
*************************************因為靠自己的努力和實踐寫出來的效果才真正是自己的東西,人一定要靠自己***************************
