Skip to content

Commit fef5bcd

Browse files
patricklabatutfacebook-github-bot
authored andcommitted
Use rotation matrices for OpenCV / PyTorch3D conversions
Summary: Use rotation matrices for OpenCV / PyTorch3D conversions: this avoids hiding issues with conversions to / from axis-angle vectors and ensure new conversion functions have a consistent interface. Reviewed By: bottler, classner Differential Revision: D29634099 fbshipit-source-id: 40b28357914eb563fedea60a965dcf69e848ccfa
1 parent 44d2a9b commit fef5bcd

File tree

2 files changed

+23
-18
lines changed

2 files changed

+23
-18
lines changed

pytorch3d/utils/camera_conversions.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313

1414

1515
def cameras_from_opencv_projection(
16-
rvec: torch.Tensor,
16+
R: torch.Tensor,
1717
tvec: torch.Tensor,
1818
camera_matrix: torch.Tensor,
1919
image_size: torch.Tensor,
2020
) -> PerspectiveCameras:
2121
"""
2222
Converts a batch of OpenCV-conventioned cameras parametrized with the
23-
axis-angle rotation vectors `rvec`, translation vectors `tvec`, and the camera
23+
rotation matrices `R`, translation vectors `tvec`, and the camera
2424
calibration matrices `camera_matrix` to `PerspectiveCameras` in PyTorch3D
2525
convention.
2626
@@ -32,16 +32,20 @@ def cameras_from_opencv_projection(
3232
More specifically, the OpenCV convention projects points to the OpenCV screen
3333
space as follows:
3434
```
35-
x_screen_opencv = camera_matrix @ (exp(rvec) @ x_world + tvec)
35+
x_screen_opencv = camera_matrix @ (R @ x_world + tvec)
3636
```
3737
followed by the homogenization of `x_screen_opencv`.
3838
3939
Note:
40-
The parameters `rvec, tvec, camera_matrix` correspond, e.g., to the inputs
41-
of `cv2.projectPoints`, or to the ouputs of `cv2.calibrateCamera`.
40+
The parameters `R, tvec, camera_matrix` correspond to the outputs of
41+
`cv2.decomposeProjectionMatrix`.
42+
43+
The `rvec` parameter of the `cv2.projectPoints` is an axis-angle vector
44+
that can be converted to the rotation matrix `R` expected here by
45+
calling the `so3_exp_map` function.
4246
4347
Args:
44-
rvec: A batch of axis-angle rotation vectors of shape `(N, 3)`.
48+
R: A batch of rotation matrices of shape `(N, 3, 3)`.
4549
tvec: A batch of translation vectors of shape `(N, 3)`.
4650
camera_matrix: A batch of camera calibration matrices of shape `(N, 3, 3)`.
4751
image_size: A tensor of shape `(N, 2)` containing the sizes of the images
@@ -51,7 +55,6 @@ def cameras_from_opencv_projection(
5155
cameras_pytorch3d: A batch of `N` cameras in the PyTorch3D convention.
5256
"""
5357

54-
R = so3_exp_map(rvec)
5558
focal_length = torch.stack([camera_matrix[:, 0, 0], camera_matrix[:, 1, 1]], dim=-1)
5659
principal_point = camera_matrix[:, :2, 2]
5760

@@ -84,21 +87,25 @@ def opencv_from_cameras_projection(
8487
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
8588
"""
8689
Converts a batch of `PerspectiveCameras` into OpenCV-convention
87-
axis-angle rotation vectors `rvec`, translation vectors `tvec`, and the camera
90+
rotation matrices `R`, translation vectors `tvec`, and the camera
8891
calibration matrices `camera_matrix`. This operation is exactly the inverse
8992
of `cameras_from_opencv_projection`.
9093
9194
Note:
92-
The parameters `rvec, tvec, camera_matrix` correspond, e.g., to the inputs
93-
of `cv2.projectPoints`, or to the ouputs of `cv2.calibrateCamera`.
95+
The outputs `R, tvec, camera_matrix` correspond to the outputs of
96+
`cv2.decomposeProjectionMatrix`.
97+
98+
The `rvec` parameter of the `cv2.projectPoints` is an axis-angle vector
99+
that can be converted from the returned rotation matrix `R` here by
100+
calling the `so3_log_map` function.
94101
95102
Args:
96103
cameras: A batch of `N` cameras in the PyTorch3D convention.
97104
image_size: A tensor of shape `(N, 2)` containing the sizes of the images
98105
(height, width) attached to each camera.
99106
100107
Returns:
101-
rvec: A batch of axis-angle rotation vectors of shape `(N, 3)`.
108+
R: A batch of rotation matrices of shape `(N, 3, 3)`.
102109
tvec: A batch of translation vectors of shape `(N, 3)`.
103110
camera_matrix: A batch of camera calibration matrices of shape `(N, 3, 3)`.
104111
"""
@@ -122,5 +129,4 @@ def opencv_from_cameras_projection(
122129
camera_matrix[:, 2, 2] = 1.0
123130
camera_matrix[:, 0, 0] = focal_length[:, 0]
124131
camera_matrix[:, 1, 1] = focal_length[:, 1]
125-
rvec = so3_log_map(R)
126-
return rvec, tvec, camera_matrix
132+
return R, tvec, camera_matrix

tests/test_camera_conversions.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,15 @@ def test_opencv_conversion(self):
129129
)
130130
camera_matrix[:, :2, 2] = principal_point
131131

132-
rvec = so3_log_map(R)
133-
134132
pts = torch.nn.functional.normalize(torch.randn(4, 1000, 3), dim=-1)
135133

136134
# project the 3D points with the opencv projection function
135+
rvec = so3_log_map(R)
137136
pts_proj_opencv = cv2_project_points(pts, rvec, tvec, camera_matrix)
138137

139138
# make the pytorch3d cameras
140139
cameras_opencv_to_pytorch3d = cameras_from_opencv_projection(
141-
rvec, tvec, camera_matrix, image_size
140+
R, tvec, camera_matrix, image_size
142141
)
143142

144143
# project the 3D points with converted cameras
@@ -155,9 +154,9 @@ def test_opencv_conversion(self):
155154
)
156155

157156
# Check the inverse.
158-
rvec_i, tvec_i, camera_matrix_i = opencv_from_cameras_projection(
157+
R_i, tvec_i, camera_matrix_i = opencv_from_cameras_projection(
159158
cameras_opencv_to_pytorch3d, image_size
160159
)
161-
self.assertClose(rvec, rvec_i)
160+
self.assertClose(R, R_i)
162161
self.assertClose(tvec, tvec_i)
163162
self.assertClose(camera_matrix, camera_matrix_i)

0 commit comments

Comments
 (0)