日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

opencv 图像 抠图 算法_图像抠图算法学习 - Shared Sampling for Real-Time Alpha Matting

發(fā)布時間:2024/7/23 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 opencv 图像 抠图 算法_图像抠图算法学习 - Shared Sampling for Real-Time Alpha Matting 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、序言

陸陸續(xù)續(xù)的如果累計起來,我估計至少有二十來位左右的朋友加我QQ,向我咨詢有關(guān)摳圖方面的算法,可惜的是,我對這方面之前一直是沒有研究過的。除了利用和Photoshop中的魔棒一樣的技術(shù)或者Photoshop中的選區(qū)菜單中的色彩范圍類似的算法(這兩個我有何PS至少90%一致的代碼)是實現(xiàn)簡單的摳圖外,現(xiàn)在一些state of art 方面的算法我都不了解。因此,也浪費了不少的將知識轉(zhuǎn)換為資產(chǎn)的機會。年30那天,偶然的一個機會,有位朋友推薦我看了一篇關(guān)于摳圖的文章,并有配套的實現(xiàn)代碼,于是我就決定從這篇文章開始我的摳圖算法研究之旅。

這篇文章就是Shared Sampling for Real-Time Alpha Matting,關(guān)于這篇文章的一些信息,可以在這個網(wǎng)站里找到很多:http://www.inf.ufrgs.br/~eslgastal/SharedMatting/,配套的一個代碼在CSDN中可以下載,具體見:http://download.csdn.net/detail/jlwyc/4676516

這篇文章的標(biāo)題很具有吸引力,發(fā)表日期為2010,也算是比較新的。在大家繼續(xù)看下去之前,我要提醒的是,這里的Real - Time有比較多的限制:主要是(1)必須依賴于強勁的GPU;(2)應(yīng)用的摳圖場合的背景應(yīng)該比較簡單。

不管如何,因為有配套的實現(xiàn)代碼,作為起步的研究來說,該文還是算不錯的。

從目前流行的摳圖技術(shù)來看,這篇文章的思路算是比較落伍的一種。

二、技術(shù)細(xì)節(jié)

好了,不管那么多,我先貼些論文中的公式及一些說明將文章的主體細(xì)路描述一下。

簡單的說,摳圖問題就是要解決如下的一個超級病態(tài)的方程:

式中:Cp是我們觀察到的圖像的顏色,FP、BP、αp均是未知量,可分別稱之為前景、背景及透明度。

要解決這樣的一個病態(tài)的方程,就必須給其增加一些附加的約束,通常,這種約束可以是和待分割圖像同等大小的TriMap或者是用戶收工劃定的scribbles的形式存在,如下兩圖所示(如未特別說明,一般白色部分表示前景,黑色表示背景,灰色表示待識別的部分):

? ? ?

TriMap                           ? ? ? scribbles

這樣的約束條件使得我們知道了那一部分是明確屬于前景(αp=1),而那一部分是屬于背景(αp=0),那么下面的主要任務(wù)就是搞定那些未知區(qū)域的αp值?。

按照論文的說法,在2010年前后解決matting問題的主要方法是基于sampling, pixel af?nities 或者兩者的結(jié)合,特別是后兩種是主流的方式。但是這兩種都需要求解一個大型的線性系統(tǒng),這個系統(tǒng)的大小和未知點的個數(shù)成正比(我簡單看了下closed form那篇摳圖文檔的代碼,就用到了一個龐大的稀疏矩陣),因此對于1MB左右大小的圖,求解時間在幾秒到幾分鐘不等。這篇論文提出的算法應(yīng)該說是基于sampling技術(shù)的,他充分利用了相鄰像素之間的相似性,并利用了算法內(nèi)在的并行性,結(jié)合GPU編程,實現(xiàn)摳圖的實時展示。

總的來說,論文提出的算法可以分成4個步驟:

第一步:Expansion,針對用戶的輸入,對已知區(qū)域(前景或背景)進行小規(guī)模的擴展;

第二步:Sample and Gather,對剩余的未知區(qū)域內(nèi)的每個點按一定的規(guī)則取樣,并選擇出最佳的一對前景和背景取樣點;

第三步:Re?nement,在一定的領(lǐng)域范圍內(nèi),對未知區(qū)域內(nèi)的每個點的最佳配對重新進行組合。

第四步:Local Smoothing,對得到的前景和背景對以及透明度值進行局部平滑,以減少噪音。

