QZFantasies's picture
add wheels
c614b0f
import os.path as osp
import argparse
import numpy as np
import torch
import pyrender
import trimesh
import smplx
from smplx.joint_names import Body
from tqdm.auto import tqdm, trange
from pathlib import Path
def main(
model_folder,
motion_file,
output_folder,
model_type="smplh",
ext="npz",
gender="neutral",
plot_joints=False,
num_betas=10,
sample_expression=True,
num_expression_coeffs=10,
use_face_contour=False,
):
output_folder = Path(output_folder)
assert output_folder.exists()
# open motion file
motion = np.load(motion_file, allow_pickle=True)
for k, v in motion.items():
if type(v) is float:
print(k, v)
else:
print(k, v.shape)
if "betas" in motion:
betas = motion["betas"]
else:
betas = np.zeros((num_betas,))
num_betas = len(betas)
# don't know where this is documented but it's from this part of amass
# https://github.com/nghorbani/amass/blob/master/src/amass/data/prepare_data.py#L39-L40
# gdr2num = {'male':-1, 'neutral':0, 'female':1}
# gdr2num_rev = {v:k for k,v in gdr2num.items()}
if "gender" in motion:
gender = str(motion["gender"])
else:
gender = gender
print(gender)
print(num_betas)
model = smplx.create(
model_folder,
model_type=model_type,
gender=gender,
use_face_contour=use_face_contour,
num_betas=num_betas,
num_expression_coeffs=num_expression_coeffs,
use_pca=False,
ext=ext,
)
betas, expression = torch.tensor(betas).float(), None
betas = betas.unsqueeze(0)[:, : model.num_betas]
if "poses" in motion:
poses = torch.tensor(motion["poses"]).float()
elif "smpl_poses" in motion:
poses = motion["smpl_poses"]
n = poses.shape[0]
if model_type == "smplh":
poses = np.stack(
[Body.from_smpl(p.reshape(-1, 3)).as_smplh() for p in poses]
)
poses = torch.tensor(poses.reshape(n, -1)).float()
global_orient = poses[:, :3]
if model_type == "smplh":
body_pose = poses[:, 3:66]
left_hand_pose = poses[:, 66:111]
right_hand_pose = poses[:, 111:156]
else:
body_pose = poses[:, 3:]
left_hand_pose = np.zeros((n, 3))
right_hand_pose = np.zeros((n, 3))
# if sample_expression:
# expression = torch.randn(
# [1, model.num_expression_coeffs], dtype=torch.float32)
# print(expression)
# print(betas.shape, body_pose.shape, expression.shape)
for pose_idx in trange(body_pose.size(0)):
pose_idx = [pose_idx]
# output = model(betas=betas, # expression=expression,
# return_verts=True)
output = model(
betas=betas,
global_orient=global_orient[pose_idx],
body_pose=body_pose[pose_idx],
left_hand_pose=left_hand_pose[pose_idx],
right_hand_pose=right_hand_pose[pose_idx],
# expression=expression,
return_verts=True,
)
vertices = output.vertices.detach().cpu().numpy().squeeze()
joints = output.joints.detach().cpu().numpy().squeeze()
vertex_colors = np.ones([vertices.shape[0], 4]) * [0.3, 0.3, 0.3, 0.8]
# process=False to avoid creating a new mesh
tri_mesh = trimesh.Trimesh(
vertices, model.faces, vertex_colors=vertex_colors, process=False
)
output_path = output_folder / "{0:04d}.obj".format(pose_idx[0])
tri_mesh.export(str(output_path))
if pose_idx[0] == 0:
print("displaying first pose, exit window to continue processing")
mesh = pyrender.Mesh.from_trimesh(tri_mesh)
scene = pyrender.Scene()
scene.add(mesh)
if plot_joints:
sm = trimesh.creation.uv_sphere(radius=0.005)
sm.visual.vertex_colors = [0.9, 0.1, 0.1, 1.0]
tfs = np.tile(np.eye(4), (len(joints), 1, 1))
tfs[:, :3, 3] = joints
joints_pcl = pyrender.Mesh.from_trimesh(sm, poses=tfs)
scene.add(joints_pcl)
pyrender.Viewer(scene, use_raymond_lighting=True)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="SMPL-X Demo")
parser.add_argument(
"--model-folder", required=True, type=str, help="The path to the model folder"
)
parser.add_argument(
"--motion-file",
required=True,
type=str,
help="The path to the motion file to process",
)
parser.add_argument(
"--output-folder", required=True, type=str, help="The path to the output folder"
)
parser.add_argument(
"--model-type",
default="smplh",
type=str,
choices=["smpl", "smplh", "smplx", "mano", "flame"],
help="The type of model to load",
)
parser.add_argument(
"--num-expression-coeffs",
default=10,
type=int,
dest="num_expression_coeffs",
help="Number of expression coefficients.",
)
parser.add_argument(
"--ext", type=str, default="npz", help="Which extension to use for loading"
)
parser.add_argument(
"--sample-expression",
default=True,
dest="sample_expression",
type=lambda arg: arg.lower() in ["true", "1"],
help="Sample a random expression",
)
parser.add_argument(
"--use-face-contour",
default=False,
type=lambda arg: arg.lower() in ["true", "1"],
help="Compute the contour of the face",
)
args = parser.parse_args()
def resolve(path):
return osp.expanduser(osp.expandvars(path))
model_folder = resolve(args.model_folder)
motion_file = resolve(args.motion_file)
output_folder = resolve(args.output_folder)
model_type = args.model_type
ext = args.ext
num_expression_coeffs = args.num_expression_coeffs
sample_expression = args.sample_expression
main(
model_folder,
motion_file,
output_folder,
model_type,
ext=ext,
sample_expression=sample_expression,
use_face_contour=args.use_face_contour,
)