kleinhe
init
c3d0293
import os.path as osp
import argparse
import numpy as np
import torch
import trimesh
from SMPLX import smplx
from SMPLX.smplx.joint_names import Body
from SMPLX.read_from_npy import npy2info, info2dict
from tqdm.auto import tqdm, trange
from pathlib import Path
import os
os.environ['PYOPENGL_PLATFORM'] = "egl"
def write_obj(
model_folder,
motion_file,
output_folder,
model_type="smplh",
gender="neutral",
num_betas=10,
num_expression_coeffs=10,
use_face_contour=False,
device="cpu"
):
output_folder = Path(output_folder)
assert output_folder.exists()
# open motion file
motion = np.load(motion_file, allow_pickle=True)
try:
poses = motion["poses"]
gender = str(motion.get("gender", "neutral"))
trans = motion.get("trans", None)
betas = motion.get("betas", np.zeros([poses.shape[0], 10]))
except:
poses, trans, gender, betas = npy2info(motion, 10)
# 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()}
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,
ext="npz", use_pca=False, batch_size=poses.shape[0])
model = model.eval().to(device)
inputs = info2dict(poses, trans, betas, model_type, device=device)
output = model(**inputs)
vertices = output.vertices.detach().cpu().numpy()
for pose_idx in range(vertices.shape[0]):
curr_vert = vertices[pose_idx]
vertex_colors = np.ones([curr_vert.shape[0], 4]) * [0.3, 0.3, 0.3, 0.8]
# process=False to avoid creating a new mesh
tri_mesh = trimesh.Trimesh(
curr_vert, model.faces, vertex_colors=vertex_colors, process=False
)
'''
humanact12 smpl 转 smplx
仅和 amass 格式对齐时使用
'''
if "humanact" in motion_file:
transf = trimesh.transformations.rotation_matrix(np.radians(90), (1, 0, 0))
tri_mesh.apply_transform(transf)
###################
output_path = output_folder / "{0:04d}.obj".format(pose_idx)
tri_mesh.export(str(output_path))
del model
del motion
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
write_obj(
model_folder,
motion_file,
output_folder,
model_type,
ext=ext,
sample_expression=sample_expression,
use_face_contour=args.use_face_contour,
)