import os import glob import json import torch import joblib import random import numpy as np import pandas as pd from scipy import linalg from arguments import Arguments from nemf.losses import GeodesicLoss from nemf.generative import Architecture from nemf.fk import ForwardKinematicsLayer from scripts.train_gmm import MaxMixturePrior from human_body_prior.tools.omni_tools import copy2cpu as c2c from utils import FOOT_IDX, estimate_angular_velocity, estimate_linear_velocity from rotations import axis_angle_to_matrix, matrix_to_rotation_6d, rotation_6d_to_matrix def padding_prior_data(data): N, T, J = data['pos'].shape[:3] # pad inputs to match the training sequence length zero_padding = torch.zeros(N, args.data.clip_length - T, J, 3).to(prior.device) identity_padding = torch.zeros(N, args.data.clip_length - T, 3, 3).to(prior.device) identity_padding[:, :, 0, 0] = 1 identity_padding[:, :, 1, 1] = 1 identity_padding[:, :, 2, 2] = 1 rotation_6d_padding = torch.zeros(N, args.data.clip_length - T, 6).to(prior.device) rotation_6d_padding[..., 0] = 1 rotation_6d_padding[..., 4] = 1 pos = torch.cat((data['pos'], zero_padding), dim=1) velocity = torch.cat((data['velocity'], zero_padding), dim=1) angular = torch.cat((data['angular'], zero_padding), dim=1) root_orient = torch.cat((data['root_orient'], rotation_6d_padding), dim=1) root_vel = torch.cat((data['root_vel'], zero_padding[:, :, 0]), dim=1) rotmat_padding = rotation_6d_padding.unsqueeze(2).expand(-1, -1, J, -1) global_xform = torch.cat((data['global_xform'], rotmat_padding), dim=1) data['pos'] = pos data['velocity'] = velocity data['global_xform'] = global_xform data['angular'] = angular data['root_orient'] = root_orient data['root_vel'] = root_vel return data def load_data(files, padding=False, max_samples=400): poses = [] trans = [] visibility = [] assert len(files) != 0, 'files not found' max_samples = min(max_samples, len(files)) # import ipdb; ipdb.set_trace() for f in files[:max_samples]: bdata = dict(np.load(f, allow_pickle=True)) if 'r_wrist_orient' in bdata.keys() and 'pose_right_hand' in bdata.keys(): coef = len(bdata['r_wrist_orient']) // args.data.clip_length for k_ in range(coef): root_orient = bdata['r_wrist_orient'][k_*args.data.clip_length:(k_+1)*args.data.clip_length] pose_body = bdata['pose_right_hand'][k_*args.data.clip_length:(k_+1)*args.data.clip_length] trans.append(bdata['r_wrist_location'][k_*args.data.clip_length:(k_+1)*args.data.clip_length]) poses.append(np.concatenate((root_orient, pose_body), axis=-1)) elif 'pose_right_hand' in bdata.keys(): pass # poses.append(bdata['pose_right_hand'][:, :72]) else: raise RuntimeError(f'missing pose parameters in the file: {f}') if 'visibility' in bdata.keys(): visibility.append(bdata['visibility']) # if 'poses' in bdata.keys(): # poses.append(bdata['poses'][:, :72]) # elif 'root_orient' in bdata.keys() and 'pose_body' in bdata.keys(): # root_orient = bdata['root_orient'] # pose_body = bdata['pose_body'] # poses.append(np.concatenate((root_orient, pose_body), axis=-1)) # else: # raise RuntimeError(f'missing pose parameters in the file: {f}') # trans.append(bdata['trans']) # if 'visibility' in bdata.keys(): # visibility.append(bdata['visibility']) trans = torch.from_numpy(np.asarray(trans, np.float32)).cuda() # global translation (N, T, 3) N, T = trans.shape[:2] poses = torch.from_numpy(np.asarray(poses, np.float32)).cuda() poses = poses.view(N, T, -1, 3) # axis-angle (N, T, J, 3) # poses = poses.view(N, T, 24, 3) # axis-angle (N, T, J, 3) root_orient = poses[:, :, 0].clone() root_rotation = axis_angle_to_matrix(root_orient) # (N, T, 3, 3) poses[:, :, 0] = 0 rotmat = axis_angle_to_matrix(poses) # (N, T, J, 3, 3) angular = estimate_angular_velocity(rotmat.clone(), dt=1.0 / args.data.fps) # angular velocity of all the joints (N, T, J, 3) pos, global_xform = fk(rotmat.view(-1, 16, 3, 3)) pos = pos.contiguous().view(N, T, 16, 3) # local joint positions (N, T, J, 3) global_xform = global_xform.view(N, T, 16, 4, 4) global_xform = global_xform[:, :, :, :3, :3] # global transformation matrix for each joint (N, T, J, 3, 3) velocity = estimate_linear_velocity(pos, dt=1.0 / args.data.fps) # linear velocity of all the joints (N, T, J, 3) root_vel = estimate_linear_velocity(trans, dt=1.0 / args.data.fps) # linear velocity of the root joint (N, T, 3) global_pos = torch.matmul(root_rotation.unsqueeze(2), pos.unsqueeze(-1)).squeeze(-1) # (N, T, J, 3) global_pos = global_pos + trans.unsqueeze(2) data = { 'pos': pos, 'velocity': velocity, 'global_xform': matrix_to_rotation_6d(global_xform), 'angular': angular, 'root_orient': matrix_to_rotation_6d(root_rotation), 'root_vel': root_vel, 'global_pos': global_pos, 'rotmat': rotmat, 'trans': trans } if len(visibility) != 0: visibility = torch.from_numpy(np.asarray(visibility, bool)).cuda() padding = torch.zeros(visibility.shape[:2] + torch.Size([2]), dtype=torch.bool).cuda() visibility = torch.cat((visibility, padding), dim=-1) # (T, 24) print(f'visibility: {visibility.shape}') data['visibility'] = visibility if T < args.data.clip_length: padding = True if padding: if T == args.data.clip_length: data['pos'] = data['pos'].view(2 * N, T // 2, 24, 3) data['velocity'] = data['velocity'].view(2 * N, T // 2, 24, 3) data['global_xform'] = data['global_xform'].view(2 * N, T // 2, 24, 6) data['angular'] = data['angular'].view(2 * N, T // 2, 24, 3) data['root_orient'] = data['root_orient'].view(2 * N, T // 2, 6) data['root_vel'] = data['root_vel'].view(2 * N, T // 2, 3) data['global_pos'] = data['global_pos'].view(2 * N, T // 2, 24, 3) data['rotmat'] = data['rotmat'].view(2 * N, T // 2, 24, 3, 3) data['trans'] = data['trans'].view(2 * N, T // 2, 3) padding_data = padding_prior_data(data) for key in padding_data.keys(): data[key] = padding_data[key] return data def calculate_feature_statistics(feature): feature = feature.cpu().numpy() mu = np.mean(feature, axis=0) sigma = np.cov(feature, rowvar=False) return mu, sigma def calculate_frechet_distance(mu1, sigma1, mu2, sigma2, eps=1e-6): """Numpy implementation of the Frechet Distance. The Frechet distance between two multivariate Gaussians X_1 ~ N(mu_1, C_1) and X_2 ~ N(mu_2, C_2) is d^2 = ||mu_1 - mu_2||^2 + Tr(C_1 + C_2 - 2*sqrt(C_1*C_2)). Stable version by Dougal J. Sutherland. Params: -- mu1 : Numpy array containing the activations of a layer of the inception net (like returned by the function 'get_predictions') for generated samples. -- mu2 : The sample mean over activations, precalculated on an representative data set. -- sigma1: The covariance matrix over activations for generated samples. -- sigma2: The covariance matrix over activations, precalculated on an representative data set. Returns: -- : The Frechet Distance. """ mu1 = np.atleast_1d(mu1) mu2 = np.atleast_1d(mu2) sigma1 = np.atleast_2d(sigma1) sigma2 = np.atleast_2d(sigma2) assert mu1.shape == mu2.shape, \ 'Training and test mean vectors have different lengths' assert sigma1.shape == sigma2.shape, \ 'Training and test covariances have different dimensions' diff = mu1 - mu2 # Product might be almost singular covmean, _ = linalg.sqrtm(sigma1.dot(sigma2), disp=False) if not np.isfinite(covmean).all(): msg = ('fid calculation produces singular product; ' 'adding %s to diagonal of cov estimates') % eps print(msg) offset = np.eye(sigma1.shape[0]) * eps covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset)) # Numerical error might give slight imaginary component if np.iscomplexobj(covmean): if not np.allclose(np.diagonal(covmean).imag, 0, atol=1e-3): m = np.max(np.abs(covmean.imag)) raise ValueError('Imaginary component {}'.format(m)) covmean = covmean.real tr_covmean = np.trace(covmean) return (diff.dot(diff) + np.trace(sigma1) + np.trace(sigma2) - 2 * tr_covmean) def evaluate_fid(data, gt_data, num_motions=None, mask=None): if num_motions is None: index = list(range(data['pos'].shape[0])) index_gt = list(range(gt_data['pos'].shape[0])) else: # randomly pick [num_motions] motions from the data assert num_motions <= data['pos'].shape[0] assert num_motions <= gt_data['pos'].shape[0] index = list(range(data['pos'].shape[0])) index = random.sample(index, num_motions) index_gt = list(range(gt_data['pos'].shape[0])) index_gt = random.sample(index_gt, num_motions) data_selected = {} gt_data_selected = {} for key in data.keys(): data_selected[key] = data[key][index].clone() gt_data_selected[key] = gt_data[key][index_gt].clone() if mask is not None: # replace with ground truth for key in data.keys(): data_selected[key][:, mask] = gt_data[key][:, mask].clone() with torch.no_grad(): prior.set_input(data_selected) f_l, _, _ = prior.encode_local() f_g, _, _ = prior.encode_global() feature = torch.cat((f_l, f_g), dim=-1) prior.set_input(gt_data_selected) f_l_gt, _, _ = prior.encode_local() f_g_gt, _, _ = prior.encode_global() feature_gt = torch.cat((f_l_gt, f_g_gt), dim=-1) statistics = calculate_feature_statistics(feature.view(feature.shape[0], -1)) statistics_gt = calculate_feature_statistics(feature_gt.view(feature_gt.shape[0], -1)) fid = calculate_frechet_distance(statistics[0], statistics[1], statistics_gt[0], statistics_gt[1]) print(f'FID: {fid:.4f}') return fid def evaluate_diversity(data): index = list(range(data['pos'].shape[0])) random.shuffle(index) index1 = index[:data['pos'].shape[0] // 2] index2 = index[data['pos'].shape[0] // 2:] with torch.no_grad(): prior.set_input(data) f_l, _, _ = prior.encode_local() # f_g, _, _ = prior.encode_global() # feature = torch.cat((f_l, f_g), dim=-1) feature = f_l.clone() diversity = 0 for first_idx, second_idx in zip(index1, index2): diversity += torch.dist(feature[first_idx], feature[second_idx]) diversity /= len(index1) print(f'Diversity: {diversity.item():.4f}') return diversity.item() def evaluate_recon_error(data, gt_data): criterion_geo = GeodesicLoss() root_orient = rotation_6d_to_matrix(data['root_orient']) root_orient_gt = rotation_6d_to_matrix(gt_data['root_orient']) orientation_error = criterion_geo(root_orient.view(-1, 3, 3), root_orient_gt.view(-1, 3, 3)) rotation_error = criterion_geo(data['rotmat'][:, :, 1:].reshape(-1, 3, 3), gt_data['rotmat'][:, :, 1:].reshape(-1, 3, 3)) position_error = torch.linalg.norm((data['pos'] - gt_data['pos']), dim=-1).mean() translation_error = torch.linalg.norm((data['trans'] - gt_data['trans']), dim=-1).mean() final_displacement_error = torch.linalg.norm(data['global_pos'][:, -1] - gt_data['global_pos'][:, -1], dim=-1).mean() return { 'rotation': c2c(rotation_error) * 180.0 / np.pi, 'position': c2c(position_error) * 100.0, 'orientation': c2c(orientation_error) * 180.0 / np.pi, 'translation': c2c(translation_error) * 100.0, 'final_displacement': c2c(final_displacement_error) * 100.0 } def synthetic_trajectory(N_motions): z_l = torch.normal(mean=0, std=1, size=(N_motions, args.local_prior.z_dim)).to(prior.device) z_g = torch.normal(mean=0, std=1, size=(N_motions, args.global_prior.z_dim)).to(prior.device) # z_g = torch.zeros((N_motions, args.global_prior.z_dim)).to(prior.device) with torch.no_grad(): output = prior.decode(z_l, z_g, length=args.data.clip_length, step=1) rotmat = output["rotmat"] # (N, T, J, 3, 3) angular = estimate_angular_velocity(rotmat.clone(), dt=1.0 / args.data.fps) # angular velocity of all the joints (N, T, J, 3) pos, global_xform = fk(rotmat.view(-1, 16, 3, 3)) pos = pos.contiguous().view(N_motions, args.data.clip_length, 16, 3) # local joint positions (N, T, J, 3) global_xform = global_xform.view(N_motions, args.data.clip_length, 16, 4, 4) global_xform = global_xform[:, :, :, :3, :3] # global transformation matrix for each joint (N, T, J, 3, 3) velocity = estimate_linear_velocity(pos, dt=1.0 / args.data.fps) # linear velocity of all the joints (N, T, J, 3) root_rotation = rotmat[:, :, 0] # (N, T, 3, 3) global_pos = torch.matmul(root_rotation.unsqueeze(2), pos.unsqueeze(-1)).squeeze(-1) # (N, T, J, 3) output_ = {'pos': pos, 'velocity': velocity, 'global_xform': matrix_to_rotation_6d(global_xform), 'angular': angular, 'root_orient': matrix_to_rotation_6d(root_rotation), 'rotmat': rotmat} return output_ def ablation_study(path, subtask): num_motions = 100 print(os.path.join(path, 'TCDHands', '*.npz')) test_data = load_data(sorted(glob.glob(os.path.join('./outputs/generative_copy/TCDHands', '*.npz')))) # test data TCDHands val_data = load_data(sorted(glob.glob(os.path.join('./outputs/generative_copy/GRAB', '*.npz')))) # test data TCDHands train_data = load_data(sorted(glob.glob(os.path.join('./outputs/generative_copy/SAMP', '*.npz')))) # test data TCDHands synthetic_data = synthetic_trajectory(N_motions=num_motions) pca_aa = joblib.load("data/pca.pkl") pca_mean = torch.tensor(pca_aa.mean_).to("cuda") pca_cov = torch.tensor(pca_aa.get_covariance()).to("cuda") pca_sv = torch.tensor(pca_aa.singular_values_).to("cuda") pca_pc = torch.tensor(pca_aa.components_).to("cuda") random_samples_low_dim = np.random.randn(num_motions, pca_aa.n_components_) pca_data = pca_aa.inverse_transform(random_samples_low_dim) gmm_aa = MaxMixturePrior() gmm_data = gmm_aa.gmm.sample(num_motions) identity = torch.eye(3) identity = identity.view(1, 1, 3, 3).repeat(num_motions, args.data.clip_length, 1, 1) # convert to tensor gmm_data = torch.tensor(gmm_data[0].reshape(num_motions, args.data.clip_length, -1, 3)) gmm_data = torch.cat((identity.unsqueeze(2), axis_angle_to_matrix(gmm_data)), dim=2) gmm_rot6d = matrix_to_rotation_6d(gmm_data).reshape(num_motions * args.data.clip_length, -1, 6) # convert to tensor pca_data = torch.tensor(pca_data.reshape(num_motions, args.data.clip_length, -1, 3)) pca_data = torch.cat((identity.unsqueeze(2), axis_angle_to_matrix(pca_data)), dim=2) pca_rot6d = matrix_to_rotation_6d(pca_data).reshape(num_motions * args.data.clip_length, -1, 6) gmm_pos, gmm_xform = fk(gmm_rot6d.to(fk.device)) pca_pos, pca_xform = fk(pca_rot6d.to(fk.device)) gmm_xform = matrix_to_rotation_6d(gmm_xform[:, :, :3, :3]) pca_xform = matrix_to_rotation_6d(pca_xform[:, :, :3, :3]) gmm_velocity = estimate_linear_velocity(gmm_pos.unsqueeze(0), dt=1.0 / args.data.fps).squeeze(0) pca_velocity = estimate_linear_velocity(pca_pos.unsqueeze(0), dt=1.0 / args.data.fps).squeeze(0) gmm_angular = estimate_angular_velocity(gmm_data.unsqueeze(0), dt=1.0 / args.data.fps).squeeze(0) pca_angular = estimate_angular_velocity(pca_data.unsqueeze(0), dt=1.0 / args.data.fps).squeeze(0) gmm_dict = {'pos': gmm_pos.reshape(num_motions, args.data.clip_length, -1, 3), 'global_xform': gmm_xform.reshape(num_motions, args.data.clip_length, -1, 6), 'velocity': gmm_velocity.reshape(num_motions, args.data.clip_length, -1, 3), 'angular': gmm_angular} pca_dict = {'pos': pca_pos.reshape(num_motions, args.data.clip_length, -1, 3), 'global_xform': pca_xform.reshape(num_motions, args.data.clip_length, -1, 6), 'velocity': pca_velocity.reshape(num_motions, args.data.clip_length, -1, 3), 'angular': pca_angular} metrics = dict() def f(data, num_motions, key): div = evaluate_diversity(data) if key in metrics.keys(): pass else: metrics[key] = {'DIV': [div]} print('Motion Synthesis:') div_test = f(test_data, num_motions, 'TEST_DATA (TDCHANDS)') div_val = f(val_data, num_motions, 'VAL_DATA (SAMP)') div_train = f(train_data, num_motions, 'TRAIN_DATA (GRAB)') div_synt = f(synthetic_data, num_motions, 'SYNTHETIC') div_gmm = f(gmm_dict, num_motions, 'GMM') div_pca = f(pca_dict, num_motions, 'PCA') df = pd.DataFrame.from_dict(metrics) df.to_csv(os.path.join(path, f'diversity.csv'), index=False) def comparison(path, subtask): gt_data = load_data(sorted(glob.glob(os.path.join(path, 'gt', '*.npz')))) file_path = os.path.join(path, subtask) metrics = dict() if subtask == 'recon': humor_data = load_data(sorted(glob.glob(os.path.join(file_path, 'humor', '*.npz')))) hm_vae_data = load_data(sorted(glob.glob(os.path.join(file_path, 'hm-vae', '*.npz')))) nemf_data = load_data(sorted(glob.glob(os.path.join(file_path, 'nemf', '*.npz')))) metrics['HUMOR'] = evaluate_recon_error(humor_data, gt_data) metrics['HMVAE'] = evaluate_recon_error(hm_vae_data, gt_data) metrics['NEMF'] = evaluate_recon_error(nemf_data, gt_data) print('Motion Reconstruction:') print(json.dumps(metrics, sort_keys=True, indent=4)) df = pd.DataFrame.from_dict(metrics) df.to_csv(os.path.join(file_path, f'{subtask}.csv')) elif subtask == 'synth': humor_data = load_data(sorted(glob.glob(os.path.join(file_path, 'humor', '.*.npz'))), padding=True) hm_vae_data = load_data(sorted(glob.glob(os.path.join(file_path, 'hm-vae', '*.npz'))), padding=True) nemf_data = load_data(sorted(glob.glob(os.path.join(file_path, 'nemf', '*.npz'))), padding=True) num_motions = 100 def f(data, num_motions, key): fid = evaluate_fid(data, gt_data, num_motions) div = evaluate_diversity(data) index = list(range(data['global_pos'].shape[0])) index = random.sample(index, num_motions) s = foot_skating(data['global_pos'][index]) if key in metrics.keys(): metrics[key]['FID'].append(fid) metrics[key]['DIV'].append(div) metrics[key]['FOOT'].append(s) else: metrics[key] = {'FID': [fid], 'DIV': [div], 'FOOT': [s]} print('Motion Synthesis:') for replication in range(20): print(f'==================== Replication {replication} ====================') f(humor_data, num_motions, 'HUMOR') f(hm_vae_data, num_motions, 'HMVAE') f(nemf_data, num_motions, 'NEMF') for key in metrics.keys(): df = pd.DataFrame.from_dict(metrics[key]) df.to_csv(os.path.join(file_path, f'{subtask}_{key}.csv'), index=False) else: raise RuntimeError(f'unsupported evaluation task for comparison: {subtask}') def inbetween_benchmarks(path, keyframes=False): trans_lengths = [5, 10, 15, 20] if keyframes else [10, 20, 30] gt_data = load_data(sorted(glob.glob(os.path.join(path, 'gt', '*.npz')))) s = foot_skating(gt_data['global_pos']) print(f'average foot skating of the real motions: {s:.4f}') def f(data, key, mask): fid = evaluate_fid(data, gt_data, mask=mask) s = foot_skating(data['global_pos'], mask=mask) if key in statistics.keys(): statistics[key]['FID'].append(fid) statistics[key]['FOOT'].append(s) else: statistics[key] = {'FID': [fid], 'FOOT': [s]} file_path = os.path.join(path, 'keyframe') if keyframes else os.path.join(path, 'clip') statistics = {} for t in trans_lengths: print(f'==================== Length {t} ====================') if keyframes: mask = None # don't mask data for keyframe inbetweening else: end_frame = (args.data.clip_length - t) // 2 mask = list(range(end_frame)) + list(range(end_frame + t, args.data.clip_length)) print(f'mask: {mask}') slerp_data = load_data(sorted(glob.glob(os.path.join(file_path, 'slerp', f'{t}_frames', '*.npz')))) f(slerp_data, 'SLERP', mask) if not keyframes: inertia_data = load_data(sorted(glob.glob(os.path.join(file_path, 'inertialization', f'{t}_frames', '*.npz')))) f(inertia_data, 'INERTIALIZATION', mask) rmi_data = load_data(sorted(glob.glob(os.path.join(file_path, 'rmi', f'{t}_frames', '*.npz')))) f(rmi_data, 'RMI', mask) hm_vae_data = load_data(sorted(glob.glob(os.path.join(file_path, 'hm-vae', f'{t}_frames', '*.npz')))) f(hm_vae_data, 'HMVAE', mask) nemf_data = load_data(sorted(glob.glob(os.path.join(file_path, 'nemf', f'{t}_frames', '*.npz')))) f(nemf_data, 'NEMF', mask) for key in statistics.keys(): df = pd.DataFrame.from_dict(statistics[key]) df.to_csv(os.path.join(file_path, f'inbetween_{key}.csv')) def compute_joint_accel(joint_seq, fps): ''' Magnitude of joint accelerations for joint_seq : B x T x J x 3 ''' joint_accel = joint_seq[:, :-2] - (2 * joint_seq[:, 1:-1]) + joint_seq[:, 2:] DATA_h = 1.0 / fps joint_accel = joint_accel / ((DATA_h**2)) joint_accel_mag = torch.norm(joint_accel, dim=-1) return joint_accel, joint_accel_mag def evaluate_super_sampling(path): fps_30_data = load_data(sorted(glob.glob(os.path.join(path, '30fps/*.npz')))) fps_60_data = load_data(sorted(glob.glob(os.path.join(path, '60fps/*.npz')))) fps_120_data = load_data(sorted(glob.glob(os.path.join(path, '120fps/*.npz')))) fps_240_data = load_data(sorted(glob.glob(os.path.join(path, '240fps/*.npz')))) smoothness = dict() joint_vel = estimate_linear_velocity(fps_30_data['global_pos'], dt=1.0 / 30.0) joint_vel_mag = torch.norm(joint_vel, dim=-1).mean() smoothness['30FPS'] = [c2c(joint_vel_mag * 100.0)] joint_vel = estimate_linear_velocity(fps_60_data['global_pos'], dt=1.0 / 60.0) joint_vel_mag = torch.norm(joint_vel, dim=-1).mean() smoothness['60FPS'] = [c2c(joint_vel_mag * 100.0)] joint_vel = estimate_linear_velocity(fps_120_data['global_pos'], dt=1.0 / 120.0) joint_vel_mag = torch.norm(joint_vel, dim=-1).mean() smoothness['120FPS'] = [c2c(joint_vel_mag * 100.0)] joint_vel = estimate_linear_velocity(fps_240_data['global_pos'], dt=1.0 / 240.0) joint_vel_mag = torch.norm(joint_vel, dim=-1).mean() smoothness['240FPS'] = [c2c(joint_vel_mag * 100.0)] df = pd.DataFrame.from_dict(smoothness) df.to_csv(os.path.join(path, f'smoothness.csv')) def evaluate_smoothness_test(path): smoothness = dict() for fps in [30, 60]: smoothness[f'{fps}FPS'] = dict() for L in range(1, 23, 2): data = load_data([os.path.join(path, f'L{L}', f'test_{fps}fps.npz')]) # _, joint_accel_mag = compute_joint_accel(data['global_pos'], fps=fps) # smoothness[f'{fps}FPS'][L] = c2c(joint_accel_mag.mean() * 100.0) joint_vel = estimate_linear_velocity(data['global_pos'], dt=1.0 / fps) joint_vel_mag = torch.norm(joint_vel, dim=-1).mean() smoothness[f'{fps}FPS'][L] = c2c(joint_vel_mag.mean() * 100.0) df = pd.DataFrame.from_dict(smoothness) df.to_csv(os.path.join(path, f'smoothness.csv')) import matplotlib.pyplot as plt plt.rcParams['text.usetex'] = True smoothness_30fps = np.array(list(smoothness['30FPS'].values())) smoothness_60fps = np.array(list(smoothness['60FPS'].values())) plt.plot(np.arange(1, 23, 2), smoothness_30fps, marker='o', label='30fps') plt.plot(np.arange(1, 23, 2), smoothness_60fps, marker='o', label='60fps') plt.legend() plt.grid(linestyle='--') plt.xticks(np.arange(1, 23, 2)) plt.xlabel(r'$L$') # plt.yscale('log') plt.ylabel('Mean Per-Joint Velocity') plt.savefig(os.path.join(path, 'smoothness.pdf')) if __name__ == '__main__': from argparse import ArgumentParser parser = ArgumentParser() parser.add_argument('--config', type=str, default='evaluation.yaml', help='name of the configutation file') parser.add_argument('--task', type=str, help='name of application tasks', required=True) parser.add_argument('--load_path', type=str, help='path to load results for evaluation', required=True) opt = parser.parse_args() args = Arguments('./configs', filename=opt.config) torch.set_default_dtype(torch.float32) torch.manual_seed(0) np.random.seed(0) random.seed(0) ngpu = 1 if args.multi_gpu is True: ngpu = torch.cuda.device_count() if ngpu == 1: args.multi_gpu = False print(f'Number of GPUs: {ngpu}') prior = Architecture(args, ngpu) prior.load(optimal=True) prior.eval() fk = ForwardKinematicsLayer(args) if opt.task == 'ablation_study': print('Evaluating ablation study ...') ablation_study(opt.load_path, subtask='synth') elif opt.task == 'comparison': print('Evaluating comparison with other methods ...') comparison(opt.load_path, subtask='recon') comparison(opt.load_path, subtask='synth') elif opt.task == 'inbetween_benchmark': print('Evaluating motion in-betweening ...') inbetween_benchmarks(opt.load_path, keyframes=False) inbetween_benchmarks(opt.load_path, keyframes=True) elif opt.task == 'super_sampling': print('Evaluating super sampling ...') evaluate_super_sampling(opt.load_path) elif opt.task == 'smoothness_test': print('Evaluating smoothness testing ...') evaluate_smoothness_test(opt.load_path) else: raise NotImplementedError(f'unknown evaluation: {opt.task}')