forked from facebookresearch/ContactPose
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mano_fitting.py
136 lines (118 loc) · 4.46 KB
/
mano_fitting.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# Copyright (c) Facebook, Inc. and its affiliates.
# Code by Samarth Brahmbhatt
from utilities.import_open3d import *
from open3d import pipelines
import utilities.misc as mutils
assert(mutils.load_mano_model is not None)
import numpy as np
import chumpy as ch
import os
import json
import transforms3d.quaternions as txq
import pickle
osp = os.path
o3dr = pipelines.registration
def mano_param_dict(n_pose_params, n_betas=10):
out = {
'pose': [0.0 for _ in range(n_pose_params+3)],
'betas': [0.0 for _ in range(n_betas)],
'valid': False,
'mTc': {
'translation': [0.0, 0.0, 0.0],
'rotation': [1.0, 0.0, 0.0, 0.0],
}
}
return out
def get_palm_joints(p, n_joints_per_finger=4):
"""
get the 6 palm joints (root + base of all 5 fingers)
"""
idx = [0]
for fidx in range(5):
idx.append(1 + fidx*n_joints_per_finger)
return p[idx]
def register_pcs(src, tgt, verbose=True):
"""
registers two pointclouds by rigid transformation
target_x = target_T_source * source_x
"""
assert(len(src) == len(tgt))
ps = o3dg.PointCloud()
ps.points = o3du.Vector3dVector(src)
pt = o3dg.PointCloud()
pt.points = o3du.Vector3dVector(tgt)
c = [[i, i] for i in range(len(src))]
c = o3du.Vector2iVector(c)
r = o3dr.TransformationEstimationPointToPoint()
r.with_scaling = False
if verbose:
print('Rigid registration RMSE (before) = {:f}'.
format(r.compute_rmse(ps, pt, c)))
tTs = r.compute_transformation(ps, pt, c)
pst = ps.transform(tTs)
if verbose:
print('Rigid registration RMSE (after) = {:f}'.
format(r.compute_rmse(pst, pt, c)))
return tTs
class MANOFitter(object):
_mano_dicts = None
def __init__(self):
if MANOFitter._mano_dicts is None:
MANOFitter._mano_dicts = []
for hand_name in ('LEFT', 'RIGHT'):
filename = osp.join('thirdparty', 'mano', 'models',
'MANO_{:s}.pkl'.format(hand_name))
with open(filename, 'rb') as f:
MANOFitter._mano_dicts.append(pickle.load(f, encoding='latin1'))
@staticmethod
def fit_joints(both_joints, n_pose_params=15, shape_sigma=10.0,
save_filename=None):
"""
Fits the MANO model to hand joint 3D locations
both_jonts: tuple of length 2, 21 joints per hand, e.g. output of ContactPose.hand_joints()
n_pose_params: number of pose parameters (excluding 3 global rotation params)
shape_sigma: reciprocal of shape regularization strength
save_filename: file where the fitting output will be saved in JSON format
"""
mano_params = []
for hand_idx, joints in enumerate(both_joints):
if joints is None: # hand is not present
mano_params.append(mano_param_dict(n_pose_params)) # dummy
continue
cp_joints = mutils.openpose2mano(joints)
# MANO model
m = mutils.load_mano_model(MANOFitter._mano_dicts[hand_idx],
ncomps=n_pose_params, flat_hand_mean=False)
m.betas[:] = np.zeros(m.betas.size)
m.pose[:] = np.zeros(m.pose.size)
mano_joints = mutils.mano_joints_with_fingertips(m)
mano_joints_np = np.array([[float(mm) for mm in m] for m in mano_joints])
# align palm
cp_palm = get_palm_joints(np.asarray(cp_joints))
mano_palm = get_palm_joints(np.asarray(mano_joints_np))
mTc = register_pcs(cp_palm, mano_palm)
cp_joints = np.dot(mTc, np.vstack((cp_joints.T, np.ones(len(cp_joints)))))
cp_joints = cp_joints[:3].T
cp_joints = ch.array(cp_joints)
# set up objective
objective = [m-c for m,c in zip(mano_joints, cp_joints)]
mean_betas = ch.array(np.zeros(m.betas.size))
objective.append((m.betas - mean_betas) / shape_sigma)
# optimize
ch.minimize(objective, x0=(m.pose, m.betas, m.trans), method='dogleg')
p = mano_param_dict(n_pose_params)
p['pose'] = np.array(m.pose).tolist()
p['betas'] = np.array(m.betas).tolist()
p['valid'] = True
p['mTc']['translation'] = (mTc[:3, 3] - np.array(m.trans)).tolist()
p['mTc']['rotation'] = txq.mat2quat(mTc[:3, :3]).tolist()
mano_params.append(p)
# # to access hand mesh vertices and faces
# vertices = np.array(m.r)
# vertices = mutils.tform_points(np.linalg.inv(mTc), vertices)
# faces = np.array(m.f)
if save_filename is not None:
with open(save_filename, 'w') as f:
json.dump(mano_params, f, indent=4, separators=(',', ':'))
print('{:s} written'.format(save_filename))
return mano_params