​单张图像三维人脸重建必备入门face3d—3DMM

news/2024/7/7 23:24:23

作者:小灰灰 来源:投稿
编辑:学姐

本次的例子是将pipeline生成的图片作用于3DMM,重新拟合成新的图片。

load model

3DMM的表达式:

𝑆̅ ∈ 𝑅3𝑛是平均人脸形状,𝐴 脸扫描训练得到的身份基,是人脸的身份参数。𝐴𝑒𝑥𝑝是表情基,是人脸的表情参数。这个公式只要我们确定199维的形状参数和29维的表情参数就可以得到一张三维模型。

bfm = MorphabelModel('Data/BFM/Out/BFM.mat')

这里面是使用牙买加人脸,200个人脸,男生与女生个100个训练出来的。这个mode里面有:

'shapeMU': [3*nver, 1]. *  自然状态下的一个人脸
 'shapePC': [3*nver, n_shape_para]. *  s1,s2  有199 特征向量
 'shapeEV': [n_shape_para, 1]. ~   形状的特征值
 'expMU': [3*nver, 1]. ~       有平均的表情形状,有一个人脸
 'expPC': [3*nver, n_exp_para]. ~    有29个表情特征向量
 'expEV': [n_exp_para, 1]. ~          有29个特征值
 'texMU': [3*nver, 1]. ~           有平均形状的纹理
 'texPC': [3*nver, n_tex_para]. ~   有纹理199特征向量
 'texEV': [n_tex_para, 1]. ~           有 199纹理特征值
 'tri': [ntri, 3] (start from 1, should sub 1 in python and c++). *  105840行三角形
 'tri_mouth': [114, 3] (start from 1, as a supplement to mouth triangles). ~  嘴114
 'kpt_ind': [68,] (start from 1). ~    在53215中其中68个是关键点

可以看到我们将表情的与形状的值加起来得到新的。

model['shapeMU'] = (model['shapeMU'] + model['expMU']).astype(np.float32)
model['tri'] = model['tri'].T.copy(order = 'C').astype(np.int32) - 1 
#减一是从1开始,从零开始
model['tri_mouth'] = model['tri_mouth'].T.copy(order = 'C').astype(np.int32) - 1 
# 114,3 包含嘴的三角形 
# kpt ind
model['kpt_ind'] = (np.squeeze(model['kpt_ind']) - 1).astype(np.int32)
#原始的索引都是从1开始的,68个索引。

generate face mesh

根据3DMM我们知道就可以生成一张图片,我们将需要的参数进行随机数乘以一个非常大的数。

sp = bfm.get_shape_para('random')#形状
 ep = bfm.get_exp_para('random')#表情

(-1.5,1.5)之间,有可能增强表情,优肯削弱表情。

在根据sp, ep的参数,根据3DMM公式去生成人脸。

公式对应的的代码

vertices = self.model['shapeMU'] + self.model['shapePC'].dot(shape_para) + self.model['expPC'].dot(exp_para)
vertices = np.reshape(vertices, [int(3), int(len(vertices)/3)], 'F').T
colors = self.model['texMU'] + self.model['texPC'].dot(tex_para*self.model['texEV'])
colors = np.reshape(colors, [int(3), int(len(colors)/3)], 'F').T/255.

随机生成的三维人脸如下:

transform vertices to proper position

我们将随机生成的三维人脸经过👇

s = 8e-04
 angles = [10, 30, 20]
 t = [0, 0, 0]

pipepline中讲过,通过s,a,t可以直接生成下面的图片左图所示。对应的三维模型右图所示。这是通过代码生成的图片。

本节介绍怎样通过3DMM来拟合生成跟输入的上述图片近似一样。

代码中取出68个点进行比较。

x = projected_vertices[bfm.kpt_ind, :2]

接下来需要68个点x,68个点对应的索引。

fitted_sp, fitted_ep, fitted_s, fitted_angles, fitted_t = bfm.fit(x, X_ind, max_iter = 3)

x: (n, 2) 是二维图片的坐标,X_ind:代表的是68个点对应的索引的三维点。max_iter:是迭代的次数。

3.1 fit_points

进行优化

fitted_sp, fitted_ep, s, R, t = fit.fit_points(x, X_ind, self.model, n_sp = self.n_shape_para, n_ep = self.n_exp_para,max_iter = max_iter)

接下来看代码:

sp = np.zeros((n_sp, 1), dtype = np.float32)
 ep = np.zeros((n_ep, 1), dtype = np.float32)

shapeMU = model['shapeMU'][valid_ind, :]
 shapePC = model['shapePC'][valid_ind, :n_sp]
 expPC = model['expPC'][valid_ind, :n_ep]

上面的shapeMU是根据平均人脸,shapePC选出平均的199个包含68个关键点的人脸,expPC选出29个表情包含68个关键点的人脸。

上面的就是以前53215个点,现在变成68个点进行采集,204是68*3,3表示的是x,y,z

接下来进行迭代:

for i in range(max_iter):

我们根据3DMM公式:

对应的代码为:

X = shapeMU + shapePC.dot(sp) + expPC.dot(ep) #X.shape=(204, 1)

将(204,1)变成(3,68)

X = np.reshape(X, [int(len(X)/3), 3]).T

3.2 estimate_affine_matrix_3d22d

这个函数是我们要找一个变换将三维空间的点变换到一个二维空间的点。

X表示三维的点,x表示二维的点。

X = X.T; x = x.T
 assert(x.shape[1] == X.shape[1])
 n = x.shape[1]
 assert(n >= 4)

