|
import torch |
|
import numpy as np |
|
import argparse |
|
import pickle |
|
import smplx |
|
|
|
from utils import bvh, quat |
|
|
|
|
|
def parse_args(): |
|
parser = argparse.ArgumentParser() |
|
parser.add_argument("--model_path", type=str, default="./visualization/data/smpl/") |
|
parser.add_argument("--model_type", type=str, default="smpl", choices=["smpl", "smplx"]) |
|
parser.add_argument("--gender", type=str, default="MALE", choices=["MALE", "FEMALE", "NEUTRAL"]) |
|
parser.add_argument("--num_betas", type=int, default=10, choices=[10, 300]) |
|
parser.add_argument("--poses", type=str, default="data/gWA_sFM_cAll_d27_mWA5_ch20.pkl") |
|
parser.add_argument("--fps", type=int, default=60) |
|
parser.add_argument("--output", type=str, default="data/gWA_sFM_cAll_d27_mWA5_ch20.bvh") |
|
parser.add_argument("--mirror", action="store_true") |
|
return parser.parse_args() |
|
|
|
def mirror_rot_trans(lrot, trans, names, parents): |
|
joints_mirror = np.array([( |
|
names.index("Left"+n[5:]) if n.startswith("Right") else ( |
|
names.index("Right"+n[4:]) if n.startswith("Left") else |
|
names.index(n))) for n in names]) |
|
|
|
mirror_pos = np.array([-1, 1, 1]) |
|
mirror_rot = np.array([1, 1, -1, -1]) |
|
grot = quat.fk_rot(lrot, parents) |
|
trans_mirror = mirror_pos * trans |
|
grot_mirror = mirror_rot * grot[:,joints_mirror] |
|
|
|
return quat.ik_rot(grot_mirror, parents), trans_mirror |
|
|
|
def smpl2bvh(model_path:str, poses:str, output:str, mirror:bool, |
|
model_type="smpl", gender="MALE", |
|
num_betas=10, fps=60) -> None: |
|
"""Save bvh file created by smpl parameters. |
|
|
|
Args: |
|
model_path (str): Path to smpl models. |
|
poses (str): Path to npz or pkl file. |
|
output (str): Where to save bvh. |
|
mirror (bool): Whether save mirror motion or not. |
|
model_type (str, optional): I prepared "smpl" only. Defaults to "smpl". |
|
gender (str, optional): Gender Information. Defaults to "MALE". |
|
num_betas (int, optional): How many pca parameters to use in SMPL. Defaults to 10. |
|
fps (int, optional): Frame per second. Defaults to 30. |
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
names = [ |
|
"Hips", |
|
"LeftUpLeg", |
|
"RightUpLeg", |
|
"Spine", |
|
"LeftLeg", |
|
"RightLeg", |
|
"Spine1", |
|
"LeftFoot", |
|
"RightFoot", |
|
"Spine2", |
|
"LeftToe", |
|
"RightToe", |
|
"Neck", |
|
"LeftShoulder", |
|
"RightShoulder", |
|
"Head", |
|
"LeftArm", |
|
"RightArm", |
|
"LeftForeArm", |
|
"RightForeArm", |
|
"LeftHand", |
|
"RightHand", |
|
"LeftThumb", |
|
"RightThumb", |
|
] |
|
|
|
|
|
|
|
model = smplx.create(model_path=model_path, |
|
model_type=model_type, |
|
gender=gender, |
|
batch_size=1) |
|
|
|
parents = model.parents.detach().cpu().numpy() |
|
|
|
|
|
rest = model( |
|
|
|
) |
|
rest_pose = rest.joints.detach().cpu().numpy().squeeze()[:24,:] |
|
|
|
root_offset = rest_pose[0] |
|
offsets = rest_pose - rest_pose[parents] |
|
offsets[0] = root_offset |
|
offsets *= 1 |
|
|
|
scaling = None |
|
|
|
|
|
if poses.endswith(".npz"): |
|
poses = np.load(poses) |
|
rots = np.squeeze(poses["poses"], axis=0) |
|
trans = np.squeeze(poses["trans"], axis=0) |
|
|
|
elif poses.endswith(".pkl"): |
|
with open(poses, "rb") as f: |
|
poses = pickle.load(f) |
|
rots = poses["smpl_poses"] |
|
rots = rots.reshape(rots.shape[0], -1, 3) |
|
scaling = poses["smpl_scaling"] |
|
trans = poses["smpl_trans"] |
|
|
|
else: |
|
raise Exception("This file type is not supported!") |
|
|
|
if scaling is not None: |
|
trans /= scaling |
|
|
|
|
|
rots = quat.from_axis_angle(rots) |
|
|
|
order = "zyx" |
|
pos = offsets[None].repeat(len(rots), axis=0) |
|
positions = pos.copy() |
|
|
|
positions[:, 0] += trans |
|
rotations = np.degrees(quat.to_euler(rots, order=order)) |
|
|
|
bvh_data ={ |
|
"rotations": rotations[:, :22], |
|
"positions": positions[:, :22], |
|
"offsets": offsets[:22], |
|
"parents": parents[:22], |
|
"names": names[:22], |
|
"order": order, |
|
"frametime": 1 / fps, |
|
} |
|
|
|
if not output.endswith(".bvh"): |
|
output = output + ".bvh" |
|
|
|
bvh.save(output, bvh_data) |
|
|
|
if mirror: |
|
rots_mirror, trans_mirror = mirror_rot_trans( |
|
rots, trans, names, parents) |
|
positions_mirror = pos.copy() |
|
positions_mirror[:,0] += trans_mirror |
|
rotations_mirror = np.degrees( |
|
quat.to_euler(rots_mirror, order=order)) |
|
|
|
bvh_data ={ |
|
"rotations": rotations_mirror, |
|
"positions": positions_mirror, |
|
"offsets": offsets, |
|
"parents": parents, |
|
"names": names, |
|
"order": order, |
|
"frametime": 1 / fps, |
|
} |
|
|
|
output_mirror = output.split(".")[0] + "_mirror.bvh" |
|
bvh.save(output_mirror, bvh_data) |
|
|
|
|
|
def joints2bvh() |
|
|
|
if __name__ == "__main__": |
|
args = parse_args() |
|
|
|
smpl2bvh(model_path=args.model_path, model_type=args.model_type, |
|
mirror = args.mirror, gender=args.gender, |
|
poses=args.poses, num_betas=args.num_betas, |
|
fps=args.fps, output=args.output) |
|
|
|
print("finished!") |