2.1 ?Expansion

這一步,按照我的經(jīng)驗,可以不做,他唯一的作用就是減少未知點的個數(shù),可能在一定程度上減小后期的計算量,原理也很簡單,就是對一個未知點,在其一定的鄰域半徑內(nèi)(文中推薦值10,

并且是圓形半徑),如果有已知的背景點或前景點,則計算其顏色和這些已知點顏色的距離,然后把這個未知點歸屬于和其顏色距離小于某個值并且最靠近該點的對象(前景或背景)。

在CSDN提供的參考代碼中,這一部分的編碼其實寫的還是很有特色的,他的循環(huán)方式不同于我們普通的鄰域編碼,他是從像素點逐漸向外部循環(huán)開來,有點類似左圖的這種循環(huán)方式(實際上還是有點區(qū)別的,實際是上下兩行一起處理,在左右兩列處理,然后再向外層擴散),這種處理方式的明顯好處就是,只要找到某個點顏色距離小于設(shè)定的值,就可以停止循環(huán)了,因為這個點肯定是第一個符合顏色距離條件又同時符合物理距離最小的要求的。

這一步做不做,最最終的結(jié)果又一定的影響,但是他不具有質(zhì)的影響。

2.2 ?Sample and Gather

總的來說,這一步是算法的核心部分,也是對結(jié)果影響最大的,他的步驟說起來其實也很簡單,我們先看下圖。

在這個圖中,P和q點都是未知區(qū)域,我們需要通過一定的原則在已知區(qū)域為其取得一定的樣本對,論文中提出的提取方法是:

設(shè)定一個參數(shù)Kg,其意義為一個點最多可能取樣的前景點和背景點的個數(shù),也就意味著最多的取樣對為Kg*Kg組,通常這個值可以取為4或者更多,論文建議取4就可以了,越大則程序越耗時。

這樣對于每個未知點,從該點出發(fā),引出Kg條路徑,每個路徑之間成360/Kg的夾角,記錄下每條路徑經(jīng)過的路線中首次遇到的前景或背景點,直到超出圖像的邊緣。

為了算法的穩(wěn)定性,每3*3的矩形區(qū)域內(nèi)(4*4或者5*5也沒說不可以的),路徑的起始角度周期性的改變,這樣相鄰像素的Kg條路徑經(jīng)過的區(qū)域就有著較大的不同能得到更為有效的結(jié)果集。

由上圖可以看到,在不少情況下,未知點的前景和背景取樣數(shù)并不能達(dá)到Kg個,甚至極端情況下,找不到任何一個取樣點,這樣該點就無法進行透明度的計算了,這就要靠后面的過程了。

?

?

前景取樣點數(shù)量分布                   ?背景取樣點數(shù)量分布               ?前景+背景取樣點數(shù)量分布

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

?

?

前景取樣點數(shù)量分布背景取樣點數(shù)量分布前景+背景取樣點數(shù)量分布

特別是前景取樣分布的結(jié)果似乎不太令人滿意,有些部分取樣數(shù)為0了,這個問題下面還會談到。

在完成取樣計算后,我們就需要找出這些取樣點中那些是最佳的組合,這個時候就涉及到一般優(yōu)化時常談到的目標(biāo)函數(shù)了,在這篇論文中,對目標(biāo)函數(shù)用了四個小函數(shù)的乘積來計算,分別如下:

1:

其中

為了全面,我們將上式中αp的計算公式列出:

公式(2)的道理很為明顯,用一對F/B算出的α值如果很合理的話,那么用α結(jié)合F/B重新計算出的顏色應(yīng)該和原始顏色的差距很小。公式(3)在表明在一定的領(lǐng)域內(nèi),由于像素一般不會有突變,差值的平均值也應(yīng)該很小。

為方便理解,我貼出計算α的部分代碼:

///

///通過當(dāng)前點、前景點以及背景點的顏色值計算對應(yīng)的Alpha值,對應(yīng)論文的公式(12)。///

/// 當(dāng)前點的BGR顏色分量值。

/// 前景點的BGR顏色分量值。

/// 背景點的BGR顏色分量值。

/// Alpha會出現(xiàn)不在[0,1]區(qū)間的情況,因此需要抑制。

double CalcAlpha(int BC, int GC, int RC, int BF, int GF, int RF, int BB, int GB, intRB)

