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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

图形处理(七)基于热传播的测地距离计算-Siggraph 2013

發布時間:2025/3/21 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 图形处理(七)基于热传播的测地距离计算-Siggraph 2013 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這里要跟大家分享的是2013年Siggraph上面的一篇paper,名為《Geodesics in Heat:A New Approach to Computing Distance Based on Heat Flow》,這篇paper沒有提供源代碼,但是因為算法的思想相當新穎,如果你之前有研究過其它的測地三角網格曲面上的測地距離算法,那么看到這篇paper后,你會非常的激動,覺得這個算法相當神奇,網格曲面上測地距離的計算方法又有了新的突破。因為看到這篇paper非常激動,以至于我興奮得馬上去把代碼寫了一遍,雖然測地距離的計算方法,對于我來說沒什么用,但它的想法,它的思想值得我們學習。

《Geodesics in Heat:A New Approach to Computing Distance Based on Heat Flow》算法主要是通過向量場的方法,通過求解熱流,求解泊松方程。這篇paper我覺得應該把它歸類為向量場類型的文獻,曲面上向量場的應用非常廣泛,可以用于網格優化重建、參數化紋理映射、網格變形……等,如今這篇paper又讓我明白向量場也可以求解測地距離。通過熱量傳播的方法,去求解測地距離,真是大牛啊


一、理論知識

在很早之前對于測地距離,大牛們就推導出了測地距離的求解歸結為求解eikonal equation(程函方程):


且滿足邊界約束條件為:

上面符號Φ就是測地距離。


也就是不管是歐式空間還是曲面空間,其測地距離的梯度模長恒等于1,然后邊界條件:源點的測地距離值為0。

因此很多大牛,都是針對上面的程函方程的求解進行研究,然而所提出的算法都是非線性的方法,因為上面的方程就是非線性方程,在網格曲面上計算量非常大。而這篇paper作者的真正貢獻在于把非線性問題轉換為線性問題,到最后只需要求解一個泊松方程就可以了。開始這個算法之前,建議先看一下《Mesh Editing with Poisson-Based Gradient Field Manipulation》這篇利用泊松方程進行三角網格模型編輯,其實這篇paper的思想應該是借用了《Poisson Image Editing》,如果你已經很熟悉《Mesh Editing with Poisson-Based Gradient Field Manipulation》那么看這篇paper將事半功倍。

1、時間離散化,熱傳播方程時間離散化為:


2、空間離散化,在網格曲面上,離散的拉普拉斯坐標定義為:



Ai表示三角面片的面積的三份之一,j表示頂點i的鄰接頂點。對于有V個頂點的網格模型,我們可以列出上面的V個方程,寫出矩陣形式為:

其中,Lc為拉普拉矩陣,A為包含每個頂點面積的對角矩陣。

代入公式(3)可得:


在網格曲面上,對于一個給定的三角面片平面上,標量場的梯度計算公式如下:



Af表示三角形的面積,N表示三角面片的法矢,ui就是標量場,我們可以把網格曲面每個頂點的測地距離看成是一個標量場。

頂點i的散度離散形式為:



懶得多說廢話了,總之到最后歸結為求解如下方程:


其中b我們需要先通過求解標量場u,然后根據散度的計算公式,計算出頂點測地距離的散度。

二、算法實現

算法總流程:


示意圖:


其中,Φ就是我們要求的測地距離,t是一個無窮小的數,趨近于0

算法具體實現,先說明一下,我下面自己寫的代碼很亂,懶得整理,因為我只是為了學習:

1、求解u

根據公式:


求解u,因此我們需要先算出A,t,Lc,還有δ。接著我將逐漸講解著四個參數的求解:

a、時間t計算:見文獻3.2.4,時間理論上來說是一個非常小的數,文中作者給出了其合適的值為:網格平均邊長的平方


代碼實現如下:

