ARAP参数化算法

article/2025/9/7 22:35:47

ARAP参数化算法实现

综述

三维模型的参数化把三维模型映射到二维平面,LSCM在映射的过程中尽可能地保持三角形的角度相同,ARAP参数化算法在LSCM的基础上尽可能的保证三角形没有扭曲地映射在二维平面上。

算法设计

因为需要映射过程中尽可能保持三角形没有扭曲,所有发生变换的矩阵为旋转矩阵。我们可以通过限制变换矩阵,得到相应参数化结果。

假设三维模型上的一个三角形 t t t表示为 x t = x_t= xt={ x t 0 , x t 1 , x t 2 x_t^0,x_t^1,x_t^2 xt0,xt1,xt2},相应的映射后的三角形表示为 u t u_t ut={ u t 0 , u t 1 , u t 2 u_t^0,u_t^1,u_t^2 ut0,ut1,ut2}。这两个三角形之间的关系可以用一个2×2的雅可比(Jobabian)矩阵 J t ( u ) J_t(u) Jt(u)来表示。

L t L_t Lt来表示受到限制的变换矩阵,也就是旋转变换矩阵,可以定义如下的能量函数来表示可能的变换和目标变换之间的差别:
E ( u , L ) = ∑ t = 1 T A t ∣ ∣ J t ( u ) − L t ∣ ∣ F 2 E(u,L)=\sum_{t=1}^TA_t||J_t(u)-L_t||^2_F E(u,L)=t=1TAtJt(u)LtF2
其中 A t A_t At是三角形的面积。能量函数也可以重构为:
E ( u , L ) = 1 2 ∑ t = 1 T ∑ i = 0 2 c o t ( θ t i ) ∣ ∣ ( u t i − u t i + 1 ) − L t ( x t i − x t i + 1 ) ∣ ∣ 2 = 1 2 ∑ ( i , j ) ∈ h a l f e d g e c o t ( θ i j ) ∣ ∣ ( u i − u j ) − L t ( i , j ) ( x i − x j ) ∣ ∣ 2 E(u,L)=\frac{1}{2}\sum_{t=1}^T \sum_{i=0}^2 cot(\theta _t^i)||(u_t^i-u_t^{i+1}) - L_t(x_t^i-x_t^{i+1})||^2 \\ =\frac{1}{2}\sum_{(i,j)\in halfedge}cot(\theta _{ij})||(u_i-u_j)-L_{t(i,j)}(x_i-x_j)||^2 E(u,L)=21t=1Ti=02cot(θti)(utiuti+1)Lt(xtixti+1)2=21(i,j)halfedgecot(θij)(uiuj)Lt(i,j)(xixj)2
求解能量函数的最小值,对其进行求导
∑ j ∈ N ( i ) [ c o t ( θ i j ) + c o t ( θ i j ) ] ( u i − u j ) = ∑ j ∈ N ( i ) [ c o t ( θ i j ) L t ( i , j ) + c o t ( θ j i ) L t ( j , i ) ] ( x i − x j ) ( i = 1 , . . . , n ) \sum_{j\in N(i)}[cot(\theta_{ij})+cot(\theta_{ij})](u_i-u_j) = \sum_{j\in N(i)}[cot(\theta_{ij})L_{t(i,j)}+cot(\theta_{ji})L_{t(j,i)}](x_i-x_j) \\(i=1,...,n) jN(i)[cot(θij)+cot(θij)](uiuj)=jN(i)[cot(θij)Lt(i,j)+cot(θji)Lt(j,i)](xixj)(i=1,...,n)

其中 θ i j \theta_{ij} θij是与边相对的角度,在上面的公式中,未知数为二维平面上的映射的顶点 u u u与变换矩阵 L L L

因此参数化算法构造为一个如下的优化问题:
( u , t ) = a r g m i n ( u , t ) E ( u , t ) , L t ∈ M (u,t)=argmin_(u,t)E(u,t),L_t\in M (u,t)=argmin(u,t)E(u,t),LtM

