网格拟合

这里的网格拟合,当然是三维网格拟合。

三维网格拟合有什么用?呃.....以后再说吧......

什么是网格拟合

嗯,经常经常听到[拟合]这个名词,它表示一个动作。最常听到的是最小二乘拟合---把一系列点拟合出一条线---把三维空间散乱点拟合出一个平面、球面、柱面...

拟合,总需要一个拟合源,总要有一个目标。上面场景中,拟合源一般是二维或者三维的点数据,目标一般是线、平面、球面或者柱面等等的数学参数表示。

网格拟合_第1张图片

OK,大概已经知道拟合是做什么的了。那我们的三维网格拟合,又是怎么回事呢?

其实,三维网格的拟合和上述所谓拟合并没有本质区别,只是数据源和拟合目标都是可任意的三维网格数据而已,将两个三维网格拟合,其结果仍然是一个三维网格,只不过拟合后的结果网格可能带有源数据和目标数据的部分显著特征。用公式表示就是:

Mesh A + Mesh B = Mesh C,其中 “+”表示拟合计算

注意:上述所谓三维网格的拟合,并不包含网格带纹理的情况。即纹理不参与拟合运算。

说白了,所谓三维网格的拟合,其实就是多个三维网格经过一定的 ‘’ 修修剪剪 ‘’ 产生一个新网格的过程,新网格就是拟合的结果。

怎样进行网格拟合

哦,我已经知道了什么是网格拟合,但怎么进行网格拟合计算呢。

假如,我有A和B两个三维网格数据,网格A有10w点,30w三角面片,网格B有7w点,20w三角面片,额(⊙﹏⊙)....它俩应该怎么进行拟合计算呢?---我应分别取A或B的哪一部分参加计算呢?A和B不在同一个坐标系下怎么办呢?我的拟合运算会不会改变A或者B的三角网格顶点之间的连接关系呢?A和B体积相差太大(比如一只狗和一只猫)怎么办呢?需要缩放吗?缩放系数怎么处理呢?拟合质量如何评价呢?......呃呃...头好大....

既然头好大,那就规定特殊场景吧。

场景

还是来拿手头的人头网格说事。首先,人头点云,隐含了点云的体积头差不多(小孩子除外);其次,同一个扫描设备获取的三维人头网格数据量不会差别太大(其实这个条件没啥影响);最后,就是...我手头只有三维人头数据可以测试(么有去找更多的)。

有下面两组数据,其中上边的图像记为Mesh A,下边的记为Mesh B,

网格拟合_第2张图片

网格拟合_第3张图片

Mesh A:只有真实人脸部分,网格和顶点数量较多,三角网格之间并不是很“顺滑”;Mesh B:虚拟全人头网格,网格数目和顶点数相对A来说较少,且mesh表面光顺性较好。

目标,将A和B拟合,构造带有真实人脸网格模型的三维人头(实际应用中是有重要意义的)。

简单拟合

至此,可能很多很多朋友,已经想到了一种非常简单的网格拟合方法。怎么做呢?

首先,将Mesh A和Mesh B初步对齐,即将两个网格人脸部分对齐,放在同一个坐标系下。如何对齐呢?很简单,因为Mesh A和Mesh B都表示人脸网格,则其面部必有共同特征---眼睛、鼻子、嘴巴、鼻梁、眉毛.....通过选两组Mesh中对应的特征点,基于指定的精确的对应点对,通过ICP计算,即可获得某Mesh的变换矩阵,从而使得两个Mesh统一到统一坐标系下。示意图如下:

网格拟合_第4张图片

其次,在完成上述初步对齐--即两个模型的眼睛对眼睛、鼻尖对鼻尖...后,我们只要改变Mesh B的面部网格结构形状即可。通过设定脸部距离阈值,在保证Mesh B各个顶点链接关系不变的条件下(即不改变拓扑关系),改变Mesh B的脸部特征点的网格顶点坐标,使得Mesh B脸部呈现Mesh A的外观特征,即完成了两个网格的拟合。

如何实现上述计算呢?最简单的---置换或者说替换。通过设定的距离阈值,找到Mesh B和Mesh A的脸部对应点对,将两个Mesh的对应点对的坐标直接进行替换,同时保证Mesh B中点的链接关系不变(若该边,可能 “ 脸就不是脸了 ”)。如下图:

网格拟合_第5张图片

可以看到,基于【简单拟合】理论,基本可以两个网格的拟合,并能基本得到自己想要的结果---含有真实人脸形状的全人头网格模型。

这就够了吗?

风险

显然不够!其实,上边的【简单拟合】存在较大的风险,其结果基本无法应用在实际生产项目中。

风险点1:质量较差。以上述方式拟合网格模型,如上图边界部分所示,拟合结果存在较大的突起,有非常突兀的形状变化。

