-
Notifications
You must be signed in to change notification settings - Fork 35
/
utils.py
77 lines (58 loc) · 2.13 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import numpy as np
class AverageMeter(object):
"""
Computes and stores the average and
current value.
"""
def __init__(self):
self.reset()
def reset(self):
self.val = 0
self.avg = 0
self.sum = 0
self.count = 0
def update(self, val, n=1):
self.val = val
self.sum += val * n
self.count += n
self.avg = self.sum / self.count
def pitchyaw_to_vector(pitchyaws):
r"""Convert given yaw (:math:`\theta`) and pitch (:math:`\phi`) angles to unit gaze vectors.
Args:
pitchyaws (:obj:`numpy.array`): yaw and pitch angles :math:`(n\times 2)` in radians.
Returns:
:obj:`numpy.array` of shape :math:`(n\times 3)` with 3D vectors per row.
"""
n = pitchyaws.shape[0]
sin = np.sin(pitchyaws)
cos = np.cos(pitchyaws)
out = np.empty((n, 3))
out[:, 0] = np.multiply(cos[:, 0], sin[:, 1])
out[:, 1] = sin[:, 0]
out[:, 2] = np.multiply(cos[:, 0], cos[:, 1])
return out
def vector_to_pitchyaw(vectors):
r"""Convert given gaze vectors to yaw (:math:`\theta`) and pitch (:math:`\phi`) angles.
Args:
vectors (:obj:`numpy.array`): gaze vectors in 3D :math:`(n\times 3)`.
Returns:
:obj:`numpy.array` of shape :math:`(n\times 2)` with values in radians.
"""
n = vectors.shape[0]
out = np.empty((n, 2))
vectors = np.divide(vectors, np.linalg.norm(vectors, axis=1).reshape(n, 1))
out[:, 0] = np.arcsin(vectors[:, 1]) # theta
out[:, 1] = np.arctan2(vectors[:, 0], vectors[:, 2]) # phi
return out
def angular_error(a, b):
"""Calculate angular error (via cosine similarity)."""
a = pitchyaw_to_vector(a) if a.shape[1] == 2 else a
b = pitchyaw_to_vector(b) if b.shape[1] == 2 else b
ab = np.sum(np.multiply(a, b), axis=1)
a_norm = np.linalg.norm(a, axis=1)
b_norm = np.linalg.norm(b, axis=1)
# Avoid zero-values (to avoid NaNs)
a_norm = np.clip(a_norm, a_min=1e-7, a_max=None)
b_norm = np.clip(b_norm, a_min=1e-7, a_max=None)
similarity = np.divide(ab, np.multiply(a_norm, b_norm))
return np.arccos(similarity) * 180.0 / np.pi