因为 L t L_t Lt限制为旋转矩阵(等距变换矩阵),形式如下:
M = ( c o s θ s i n θ − s i n θ c o s θ ) : θ ∈ [ 0 , 2 π ) M=\begin{pmatrix}cos \theta & sin \theta\\-sin\theta & cos\theta\end{pmatrix} :\theta \in [0,2\pi) M=(cosθsinθsinθcosθ):θ[0,2π)

针对该能量函数有两个未知量,不太好求解,基本思路分为两个步骤求解:

  1. local phase:固定所有的 u u u,求出每个三角形对应的最佳旋转矩阵 L t L_t Lt

    使用LSCM算法得到的二维坐标初始化 U U U

    对于如下的2×2矩阵:
    S t ( u ) = ∑ i = 0 2 c o t ( θ t i ) ( u t i − u t i + 1 ) ( x t i − x t i + 1 ) S_t(u)=\sum_{i=0}^2 cot(\theta_t^i)(u_t^i-u_t^{i+1})(x_t^i-x_t^{i+1}) St(u)=i=02cot(θti)(utiuti+1)(xtixti+1)
    可以做SVD分解为:
    J = U ∑ V T J=U\sum V^T J=UVT
    其中
    ∑ = ( σ 1 0 0 σ 2 ) \sum =\begin{pmatrix} \sigma_1 & 0\\0 & \sigma_2\end{pmatrix} =(σ100σ2)
    那么可以得到最佳的变换矩阵:
    L = U V T L=UV^T L=UVT
    参考:SVD分解求变换矩阵

  2. global phase:固定每个三角形对应的旋转矩阵,求解最佳的U

    通过上一步将求解的 L L L代入上述求解能量方程最小值的线性系统中,求解 U U U

    方程左侧为固定矩阵,可以构建线性矩阵 A X = B AX=B AX=B(A矩阵构建方法参考另一篇博客)

    B矩阵为 n × 2 n×2 n×2的矩阵,在相应的行上修改值。

  3. 重复迭代以上的两个步骤,直到能量变化不明显

算法结果

初始模型LSCMARAP
在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述

从实验结果可以看出ARAP在尽可能保持三角形角度不变的同时保证三角形不发生扭曲

代码

