Spaces:
Configuration error
Configuration error
| import torch | |
| import torch.nn as nn | |
| import numpy as np | |
| import torch.nn.functional as F | |
| # Numpy-based errors | |
| def mpjpe(predicted, target): | |
| """ | |
| Mean per-joint position error (i.e. mean Euclidean distance), | |
| often referred to as "Protocol #1" in many papers. | |
| """ | |
| assert predicted.shape == target.shape | |
| return np.mean(np.linalg.norm(predicted - target, axis=len(target.shape)-1), axis=1) | |
| def p_mpjpe(predicted, target): | |
| """ | |
| Pose error: MPJPE after rigid alignment (scale, rotation, and translation), | |
| often referred to as "Protocol #2" in many papers. | |
| """ | |
| assert predicted.shape == target.shape | |
| muX = np.mean(target, axis=1, keepdims=True) | |
| muY = np.mean(predicted, axis=1, keepdims=True) | |
| X0 = target - muX | |
| Y0 = predicted - muY | |
| normX = np.sqrt(np.sum(X0**2, axis=(1, 2), keepdims=True)) | |
| normY = np.sqrt(np.sum(Y0**2, axis=(1, 2), keepdims=True)) | |
| X0 /= normX | |
| Y0 /= normY | |
| H = np.matmul(X0.transpose(0, 2, 1), Y0) | |
| U, s, Vt = np.linalg.svd(H) | |
| V = Vt.transpose(0, 2, 1) | |
| R = np.matmul(V, U.transpose(0, 2, 1)) | |
| # Avoid improper rotations (reflections), i.e. rotations with det(R) = -1 | |
| sign_detR = np.sign(np.expand_dims(np.linalg.det(R), axis=1)) | |
| V[:, :, -1] *= sign_detR | |
| s[:, -1] *= sign_detR.flatten() | |
| R = np.matmul(V, U.transpose(0, 2, 1)) # Rotation | |
| tr = np.expand_dims(np.sum(s, axis=1, keepdims=True), axis=2) | |
| a = tr * normX / normY # Scale | |
| t = muX - a*np.matmul(muY, R) # Translation | |
| # Perform rigid transformation on the input | |
| predicted_aligned = a*np.matmul(predicted, R) + t | |
| # Return MPJPE | |
| return np.mean(np.linalg.norm(predicted_aligned - target, axis=len(target.shape)-1), axis=1) | |
| # PyTorch-based errors (for losses) | |
| def loss_mpjpe(predicted, target): | |
| """ | |
| Mean per-joint position error (i.e. mean Euclidean distance), | |
| often referred to as "Protocol #1" in many papers. | |
| """ | |
| assert predicted.shape == target.shape | |
| return torch.mean(torch.norm(predicted - target, dim=len(target.shape)-1)) | |
| def weighted_mpjpe(predicted, target, w): | |
| """ | |
| Weighted mean per-joint position error (i.e. mean Euclidean distance) | |
| """ | |
| assert predicted.shape == target.shape | |
| assert w.shape[0] == predicted.shape[0] | |
| return torch.mean(w * torch.norm(predicted - target, dim=len(target.shape)-1)) | |
| def loss_2d_weighted(predicted, target, conf): | |
| assert predicted.shape == target.shape | |
| predicted_2d = predicted[:,:,:,:2] | |
| target_2d = target[:,:,:,:2] | |
| diff = (predicted_2d - target_2d) * conf | |
| return torch.mean(torch.norm(diff, dim=-1)) | |
| def n_mpjpe(predicted, target): | |
| """ | |
| Normalized MPJPE (scale only), adapted from: | |
| https://github.com/hrhodin/UnsupervisedGeometryAwareRepresentationLearning/blob/master/losses/poses.py | |
| """ | |
| assert predicted.shape == target.shape | |
| norm_predicted = torch.mean(torch.sum(predicted**2, dim=3, keepdim=True), dim=2, keepdim=True) | |
| norm_target = torch.mean(torch.sum(target*predicted, dim=3, keepdim=True), dim=2, keepdim=True) | |
| scale = norm_target / norm_predicted | |
| return loss_mpjpe(scale * predicted, target) | |
| def weighted_bonelen_loss(predict_3d_length, gt_3d_length): | |
| loss_length = 0.001 * torch.pow(predict_3d_length - gt_3d_length, 2).mean() | |
| return loss_length | |
| def weighted_boneratio_loss(predict_3d_length, gt_3d_length): | |
| loss_length = 0.1 * torch.pow((predict_3d_length - gt_3d_length)/gt_3d_length, 2).mean() | |
| return loss_length | |
| def get_limb_lens(x): | |
| ''' | |
| Input: (N, T, 17, 3) | |
| Output: (N, T, 16) | |
| ''' | |
| limbs_id = [[0,1], [1,2], [2,3], | |
| [0,4], [4,5], [5,6], | |
| [0,7], [7,8], [8,9], [9,10], | |
| [8,11], [11,12], [12,13], | |
| [8,14], [14,15], [15,16] | |
| ] | |
| limbs = x[:,:,limbs_id,:] | |
| limbs = limbs[:,:,:,0,:]-limbs[:,:,:,1,:] | |
| limb_lens = torch.norm(limbs, dim=-1) | |
| return limb_lens | |
| def loss_limb_var(x): | |
| ''' | |
| Input: (N, T, 17, 3) | |
| ''' | |
| if x.shape[1]<=1: | |
| return torch.FloatTensor(1).fill_(0.)[0].to(x.device) | |
| limb_lens = get_limb_lens(x) | |
| limb_lens_var = torch.var(limb_lens, dim=1) | |
| limb_loss_var = torch.mean(limb_lens_var) | |
| return limb_loss_var | |
| def loss_limb_gt(x, gt): | |
| ''' | |
| Input: (N, T, 17, 3), (N, T, 17, 3) | |
| ''' | |
| limb_lens_x = get_limb_lens(x) | |
| limb_lens_gt = get_limb_lens(gt) # (N, T, 16) | |
| return nn.L1Loss()(limb_lens_x, limb_lens_gt) | |
| def loss_velocity(predicted, target): | |
| """ | |
| Mean per-joint velocity error (i.e. mean Euclidean distance of the 1st derivative) | |
| """ | |
| assert predicted.shape == target.shape | |
| if predicted.shape[1]<=1: | |
| return torch.FloatTensor(1).fill_(0.)[0].to(predicted.device) | |
| velocity_predicted = predicted[:,1:] - predicted[:,:-1] | |
| velocity_target = target[:,1:] - target[:,:-1] | |
| return torch.mean(torch.norm(velocity_predicted - velocity_target, dim=-1)) | |
| def loss_joint(predicted, target): | |
| assert predicted.shape == target.shape | |
| return nn.L1Loss()(predicted, target) | |
| def get_angles(x): | |
| ''' | |
| Input: (N, T, 17, 3) | |
| Output: (N, T, 16) | |
| ''' | |
| limbs_id = [[0,1], [1,2], [2,3], | |
| [0,4], [4,5], [5,6], | |
| [0,7], [7,8], [8,9], [9,10], | |
| [8,11], [11,12], [12,13], | |
| [8,14], [14,15], [15,16] | |
| ] | |
| angle_id = [[ 0, 3], | |
| [ 0, 6], | |
| [ 3, 6], | |
| [ 0, 1], | |
| [ 1, 2], | |
| [ 3, 4], | |
| [ 4, 5], | |
| [ 6, 7], | |
| [ 7, 10], | |
| [ 7, 13], | |
| [ 8, 13], | |
| [10, 13], | |
| [ 7, 8], | |
| [ 8, 9], | |
| [10, 11], | |
| [11, 12], | |
| [13, 14], | |
| [14, 15] ] | |
| eps = 1e-7 | |
| limbs = x[:,:,limbs_id,:] | |
| limbs = limbs[:,:,:,0,:]-limbs[:,:,:,1,:] | |
| angles = limbs[:,:,angle_id,:] | |
| angle_cos = F.cosine_similarity(angles[:,:,:,0,:], angles[:,:,:,1,:], dim=-1) | |
| return torch.acos(angle_cos.clamp(-1+eps, 1-eps)) | |
| def loss_angle(x, gt): | |
| ''' | |
| Input: (N, T, 17, 3), (N, T, 17, 3) | |
| ''' | |
| limb_angles_x = get_angles(x) | |
| limb_angles_gt = get_angles(gt) | |
| return nn.L1Loss()(limb_angles_x, limb_angles_gt) | |
| def loss_angle_velocity(x, gt): | |
| """ | |
| Mean per-angle velocity error (i.e. mean Euclidean distance of the 1st derivative) | |
| """ | |
| assert x.shape == gt.shape | |
| if x.shape[1]<=1: | |
| return torch.FloatTensor(1).fill_(0.)[0].to(x.device) | |
| x_a = get_angles(x) | |
| gt_a = get_angles(gt) | |
| x_av = x_a[:,1:] - x_a[:,:-1] | |
| gt_av = gt_a[:,1:] - gt_a[:,:-1] | |
| return nn.L1Loss()(x_av, gt_av) | |