这里面我们要解方程组,八个未知数,所以n的大于等于4才能解出方程组。

求x的均值,求模长的平均

x = x - np.tile(mean[:, np.newaxis], [1, n])
average_norm = np.mean(np.sqrt(np.sum(x**2, 0)))
scale = np.sqrt(2) / average_norm #0.03789943607443278

为什么是np.sqrt(2),因为边长为1的等腰直角三角形的斜边就是np.sqrt(2)

三维的点同理可得:

我们需要求八个数,来得到二维坐标

A = np.zeros((n*2, 8), dtype = np.float32);
 X_homo = np.vstack((X, np.ones((1, n)))).T
 A[:n, :4] = X_homo
 A[n:, 4:] = X_homo
 b = np.reshape(x, [-1, 1])
X1=P1X1+P2X2+P3X3+P4
Y1=P1Y1+P2Y2+P3Y3+P4
X1=P1XZ1+P2YZ2+P3Z3+P4Z

可使用下面的代码可得

我们估计P矩阵,第一行是P8的前四个数,第二行是P8的后四个是,第三行是0 0 0 1

通过齐次坐标可以是x=PX实现二维点与三维点的转换。

这里面的UV是下面的这个图:

3.3 P2sRt(P)

  • 代码对应的意思就是将x,y,z旋转之后到x,y的坐标轴上。

  • 主要恢复S,R,T

3.4 matrix2angle(R)

主要用R矩阵恢复夹角。

x: pitch y: yaw z: roll

会把旋转的三个角度算出来,把算出来的三个角度与一开始生成图片的角度的图片10,30,20,匹配看是否相等,那么根据步骤(2 再固定αi,βi 优化s,R,t ) 我们已经把s,r,t估计出来了,那就会估计 αi,βi 形状参数和表情参数。

3.5 estimate shape

下面就是估计

3 固定s,R,t,先优化βi 。代码里面是estimate_expression.

4 固定s,R,t,在优化αi。代码里面是estimate_shape.

verify fitted parameters

接下来就是通过参数进行顶点的生成,通过s,r,t得到旋转的顶点,然后渲染回2D图片,具体可详看pipepline。

# verify fitted parameters
 fitted_vertices = bfm.generate_vertices(fitted_sp, fitted_ep)
 transformed_vertices = bfm.transform(fitted_vertices, fitted_s, fitted_angles, fitted_t)
 
 image_vertices = mesh.transform.to_image(transformed_vertices, h, w)
 fitted_image = mesh.render.render_colors(image_vertices, bfm.triangles, colors, h, w)

三维人脸重建经典论文解读👇👇👇点击卡片学起来


http://lihuaxi.xjx100.cn/news/401414.html

相关文章

【操作系统】模式切换篇

CPU的模式 什么是CPU的模式?这和CPU的发展过程有关,最开始CPU是8位的,后来发展到16位,然后是32位,现在是64位,多少多少位指的是寄存器的位宽。CPU能使用的寄存器宽度以及CPU使用的指令等就构成了CPU的模式…

十四、JavaScript——类型转化_数值

一、定义 将其他的数据类型转化为数值 使用Number()函数来将其他类型转化为数值,适用于任何类型使用parseInt() -- 将一个字符串转化为一个整数 -解析时,会自作向右读取一个字符串,直到读取到所有的整数位 parseFloat()…

特征融合与灰色回归的滚动轴承性能退化评估

摘要 针对传统退化指标无法准确反映滚动轴承全寿命周期内退化状态的问题,提出一种特征融合与灰色回归的滚动轴承性能退化评估方法.该方法提取滚动轴承振动信号的高维退化特征,构建基于单调性、相关性和鲁棒性的综合评价准则,选择有效退化特征并构建敏感指标集;提出核独立成…

jemeter 压测并生成报告

文章目录前言一、压测步骤1.1 创建jvm文件1.2 压测生成测试文件及html压测结果二、结果分析前言 jmeter:是Apche公司使用Java平台开发的一款测试工具。 一、压测步骤 1.1 创建jvm文件 添加测试计划 (当打开jmeter默认有一个测试计划)添加…

协同过滤毕业设计 商品推荐系统

文章目录1 简介2 常见推荐算法2.1 协同过滤2.2 分解矩阵2.3 聚类2.4 深度学习3 协同过滤原理4 系统设计4.1 示例代码(py)5 系统展示5.1 系统界面5.2 推荐效果6 最后1 简介 🔥 Hi,大家好,这里是学长的毕设系列文章! &#x1f525…

MySQL索引优化(二)

文章目录一、查询优化1. 索引失效(1)不满足最左前缀法则,索引失效(2)在索引列上做任何计算、函数操作,索引失效(3)存储引擎使用索引中范围条件右边的列,索引失效&#xf…

一条道简单的算法引发的思考

前言 新一季的 Rick&Morty 已经上线,剧集质量虽然有所下降,但 E03 中的 SheepCounter 挺有意思。自己照着剧中的设定开发了一款界面极其相似、交互更为丰富的小程序,小程序的终极目标只有一个:数羊!数羊&#xff…

【字节码】Java Instrumentation 简介 以及 ASM 组合案例

1.概述 本文来自:深入理解JVM字节码 并且对其进行补充。 2.Java Instrumentation简介 JDK从1.5版本开始引人了java.lang.instrument 包,开发者可以更方便的实现字节码增强。其核心功能由java.lang.instrument.Instrumentation 提供,这个接口的方法提供了注册类文件转换器…