//用LACM算法得到初始的参数化坐标作为初始值
void MeshViewerWidget::InitUV() {Mesh mesh1=Parameterize();vt.resize(v_count);for (int i = 0; i < v_count; i++) {vt[i][0] = New_U[i];vt[i][1] = New_V[i];}
}//计算每个点的cot
void MeshViewerWidget::CalCots() {Cots.resize(f_count);W.resize(v_count);for (int k = 0; k < f_count; ++k) {Cots[k].resize(3);//每行为3列}for (int k = 0; k < v_count; k++) {W[k].resize(v_count);}//遍历所有的面for (auto f_it = mesh.faces_begin(); f_it != mesh.faces_end(); f_it++) {vector<int> v(3, 0);int i = 0;for (auto fv_it = mesh.fv_iter(*f_it); fv_it.is_valid(); ++fv_it) {  //遍历该面的所有点v[i] = (*fv_it).idx();   //保存该面顶点编号i++;}//计算3条边的边长double a, b, c;a = (mesh.point((mesh.vertex_handle(v[0]))) - mesh.point((mesh.vertex_handle(v[1])))).length();b = (mesh.point((mesh.vertex_handle(v[1]))) - mesh.point((mesh.vertex_handle(v[2])))).length();c = (mesh.point((mesh.vertex_handle(v[2]))) - mesh.point((mesh.vertex_handle(v[0])))).length();double angle1 = acos((a*a + c * c - b * b) / (2 * a*c));double angle2 = acos((a*a + b * b - c * c) / (2 * a*b));double angle3 = acos((b*b + c * c - a * a) / (2 * b*c));		Cots[(*f_it).idx()][0] = 1.0 / tan(angle1);Cots[(*f_it).idx()][1] = 1.0 / tan(angle2);Cots[(*f_it).idx()][2] = 1.0 / tan(angle3);		W[v[1]][v[2]] = 1.0 / tan(angle1);W[v[2]][v[0]] = 1.0 / tan(angle2);W[v[0]][v[1]] = 1.0 / tan(angle3);}
}//对于每个三角形计算参数化之前和参数化之后的变换矩阵
Eigen::Matrix3d MeshViewerWidget::ComputeTransformMatrix(Mesh::FaceIter face) {Eigen::Vector2d u[3];int face_id = (*face).idx();int i = 0;for (auto fv_it = mesh.fv_iter(*face); fv_it.is_valid(); ++fv_it) {  //遍历面上的所有点u[i] = vt[(*fv_it).idx()];i++;}Eigen::MatrixXd uu, xx, cc;uu.resize(3, 3);xx.resize(3, 3);cc.resize(3, 3);for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {uu(i, j) = 0.0;xx(i, j) = 0.0;cc(i, j) = 0.0;}}for (i = 0; i < 3; i++) {int n = (i + 1) % 3;int p = (i + 2) % 3;uu(i, 0) = u[p][0] - u[n][0];uu(i, 1) = u[p][1] - u[n][1];xx(i, 0) = edgeVectors[face_id][i][0];xx(i, 1) = edgeVectors[face_id][i][1];cc(i, i) = Cots[face_id][i];}Eigen::Matrix3d CovMat = xx.transpose() * cc * uu;return CovMat;
}//对每个三角形点的变换矩阵用SVD分解方法计算变换矩阵的旋转部分
Eigen::Matrix3d MeshViewerWidget::SVD(Eigen::Matrix3d CovMat) {JacobiSVD<Eigen::MatrixXd> svd(CovMat, ComputeThinU | ComputeThinV);Matrix3d V = svd.matrixV(), U = svd.matrixU();Matrix3d rot = V * U.transpose();if (rot.determinant() < 0) {if (svd.singularValues()[0] < svd.singularValues()[1]) {for (int i = 0; i < 2; i++) {U(i,0) = -1 * U(i,0);}}else {for (int i = 0; i < 2; i++) {U(i, 1) = -1 * U(i, 1);}}rot = V * U.transpose();}return rot;
}//计算所有三角形的旋转矩阵
vector < Eigen::Matrix2d> MeshViewerWidget::ARAPLocal() {vector < Eigen::Matrix2d> R(f_count);for (auto f_it = mesh.faces_begin(); f_it != mesh.faces_end(); f_it++) {Eigen::Matrix3d CovMat = ComputeTransformMatrix(f_it);Eigen::Matrix3d rot = SVD(CovMat);R[(*f_it).idx()] << rot(0, 0), rot(0, 1),rot(1, 0), rot(1, 1);}return R;
}//计算拉普拉斯矩阵L
void MeshViewerWidget::Laplacian() {Eigen::SparseMatrix<double> D, M;M.resize(v_count, v_count);std::vector<Eigen::Triplet<double>> tripletlist_M;for (auto v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); v_it++) {int v_idx = (*v_it).idx();double w = 0;for (auto vv_it = mesh.vv_begin(*v_it); vv_it.is_valid(); ++vv_it) {int vv_idx = (*vv_it).idx();w += W[v_idx][vv_idx] + W[vv_idx][v_idx];tripletlist_M.push_back(Eigen::Triplet<double>(v_idx, vv_idx, W[v_idx][vv_idx] + W[vv_idx][v_idx]));}tripletlist_M.push_back(Eigen::Triplet<double>(v_idx, v_idx,-1.0 * w));}M.setFromTriplets(tripletlist_M.begin(), tripletlist_M.end());// L = D * M;L = M;
}void MeshViewerWidget::ARAPGlobal(vector < Eigen::Matrix2d> R) {Eigen::MatrixXd b_xy;b_xy.resize(v_count, 2);for (int i = 0; i < v_count; i++) {b_xy(i, 0) = 0.0;b_xy(i, 1) = 0.0;}for (auto f_it = mesh.faces_begin(); f_it != mesh.faces_end(); f_it++) {int face_idx = (*f_it).idx();vector<int> v(3, 0);int i = 0;for (auto fv_it = mesh.fv_iter(*f_it); fv_it.is_valid(); ++fv_it) {  //遍历该面的所有点v[i] = (*fv_it).idx();   //保存该面顶点编号i++;}for (i = 0; i < 3; i++) {int n = (i + 1) % 3;int p = (i + 2) % 3;Eigen::Vector2d E_ij = edgeVectors[face_idx][i];//Eigen::Vector2d b = R[face_idx] * E_ij * Cots[face_idx][i];Eigen::Vector2d b = Cots[face_idx][i] * R[face_idx] *  E_ij  ;b_xy(v[n], 0) -= b[0];b_xy(v[p], 0) += b[0];b_xy(v[n], 1) -= b[1];b_xy(v[p], 1) += b[1];}}Eigen::LeastSquaresConjugateGradient<Eigen::SparseMatrix<double> > bxy_Solver_sparse;bxy_Solver_sparse.compute(L);Eigen::SparseMatrix<double> Bxy_Sparse;Bxy_Sparse.resize(v_count, 2);buildSparseMatrix(Bxy_Sparse, b_xy, v_count, 2);Eigen::MatrixXd U_xy;U_xy.resize(v_count, 2);U_xy = bxy_Solver_sparse.solve(Bxy_Sparse);for (int i = 0; i < v_count; i++) {vt[i][0] = U_xy(i, 0);vt[i][1] = U_xy(i, 1);}}double MeshViewerWidget::CalEdgeVectors(vector < Eigen::Matrix2d> R) {double E = 0;for (auto f_it = mesh.faces_begin(); f_it != mesh.faces_end(); f_it++) {int face_idx = (*f_it).idx();vector<int> v(3, 0);int i = 0;for (auto fv_it = mesh.fv_iter(*f_it); fv_it.is_valid(); ++fv_it) {  //遍历该面的所有点v[i] = (*fv_it).idx();   //保存该面顶点编号i++;}for (i = 0; i < 3; i++) {int n = (i + 1) % 3;int p = (i + 2) % 3;Eigen::Vector2d U_ij = vt[v[p]] - vt[v[n]];Eigen::Vector2d E_ij = edgeVectors[face_idx][i];E += pow(Cots[face_idx][i] * (U_ij - R[face_idx] * E_ij).norm(), 2);}return E;}return E;
}
Mesh MeshViewerWidget::ARAP() {InitUV();//edgeVectors;CalCots();  //计算每个三角面的顶点对应的cotLaplacian();double E = -1;double Epre = 0;int iterations = 0;cout << endl << endl << endl;while (abs(Epre - E) >0.01 ){cout << "iterations:                 " << iterations << endl;iterations++;Epre = E;vector < Eigen::Matrix2d> R;R=ARAPLocal();ARAPGlobal(R);E=CalEdgeVectors(R);if (iterations > 3) break;}Mesh temp;Mesh::VertexHandle *vhandle;    //点的迭代器指针vhandle = new Mesh::VertexHandle[mesh.n_vertices()];  //开辟的空间大小for (auto v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it) {   //遍历每个点,将每个点对应的2D坐标加入tempvhandle[(*v_it).idx()] = temp.add_vertex(Mesh::Point(vt[(*v_it).idx()][0], vt[(*v_it).idx()][1], 0));}for (auto f_it = mesh.faces_begin(); f_it != mesh.faces_end(); ++f_it) {   //遍历每个面std::vector<Mesh::VertexHandle>  face_vhandles;   //存放2D的每个三角形平面for (auto fv_it = mesh.fv_iter(*f_it); fv_it.is_valid(); ++fv_it) {  //遍历该面的所有点face_vhandles.push_back(vhandle[(*fv_it).idx()]);  //通过vhandle将3D的点转化到2D平面上}temp.add_face(face_vhandles);}return temp;
}

参考文献

[1] Liu L, Zhang L, Xu Y, et al. A local/global approach to mesh parameterization[C]. symposium on geometry processing, 2008, 27(5): 1495-1504.

[2] Sorkine O, Alexa M. As-rigid-as-possible surface modeling[C]. symposium on geometry processing, 2007: 109-116.

[3] Least-Squares Rigid Motion Using SVD

[4] https://blog.csdn.net/why18767183086/article/details/108002084


http://chatgpt.dhexx.cn/article/XyQsYQVj.shtml

相关文章

经典论文推导: As-Rigid-As-Possible(ARAP) Surface Modeling

论文As-Rigid-As-Possible Surface Modeling 发表于SGP 2007&#xff0c;是变形领域的经典论文&#xff0c;目前引用已经超过1000次。网格变形要求产生视觉合理并且大致满足物理规律的变形效果&#xff0c;而模型细节的保持很大程度地满足了这种需求。刚性作为一个重要的属性在…

ARAP(As-Rigid-As-Possible)变形算法

上图中&#xff0c;最左侧的模型为其初始状态&#xff0c;由后面几种模型形状的变换我们可以发现它实际上就是要求变形前后模型每一个局部都只经历了平移或者旋转&#xff0c;也就是刚体变换&#xff08;rigid -transformation&#xff09;。我们知道&#xff0c;刚体变换是不会…

MiKTeX安装

MiKTeX安装&#xff08;导师使用这个版本&#xff09; 1&#xff0c;下载 2&#xff0c;打开文件 3&#xff0c;安装 安装好点击下一步&#xff0c; 二&#xff1a;选择并配置编辑器TeXstudio 1&#xff0c;下载地址&#xff1a;http://texstudio.sourceforge.net/ 错了…

LaTeX配置:MiKTeX+WinEdt

LaTeX(Tex)是一种语言&#xff0c;搭配上编译器(MIkTEX、TexLive)和编辑器(WinEdt、VS Code等)可以程序化生成PDF文档。本次给大家分享的是MiKTeXWinEdt的配置(安装方便快捷&#xff0c;占用空间小)。具体步骤如下&#xff1a; 预下载文件 下载Ctex2.9版本套装&#xff1a;http…

MikTex与Texlive 共存,vscode设置使用Texlive编译

为了在word中使用latex&#xff0c;安装了MikTex。 但在电脑上同时安装了MikTex 与Texlive之后&#xff0c;vscode本来之前默认用texlive编译项目&#xff0c;项目默认的编辑器变成了MikTex。 解决办法一&#xff1a; 一种说法是&#xff0c;在程序路径中删除MikTex的相关路径变…

MikTex+TexStudio配置

安装MikTex TexStudio cmd下mpm调出MikTex宏包管理器&#xff0c;下载宏包 相关参考&#xff1a; http://freshstu.com/2013/05/how-to-use-chinese-fonts-in-latax/ http://blog.sina.com.cn/s/blog_564b1b9b0100njfc.html http://guo-ch.blog.163.com/blog/static/120255…

【LaTeX】MiKTeX+TeXstudio安装过程

下载并安装MiKTeX 下载地址&#xff1a;MiKTeX 一路点击下一步即可 下载并安装TeXstudio 下载地址&#xff1a;TeXstudio 一路点击下一步即可 安装顺序一定是先MiKTeX&#xff0c;然后是TeXstudio&#xff0c;否则会报错 检查下配置 option->configure Texstudio-&g…

二、VSCode——MiKTeX编写latex编码

免安装下载VSCode https://blog.csdn.net/qq_40837795/article/details/128037675 下载MiKTeX https://miktex.org/download 配置MiKTeX https://blog.csdn.net/qq_40837795/article/details/120388489 配置VSCode LaTeX workshop 1、点击左侧Extensions&#xff0c;搜…

关于MikTex和TexStudio的安装

一位老师在课上向我们推荐使用了MikTex和TexStudio&#xff0c;要求我们提交pdf文档&#xff0c;以及学习一下新工具&#xff0c;这里记录一下这两个工具的安装过程。 1. Mik的下载与安装。 直接搜索MikTex&#xff0c;进入官方网址就可以下载。MikTex: https://miktex.org/ …

Texmaker+Miktex配置

Mixtex下载链接&#xff1a;https://miktex.org/download Texmaker下载链接&#xff1a;http://www.xm1math.net/texmaker/ 两者正常下载安装&#xff0c;记住Mixtex安装路径&#xff0c;主要是配置Texmaker 打开Texmaker&#xff0c;点击选项——配置Texmaker&#xff0c;出…

论文写作的又一利器:VSCode + Latex Workshop + MikTex + Git

论文写作的又一利器&#xff1a;VSCode Latex Workshop MikTex Git 摘要 本文介绍了Windows平台下面&#xff0c;MiktexVSCodeLatex WorkshopGit配置用于撰写学术论文的方法。 对正反向搜索的配置也进行了介绍。 1.引言 学术论文写作&#xff0c;Latex是不二的选择。它是免…

关于TeX,LaTeX,MikTex,CTeX,etc.

下文是王垠介绍的TeX&#xff0d;&#xff0d;一个专业的排版系统&#xff01; 这是一个介绍 TeX — 一个漂亮&#xff0c;有趣又可靠的排版程序的网页。希望通过我的介绍&#xff0c;你能体会到使用 TeX 的快乐感觉。 这页分成了很多小主题&#xff0c;可以叫做一个非技术性…

LaTeX相关概念介绍及CTeX、MiKTeX+TeXstudio环境搭建

1. LaTeX相关概念 TeX是Donald E.Knuth 开发的&#xff0c;用来进行文字排版的软件&#xff0c;特别的是TeX是数学公式排版最好的系统&#xff0c;很多世界一流的出版社均采用TeX系统出版书籍和期刊。 LaTeX 是一种格式&#xff0c;这种格式采用TeX作为自己的排版引擎。LaTeX…

Windows 下基于 MikTeX 的 Latex 环境配置小记

前言 前一阵子换了C盘&#xff0c;好多软件要重新安装。最近需要构建 tex 文件生成 pdf&#xff0c;发现之前配置好的后端已经无了。之前用的 TexLive&#xff0c;这次试试 MikTeX。TeX 相关的名词可以参考下面的链接。&#xff08;大概会被CSDN扣流量了&#xff09; TeX 家族…

MiKTeX 中文支持的解决方案

首先&#xff0c;在开始菜单中&#xff0c;展开MiKTeX 2.9文件夹&#xff0c;打开MiKTeX Console&#xff0c;并且选择以admin启动。然后切换到Packages面板&#xff0c;搜索ctex。选择ctex宏包右键安装。安装完毕后&#xff0c;列表中ctex右边的Installed on会出现安装日期。 …

Miktex update更新失败 code=7 更换镜像 取消代理

刚安装的miktex发现直接更新失败&#xff0c;提示connect failed, a connection to api2.miktex.org could not be established&#xff0c;报错如下&#xff0c;code 7&#xff1a; 搜索了一下发现国内最好要使用镜像源&#xff0c;新版的miktex需要在console里面更新&#xf…

【LaTeX】MikTex+TexStudio安装及配置论文写作环境

最近在BMC投稿&#xff0c;说真心&#xff0c;投稿真是比写代码麻烦多了&#xff0c;本来用word编辑好好的&#xff0c;满心喜悦等待着投稿&#xff0c;登上网站一看人家要Latex编辑成文的&#xff0c;好吧&#xff0c;开始下软件往死里搞&#xff0c;终于在今天投出去了&#…

windows下 MikTex的安装和初步使用

*2023.6.27更&#xff1a;推荐使用overleaf&#xff0c;可在线编辑&#xff0c;而且几乎不需要管各种bug&#xff0c;但是是适用于英文投稿&#xff0c;而且编译稍慢。* 以下原文&#xff1a; 这篇文章是给初次使用的小白看的&#xff0c;大神请离开。 作为小白一个&#xf…

【科研工具】【MikTex】MikTex安装和使用

MikTex下载地址&#xff1a;Getting MiKTeX MikTex&#xff1a;读作&#xff08;mick-tech&#xff09;是Tex/LaTeX及相关程序的最新实现。[1] Tex是Donald Ervin写的一个排版系统&#xff0c;用于书籍的美观排版&#xff0c;尤其是包含了大量数学内容的书[1]。 安装&#xf…