{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階段似乎就不對了,有反常的噪點產(chǎn)生

if (Alpha > 1)

Alpha= 1;else if (Alpha < 0)

Alpha= 0;returnAlpha;

}

2: 作者考慮在未知點到取樣的前景和背景點之間的直線路徑上,應(yīng)該盡量要少有像素的突變,比如如果這條路徑需要經(jīng)過圖像的邊緣區(qū)域,則應(yīng)該設(shè)計一個函數(shù)使得該函數(shù)的返回值較大,于是作者使用了下面的公式:

上式即沿著路徑對像素顏色進行積分,離散化后也就是一些累加,CSDN的提供的代碼在這個函數(shù)的處理過程中是有錯誤的,因為他最后一個判斷條件使得循環(huán)只會進行一次,有興趣的朋友可以自己去改改。

按照公式(4)的意義,一個未知點屬于前景的可能性可由下式表示:

而一個好的組合也應(yīng)該最小化下式:

3、未知點和前景點之間的物理距離,一個好的組合中的前景點應(yīng)該要盡量靠近未知點;

4、未知點和背景點之間的物理距離,一個好的組合中的背景點也應(yīng)該要盡量靠近未知點;

將這四個條件組合起來,最終得到如下的目標(biāo)函數(shù):

各子項的指數(shù)數(shù)據(jù)可詳見論文本身。

按照這個要求,對前面進行取樣得到數(shù)據(jù)進行處理,并記錄下使上式最小的那一對組合,就初步確定了最佳的取樣點。

其實,這個時候我們也就可以初步獲得處理后的α值了,比如對于我們前面所說的Trimap,其原始圖像及經(jīng)過sample和gather處理后的結(jié)果如下圖:

???

從處理結(jié)果看,已經(jīng)可以粗略的得到處理的效果了。

2.3、Re?nement

初步的gather處理后,正如前文所說,得到的結(jié)果還不夠細(xì)膩,并且有些未知點由于采樣的過程未收集到有效的前景和背景數(shù)據(jù),造成該點無法進行處理,因此,在Re?nement階段需要進一步解決這個問題。

論文提出,首先,在一定的鄰域內(nèi),比如半徑為5的領(lǐng)域內(nèi),首先統(tǒng)計出公式(2)對應(yīng)的MP值最小的3個點相關(guān)顏色數(shù)據(jù),并對這些數(shù)據(jù)進行加權(quán)平均,得到數(shù)據(jù)對:

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

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

? ?

可見在這一步得到的結(jié)果對于上圖來說已經(jīng)相當(dāng)完美了。

2.4?Local Smoothing

這一步說實在的我沒有花太多的精力去看,他的實現(xiàn)過程大概有點類似于高斯模糊,但里面多了很多其他方面的處理,一個很好的事情就是在CSDN提供的代碼中對這部分每個公式的實現(xiàn)都是正確的,也是完整的,因此,有興趣的朋友需要自己多看下論文和對應(yīng)的代碼了。

三、算法的效果

按照論文提供的相關(guān)資料集我自己搜集的一些圖及配套的Trimap測試了該算法的一些結(jié)果,現(xiàn)貼出如下所示:

??

??

??

??

??

??

??

??

??

??

??

??

??

??

原圖                        Trimap                      合成后的效果圖

可見,對于這些Trimap圖,在很多情況下是能獲得較為滿意的效果的。

我還找了一些簡單的圖,使用scribble的方式進行處理,效果如下所示:

??

??

??

??

??

?

??

??

??

??

原圖                 ? ? ? ?    操作界面                    ? ?     結(jié)果圖

我選的這些都是背景比較簡單的圖,因此還能獲得較為理想的效果,如果是比較復(fù)雜的圖,使用scribble是基本上獲取不到很理想的效果的,除非人工仔細(xì)的劃分邊界。

四、編程實現(xiàn)

在編程實現(xiàn)方面,CSDN提供的那個代碼基本的意思已經(jīng)達(dá)到了,并且里面的函數(shù)意義也非常清晰,只不過他使用的opencv的庫的,相信專心去研究摳圖的人,把他改成其他語言也不是個難題,比如里面用到的vector在C#中就可以用list代替,那些Opencv的結(jié)構(gòu)體也可以在C#中重新定義。

不過那個代碼占用的內(nèi)存非常厲害,這主要是由于VECTOR等數(shù)據(jù)類型決定的,實際上這里完全可以用數(shù)組來搞定的。

我貼一部分代碼大家看看:

///

