-
Notifications
You must be signed in to change notification settings - Fork 6
/
transform_utils.py
136 lines (122 loc) · 5.66 KB
/
transform_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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import torch
from pytorch3d.transforms import euler_angles_to_matrix
from pytorch3d.transforms import matrix_to_euler_angles
def matrix_from_angles(rot):
"""Create a rotation matrix from a triplet of rotation angles.
Args:
rot: a torch.Tensor of shape [..., 3], where the last dimension is the rotation
angles, along x, y, and z.
Returns:
A torch.tensor of shape [..., 3, 3], where the last two dimensions are the
rotation matrix.
This function mimics _euler2mat from struct2depth/project.py, for backward
compatibility, but wraps tensorflow_graphics instead of reimplementing it.
The negation and transposition are needed to bridge the differences between
the two.
"""
rank = rot.dim()
# Swap the two last dimensions
perm = torch.cat([torch.arange(0, rank - 1, dtype=torch.long), torch.tensor([rank]), torch.tensor([rank - 1])], dim=0)
return euler_angles_to_matrix(-rot, convention="XYZ").permute(*perm)
def angles_from_matrix(matrix):
"""Get a triplet of rotation angles from a rotation matrix.
Args:
matrix: A torch.tensor of shape [..., 3, 3], where the last two dimensions are
the rotation matrix.
Returns:
A torch.Tensor of shape [..., 3], where the last dimension is the rotation
angles, along x, y, and z.
This function mimics _euler2mat from struct2depth/project.py, for backward
compatibility, but wraps tensorflow_graphics instead of reimplementing it.
The negation and transposition are needed to bridge the differences between
the two.
"""
rank = matrix.dim()
# Swap the two last dimensions
perm = torch.cat([torch.arange(0, rank - 2, dtype=torch.long), torch.tensor([rank - 1]), torch.tensor([rank - 2])], dim=0)
return -matrix_to_euler_angles(matrix.permute(*perm), convention="XYZ")
def unstacked_matrix_from_angles(rx, ry, rz, name=None):
"""Create an unstacked rotation matrix from rotation angles.
Args:
rx: A torch.Tensor of rotation angles abound x, of any shape.
ry: A torch.Tensor of rotation angles abound y (of the same shape as x).
rz: A torch.Tensor of rotation angles abound z (of the same shape as x).
name: A string, name for the op.
Returns:
A 3-tuple of 3-tuple of torch.Tensors of the same shape as x, representing the
respective rotation matrix. The small 3x3 dimensions are unstacked into a
tuple to avoid tensors with small dimensions, which bloat the TPU HBM
memory. Unstacking is one of the recommended methods for resolving the
problem.
"""
angles = [-rx, -ry, -rz]
sx, sy, sz = [torch.sin(a) for a in angles]
cx, cy, cz = [torch.cos(a) for a in angles]
m00 = cy * cz
m10 = (sx * sy * cz) - (cx * sz)
m20 = (cx * sy * cz) + (sx * sz)
m01 = cy * sz
m11 = (sx * sy * sz) + (cx * cz)
m21 = (cx * sy * sz) - (sx * cz)
m02 = -sy
m12 = sx * cy
m22 = cx * cy
return ((m00, m01, m02), (m10, m11, m12), (m20, m21, m22))
def invert_rot_and_trans(rot, trans):
"""Inverts a transform comprised of a rotation and a translation.
Args:
rot: a torch.Tensor of shape [..., 3] representing rotatation angles.
trans: a torch.Tensor of shape [..., 3] representing translation vectors.
Returns:
a tuple (inv_rot, inv_trans), representing rotation angles and translation
vectors, such that applting rot, transm inv_rot, inv_trans, in succession
results in identity.
"""
inv_rot = inverse_euler(rot) # inv_rot = -rot for small angles
inv_rot_mat = matrix_from_angles(inv_rot)
inv_trans = -torch.matmul(inv_rot_mat, torch.unsqueeze(trans, -1))
inv_trans = torch.squeeze(inv_trans, -1)
return inv_rot, inv_trans
def inverse_euler(angles):
"""Returns the euler angles that are the inverse of the input.
Args:
angles: a torch.Tensor of shape [..., 3]
Returns:
A tensor of the same shape, representing the inverse rotation.
"""
sin_angles = torch.sin(angles)
cos_angles = torch.cos(angles)
sz, sy, sx = torch.unbind(-sin_angles, dim=-1)
cz, _, cx = torch.unbind(cos_angles, dim=-1)
y = torch.asin((cx * sy * cz) + (sx * sz))
x = -torch.asin((sx * sy * cz) - (cx * sz)) / tf.cos(y)
z = -torch.asin((cx * sy * sz) - (sx * cz)) / tf.cos(y)
return torch.stack([x, y, z], dim=-1)
def combine(rot_mat1, trans_vec1, rot_mat2, trans_vec2):
"""Composes two transformations, each has a rotation and a translation.
Args:
rot_mat1: A torch.tensor of shape [..., 3, 3] representing rotation matrices.
trans_vec1: A torch.tensor of shape [..., 3] representing translation vectors.
rot_mat2: A torch.tensor of shape [..., 3, 3] representing rotation matrices.
trans_vec2: A torch.tensor of shape [..., 3] representing translation vectors.
Returns:
A tuple of 2 torch.Tensors, representing rotation matrices and translation
vectors, of the same shapes as the input, representing the result of
applying rot1, trans1, rot2, trans2, in succession.
"""
# Building a 4D transform matrix from each rotation and translation, and
# multiplying the two, we'd get:
#
# ( R2 t2) . ( R1 t1) = (R2R1 R2t1 + t2)
# (0 0 0 1 ) (0 0 0 1 ) (0 0 0 1 )
#
# Where each R is a 3x3 matrix, each t is a 3-long column vector, and 0 0 0 is
# a row vector of 3 zeros. We see that the total rotation is R2*R1 and the t
# total translation is R2*t1 + t2.
r2r1 = torch.matmul(rot_mat2, rot_mat1)
r2t1 = torch.matmul(rot_mat2, torch.unsqueeze(trans_vec1, -1))
r2t1 = torch.squeeze(r2t1, axis=-1)
return r2r1, r2t1 + trans_vec2