相机参数与坐标转换

前言

在 3D 视觉领域,一个非常重要的基础是能够完成不同坐标系(如世界、相机、像素)之间的转换,并保证在这些坐标系中,所描述的 3D 对象一致。因此,熟练掌握各种坐标系间的相互转换就成了非常重要的一个过程。

Preliminary

旋转向量与旋转矩阵

旋转向量和旋转矩阵是在几何变换中用于表示和描述物体在三维空间中的旋转操作的工具。

旋转向量:
旋转向量(Rotation Vector)是一个三维向量,它表示绕一个轴进行旋转的角度和方向。通常,旋转向量的长度表示旋转的角度,而方向表示旋转的轴。旋转向量的范数(长度)与旋转的角度成正比。

例如,一个旋转向量为 [0, 0, π/2] 表示绕 z 轴逆时针旋转 90 度。

旋转矩阵:
旋转矩阵(Rotation Matrix)是一个 3x3 的正交矩阵,它描述了一个物体在三维空间中绕某个轴旋转的操作。旋转矩阵将一个向量从一个坐标系旋转到另一个坐标系。旋转矩阵满足以下性质:

正交性:旋转矩阵的行向量和列向量都是单位向量,行向量与列向量互为正交。
行列式为 1:旋转矩阵的行列式为 1,保证了体积的不变性。
旋转矩阵可以表示绕不同轴的旋转操作,如绕 x 轴、y 轴、z 轴等。不同的旋转矩阵组合可以实现复杂的旋转操作。

例如,绕 z 轴逆时针旋转 90 度的旋转矩阵可以表示为:

1
2
3
R_z(θ) = | cos(θ) -sin(θ) 0 |
| sin(θ) cos(θ) 0 |
| 0 0 1 |

绕 x 轴、y 轴的旋转矩阵可以类似表示。

旋转向量与旋转矩阵的相互转化

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
def angle_axis_to_rotation_matrix(angle_axis):
"""Convert 3d vector of axis-angle rotation to 4x4 rotation matrix

Args:
angle_axis (Tensor): tensor of 3d vector of axis-angle rotations.

Returns:
Tensor: tensor of 4x4 rotation matrices.

Shape:
- Input: :math:`(N, 3)`
- Output: :math:`(N, 4, 4)`

Example:
>>> input = torch.rand(1, 3) # Nx3
>>> output = tgm.angle_axis_to_rotation_matrix(input) # Nx4x4
"""
def _compute_rotation_matrix(angle_axis, theta2, eps=1e-6):
# We want to be careful to only evaluate the square root if the
# norm of the angle_axis vector is greater than zero. Otherwise
# we get a division by zero.
k_one = 1.0
theta = torch.sqrt(theta2)
wxyz = angle_axis / (theta + eps)
wx, wy, wz = torch.chunk(wxyz, 3, dim=1)
cos_theta = torch.cos(theta)
sin_theta = torch.sin(theta)

r00 = cos_theta + wx * wx * (k_one - cos_theta)
r10 = wz * sin_theta + wx * wy * (k_one - cos_theta)
r20 = -wy * sin_theta + wx * wz * (k_one - cos_theta)
r01 = wx * wy * (k_one - cos_theta) - wz * sin_theta
r11 = cos_theta + wy * wy * (k_one - cos_theta)
r21 = wx * sin_theta + wy * wz * (k_one - cos_theta)
r02 = wy * sin_theta + wx * wz * (k_one - cos_theta)
r12 = -wx * sin_theta + wy * wz * (k_one - cos_theta)
r22 = cos_theta + wz * wz * (k_one - cos_theta)
rotation_matrix = torch.cat(
[r00, r01, r02, r10, r11, r12, r20, r21, r22], dim=1)
return rotation_matrix.view(-1, 3, 3)

def _compute_rotation_matrix_taylor(angle_axis):
rx, ry, rz = torch.chunk(angle_axis, 3, dim=1)
k_one = torch.ones_like(rx)
rotation_matrix = torch.cat(
[k_one, -rz, ry, rz, k_one, -rx, -ry, rx, k_one], dim=1)
return rotation_matrix.view(-1, 3, 3)

# stolen from ceres/rotation.h

_angle_axis = torch.unsqueeze(angle_axis, dim=1)
theta2 = torch.matmul(_angle_axis, _angle_axis.transpose(1, 2))
theta2 = torch.squeeze(theta2, dim=1)

# compute rotation matrices
rotation_matrix_normal = _compute_rotation_matrix(angle_axis, theta2)
rotation_matrix_taylor = _compute_rotation_matrix_taylor(angle_axis)

# create mask to handle both cases
eps = 1e-6
mask = (theta2 > eps).view(-1, 1, 1).to(theta2.device)
mask_pos = (mask).type_as(theta2)
mask_neg = (mask == False).type_as(theta2) # noqa

# create output pose matrix
batch_size = angle_axis.shape[0]
rotation_matrix = torch.eye(4).to(angle_axis.device).type_as(angle_axis)
rotation_matrix = rotation_matrix.view(1, 4, 4).repeat(batch_size, 1, 1)
# fill output matrix with masked values
rotation_matrix[..., :3, :3] = \
mask_pos * rotation_matrix_normal + mask_neg * rotation_matrix_taylor
return rotation_matrix # Nx4x4

相机参数

相机外参

相机内参

坐标系的转化

世界坐标系到相机坐标系

相机坐标系到像素坐标系

模型坐标系到世界坐标系

观察空间到标准空间

LBS 技术