///對每個未知區(qū)域的像素按設(shè)定的循環(huán)角度搜索有效的前景和背景取樣點///

/// 未知區(qū)域的X坐標(biāo)。

/// 未知區(qū)域的Y坐標(biāo)。

/// 用于保存前景點的內(nèi)存區(qū)域。

/// 用于保存背景點的內(nèi)存區(qū)域。

/// 最終獲取的前景點的數(shù)量。

/// 最終獲取的背景點的數(shù)量。

/// 對于有些點,是有可能獲取不到有效的點的。

void Sample(int X, int Y, Point *F, Point *B, int &CountF, int &CountB)

{intZ, XX, YY, Alpha;boolF1, F2;doubleInitAngle, IncAngle, Angle;doubleDx, Dy, Step, Value, XD, YD, StepX, StepY;

IncAngle= 360 / KG; //每次掃描增加的角度

InitAngle = (Y % 5 * 5 + X % 25) * IncAngle / 25; //起始角度,范圍在[0,IncAngle]之間,按照3*3的方式輪流替換,有利于提供結(jié)果的穩(wěn)定性,如果KG取值較大,也可以使用4*4或者5*5

CountF = 0; CountB = 0; //起步時需要記為0,注意參數(shù)中的引用(&)

for (Z = 0; Z < KG; Z++)

{

F1= false; F2 = false; //開始尋找,暫時未找到任何前景點和背景點

Angle = (InitAngle + Z * IncAngle) / 180.0f * 3.1415926f; //每次搜索的角度

Dx = cos(Angle); Dy =sin(Angle);

Step= min(1.0f / (abs(Dx) + 1e-10), 1.0f / (abs(Dy) + 1e-10));

XD= X + 0.5; YD = Y + 0.5; //+0.5使用于int四舍五入取整,相當(dāng)于其他語言的round函數(shù)

StepX = Step * Dx ; StepY = Step * Dy ; //StepX和StepY中必然有一個為1,另外一個小于1,這樣保證每次搜索的像素點都會不同,一個好的建議是加大這個循環(huán)步長//這樣有兩個好處。1:加快查找速度;2:讓搜索點可以進入已知點的內(nèi)部,從而避免了只從已知點的邊緣取樣。但如果已知點的區(qū)域狹長,可能丟失有效取樣點。

for(; ;)

{

XX= int(XD);if (XX < 0 || XX >= Width) break; //已經(jīng)超出了圖像的邊界了,結(jié)束循環(huán)

YY = int(YD);if (YY < 0 || YY >= Height) break;

XD+= StepX; YD +=StepY;

Alpha= Mask[YY * MaskStride + XX]; //得到路徑中該點的特征值(前景/背景/未知)

if (F1 == false && Alpha == 0) //如果沿這條路徑尚未找到背景點,并且改點具有背景點的特征,則記錄下改點到背景點序列中

{

B[CountB].X= XX; //背景點的X坐標(biāo)

B[CountB].Y = YY; //背景點的Y坐標(biāo)

CountB++; //背景點數(shù)量增加1

F1 = true; //在此路徑已經(jīng)找到了背景點,不用再找背景點了。

}else if (F2 == false && Alpha == 255) //如果沿這條路徑尚未找到前景點,并且改點具有前景點的特征,則記錄下改點到前景點序列中

{

F[CountF].X= XX; //前景點的X坐標(biāo)

F[CountF].Y = YY; //前景點的X坐標(biāo)

CountF++; //前景點數(shù)量增加1

F2 = true; //在此路徑已經(jīng)找到了前景點,不用再找前景點了。

}else if (F1 == true && F2 == true) //如果前景點和背景點都已經(jīng)找到了,則結(jié)束循環(huán)

{break;

}

}

}

}

通過以上的Sample代碼,我們就可以避免使用Vector之類的數(shù)據(jù)結(jié)構(gòu)了,速度和內(nèi)存占用都會得到改進。

然后在Gather階段的代碼改成如下的方式。

voidGathering()

