- 转载请注明出处
这个代码的主要内容就是根据一些随机的参数从3d模型渲染到2d图片
然后从图片再变换回去
代码解析
1. 载入模型
bfm = MorphabelModel('Data/BFM/Out/BFM.mat')
# 这里面初始化的参数有:
def __init__(self, model_path, model_type = 'BFM'):
super( MorphabelModel, self).__init__()
if model_type=='BFM':
self.model = load.load_BFM(model_path)
# fixed attributes
self.nver = self.model['shapePC'].shape[0]/3 # 顶点的个数
self.ntri = self.model['tri'].shape[0] # 三角形的个数
self.n_shape_para = self.model['shapePC'].shape[1] # 199
self.n_exp_para = self.model['expPC'].shape[1] # 29
self.n_tex_para = self.model['texMU'].shape[1] # 1
self.kpt_ind = self.model['kpt_ind'] # 关键点索引
self.triangles = self.model['tri']
self.full_triangles = np.vstack((self.model['tri'], self.model['tri_mouth'])) # 按行堆叠, 例如两个(1, 3)成为(2, 3)
2. 生成mesh, 也就是一些点,三角形的集合
sp = bfm.get_shape_para('random')
ep = bfm.get_exp_para('random')
# 上面就是生成一些随机的形状与表情参数
vertices = bfm.generate_vertices(sp, ep)
# 模型所有不包含颜色后加和的点
tp = bfm.get_tex_para('random')
colors = bfm.generate_colors(tp)
colors = np.minimum(np.maximum(colors, 0), 1)
- 看看这个generate_vertices, 输入形状系数和表情系数,返回顶点, 大小是(nver, 3)
def generate_vertices(self, shape_para, exp_para):
'''
Args:
shape_para: (n_shape_para, 1)
exp_para: (n_exp_para, 1)
Returns:
vertices: (nver, 3)
'''
vertices = self.model['shapeMU'] + self.model['shapePC'].dot(shape_para) +
self.model['expPC'].dot(exp_para)
# 这个求解的过程就是均值加上系数乘PCA降维后的基向量
vertices = np.reshape(vertices, [int(3), int(len(vertices)/3)], 'F').T
return vertices
- 这个generated_colors是根据纹理系数生成颜色, 其实这里并没有对
纹理也就是颜色做后续的fit, 只不过是用一个随机数乘了原来的系数
# 输入纹理系数, 返回颜色
def generate_colors(self, tex_para):
'''
Args:
tex_para: (n_tex_para, 1) 这个其实是1,1
Returns:
colors: (nver, 3)
'''
colors = self.model['texMU'] +
self.model['texPC'].dot(tex_para*self.model['texEV'])
# 这里是个随机的数乘原来的texEV
'''
texEV是纹理正交空间的系数, 大小是199*1
'''
colors = np.reshape(colors, [int(3), int(len(colors)/3)], 'F').T/255.
return colors
3. 坐标变换
- 指定了s, R, t等缩放旋转位移参数, 然后把3D的点变到2D
s = 8e-04
angles = [0, 0, 0]
t = [0, 0, 0]
transformed_vertices = bfm.transform(vertices, s, angles, t)
projected_vertices = transformed_vertices.copy()
'''
这里应该就对应相机坐标系的变换, 相机坐标系和两个投影变换不要混了
'''
# using stantard camera & orth projection
h = w = 256; c = 3
# 这个是变换到图片坐标系上,x,y加上图片的一半, y轴反转
image_vertices = mesh.transform.to_image(projected_vertices, h, w)
# 这个在之前的精度中讲过, 就是利用重心坐标对颜色进行插值
'''
这个的返回值是image: [h, w, 3] , 三个通道是一样的
'''
image = mesh.render.render_colors(image_vertices, bfm.triangles, colors, h, w)
4. 使用68个关键点做fit
x = projected_vertices[bfm.kpt_ind, :2]
# 这个是相机坐标系下的人脸关键点
X_ind = bfm.kpt_ind # index of keypoints in 3DMM. fixed.
# fit 根据二维点来解出对应的3d参数叫fit
fitted_sp, fitted_ep, fitted_s, fitted_angles, fitted_t = bfm.fit(x, X_ind, max_iter = 3)
- 这里的fit算法里面有个黄金标准算法, 网上没有讲的很深入的, 同时现在大多数是用深度学习的方法fit了, 这个先占坑
这里的fit结果有5个参数, 分别是形状, 表情, s, R, t