[cpp]?view plaincopy
  • //計算時間步長,文獻中時間步長為:網格模型的邊長平均,然后平方??
  • void?CHeatGeodesics::Set_Time()??
  • {??
  • ????m_BaseMesh->need_edge();??
  • ????int?en=m_BaseMesh->m_edges.size();??
  • ????float?sumlength=0;??
  • ????for?(int?i=0;i<en;i++)??
  • ????{??
  • ????????sumlength+=m_BaseMesh->m_edges[i].length();??
  • ????}??
  • ????sumlength=sumlength/en;//邊長的平均長度??
  • ????sumlength=sumlength*sumlength;??
  • ????m_Time_Step=sumlength;??
  • }??
  • b、面積對角矩陣A的計算:

    [cpp]?view plaincopy
  • //計算Geodesics?in?Heat?文獻中的包含頂點Vi的面積矩陣??
  • void?CHeatGeodesics::Get_Matrix_A_areas()??
  • {??
  • ????m_BaseMesh->need_adjacentfaces();??
  • ????gsi::SparseMatrix?&A=m_A_areas;??
  • ????A.Resize(m_NumberV,m_NumberV);??
  • ????//計算每個三角面片的面積??
  • ????int?fn=m_BaseMesh->faces.size();??
  • ????vector<float>&face_area=m_Faces_Area;??
  • ????face_area.resize(fn);??
  • ????for?(int?i=0;i<fn;i++)??
  • ????{??
  • ????????TriMesh::Face?&f=m_BaseMesh->faces[i];??
  • ????????vec?vij=m_BaseMesh->vertices[f[1]]-m_BaseMesh->vertices[f[0]];??
  • ????????vec?vik=m_BaseMesh->vertices[f[2]]-m_BaseMesh->vertices[f[0]];??
  • ????????float?areaf=0.5*len(vij?CROSS?vik);??
  • ????????face_area[i]=areaf;??
  • ????}??
  • ????//計算拉普拉斯算子中每個頂點所占據的面積,即鄰接三角面片面積總和的三分之一??
  • ????for?(int?i=0;i<m_NumberV;i++)??
  • ????{??
  • ????????vector<int>&af=m_BaseMesh->adjacentfaces[i];??
  • ????????int?n_af=af.size();??
  • ????????float?sumarea=0.0;??
  • ????????for?(int?j=0;j<n_af;j++)??
  • ????????{??
  • ????????????sumarea+=face_area[af[j]];??
  • ????????}??
  • ????????//包含頂點的面積為:鄰接三角面片面積總和的三分之一??
  • ????????sumarea=sumarea/3.0;??
  • ????????A(i,i)?=sumarea;??
  • ????}??
  • }??
  • C、拉普拉斯矩陣Lc計算:

    [cpp]?view plaincopy
  • //計算Geodesics?in?Heat?文獻中的Lc矩陣,即拉普拉斯算子??
  • void?CHeatGeodesics::Get_Matrix_Lc()??
  • {??
  • ????Ls.resize(m_NumberV,m_NumberV);??
  • ????int?m_nEdges=10000?;??
  • ????Ls.reserve(m_nEdges+m_NumberV);??
  • ????for?(int?i?=?0;i<m_NumberV;++i)???
  • ????{??
  • ????????VProperty?&?vi?=?m_vertices[i];??
  • ????????int?nNbrs?=?vi.VNeighbors.size();??
  • ????????for?(int?k?=?0;k<nNbrs;++k)???
  • ????????{??
  • ????????????Ls.insert(i,?vi.VNeighbors[k])?=?vi.VNeiWeight[k];??
  • ????????}??
  • ????????Ls.insert(i,?i)?=?-vi.VSumWeight;??
  • ????}??
  • ????Ls.finalize();??
  • ????gsi::SparseMatrix?&A=m_Lc;??
  • ????A.Resize(m_NumberV,m_NumberV);??
  • ????for(int?k=0;k<m_NumberV;++k)???
  • ????{??
  • ????????for?(?SparseMatrixType::InnerIterator?it(Ls,k);?it;?++it)??
  • ????????{??
  • ????????????A.Set(?it.row(),?it.col(),?it.value()?);??
  • ????????}?????
  • ????}??
  • ??
  • }??
  • 鄰接頂點的余切cot權重計算:

    [cpp]?view plaincopy
  • //鄰接頂點的余切權重計算??
  • void?CHeatGeodesics::CotangentWeights(TriMesh*TMesh,int?vIndex,vector<float>&vweight,float?&WeightSum,bool?bNormalize)//計算一階鄰近點的各自cottan權重??
  • {?????
  • ????int?NeighborNumber=TMesh->neighbors[vIndex].size();??
  • ????vweight.resize(NeighborNumber);??
  • ????WeightSum=0;??
  • ????vector<int>&NeiV=TMesh->neighbors[vIndex];??
  • ????for?(int?i=0;i<NeighborNumber;i++)??
  • ????{??
  • ????????int?j_nei=NeiV[i];??
  • ????????vector<int>tempnei;??
  • ????????Co_neighbor(TMesh,vIndex,j_nei,tempnei);??
  • ????????float?cotsum=0.0;??
  • ????????for?(int?j=0;j<tempnei.size();j++)??
  • ????????{??
  • ????????????vec?vivo=TMesh->vertices[vIndex]-TMesh->vertices[tempnei[j]];??
  • ????????????vec?vjvo=TMesh->vertices[j_nei]-TMesh->vertices[tempnei[j]];??
  • ????????????float?dotvector=vivo?DOT?vjvo;??
  • ????????????dotvector=dotvector/sqrt(len2(vivo)*len2(vjvo)-dotvector*dotvector);??
  • ????????????cotsum+=dotvector;??
  • ????????}??
  • ????????vweight[i]=cotsum/2.0;??
  • ????????WeightSum+=vweight[i];??
  • ????}??
  • ??
  • ????if?(?bNormalize?)???
  • ????{??
  • ????????for?(int?k=0;k<NeighborNumber;++k)??
  • ????????{??
  • ????????????vweight[k]/=WeightSum;??
  • ????????}??
  • ????????WeightSum=1.0;??
  • ????}??
  • }??
  • ??
  • void?CHeatGeodesics::?UniformWeights(TriMesh*TMesh,int?vIndex,vector<float>&vweight,float?&WeightSum,bool?bNormalize)??
  • {??
  • ????int?NeighborNumber=TMesh->neighbors[vIndex].size();??
  • ????vweight.resize(NeighborNumber);??
  • ????WeightSum?=?0;??
  • ????for?(int?j?=?0;?j?<NeighborNumber;?++j?)??
  • ????{??
  • ????????vweight[j]?=?1;??
  • ????????WeightSum?+=?vweight[j];??
  • ????}??
  • ??
  • ????if?(?bNormalize?)??
  • ????{??
  • ????????for?(?int?k?=?0;?k?<?NeighborNumber;?++k?)??
  • ????????????vweight[k]?/=?WeightSum;??
  • ????????WeightSum=1.0;??
  • ????}??
  • }??
  • //獲取兩頂點的共同鄰接頂點??
  • void?CHeatGeodesics::Co_neighbor(TriMesh?*Tmesh,int?u_id,int?v_id,vector<int>&co_neiv)??
  • {??
  • ????Tmesh->need_adjacentedges();??
  • ????vector<int>&u_id_ae=Tmesh->adjancetedge[u_id];???
  • ????int?en=u_id_ae.size();??
  • ????Tedge?Co_Edge;??
  • ????for?(int?i=0;i<en;i++)??
  • ????{??
  • ????????Tedge?&ae=Tmesh->m_edges[u_id_ae[i]];??
  • ????????int?opsi=ae.opposite_vertex(u_id);??
  • ????????if?(opsi==v_id)??
  • ????????{??
  • ????????????Co_Edge=ae;??
  • ????????????break;??
  • ????????}??
  • ????}??
  • ????for?(int?i=0;i<Co_Edge.m_adjacent_faces.size();i++)??
  • ????{??
  • ????????TriMesh::Face?af=Co_Edge.m_adjacent_faces[i];??
  • ????????for?(int?j=0;j<3;j++)??
  • ????????{??
  • ????????????if((af[j]!=u_id)&&(af[j]!=v_id))??
  • ????????????{??
  • ????????????????co_neiv.push_back(af[j]);??
  • ????????????}??
  • ????????}??
  • ????}??
  • }??
  • 最后求解方程組,就可以把u求出來了。

    2、求解三角網格曲面的熱量場▽u (Heat flow):

    這一步直接根據公式:


    求解就可以了。然后▽u進行歸一化,并取熱量場的反方向,即求測地距離的梯度場:

    [cpp]?view plaincopy
  • for?(int?i=0;i<fn;i++)??
  • {??
  • ????TriMesh::Face?&f=m_BaseMesh->faces[i];??
  • ????for?(int?j=0;j<3;j++)??
  • ????{??
  • ????????vec?ei=m_BaseMesh->vertices[f[(j+2)%3]]-m_BaseMesh->vertices[f[(j+1)%3]];??
  • ????????vec?FNormal=normalize(m_BaseMesh->FaceNormal[i]);??
  • ????????vec?gradient=float(m_vertices[f[j]].VU*0.5/m_Faces_Area[i])*(FNormal?CROSS?ei);??
  • ????????FGradient[i]=FGradient[i]+gradient;??
  • ????}??
  • ????normalize(FGradient[i]);//文獻的重要兩步?即梯度單位化和梯度反向??
  • ????FGradient[i]=float(-1.0)*FGradient[i];??
  • ????//length0[i]=len(FGradient[i]);??
  • }??

  • 3、對測地距離的梯度場X,求取散度。

    根據公式:


    求解測地距離標量場梯度場的散度。

    [cpp]?view plaincopy
  • //計算頂點的散度??
  • for?(int?i=0;i<vn;i++)??
  • {?????
  • ????vector<int>&adjacentface=m_BaseMesh->adjacentfaces[i];??
  • ????for?(int?j=0;j<adjacentface.size();j++)??
  • ????{??
  • ????????TriMesh::Face?&f=m_BaseMesh->faces[adjacentface[j]];??
  • ????????for?(int?k=0;k<3;k++)??
  • ????????{??
  • ????????????if?(f[k]==i)??
  • ????????????{??
  • ????????????????vec?ei=m_BaseMesh->vertices[f[(k+2)%3]]-m_BaseMesh->vertices[f[(k+1)%3]];??
  • ????????????????/*vec?gradient=float(0.5/m_Faces_Area[adjacentface[j]])*(m_BaseMesh->FaceNormal[adjacentface[j]]?CROSS?ei);?
  • ????????????????//計算散度?
  • ????????????????m_vertices[i].VDivergence+=(FGradient[adjacentface[j]]?DOT?gradient)*m_Faces_Area[adjacentface[j]];*/??
  • ??
  • ????????????????vec?e1=m_BaseMesh->vertices[f[(k+1)%3]]-m_BaseMesh->vertices[f[k]];??
  • ????????????????vec?e2=m_BaseMesh->vertices[f[(k+2)%3]]-m_BaseMesh->vertices[f[k]];??
  • ????????????????float?cot_angle1=Cot_angle(e2,ei);??
  • ????????????????float?cot_angle2=Cot_angle(float(-1.0)*e1,ei);??
  • ????????????m_vertices[i].VDivergence+=0.5*(cot_angle1*(e1?DOT?FGradient[adjacentface[j]])+cot_angle2*(e2?DOT?FGradient[adjacentface[j]]));??
  • ????????????????break;??
  • ????????????}??
  • ????????}??
  • ????}??
  • ??
  • }??
  • 4、最后求解通過求解泊松方程,求取網格曲面上每個頂點的測地距離。

    [cpp]?view plaincopy
  • gsi::SparseLinearSystem?*pSystemPos2?=?new?gsi::SparseLinearSystem();??
  • ????gsi::Solver_TAUCS?*?pSolverPos2?=?new?gsi::Solver_TAUCS(pSystemPos2);??
  • ????pSystemPos2->Resize(m_NumberV,?m_NumberV);??
  • ????pSystemPos2->ResizeRHS(1);??
  • ????//由于以下解方程組使用Solver_TAUCS::TAUCS_LLT?,即半正定矩陣模式??
  • ????m_Lc.Multiply(-1.0);??
  • ??
  • ????m_Lc(0,0)?=m_Lc(0,0)?+10;??
  • ??
  • ????pSystemPos2->SetMatrix(m_Lc);??
  • ????if?(?!?pSystemPos2->Matrix().IsSymmetric()?)assert(0);??
  • ????pSolverPos2->OnMatrixChanged();??
  • ????pSolverPos2->SetStoreFactorization(true);??
  • ????pSolverPos2->SetSolverMode(?gsi::Solver_TAUCS::TAUCS_LLT?);??
  • ????pSolverPos2->SetOrderingMode(?gsi::Solver_TAUCS::TAUCS_METIS?);??
  • ????gsi::Vector?pRHSPos2?;??
  • ????pRHSPos2.Resize(m_NumberV);??
  • ????//?右邊項約束添加??
  • ????for?(int?i=0;i<m_NumberV;i++)??
  • ????{??
  • ????????pRHSPos2[i]=float(-1.0)*m_vertices[i].VDivergence;????
  • ????}??
  • ??
  • ?????//初始條件為方程熱源的測地距離為0??
  • ????pRHSPos2[0]=pRHSPos2[0]+10;??
  • ??????
  • ????pSystemPos2->SetRHS(0,?pRHSPos2);??
  • ????bool?boksoveLs2=pSolverPos2->Solve();??
  • ????if?(!boksoveLs2)assert(0);??
  • ????m_GeodesicsDistance.resize(m_NumberV);??
  • ????for?(?int?i=0;i<m_NumberV;++i)???
  • ????{?????
  • ???????m_GeodesicsDistance[i]=(float)pSystemPos2->GetSolution(i,0);??
  • ????}??
  • 可以說到了這里算法已經結束了,當然paper后面還有后續的處理,比如距離光順,還有邊界條件的等問題。但都屬于后處理,你如果要實現跟paper一模一樣的效果,還是得好好看一看后面的邊界條件問題。


    邊界條件對于結果的影響還是蠻大的。

    OK,做一下簡單的總結:算法整個過程就說白了就是求解一個泊松方程,說的更白一點,就是求解一個大型的方程組AX=b,因此我們的目標就是先算出A、b,其中對于一個給定的網格曲面模型來說,A就是拉普拉斯矩陣,是固定的。b的求解就是整個算法成功的關鍵。本文地址:http://blog.csdn.net/hjimce/article/details/46415499?? ? 作者:hjimce ? ? 聯系qq:1393852684 ??更多資源請關注我的博客:http://blog.csdn.net/hjimce? ? ? ? ? ? ? ? 原創文章,轉載請保留本行信息。

    參考文獻:

    1、《Geodesics in Heat:A New Approach to Computing Distance Based on Heat Flow》

    2、《Mesh Editing with Poisson-Based Gradient Field Manipulation》

    3、《Poisson Image Editing》

    總結

    以上是生活随笔為你收集整理的图形处理(七)基于热传播的测地距离计算-Siggraph 2013的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。