Spaces:
Running
Running
import time | |
import numpy as np | |
import torch | |
import torch.nn.functional as F | |
from scipy import linalg | |
import math | |
from data_utils.rotation_conversion import axis_angle_to_matrix, matrix_to_rotation_6d | |
import warnings | |
warnings.filterwarnings("ignore", category=RuntimeWarning) # ignore warnings | |
change_angle = torch.tensor([6.0181e-05, 5.1597e-05, 2.1344e-04, 2.1899e-04]) | |
class EmbeddingSpaceEvaluator: | |
def __init__(self, ae, vae, device): | |
# init embed net | |
self.ae = ae | |
# self.vae = vae | |
# storage | |
self.real_feat_list = [] | |
self.generated_feat_list = [] | |
self.real_joints_list = [] | |
self.generated_joints_list = [] | |
self.real_6d_list = [] | |
self.generated_6d_list = [] | |
self.audio_beat_list = [] | |
def reset(self): | |
self.real_feat_list = [] | |
self.generated_feat_list = [] | |
def get_no_of_samples(self): | |
return len(self.real_feat_list) | |
def push_samples(self, generated_poses, real_poses): | |
# self.net.eval() | |
# convert poses to latent features | |
real_feat, real_poses = self.ae.extract(real_poses) | |
generated_feat, generated_poses = self.ae.extract(generated_poses) | |
num_joints = real_poses.shape[2] // 3 | |
real_feat = real_feat.squeeze() | |
generated_feat = generated_feat.reshape(generated_feat.shape[0]*generated_feat.shape[1], -1) | |
self.real_feat_list.append(real_feat.data.cpu().numpy()) | |
self.generated_feat_list.append(generated_feat.data.cpu().numpy()) | |
# real_poses = matrix_to_rotation_6d(axis_angle_to_matrix(real_poses.reshape(-1, 3))).reshape(-1, num_joints, 6) | |
# generated_poses = matrix_to_rotation_6d(axis_angle_to_matrix(generated_poses.reshape(-1, 3))).reshape(-1, num_joints, 6) | |
# | |
# self.real_feat_list.append(real_poses.data.cpu().numpy()) | |
# self.generated_feat_list.append(generated_poses.data.cpu().numpy()) | |
def push_joints(self, generated_poses, real_poses): | |
self.real_joints_list.append(real_poses.data.cpu()) | |
self.generated_joints_list.append(generated_poses.squeeze().data.cpu()) | |
def push_aud(self, aud): | |
self.audio_beat_list.append(aud.squeeze().data.cpu()) | |
def get_MAAC(self): | |
ang_vel_list = [] | |
for real_joints in self.real_joints_list: | |
real_joints[:, 15:21] = real_joints[:, 16:22] | |
vec = real_joints[:, 15:21] - real_joints[:, 13:19] | |
inner_product = torch.einsum('kij,kij->ki', [vec[:, 2:], vec[:, :-2]]) | |
inner_product = torch.clamp(inner_product, -1, 1, out=None) | |
angle = torch.acos(inner_product) / math.pi | |
ang_vel = (angle[1:] - angle[:-1]).abs().mean(dim=0) | |
ang_vel_list.append(ang_vel.unsqueeze(dim=0)) | |
all_vel = torch.cat(ang_vel_list, dim=0) | |
MAAC = all_vel.mean(dim=0) | |
return MAAC | |
def get_BCscore(self): | |
thres = 0.01 | |
sigma = 0.1 | |
sum_1 = 0 | |
total_beat = 0 | |
for joints, audio_beat_time in zip(self.generated_joints_list, self.audio_beat_list): | |
motion_beat_time = [] | |
if joints.dim() == 4: | |
joints = joints[0] | |
joints[:, 15:21] = joints[:, 16:22] | |
vec = joints[:, 15:21] - joints[:, 13:19] | |
inner_product = torch.einsum('kij,kij->ki', [vec[:, 2:], vec[:, :-2]]) | |
inner_product = torch.clamp(inner_product, -1, 1, out=None) | |
angle = torch.acos(inner_product) / math.pi | |
ang_vel = (angle[1:] - angle[:-1]).abs() / change_angle / len(change_angle) | |
angle_diff = torch.cat((torch.zeros(1, 4), ang_vel), dim=0) | |
sum_2 = 0 | |
for i in range(angle_diff.shape[1]): | |
motion_beat_time = [] | |
for t in range(1, joints.shape[0]-1): | |
if (angle_diff[t][i] < angle_diff[t - 1][i] and angle_diff[t][i] < angle_diff[t + 1][i]): | |
if (angle_diff[t - 1][i] - angle_diff[t][i] >= thres or angle_diff[t + 1][i] - angle_diff[ | |
t][i] >= thres): | |
motion_beat_time.append(float(t) / 30.0) | |
if (len(motion_beat_time) == 0): | |
continue | |
motion_beat_time = torch.tensor(motion_beat_time) | |
sum = 0 | |
for audio in audio_beat_time: | |
sum += np.power(math.e, -(np.power((audio.item() - motion_beat_time), 2)).min() / (2 * sigma * sigma)) | |
sum_2 = sum_2 + sum | |
total_beat = total_beat + len(audio_beat_time) | |
sum_1 = sum_1 + sum_2 | |
return sum_1/total_beat | |
def get_scores(self): | |
generated_feats = np.vstack(self.generated_feat_list) | |
real_feats = np.vstack(self.real_feat_list) | |
def frechet_distance(samples_A, samples_B): | |
A_mu = np.mean(samples_A, axis=0) | |
A_sigma = np.cov(samples_A, rowvar=False) | |
B_mu = np.mean(samples_B, axis=0) | |
B_sigma = np.cov(samples_B, rowvar=False) | |
try: | |
frechet_dist = self.calculate_frechet_distance(A_mu, A_sigma, B_mu, B_sigma) | |
except ValueError: | |
frechet_dist = 1e+10 | |
return frechet_dist | |
#################################################################### | |
# frechet distance | |
frechet_dist = frechet_distance(generated_feats, real_feats) | |
#################################################################### | |
# distance between real and generated samples on the latent feature space | |
dists = [] | |
for i in range(real_feats.shape[0]): | |
d = np.sum(np.absolute(real_feats[i] - generated_feats[i])) # MAE | |
dists.append(d) | |
feat_dist = np.mean(dists) | |
return frechet_dist, feat_dist | |
def calculate_frechet_distance(mu1, sigma1, mu2, sigma2, eps=1e-6): | |
""" from https://github.com/mseitzer/pytorch-fid/blob/master/fid_score.py """ | |
"""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) |