圖形處理(十二)拉普拉斯網格優化、最小二乘網格模型光順
拉普拉斯網格優化、最小二乘網格模型光順
原文地址:http://blog.csdn.net/hjimce/article/details/46505863
作者:hjimce
看這篇博文前,請先參考我的另外一篇博文《圖形處理(三)簡單拉普拉斯網格變形-Siggraph 2004》學習拉普拉斯座標的相關理論知識。這裡要分享的paper,是通過拉普拉斯的方法實現三角網格模型的優化。如果你已經非常熟悉三角網格曲面的拉普拉斯相關理論,實現這篇paper也就非常容易了。網格曲面的拉普拉斯座標不但可以用於變形、光順,還可以用於優化,總之好處多多,你只要學會了這一招,那麼就可以學會這些演算法了。
一、優化原理
利用Laplacian 座標重建的方法進行網格光順,原理很簡單,最簡單的就是隻要把源網格模型Laplacian 座標δ的模長縮小,方向不變,就可以然後進行Laplacian 網格重建,就可以實現簡單的光順效果。
然而如果要進行網格優化呢?怎麼實現?大牛們告訴我們一個比較規則的網格模型一個特點:當網格曲面上任意頂點的區域性片中包含的所有三角面片都為等腰三角形時,該頂點的同一Laplacian 座標和餘切Laplacian 座標相等。當將上述結論由某一個三角面片擴充套件到整個模型表面時可以發現:如果所有的三角面片都接近於正三角形,所有頂點的同一Laplacian 座標和餘切Laplacian 座標接近相等。
ok,上面的原理便是paper的思想,只要你懂得了抓住這個思想,那麼演算法實現起來就容易了。
在三角網格曲面上,頂點vi的拉普拉斯座標定義為,vi點與其一鄰接頂點加權組合的差:
當然權重wij需要滿足歸一化
權值的選取很多,但比較常用的權值是均勻權、餘切權,其計算公式如下:
二、網格優化演算法實現
演算法原理主要是:
1、通過先求解網格曲面餘切權計算得到的Laplacian 座標δ
2、構建均勻權下的拉普拉斯矩陣A
3、然後求解AX=δ.
void CMeshOptimize::OptimizeMesh(TriMesh*Tmesh)
{
Tmesh->need_neighbors();
int vn=Tmesh->vertices.size();
//拉普拉斯矩陣設定
int count0=0;
vector<int>begin_N(vn);
for (int i=0;i<vn;i++)
{
begin_N[i]=count0;
count0+=Tmesh->neighbors[i].size()+1;
}
typedef Eigen::Triplet<double> T;
std::vector<T> tripletList(count0+vn);
for(int i=0;i<vn;i++)
{
int nei_n=Tmesh->neighbors[i].size();
tripletList[begin_N[i]]=T(i,i,-1.0*nei_n);
for (int k = 0;k<nei_n;++k)
{
tripletList[begin_N[i]+k+1]=T(i,Tmesh->neighbors[i][k],1);
}
}
//約束矩陣設定
m_boundary_wieght=100*m_contrain_weight;
for (int i=0;i<vn;i++)
{
if(Tmesh->is_bdy(i))tripletList[count0+i]=T(vn+i,i,m_boundary_wieght);
else tripletList[count0+i]=T(vn+i,i,m_contrain_weight);
}
SparseMatrixType Ls(2*vn,vn);
Ls.setFromTriplets(tripletList.begin(), tripletList.end());
//最小二乘解超靜定方程組
SparseMatrixType ls_transpose=Ls.transpose();
SparseMatrixType LsLs =ls_transpose* Ls;
vector<Eigen::VectorXd> RHSPos;//超靜定方程組右邊
Compute_RHS(RHSPos);
Eigen::SimplicialCholesky<SparseMatrixType>MatricesCholesky(LsLs);
#pragma omp parallel for
for (int i=0;i<3;i++)
{
Eigen::VectorXd xyzRHS=ls_transpose*RHSPos[i];
Eigen::VectorXd xyz=MatricesCholesky.solve(xyzRHS);
for(int j=0;j<vn;j++)
{
Tmesh->vertices[j][i]=xyz[j];
}
}
}
//設定方程組右邊項
void CMeshOptimize::Compute_RHS(vector<Eigen::VectorXd> &RHSPos)
{
int vn=m_OptimizeMesh->vertices.size();
m_OptimizeMesh->need_neighbors();
m_OptimizeMesh->need_adjacentfaces();
RHSPos.clear();
RHSPos.resize(3);
for (int i=0;i<3;i++)
{
RHSPos[i].resize(2*vn);
RHSPos[i].setZero();
}
int fn=m_OptimizeMesh->faces.size();
m_OptimizeMesh->need_adjacentedges();
#pragma omp parallel for
for (int i=0;i<vn;i++)
{
vector<float>CotWeight;
float SumWeight;
CotangentWeights(m_OptimizeMesh,i,CotWeight,SumWeight);
int nei_n=m_OptimizeMesh->neighbors[i].size();
//歸一化
vector<int>&a=m_OptimizeMesh->neighbors[i];
vec ls;
for (int j=0;j<nei_n;j++)
{
ls=ls+CotWeight[j]*m_OptimizeMesh->vertices[a[j]];
}
ls=ls-SumWeight*m_OptimizeMesh->vertices[i];
for (int j=0;j<3;j++)
{
RHSPos[j][i]=ls[j];
}
}
for (int i=vn;i<2*vn;i++)
{
for (int j=0;j<3;j++)
{
if(m_OptimizeMesh->is_bdy(i-vn))RHSPos[j][i]=m_OptimizeMesh->vertices[i-vn][j]*m_boundary_wieght;
else RHSPos[j][i]=m_OptimizeMesh->vertices[i-vn][j]*m_contrain_weight;
}
}
}
//計算一階鄰近點的各自cottan權重
void CMeshOptimize::CotangentWeights(TriMesh*TMesh,int vIndex,vector<float>&vweight,float &WeightSum)
{
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];
}
for (int k=0;k<NeighborNumber;++k)
{
vweight[k]=NeighborNumber*vweight[k]/WeightSum;
}
WeightSum=NeighborNumber;
}
void CMeshOptimize::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=Tmesh->faces[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]);
}
}
}
}
至此三角網格的優化可以說演算法講完了。因為演算法比較簡答,本篇文章篇幅比較小,所以在這篇博文中順便講一下最小二乘網格的相關概念。
參考文獻:Laplacian Mesh Optimization
三、最小二乘網格相關理論
這邊順便講一下最小二乘網格,最小二乘網格最初的概念來源於paper《Least-squares Meshes》,我最初看到最小二乘網格這個概念是在paper《基於最小二乘網格的模型變形演算法》中看到的,因為之前學習微分域的網格變形演算法的時候,基本上對每種變形演算法都有看過相關的paper,用最小二乘網格的方法實現網格變形,其實跟基於多解析度的網格變形演算法是一樣的,都是對低頻空間中的網格模型進行變形操作。
最小二乘網格模型又稱為全域性的拉普拉斯光順網格,就是通過把網格模型的拉普拉斯座標設定為0,然後求解拉普拉斯方程:
即:
其中權重wij為:
這樣重建求解的網格即為最小二乘網格,也是一個光順後的網格模型,因為該模型的拉普拉斯座標全部為0。
當然求解上面的方程還需要控制頂點,控制頂點的個數對效果的影響還是蠻大的,可以看一下下面這個圖:
總之就是控制頂點的個數越少,越是光順。
在paper《Least-squares Meshes》中還演示了通過給定的拓撲連結關係,進行網格補洞,與原網格模型的區別。
*****************作者:hjimce 聯絡qq:1393852684 更多資源請關注我的部落格:http://blog.csdn.net/hjimce
原創文章,轉載請保留本行資訊。*****************
參考文獻:
1、《Least-squares Meshes》
2、《Laplacian Mesh Optimization》
3、《基於最小二乘網格的模型變形演算法》
相關文章
- Mesh模型的Laplace Deformation(網格形變 - 拉普拉斯形變) - C++程式碼實現模型ORMC++
- QEM 網格模型簡化演算法模型演算法
- Ol4網格生成以及優化優化
- 網格簡化 二 、QEM演算法演算法
- LSGAN:最小二乘生成對抗網路
- Processing 網格紋理製作(棋盤格)
- 讀諸葛海 語義網格 理論 模型與 方法模型
- 網格缺陷檢測(二值化閾值分析)
- 顯示網路圖片變形的處理
- 網格上的網格計算
- Python 柵格資料處理教程(二)Python
- 不規則圖形背景排版高階技巧 -- 酷炫的六邊形網格背景圖
- Processing 網格(棋盤格)無限偏移紋理動畫動畫
- Excel不顯示網格線、更改網格線顏色、列印網格線Excel
- 柵格影象的處理
- C#開發PACS醫學影像處理系統(十二):繪圖處理之圖形標記C#繪圖
- 網格簡化-QEM 頂點二次度量的最佳化
- 最小二乘(LS)與總體最小二乘(TLS)總結一TLS
- 網格簡化 QEM 方法詳解
- 交易策略-網格策略(四):示例操作二
- VuePress 部落格之 SEO 優化(二)之重定向Vue優化
- 理解最小二乘解
- 網格優化:潰堤之穴,一個也不能放過優化
- 網格優化中,你遇到過哪些吃效能的設定?優化
- 如何處理 Web 圖片優化?Web優化
- HexMap學習筆記(一)——建立六邊形網格筆記
- 部落格網站網站
- 資訊網格城市
- 快速切割網格
- 網格計算
- 小程式—九宮格心形拼圖
- 現代化網站品牌和風格網站
- UE | Procedural Mesh 程式化網格體
- 二維網格的遷移(java實現)Java
- 大規模服務網格效能優化 | Aeraki xDS 按需載入優化
- Bootstrap 4: 對比普通網格和 Flexbox 網格bootFlex
- 靜態網格體和骨架網格體的區別
- 梯度下降求解最小二乘梯度