|
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() |
|
|
|
|
|
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: |
|
''' |
|
|
|
|
|
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) |
|
|
|
|
|
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() |
|
|
|
|
|
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) |
|
|
|
glb = Animation.positions_global(anim)[:, self.re_order_inv] |
|
return anim, glb |
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) |