MoMask / visualization /joints2bvh.py
MeYourHint's picture
first demo version
c0eac48
raw
history blame
6.37 kB
import visualization.Animation as Animation
from visualization.InverseKinematics import BasicInverseKinematics, BasicJacobianIK, InverseKinematics
from visualization.Quaternions import Quaternions
import visualization.BVH_mod as BVH
from visualization.remove_fs import *
from utils.plot_script import plot_3d_motion
from utils import paramUtil
from common.skeleton import Skeleton
import torch
from torch import nn
from visualization.utils.quat import ik_rot, between, fk, ik
from tqdm import tqdm
def get_grot(glb, parent, offset):
root_quat = np.array([[1.0, 0.0, 0.0, 0.0]]).repeat(glb.shape[0], axis=0)[:, None]
local_pos = glb[:, 1:] - glb[:, parent[1:]]
norm_offset = offset[1:] / np.linalg.norm(offset[1:], axis=-1, keepdims=True)
norm_lpos = local_pos / np.linalg.norm(local_pos, axis=-1, keepdims=True)
grot = between(norm_offset, norm_lpos)
grot = np.concatenate((root_quat, grot), axis=1)
grot /= np.linalg.norm(grot, axis=-1, keepdims=True)
return grot
class Joint2BVHConvertor:
def __init__(self):
self.template = BVH.load('./visualization/data/template.bvh', need_quater=True)
self.re_order = [0, 1, 4, 7, 10, 2, 5, 8, 11, 3, 6, 9, 12, 15, 13, 16, 18, 20, 14, 17, 19, 21]
self.re_order_inv = [0, 1, 5, 9, 2, 6, 10, 3, 7, 11, 4, 8, 12, 14, 18, 13, 15, 19, 16, 20, 17, 21]
self.end_points = [4, 8, 13, 17, 21]
self.template_offset = self.template.offsets.copy()
self.parents = [-1, 0, 1, 2, 3, 0, 5, 6, 7, 0, 9, 10, 11, 12, 11, 14, 15, 16, 11, 18, 19, 20]
def convert(self, positions, filename, iterations=10, foot_ik=True):
'''
Convert the SMPL joint positions to Mocap BVH
:param positions: (N, 22, 3)
:param filename: Save path for resulting BVH
:param iterations: iterations for optimizing rotations, 10 is usually enough
:param foot_ik: whether to enfore foot inverse kinematics, removing foot slide issue.
:return:
'''
positions = positions[:, self.re_order]
new_anim = self.template.copy()
new_anim.rotations = Quaternions.id(positions.shape[:-1])
new_anim.positions = new_anim.positions[0:1].repeat(positions.shape[0], axis=-0)
new_anim.positions[:, 0] = positions[:, 0]
if foot_ik:
positions = remove_fs(positions, None, fid_l=(3, 4), fid_r=(7, 8), interp_length=5,
force_on_floor=True)
ik_solver = BasicInverseKinematics(new_anim, positions, iterations=iterations, silent=True)
new_anim = ik_solver()
# BVH.save(filename, new_anim, names=new_anim.names, frametime=1 / 20, order='zyx', quater=True)
glb = Animation.positions_global(new_anim)[:, self.re_order_inv]
if filename is not None:
BVH.save(filename, new_anim, names=new_anim.names, frametime=1 / 20, order='zyx', quater=True)
return new_anim, glb
def convert_sgd(self, positions, filename, iterations=100, foot_ik=True):
'''
Convert the SMPL joint positions to Mocap BVH
:param positions: (N, 22, 3)
:param filename: Save path for resulting BVH
:param iterations: iterations for optimizing rotations, 10 is usually enough
:param foot_ik: whether to enfore foot inverse kinematics, removing foot slide issue.
:return:
'''
## Positional Foot locking ##
glb = positions[:, self.re_order]
if foot_ik:
glb = remove_fs(glb, None, fid_l=(3, 4), fid_r=(7, 8), interp_length=2,
force_on_floor=True)
## Fit BVH ##
new_anim = self.template.copy()
new_anim.rotations = Quaternions.id(glb.shape[:-1])
new_anim.positions = new_anim.positions[0:1].repeat(glb.shape[0], axis=-0)
new_anim.positions[:, 0] = glb[:, 0]
anim = new_anim.copy()
rot = torch.tensor(anim.rotations.qs, dtype=torch.float)
pos = torch.tensor(anim.positions[:, 0, :], dtype=torch.float)
offset = torch.tensor(anim.offsets, dtype=torch.float)
glb = torch.tensor(glb, dtype=torch.float)
ik_solver = InverseKinematics(rot, pos, offset, anim.parents, glb)
print('Fixing foot contact using IK...')
for i in tqdm(range(iterations)):
mse = ik_solver.step()
# print(i, mse)
rotations = ik_solver.rotations.detach().cpu()
norm = torch.norm(rotations, dim=-1, keepdim=True)
rotations /= norm
anim.rotations = Quaternions(rotations.numpy())
anim.rotations[:, self.end_points] = Quaternions.id((anim.rotations.shape[0], len(self.end_points)))
anim.positions[:, 0, :] = ik_solver.position.detach().cpu().numpy()
if filename is not None:
BVH.save(filename, anim, names=new_anim.names, frametime=1 / 20, order='zyx', quater=True)
# BVH.save(filename[:-3] + 'bvh', anim, names=new_anim.names, frametime=1 / 20, order='zyx', quater=True)
glb = Animation.positions_global(anim)[:, self.re_order_inv]
return anim, glb
if __name__ == "__main__":
# file = 'batch0_sample13_repeat0_len196.npy'
# file = 'batch2_sample10_repeat0_len156.npy'
# file = 'batch2_sample13_repeat0_len196.npy' #line #57 new_anim.positions = lpos #new_anim.positions[0:1].repeat(positions.shape[0], axis=-0) #TODO, figure out why it's important
# file = 'batch1_sample12_repeat0_len196.npy' #hard case karate
# file = 'batch1_sample14_repeat0_len180.npy'
# file = 'batch0_sample3_repeat0_len192.npy'
# file = 'batch1_sample4_repeat0_len136.npy'
# file = 'batch0_sample0_repeat0_len152.npy'
# path = f'/Users/yuxuanmu/project/MaskMIT/demo/cond4_topkr0.9_ts18_tau1.0_s1009/joints/{file}'
# joints = np.load(path)
# converter = Joint2BVHConvertor()
# new_anim = converter.convert(joints, './gen_L196.mp4', foot_ik=True)
folder = '/Users/yuxuanmu/project/MaskMIT/demo/cond4_topkr0.9_ts18_tau1.0_s1009'
files = os.listdir(os.path.join(folder, 'joints'))
files = [f for f in files if 'repeat' in f]
converter = Joint2BVHConvertor()
for f in tqdm(files):
joints = np.load(os.path.join(folder, 'joints', f))
converter.convert(joints, os.path.join(folder, 'ik_animations', f'ik_{f}'.replace('npy', 'mp4')), foot_ik=True)