Files
SMPLer-X/main/render.py
T
2023-10-23 16:55:02 +08:00

354 lines
13 KiB
Python

import numpy as np
import glob
import random
import cv2
import os
import argparse
import torch
import pyrender
import trimesh
import pandas as pd
import json
from tqdm import tqdm
from multiprocessing import Pool
# from mmhuman3d.models.body_models.builder import build_body_model
import smplx
import pdb
smpl_shape = {'betas': (-1, 10), 'transl': (-1, 3), 'global_orient': (-1, 3), 'body_pose': (-1, 69)}
smplx_shape = {'betas': (-1, 10), 'transl': (-1, 3), 'global_orient': (-1, 3),
'body_pose': (-1, 21, 3), 'left_hand_pose': (-1, 15, 3), 'right_hand_pose': (-1, 15, 3),
'leye_pose': (-1, 3), 'reye_pose': (-1, 3), 'jaw_pose': (-1, 3), 'expression': (-1, 10)}
smplx_shape_except_expression = {'betas': (-1, 10), 'transl': (-1, 3), 'global_orient': (-1, 3),
'body_pose': (-1, 21, 3), 'left_hand_pose': (-1, 15, 3), 'right_hand_pose': (-1, 15, 3),
'leye_pose': (-1, 3), 'reye_pose': (-1, 3), 'jaw_pose': (-1, 3)}
# smplx_shape = smplx_shape_except_expression
def render_pose(img, body_model_param, body_model, camera, return_mask=False):
# the inverse is same
pyrender2opencv = np.array([[1.0, 0, 0, 0],
[0, -1, 0, 0],
[0, 0, -1, 0],
[0, 0, 0, 1]])
output = body_model(**body_model_param, return_verts=True)
vertices = output['vertices'].detach().cpu().numpy().squeeze()
faces = body_model.faces
# render material
base_color = (1.0, 193/255, 193/255, 1.0)
material = pyrender.MetallicRoughnessMaterial(
metallicFactor=0,
alphaMode='OPAQUE',
baseColorFactor=base_color)
material_new = pyrender.MetallicRoughnessMaterial(
metallicFactor=0.1,
roughnessFactor=0.4,
alphaMode='OPAQUE',
emissiveFactor=(0.2, 0.2, 0.2),
baseColorFactor=(0.7, 0.7, 0.7, 1))
material = material_new
# get body mesh
body_trimesh = trimesh.Trimesh(vertices, faces, process=False)
body_mesh = pyrender.Mesh.from_trimesh(body_trimesh, material=material)
# prepare camera and light
light = pyrender.DirectionalLight(color=np.ones(3), intensity=2.0)
cam_pose = pyrender2opencv @ np.eye(4)
# build scene
scene = pyrender.Scene(bg_color=[0.0, 0.0, 0.0, 0.0],
ambient_light=(0.3, 0.3, 0.3))
scene.add(camera, pose=cam_pose)
scene.add(light, pose=cam_pose)
scene.add(body_mesh, 'mesh')
# render scene
os.environ["PYOPENGL_PLATFORM"] = "osmesa" # include this line if use in vscode
r = pyrender.OffscreenRenderer(viewport_width=img.shape[1],
viewport_height=img.shape[0],
point_size=1.0)
color, _ = r.render(scene, flags=pyrender.RenderFlags.RGBA)
color = color.astype(np.float32) / 255.0
# alpha = 1.0 # set transparency in [0.0, 1.0]
# color[:, :, -1] = color[:, :, -1] * alpha
valid_mask = (color[:, :, -1] > 0)[:, :, np.newaxis]
img = img / 255
# output_img = (color[:, :, :-1] * valid_mask + (1 - valid_mask) * img)
color = cv2.cvtColor(color, cv2.COLOR_BGR2RGB)
output_img = (color[:, :, :] * valid_mask + (1 - valid_mask) * img)
# output_img = color
img = (output_img * 255).astype(np.uint8)
# img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
if return_mask:
return img, valid_mask, (color * 255).astype(np.uint8)
return img
def render_multi_pose(img,
body_model_params,
body_model,
cameras):
masks, colors = [], []
# calculate distance based on transl
dists, valid_idx = [], []
for i, body_model_param in enumerate(body_model_params):
dist = np.linalg.norm(body_model_param['transl'].detach().cpu()) * 2/ (cameras[i].fx + cameras[i].fy)
if dist not in dists:
valid_idx.append(i)
dists.append(dist)
# pdb.set_trace()
# select by valid idx
body_model_params = [body_model_params[i] for i in valid_idx]
cameras = [cameras[i] for i in valid_idx]
# sort by dist
body_model_params = [x for _, x in sorted(zip(dists, body_model_params), reverse=True)]
cameras = [x for _, x in sorted(zip(dists, cameras), reverse=True)]
# render separate masks
for i, body_model_param in enumerate(body_model_params):
_, mask, color = render_pose(
img=img,
body_model_param=body_model_param,
body_model=body_model,
camera=cameras[i],
return_mask=True,
)
masks.append(mask)
colors.append(color)
# sum masks
mask_sum = np.sum(masks, axis=0)
mask_all = (mask_sum > 0)
# pp_occ = 1 - np.sum(mask_all) / np.sum(mask_sum)
# overlay colors to img
for i, color in enumerate(colors):
mask = masks[i]
img = img * (1 - mask) + color * mask
img = img.astype(np.uint8)
# img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
return img
def render_frame(framestamp, anno_ps, image_base_path, seq, smplx_model, args):
annos = [p for p in anno_ps if framestamp in os.path.basename(p)]
annos = [p for p in annos if 'person' not in os.path.basename(p)]
body_model_params = []
cameras = []
bbox_sizes = []
try:
# image_path = os.path.join(seq, f'0{framestamp}.jpg').replace(args.data_path, args.image_path)
image_path = os.path.join(image_base_path, f'0{framestamp}.jpg')
# pdb.set_trace()
image = cv2.imread(image_path)
except:
pass
# pdb.set_trace()
for anno_p in annos:
anno = dict(np.load(anno_p, allow_pickle=True))
meta = json.load(open(os.path.join(seq, 'meta',
os.path.basename(anno_p).replace('.npz', '.json')
)))
bbox_size = meta['bbox'][2] * meta['bbox'][3]
focal_length = meta['focal']
principal_point = meta['princpt']
camera = pyrender.camera.IntrinsicsCamera(
fx=focal_length[0], fy=focal_length[1],
cx=principal_point[0], cy=principal_point[1],)
# prepare body model params
intersect_key = list(set(anno.keys()) & set(smplx_shape.keys()))
body_model_param_tensor = {key: torch.tensor(
np.array(anno[key]).reshape(smplx_shape[key]), device=args.device, dtype=torch.float32)
for key in intersect_key if len(anno[key]) > 0}
cameras.append(camera)
body_model_params.append(body_model_param_tensor)
bbox_sizes.append(bbox_size)
# render pose
if args.render_biggest_person == 'True':
bid = bbox_sizes.index(max(bbox_sizes))
rendered_image = render_pose(img=image,
body_model_param=body_model_params[bid],
body_model=smplx_model,
camera=cameras[bid])
else:
rendered_image = render_multi_pose(img=image,
body_model_params=body_model_params,
body_model=smplx_model,
cameras=cameras)
sp = seq.replace(f'{args.data_path}{os.path.sep}', '')
save_path = os.path.join(args.data_path, 'output', sp)
os.makedirs(save_path, exist_ok=True)
save_name = os.path.join(save_path, framestamp+'.jpg')
cv2.imwrite(save_name, rendered_image)
def call_frame_render(args):
return render_frame(*args)
def visualize_seqs(args):
if args.seq == 'default':
seqs = glob.glob(os.path.join(args.data_path, '**/smplx'), recursive=True)
seqs = [os.path.dirname(p) for p in seqs]
else:
seqs = glob.glob(os.path.join(args.data_path, args.seq), recursive=True)
kwargs = dict(gender='neutral',
num_betas=10,
use_face_contour=True,
flat_hand_mean=args.flat_hand_mean,
use_pca=False,
batch_size=1)
smplx_model = smplx.create(
'../common/utils/human_model_files', 'smplx',
**kwargs).to(args.device)
# seqs = [p for p in seqs if 'dance' not in os.path.basename(p)]
# pdb.set_trace()
for i, seq in enumerate(seqs):
# prepare image path
if args.load_mode == 'smplerx':
image_base_path = os.path.join(seq, 'orig_img')
else:
image_base_path = os.path.join(seq, 'frames')
assert os.path.exists(image_base_path)
smplx_path = os.path.join(seq, 'smplx')
anno_ps = sorted(glob.glob(os.path.join(smplx_path, '*.npz')))
# group by framestamps
framestamps = sorted(list(set([os.path.basename(p)[:5] for p in anno_ps
if 'person' not in os.path.basename(p)]
)))
for framestamp in tqdm(framestamps, leave=False, desc=f'Seqs {os.path.basename(seq)}'
f' : {i}/{len(seqs)}'):
annos = [p for p in anno_ps if framestamp in os.path.basename(p)]
annos = [p for p in annos if 'person' not in os.path.basename(p)]
body_model_params = []
cameras = []
bbox_sizes = []
# pdb.set_trace()
try:
if args.load_mode == 'smplerx':
image_path = os.path.join(image_base_path, f'{int(framestamp):06d}.jpg')
image = cv2.imread(image_path)
else:
image_path = os.path.join(image_base_path, f'{int(framestamp):04d}.png')
image = cv2.imread(image_path)
image = cv2.resize(image, (1280, 720), interpolation = cv2.INTER_AREA)
# image = cv2.imread(image_path)
except:
pass
for anno_p in annos:
anno = dict(np.load(anno_p, allow_pickle=True))
meta = json.load(open(os.path.join(seq, 'meta',
os.path.basename(anno_p).replace('.npz', '.json')
)))
bbox_size = meta['bbox'][2] * meta['bbox'][3]
focal_length = meta['focal']
principal_point = meta['princpt']
camera = pyrender.camera.IntrinsicsCamera(
fx=focal_length[0], fy=focal_length[1],
cx=principal_point[0], cy=principal_point[1],)
# prepare body model params
intersect_key = list(set(anno.keys()) & set(smplx_shape.keys()))
body_model_param_tensor = {key: torch.tensor(
np.array(anno[key]).reshape(smplx_shape[key]), device=args.device, dtype=torch.float32)
for key in intersect_key if len(anno[key]) > 0}
cameras.append(camera)
body_model_params.append(body_model_param_tensor)
bbox_sizes.append(bbox_size)
# render pose
if args.render_biggest_person == 'True':
bid = bbox_sizes.index(max(bbox_sizes))
rendered_image = render_pose(img=image,
body_model_param=body_model_params[bid],
body_model=smplx_model,
camera=cameras[bid])
else:
rendered_image = render_multi_pose(img=image,
body_model_params=body_model_params,
body_model=smplx_model,
cameras=cameras)
# save image
sp = seq.replace(f'{args.data_path}{os.path.sep}', '')
save_path = os.path.join(args.data_path, sp, f'{args.load_mode}_overlay_img')
os.makedirs(save_path, exist_ok=True)
save_name = os.path.join(save_path, f'{int(framestamp):06d}.jpg')
cv2.imwrite(save_name, rendered_image)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--data_path', type=str, required=False,
help='path to the data folder')
parser.add_argument('--load_mode', type=str, required=False,
default='smplerx',
help='load mode: smplerx or other test mode, please select smplerx')
parser.add_argument('--seq', type=str, required=False,
help='seq name or seq pattern',
default='default')
parser.add_argument('--image_path', type=str, required=False,
help='path to the image folder')
# optional args
parser.add_argument('--flat_hand_mean', type=bool, required=False,
help='use flat hand mean for smplx',
default=False)
parser.add_argument('--render_biggest_person', type=str, required=False,
help='render biggest person in the frame',
default='True')
args = parser.parse_args()
args.device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
visualize_seqs(args)