Skip to content

Commit da9974b

Browse files
classnerfacebook-github-bot
authored andcommitted
Add PyTorch3D->OpenCV camera parameter conversion.
Summary: This diff implements the inverse of D28992470 (8006842): a function to extract OpenCV convention camera parameters from a PyTorch3D `PerspectiveCameras` object. This is the first part of the new PyTorch3d<>OpenCV<>Pulsar conversion functions. Reviewed By: patricklabatut Differential Revision: D29278411 fbshipit-source-id: 68d4555b508dbe8685d8239443f839d194cc2484
1 parent e4039aa commit da9974b

File tree

3 files changed

+65
-3
lines changed

3 files changed

+65
-3
lines changed

pytorch3d/utils/__init__.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
# This source code is licensed under the BSD-style license found in the
55
# LICENSE file in the root directory of this source tree.
66

7-
from .camera_conversions import cameras_from_opencv_projection
7+
from .camera_conversions import (
8+
cameras_from_opencv_projection,
9+
opencv_from_cameras_projection,
10+
)
811
from .ico_sphere import ico_sphere
912
from .torus import torus
1013

pytorch3d/utils/camera_conversions.py

+52-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
# This source code is licensed under the BSD-style license found in the
55
# LICENSE file in the root directory of this source tree.
66

7+
from typing import Tuple
8+
79
import torch
810

911
from ..renderer import PerspectiveCameras
10-
from ..transforms import so3_exponential_map
12+
from ..transforms import so3_exponential_map, so3_log_map
1113

1214

1315
def cameras_from_opencv_projection(
@@ -35,7 +37,7 @@ def cameras_from_opencv_projection(
3537
followed by the homogenization of `x_screen_opencv`.
3638
3739
Note:
38-
The parameters `rvec, tvec, camera_matrix` correspond e.g. to the inputs
40+
The parameters `rvec, tvec, camera_matrix` correspond, e.g., to the inputs
3941
of `cv2.projectPoints`, or to the ouputs of `cv2.calibrateCamera`.
4042
4143
Args:
@@ -74,3 +76,51 @@ def cameras_from_opencv_projection(
7476
focal_length=focal_pytorch3d,
7577
principal_point=p0_pytorch3d,
7678
)
79+
80+
81+
def opencv_from_cameras_projection(
82+
cameras: PerspectiveCameras,
83+
image_size: torch.Tensor,
84+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
85+
"""
86+
Converts a batch of `PerspectiveCameras` into OpenCV-convention
87+
axis-angle rotation vectors `rvec`, translation vectors `tvec`, and the camera
88+
calibration matrices `camera_matrix`. This operation is exactly the inverse
89+
of `cameras_from_opencv_projection`.
90+
91+
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`.
94+
95+
Args:
96+
cameras: A batch of `N` cameras in the PyTorch3D convention.
97+
image_size: A tensor of shape `(N, 2)` containing the sizes of the images
98+
(height, width) attached to each camera.
99+
100+
Returns:
101+
rvec: A batch of axis-angle rotation vectors of shape `(N, 3)`.
102+
tvec: A batch of translation vectors of shape `(N, 3)`.
103+
camera_matrix: A batch of camera calibration matrices of shape `(N, 3, 3)`.
104+
"""
105+
R_pytorch3d = cameras.R
106+
T_pytorch3d = cameras.T
107+
focal_pytorch3d = cameras.focal_length
108+
p0_pytorch3d = cameras.principal_point
109+
T_pytorch3d[:, :2] *= -1 # pyre-ignore
110+
R_pytorch3d[:, :, :2] *= -1 # pyre-ignore
111+
tvec = T_pytorch3d.clone() # pyre-ignore
112+
R = R_pytorch3d.permute(0, 2, 1) # pyre-ignore
113+
114+
# Retype the image_size correctly and flip to width, height.
115+
image_size_wh = image_size.to(R).flip(dims=(1,))
116+
117+
principal_point = (-p0_pytorch3d + 1.0) * (0.5 * image_size_wh) # pyre-ignore
118+
focal_length = focal_pytorch3d * (0.5 * image_size_wh)
119+
120+
camera_matrix = torch.zeros_like(R)
121+
camera_matrix[:, :2, 2] = principal_point
122+
camera_matrix[:, 2, 2] = 1.0
123+
camera_matrix[:, 0, 0] = focal_length[:, 0]
124+
camera_matrix[:, 1, 1] = focal_length[:, 1]
125+
rvec = so3_log_map(R)
126+
return rvec, tvec, camera_matrix

tests/test_camera_conversions.py

+9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from pytorch3d.transforms import so3_exponential_map, so3_log_map
1616
from pytorch3d.utils import (
1717
cameras_from_opencv_projection,
18+
opencv_from_cameras_projection,
1819
)
1920

2021
DATA_DIR = get_tests_dir() / "data"
@@ -151,3 +152,11 @@ def test_opencv_conversion(self):
151152
self.assertClose(
152153
pts_proj_opencv_in_pytorch3d_screen, pts_proj_pytorch3d, atol=1e-5
153154
)
155+
156+
# Check the inverse.
157+
rvec_i, tvec_i, camera_matrix_i = opencv_from_cameras_projection(
158+
cameras_opencv_to_pytorch3d, image_size
159+
)
160+
self.assertClose(rvec, rvec_i)
161+
self.assertClose(tvec, tvec_i)
162+
self.assertClose(camera_matrix, camera_matrix_i)

0 commit comments

Comments
 (0)