|
|
from __future__ import absolute_import |
|
|
from __future__ import division |
|
|
from __future__ import print_function |
|
|
|
|
|
import numpy as np |
|
|
import scipy.io as sio |
|
|
from .. import mesh |
|
|
from . import fit |
|
|
from . import load |
|
|
|
|
|
class MorphabelModel(object): |
|
|
"""docstring for MorphabelModel |
|
|
model: nver: number of vertices. ntri: number of triangles. *: must have. ~: can generate ones array for place holder. |
|
|
'shapeMU': [3*nver, 1]. * |
|
|
'shapePC': [3*nver, n_shape_para]. * |
|
|
'shapeEV': [n_shape_para, 1]. ~ |
|
|
'expMU': [3*nver, 1]. ~ |
|
|
'expPC': [3*nver, n_exp_para]. ~ |
|
|
'expEV': [n_exp_para, 1]. ~ |
|
|
'texMU': [3*nver, 1]. ~ |
|
|
'texPC': [3*nver, n_tex_para]. ~ |
|
|
'texEV': [n_tex_para, 1]. ~ |
|
|
'tri': [ntri, 3] (start from 1, should sub 1 in python and c++). * |
|
|
'tri_mouth': [114, 3] (start from 1, as a supplement to mouth triangles). ~ |
|
|
'kpt_ind': [68,] (start from 1). ~ |
|
|
""" |
|
|
def __init__(self, model_path, model_type = 'BFM'): |
|
|
super( MorphabelModel, self).__init__() |
|
|
if model_type=='BFM': |
|
|
self.model = load.load_BFM(model_path) |
|
|
else: |
|
|
print('sorry, not support other 3DMM model now') |
|
|
exit() |
|
|
|
|
|
|
|
|
self.nver = self.model['shapePC'].shape[0]/3 |
|
|
self.ntri = self.model['tri'].shape[0] |
|
|
self.n_shape_para = self.model['shapePC'].shape[1] |
|
|
self.n_exp_para = self.model['expPC'].shape[1] |
|
|
self.n_tex_para = self.model['texMU'].shape[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'])) |
|
|
|
|
|
|
|
|
def get_shape_para(self, type = 'random'): |
|
|
if type == 'zero': |
|
|
sp = np.random.zeros((self.n_shape_para, 1)) |
|
|
elif type == 'random': |
|
|
sp = np.random.rand(self.n_shape_para, 1)*1e04 |
|
|
return sp |
|
|
|
|
|
def get_exp_para(self, type = 'random'): |
|
|
if type == 'zero': |
|
|
ep = np.zeros((self.n_exp_para, 1)) |
|
|
elif type == 'random': |
|
|
ep = -1.5 + 3*np.random.random([self.n_exp_para, 1]) |
|
|
ep[6:, 0] = 0 |
|
|
|
|
|
return ep |
|
|
|
|
|
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) |
|
|
vertices = np.reshape(vertices, [int(3), int(len(vertices)/3)], 'F').T |
|
|
|
|
|
return vertices |
|
|
|
|
|
|
|
|
def get_tex_para(self, type = 'random'): |
|
|
if type == 'zero': |
|
|
tp = np.zeros((self.n_tex_para, 1)) |
|
|
elif type == 'random': |
|
|
tp = np.random.rand(self.n_tex_para, 1) |
|
|
return tp |
|
|
|
|
|
def generate_colors(self, tex_para): |
|
|
''' |
|
|
Args: |
|
|
tex_para: (n_tex_para, 1) |
|
|
Returns: |
|
|
colors: (nver, 3) |
|
|
''' |
|
|
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. |
|
|
|
|
|
return colors |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def rotate(self, vertices, angles): |
|
|
''' rotate face |
|
|
Args: |
|
|
vertices: [nver, 3] |
|
|
angles: [3] x, y, z rotation angle(degree) |
|
|
x: pitch. positive for looking down |
|
|
y: yaw. positive for looking left |
|
|
z: roll. positive for tilting head right |
|
|
Returns: |
|
|
vertices: rotated vertices |
|
|
''' |
|
|
return mesh.transform.rotate(vertices, angles) |
|
|
|
|
|
def transform(self, vertices, s, angles, t3d): |
|
|
R = mesh.transform.angle2matrix(angles) |
|
|
return mesh.transform.similarity_transform(vertices, s, R, t3d) |
|
|
|
|
|
def transform_3ddfa(self, vertices, s, angles, t3d): |
|
|
R = mesh.transform.angle2matrix_3ddfa(angles) |
|
|
return mesh.transform.similarity_transform(vertices, s, R, t3d) |
|
|
|
|
|
|
|
|
def fit(self, x, X_ind, max_iter = 4, isShow = False): |
|
|
''' fit 3dmm & pose parameters |
|
|
Args: |
|
|
x: (n, 2) image points |
|
|
X_ind: (n,) corresponding Model vertex indices |
|
|
max_iter: iteration |
|
|
isShow: whether to reserve middle results for show |
|
|
Returns: |
|
|
fitted_sp: (n_sp, 1). shape parameters |
|
|
fitted_ep: (n_ep, 1). exp parameters |
|
|
s, angles, t |
|
|
''' |
|
|
if isShow: |
|
|
fitted_sp, fitted_ep, s, R, t = fit.fit_points_for_show(x, X_ind, self.model, n_sp = self.n_shape_para, n_ep = self.n_exp_para, max_iter = max_iter) |
|
|
angles = np.zeros((R.shape[0], 3)) |
|
|
for i in range(R.shape[0]): |
|
|
angles[i] = mesh.transform.matrix2angle(R[i]) |
|
|
else: |
|
|
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) |
|
|
angles = mesh.transform.matrix2angle(R) |
|
|
return fitted_sp, fitted_ep, s, angles, t |
|
|
|
|
|
|
|
|
|