{intX, Y, K, L, Index, Speed;intCountF, CountB, Fx, Fy, Bx, By;doublePfp, Min, Dpf, Gp;boolFlag;

Point*F = (Point *) malloc(KG * sizeof(Point)); //采用這種方式占用的內(nèi)存小很多

Point *B = (Point *) malloc(KG * sizeof(Point));for (Y = 0; Y < Height; Y++)

{

Index= Y *MaskStride;for (X = 0; X < Width; X++)

{

tuple[Index].Flag= -1; //先都設(shè)置為無效的點

if (Mask[Index] != 0 && Mask[Index] != 255) //只處理未知點

{

Sample(X, Y, F, B, CountF, CountB);//對當(dāng)前點進行前景和背景取樣

Pfp = CalcPFP(X, Y, F, B, CountF, CountB); //計算公式(5),因為公式(5)只于前景和背景取樣點的整體有關(guān),無需放到下面的循環(huán)內(nèi)部

Min =1e100;

Flag= false;for (K = 0; K < CountF; K++) //對于每一個前景點

{

Dpf= CalcDp(X, Y, F[K].X, F[K].Y, true); //計算前景點到中心點的歐式距離

for (L = 0; L < CountB; L++) //對于每一個背景點

{

Gp= CalcGp(X, Y, F[K].X, F[K].Y, B[L].X, B[L].Y, Pfp, Dpf); //按照公式(7)計算目標(biāo)函數(shù)

if (Gp

{

Min=Gp;

Fx= F[K].X; Fy = F[K].Y; //記錄下目標(biāo)函數(shù)為最小值處的前景和背景點的坐標(biāo)

Bx = B[L].X; By =B[L].Y;

Flag= true;

}

}

}if (Flag == true) //說明找到了最好的組合了,如果找不到,則原因可能是:(1)Sample過程為找到任何有效的前景和背景點;(2)Sample過程只找到前景點或只找到背景點

{ //某個點找不到也不用怕,可能在下面的Refine過程中(其算法為領(lǐng)域處理)得以彌補。

Speed = Fy * Stride + Fx * 3; //記錄下最佳前景點的顏色值

tuple[Index].BF =ImageData[Speed];

tuple[Index].GF= ImageData[Speed + 1];

tuple[Index].RF= ImageData[Speed + 2];

Speed= By * Stride + Bx * 3;

tuple[Index].BB= ImageData[Speed]; //記錄下最佳背景點的顏色值

tuple[Index].GB = ImageData[Speed + 1];

tuple[Index].RB= ImageData[Speed + 2];

tuple[Index].SigmaF= CaclSigma(Fx, Fy); //計算前景點周邊像素的均方差值

tuple[Index].SigmaB = CaclSigma(Bx, By); //計算背景點周邊像素的均方差值

tuple[Index].Flag = 1; //這個像素是個有效的處理點

}

}

Index++;

}

}

free(F);

free(B);

}

由于CSDN提供了代碼,其他的代碼我這里就不提供了。

五、算法的缺陷

以下內(nèi)容純屬個人意見,請各位閱讀的朋友根據(jù)自己的知識去判斷。

1、Sample過程存在潛在的問題:論文的圖2闡述了對某點進行取樣的操作過程,這個過程在第一次遇到前景或背景點時就把該點視為前景或背景的一個取樣點。這樣的話所有的取樣點的數(shù)據(jù)都只可能取在Trimap或者scripple圖的前景和背景區(qū)域的邊緣處。如果邊緣處的像素和內(nèi)部的有很大的區(qū)別,顯然會出現(xiàn)部分取樣點取到的數(shù)據(jù)很不合理,從而導(dǎo)致最終的透明度信息錯誤。一種可行的方案就是在Sample的過程中修改每次取樣的步長,比如,下面的方式:

StepX = Step * Dx * 10 ; StepY = Step * Dy * 10 ;

即每次沿X或者Y方向移動10個像素,這樣就會使得取樣點會進入已知區(qū)域內(nèi)部最多10個像素左右,同時也會加速取樣的速度。

不過這樣做也隱形的帶來一個新的問題,即有可能會丟失一些取樣點,當(dāng)已知點的寬度小于10像素時出現(xiàn)這種情況。

2、還是會存在某些點無法獲取有效的取樣點的,即使有了refine過程。

3、算法的復(fù)雜度還是很高,雖然整體的并行性很好,如果沒有GPU參與,純CPU實現(xiàn)起來一樣很吃力。

六、效果驗證

*********************************作者: laviewpbt ? 時間: 2014.2.15 ? 聯(lián)系QQ: ?33184777 ?轉(zhuǎn)載請保留本行信息************************

總結(jié)

以上是生活随笔為你收集整理的opencv 图像 抠图 算法_图像抠图算法学习 - Shared Sampling for Real-Time Alpha Matting的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。