這里要跟大家分享的paper為基于特征線的圖像 morphing,對應(yīng)的英文文獻為《Feature-Based Image Metamorphosis》,是1992年SIGGRAPH?上的一篇paper,比較老的一篇paper,然而這篇paper引用率非常高,用于圖像變形效果還是挺不錯的,這個算法一般用于圖像的morphing。因為這篇paper算法原理簡單,易于實現(xiàn),所以不用怕學(xué)習(xí)這個算法需要多長的時間。
開始之前先聲明一下,這篇博文主要參考自:http://www.csie.ntu.edu.tw/~b97074/vfx_html/hw1.html??同時結(jié)合我自己的理解,跟大家分享算法,幫助更多愛好者學(xué)習(xí),非商業(yè)用途。
一、相關(guān)理論
我們知道圖像變形的本質(zhì),其實就是求取光流場,就是求取目標圖像的每一個像素點在原圖像的對應(yīng)位置點,然后同過雙線性插值的方法就可以求得目標圖像。
1、單線段約束變形:
給定原圖像Source Image,我們希望把原圖像上的像素點P'、Q'位置移動到P、Q位置,那么其它的像素點的位置要怎么移動,才能使得得到的結(jié)果圖像Destination Image不會發(fā)生嚴重扭曲,這便是圖像變形的研究內(nèi)容。如圖所示,如果用逆向映射的變形方法,對于目標圖像上的任意一點X,我們只需要求取source image 上的對應(yīng)點X'就可以了,當然逆向映射X'往往不是整數(shù),需要經(jīng)過線性插值,獲取X'的像素值。
已知PQ,P'Q',X點,我們要怎么求出X的對應(yīng)點X'呢?
其實很簡單,其原理是通過保證圖中二維(u,v)坐標不變就可以了,也就是我們可以通過上面的三個計算公式,求出X'。說的簡單一點呢,就是保證X相對于PQ的比例位置坐標(u,v)不變。
2、多線段約束變形
上面是對于單線段約束而言的,對于多線段的情況,主要是通過加權(quán)平均的方法。
如圖,現(xiàn)在已知P1Q1,P2Q2,X,以及源圖像的P1'Q1',P2'Q2',我們要求取X'點。
這個時候我們可以先用單線段約束的方法
(1)通過P1Q1 、P1'Q1'、X 計算出X1’;
(2)通過P2Q2 、P2'Q2'、X 計算出X2’;
然后通過加權(quán)平均的方法,求出X':
其中權(quán)值w的計算方法就是通過點X到線段的距離成反比的函數(shù)計算:
其中l(wèi)ength表示線段的長度,dist表示點X到線段的最短距離。a,b,p為常數(shù),對于它們的取值我們可以選p = 0 , a = 1 , b = 2。
ok,到了這里算法就結(jié)束了,感覺松松,我們就可以計算出X’點,
因為X'計算出來一般不可能剛好位于原圖像像素點的位置,因此我們需要通過雙線性插值的方法,求取X'的像素值。
看完上面應(yīng)該知道怎么計算X'點了吧。接著我們要進入算法實現(xiàn)階段。
二、算法實現(xiàn)
說明以下代碼參考自:http://www.csie.ntu.edu.tw/~b97074/vfx_html/hw1.html
Algorithm:
1、根據(jù)公式1,2,計算X點相對于各線段的位置(u,v)坐標。
[cpp]?view plaincopy
double?new_u?=?dst_line.Getu(X);?? double?new_v?=?dst_line.Getv(X);??
[cpp]?view plaincopy
?? double?Line::Getu(Vector2?X)?? {?? ????double?X_P_x?=?X.x?-?P.x;??? ????double?X_P_y?=?X.y?-?P.y;?? ????double?Q_P_x?=?Q.x?-?P.x;?? ????double?Q_P_y?=?Q.y?-?P.y;?? ????double?u?=?((X_P_x?*?Q_P_x)?+?(X_P_y?*?Q_P_y))?/?(len?*?len)??;?? ????return?u?;?? }?? ?? double?Line::Getv(Vector2?X){?? ????double?X_P_x?=?X.x?-?P.x;?? ????double?X_P_y?=?X.y?-?P.y;?? ????double?Q_P_x?=?Q.x?-?P.x;?? ????double?Q_P_y?=?Q.y?-?P.y;?? ????double?Perp_Q_P_x?=?Q_P_y?;???? ????double?Perp_Q_P_y?=?-Q_P_x?;?? ????double?v?=?((X_P_x?*?Perp_Q_P_x)?+?(X_P_y?*?Perp_Q_P_y))/len?;??? ????return?v?;??? }??
2、根據(jù)公式3,然后反算X點在源圖像的位置對應(yīng)點X'。
[cpp]?view plaincopy
Vector2?src_point?=?src_line.Get_Point(new_u?,?new_v);??
[cpp]?view plaincopy
?? Vector2?Line::Get_Point(double?u?,?double?v)?? {?? ????double?Q_P_x?=?Q.x?-?P.x;?? ????double?Q_P_y?=?Q.y?-?P.y;?? ????double?Perp_Q_P_x?=?Q_P_y?;???? ????double?Perp_Q_P_y?=?-Q_P_x?;?? ????double?Point_x?=?P.x?+?u?*?(Q.x?-?P.x)?+?((v?*?Perp_Q_P_x)/len)?;?? ????double?Point_y?=?P.y?+?u?*?(Q.y?-?P.y)?+?((v?*?Perp_Q_P_y)/len)?;?? ????Vector2?X;?? ????X.x?=?Point_x;?? ????X.y?=?Point_y;?? ????return?X?;?? }??
3、計算各個線段的權(quán)重。
[cpp]?view plaincopy
double?src_weight?=?dst_line.Get_Weight(dst_point);??
[cpp]?view plaincopy
double?Line::Get_Weight(Vector2?X?)?? {?? ????double?a?=?parameter_a;?? ????double?b?=?parameter_b;?? ????double?p?=?parameter_p;?? ????double?d?=?0.0;?? ?? ????double?u?=?Getu(X);?? ????if(u?>?1.0?)?? ????????d?=?sqrt((X.x?-?Q.x)?*?(X.x?-?Q.x)?+?(X.y?-?Q.y)?*?(X.y?-?Q.y));?? ????else?if(u?<?0)?? ????????d?=?sqrt((X.x?-?P.x)?*?(X.x?-?P.x)?+?(X.y?-?P.y)?*?(X.y?-?P.y));?? ????else?? ????????d?=?abs(Getv(X));?? ?? ?? ????double?weight?=pow(pow((float)len,(float)p)/(a?+?d)?,?b);?? ????return?weight;??? }??
然后對所有的X'點進行加權(quán)求和就可以了。
ok,上面過程的代碼合在一起,遍歷每一條約束線段。
4、最后進行雙線性插值。
雙線性插值函數(shù)如下:
[cpp]?view plaincopy
void?bilinear(BitmapData?*psrcImgData,float?X?,float?Y,byte*resultpiexl)?? {?? ????int?x_floor?=?(int)X?;?? ????int?y_floor?=?(int)Y?;?? ????int?x_ceil?=?x_floor?+?1?;?? ????int?y_ceil?=?y_floor?+?1?;?? ????float?a?=?X?-?x_floor?;?? ????float?b?=?Y?-?y_floor?;?? ?? ????if(x_ceil?>=?psrcImgData->Width-1)??? ????????x_ceil?=psrcImgData->Width-1?;?? ????if(y_ceil?>=?psrcImgData->Height-1)??? ????????y_ceil?=?psrcImgData->Height-1?;?? ????byte?leftdown[3];??? ????byte?lefttop[3];??? ????byte?rightdown[3];??? ????byte?righttop[3];??? ????Get2D(psrcImgData,y_floor,x_floor,leftdown);?? ????Get2D(psrcImgData,y_ceil,x_floor,lefttop);?? ????Get2D(psrcImgData,y_floor,x_ceil,rightdown);?? ????Get2D(psrcImgData,y_ceil,x_ceil,righttop);?? ????for(int?i?=?0?;?i?<?3?;?i?++)?? ????{?? ????????resultpiexl[i]?=?(1-a)*(1-b)*leftdown[i]?+?a*(1-b)*rightdown[i]?+?a*b*righttop[i]?+?(1-a)*b*lefttop[i];?? ????}?? }?? void?Get2D(BitmapData?*psrcImgData,?int?Y,int?X,?byte*piexl)?? {?? ????byte*pdata=(byte*)psrcImgData->Scan0+(psrcImgData->Width*Y+X)*4;?? ????for?(int?i=0;i<3;i++)?? ????{?? ????????piexl[i]=pdata[i];?? ????}?? ?? }??
最后貼一下整個過程的代碼:
[cpp]?view plaincopy
int?nWidth=prightImgData->Width;?? int?nHeight=prightImgData->Height;?? ???for(int?x?=?0?;?x?<?nWidth?;?x++)?? {?? ???????for(int?y?=?0?;?y?<?nHeight?;?y++)?? ????{?? ????????Vector2?dst_point?;?? ????????dst_point.x=?x?;??? ????????dst_point.y=?y;?? ????????double?leftXSum_x?=?0.0;?? ????????double?leftXSum_y?=?0.0;?? ????????double?leftWeightSum?=?0.0;?? ????????double?rightXSum_x?=?0.0;?? ????????double?rightXSum_y?=?0.0;?? ????????double?rightWeightSum?=?0.0;?? ????????for(int?i?=?0?;?i?<?pairs.size()?;?i++)?? ????????{?? ?? ????????????Line?src_line?=?pairs[i].leftLine;???? ????????????Line?dst_line?=?pairs[i].rightLine;?? ?? ????????????double?new_u?=?dst_line.Getu(X);?? ????????????double?new_v?=?dst_line.Getv(X);?? ?? ????????????Vector2?src_point?=?src_line.Get_Point(new_u?,?new_v);?? ????????????double?src_weight?=?dst_line.Get_Weight(dst_point);?? ????????????leftXSum_x?=?leftXSum_x?+?(double)src_point.x?*?src_weight?;?? ????????????leftXSum_y?=?leftXSum_y?+?(double)src_point.y?*?src_weight?;?? ????????????leftWeightSum?=?leftWeightSum?+?src_weight?;?? ????????}?? ????????double?left_src_x?=?leftXSum_x?/?leftWeightSum;?? ????????double?left_src_y?=?leftXSum_y?/?leftWeightSum;?? ????????double?right_src_x?=?x;?? ????????double?right_src_y?=?y;?? ?? ?? ????????if(left_src_x<0)?? ????????????left_src_x=0;?? ????????if(left_src_y<0)?? ????????????left_src_y=0;?? ????????if(left_src_x>=pleftImgData->Width)?? ????????????left_src_x=pleftImgData->Width-1;?? ????????if(left_src_y>=pleftImgData->Height)?? ????????????left_src_y=pleftImgData->Height-1;?? ?? ????????byte?leftimg[3];?? ????????bilinear(pleftImgData,left_src_x,left_src_y,leftimg);?? ????????for?(int?i=0;i<3;i++)?? ????????{?? ????????????float?newpiexl=leftimg[i];?? ?? ?? ????????}?? ???????}?? ???}??
本文地址:http://blog.csdn.net/hjimce/article/details/45531039?? ? 作者:hjimce ? ? 聯(lián)系qq:1393852684 ??更多資源請關(guān)注我的博客:http://blog.csdn.net/hjimce? ? ? ? ? ? ? ? 原創(chuàng)文章,轉(zhuǎn)載請保留本行信息。
最后看一下,用這個算法實現(xiàn)的變形融合:
原圖像:
變形融合結(jié)果:
參考文獻:
1、http://www.csie.ntu.edu.tw/~b97074/vfx_html/hw1.html
2、《Feature-Based Image Metamorphosis》
總結(jié)
以上是生活随笔為你收集整理的图像处理(十)基于特征线的图像变形-Siggraph 1992的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。