风险点2:精度无法保证。其实上述方式中,只是简单的对应点的顶点替换,这就导致了在替换时,各个参与计算的点没有考虑其邻域点的分布情况。计算过程中,极大概率导致某个特征点的坐标多次变换,而且该特征点的坐标只与最后一次变化(最后一个对应点)有关---因为,Mesh B和 Mesh A的点数、网格数目及点密度都不一样,当将两个网格拟合计算时,必然存在一对多或者多对一的情况。

风险点3:尺寸差别较大。这个问题应该怎么对待呢?!实际中,绝对没有两个一摸一样的人,那么尺寸必然存在一定误差。那我们只能尽可能的减少这种网格尺寸上的误差,可见,在上面计算中,没有考虑这一点。

......

当然,还有其他很多的风险点,自己也无法一一穷举。总之,上述描述方案也许会是一种网格拟合的解决方案,但是对网格的后处理工作增加了许多的难度,需要更进一步的研究与改进。

......

有没有其他办法呢?

实现

有!先上一篇论文 《Review of Statistical Shape Spaces for 3D Data with Comparative Analysis for Human Faces》.

其实,其他方案的网格拟合(如论文)和上述<怎样进行网格拟合>大方向上基本一致:初步对齐-->更新顶点-->改变网格形状,只不过需要在拟合过程中将上述几个风险点考虑进去。

基于上述基本理解以及对所提到论文的浅见,设计并实现了如下网格拟合方案:

首先,初步对齐同<怎样进行网格拟合>所述一致;

其次,分别统计两个Mesh中特征点之间的欧式距离之和,定义:若两个距离之比接近1.0,则认为两个Mesh尺寸接近,若大于1.2或者小于0.8,怎认为尺寸差异较大,则对较小距离和的Mesh进行适当缩放,直至重新计算的比值接近1.0 (针对风险3);

第三,分别计算两个Mesh网格各个顶点法向量,设置对应点对角度阈值和距离阈值,对应点对采用如下方式计算:(1)从Mesh A中取特征点f;(2)在Mesh B中寻找距离f最近的三角面片T;(3)计算 f 到T的垂足,则该垂足即为与f对应的特征点;若该对应点对满足设置的角度阈值和距离阈值,怎认为该点对满足计算条件;

第四,基于特征点邻域信息,计算Mesh A中每个特征点的3*4变换矩阵,该变换矩阵使得特征点基于局部形状向Mesh B的对应点逐步变换,其中,变换矩阵采用L--BFGS 算法(关于如何构造LBFGS求解函数,可能还需要另外大幅篇幅介绍,且自己也并不是很精通,故此舍去相关介绍,待后期补充吧)。并设置Lbfgs算法求解参数:迭代次数、梯度容差、代价函数、前后两次误差变换阈值、近邻点计算权重等。

第五、设置总体迭代次数。

基于上述大体思路,获得到结果如下所示:

网格拟合_第6张图片

可见,基本不存在上图中的问题。

小结

三维网格拟合是三维运算的一种,可以产生新的自己所需要的网格。

在我的场景中,有一个人脸模型和一个全人头模型,我想获得一个带有真实(扫描仪获取)人脸的全人头三维网格,通过网格拟合的形式,获得了我想要的结果。

其实现在回头来看,这不仅仅是网格的拟合问题,【实现】中步骤算法还能更进一步的扩展到用于解决非刚性配准问题。因为在上述求解方案中,基于特征点及其邻域点集,为每一个特征点都计算了一个3*4的仿射变换矩阵,该矩阵在局部上能够使得点以线性的方式逐步接近目标点,从全局来看更加平稳和光滑。本质上点云或网格的非刚性配准也是如此,同时非刚性配准又有很多很多实际应用...

那三角网格拟合到底有啥用呢?比如:可以用来解决网格大面积缺失修复问题;可以用来产生新的有意思的网格,如蛇头换狗脸....;可以用来解决人头补全问题;更进一步的可是用来实现虚拟整容与预览等医美方面,能够为不同人脸虚拟定制、预览不同发型;可以用来处理牙齿建模问题....

在这里,【实现】步骤中,并没有具体介绍如何进行拟合计算的细节,同时也并没有贴相关代码,因为这需要更大的篇幅,也需要更多更深入的理解和公式推理。

在本片文章介绍中,其实还剩去了非常重要或着说非常有必要的一个步骤:纹理贴图。因为在很多很多场景中,网格和纹理是密不可分的--如医美。在进行三角网格拟合变换后,再使用原来的纹理图其实已经没有太大意义,其一:网格点坐标已经变换,基本不适用于原来的纹理坐标,会产生严重的纹理变形,视觉效果非常差;其二:网格拟合的目的就在于获取新的模型,那么再贴原来的纹理图又有什么意义呢。

关于网格拟合后纹理重映射,涉及到纹理替换、网格(纹理)展开、二三维仿射矩阵计算、投影矩阵......,需要大量篇幅介绍,这里暂且留坑吧。

注意:关于本篇文章的细节(包含纹理映射部分)已于去年(2021年)申请发明专利,目前状态还在实申中,专利中主要包含了拟合的数学公式推导、纹理映射计算等相关内容。

你可能感兴趣的:(c++算法)