Amend D38407094: Fisheye Camera for PyTorch3D

Summary:
Amend FisheyeCamera by adding tests for all
combination of params and for different batch_sizes.

Reviewed By: kjchalup

Differential Revision: D39176747

fbshipit-source-id: 830d30da24beeb2f0df52db0b17a4303ed53b59c
This commit is contained in:
Jiali Duan 2022-08-31 13:50:50 -07:00 committed by Facebook GitHub Bot
parent d4a1051e0f
commit b0515e1461
2 changed files with 161 additions and 55 deletions

View File

@ -425,7 +425,7 @@ class FishEyeCameras(CamerasBase):
# do Newton iterations to find xr_yr # do Newton iterations to find xr_yr
for _ in range(self.num_distortion_iters): for _ in range(self.num_distortion_iters):
# compute the estimated uvDistorted # compute the estimated uvDistorted
uv_distorted_est = xr_yr uv_distorted_est = xr_yr.clone()
xr_yr_squared_norm = torch.pow(xr_yr, 2).sum(dim=-1, keepdim=True) xr_yr_squared_norm = torch.pow(xr_yr, 2).sum(dim=-1, keepdim=True)
if self.use_tangential: if self.use_tangential:
@ -479,7 +479,6 @@ class FishEyeCameras(CamerasBase):
th: angle theta (in radians) of shape (...), E.g., (P), (1, P), (M, P) th: angle theta (in radians) of shape (...), E.g., (P), (1, P), (M, P)
""" """
sh = list(th_radial_desired.shape) sh = list(th_radial_desired.shape)
# th = th_radial_desired.clone()
th = th_radial_desired th = th_radial_desired
c = torch.tensor( c = torch.tensor(
[2.0 * i + 3 for i in range(self.num_radial)], device=self.device [2.0 * i + 3 for i in range(self.num_radial)], device=self.device
@ -552,7 +551,7 @@ class FishEyeCameras(CamerasBase):
+ 2.0 * xr_yr[..., 0] * tangential_params[..., 0] + 2.0 * xr_yr[..., 0] * tangential_params[..., 0]
) )
else: else:
duv_distorted_dxryr = torch.eye(2).view(*sh, 2, 2).expand(*sh, 1, 1) duv_distorted_dxryr = torch.eye(2).repeat(*sh, 1, 1)
if self.use_thin_prism: if self.use_thin_prism:
temp1 = 2.0 * ( temp1 = 2.0 * (

View File

@ -33,6 +33,7 @@
import math import math
import typing import typing
import unittest import unittest
from itertools import product
import numpy as np import numpy as np
import torch import torch
@ -1389,7 +1390,7 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase):
thin_prism_params, thin_prism_params,
) )
def setUpBatchCameras(self) -> None: def setUpBatchCameras(self, combination: None) -> None:
super().setUp() super().setUp()
focal, principal_point, p_3d = self.setUpSimpleCase() focal, principal_point, p_3d = self.setUpSimpleCase()
radial_params = torch.tensor( radial_params = torch.tensor(
@ -1412,8 +1413,12 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase):
radial_params = torch.cat([radial_params, radial_params1], dim=0) radial_params = torch.cat([radial_params, radial_params1], dim=0)
tangential_params = torch.cat([tangential_params, tangential_params1], dim=0) tangential_params = torch.cat([tangential_params, tangential_params1], dim=0)
thin_prism_params = torch.cat([thin_prism_params, thin_prism_params1], dim=0) thin_prism_params = torch.cat([thin_prism_params, thin_prism_params1], dim=0)
if combination is None:
combination = [True, True, True]
cameras = FishEyeCameras( cameras = FishEyeCameras(
use_radial=combination[0],
use_tangential=combination[1],
use_thin_prism=combination[2],
focal_length=focal, focal_length=focal,
principal_point=principal_point, principal_point=principal_point,
radial_params=radial_params, radial_params=radial_params,
@ -1474,21 +1479,31 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase):
def test_project_shape_broadcasts(self): def test_project_shape_broadcasts(self):
focal, principal_point, p_3d = self.setUpSimpleCase() focal, principal_point, p_3d = self.setUpSimpleCase()
# test case 1: torch.set_printoptions(precision=6)
# 1 transform with points of shape (P, 3) -> (P, 3) combinations = product([0, 1], repeat=3)
# 1 transform with points of shape (1, P, 3) -> (1, P, 3) for combination in combinations:
# 1 transform with points of shape (M, P, 3) -> (M, P, 3) cameras = FishEyeCameras(
points = p_3d.repeat(1, 1, 1) use_radial=combination[0],
cameras = FishEyeCameras( use_tangential=combination[1],
focal_length=focal, use_thin_prism=combination[2],
principal_point=principal_point, focal_length=focal,
use_radial=False, principal_point=principal_point,
use_tangential=False, )
use_thin_prism=False, # test case 1:
) # 1 transform with points of shape (P, 3) -> (P, 3)
uv = cameras.transform_points(p_3d) # 1 transform with points of shape (1, P, 3) -> (1, P, 3)
uv_point_batch = cameras.transform_points(points) # 1 transform with points of shape (M, P, 3) -> (M, P, 3)
self.assertClose(uv_point_batch, uv.repeat(1, 1, 1)) points = p_3d.repeat(1, 1, 1)
cameras = FishEyeCameras(
focal_length=focal,
principal_point=principal_point,
use_radial=False,
use_tangential=False,
use_thin_prism=False,
)
uv = cameras.transform_points(p_3d)
uv_point_batch = cameras.transform_points(points)
self.assertClose(uv_point_batch, uv.repeat(1, 1, 1))
points = p_3d.repeat(3, 1, 1) points = p_3d.repeat(3, 1, 1)
uv_point_batch = cameras.transform_points(points) uv_point_batch = cameras.transform_points(points)
@ -1497,12 +1512,9 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase):
# test case 2 # test case 2
# test with N transforms and points of shape (P, 3) -> (N, P, 3) # test with N transforms and points of shape (P, 3) -> (N, P, 3)
# test with N transforms and points of shape (1, P, 3) -> (N, P, 3) # test with N transforms and points of shape (1, P, 3) -> (N, P, 3)
torch.set_printoptions(sci_mode=False)
# first camera transform params
cameras = self.setUpBatchCameras()
p_3d = torch.tensor( p_3d = torch.tensor(
[ [
[2.0, 3.0, 1.0],
[2.0, 3.0, 1.0], [2.0, 3.0, 1.0],
[3.0, 2.0, 1.0], [3.0, 2.0, 1.0],
] ]
@ -1510,22 +1522,95 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase):
expected_res = torch.tensor( expected_res = torch.tensor(
[ [
[ [
[493.0993, 499.6489, 1.0], [
[493.0993, 499.6489, 1.0], [800.000000, 960.000000, 1.000000],
[579.6489, 413.0993, 1.0], [1040.000000, 720.000000, 1.000000],
],
[
[1929.862549, 2533.643311, 1.000000],
[2538.788086, 1924.717773, 1.000000],
],
], ],
[ [
[1660.2700, 2128.2273, 1.0], [
[1660.2700, 2128.2273, 1.0], [800.000000, 960.000000, 1.000000],
[2134.5815, 1650.9565, 1.0], [1040.000000, 720.000000, 1.000000],
],
[
[1927.272095, 2524.220459, 1.000000],
[2536.197754, 1915.295166, 1.000000],
],
],
[
[
[800.000000, 960.000000, 1.000000],
[1040.000000, 720.000000, 1.000000],
],
[
[1930.050293, 2538.434814, 1.000000],
[2537.956543, 1927.569092, 1.000000],
],
],
[
[
[800.000000, 960.000000, 1.000000],
[1040.000000, 720.000000, 1.000000],
],
[
[1927.459839, 2529.011963, 1.000000],
[2535.366211, 1918.146484, 1.000000],
],
],
[
[
[493.099304, 499.648926, 1.000000],
[579.648926, 413.099304, 1.000000],
],
[
[1662.673950, 2132.860352, 1.000000],
[2138.005127, 1657.529053, 1.000000],
],
],
[
[
[493.099304, 499.648926, 1.000000],
[579.648926, 413.099304, 1.000000],
],
[
[1660.083496, 2123.437744, 1.000000],
[2135.414795, 1648.106445, 1.000000],
],
],
[
[
[493.099304, 499.648926, 1.000000],
[579.648926, 413.099304, 1.000000],
],
[
[1662.861816, 2137.651855, 1.000000],
[2137.173828, 1660.380371, 1.000000],
],
],
[
[
[493.099304, 499.648926, 1.000000],
[579.648926, 413.099304, 1.000000],
],
[
[1660.271240, 2128.229248, 1.000000],
[2134.583496, 1650.957764, 1.000000],
],
], ],
] ]
) )
uv_point_batch = cameras.transform_points(p_3d) combinations = product([0, 1], repeat=3)
self.assertClose(uv_point_batch, expected_res) for i, combination in enumerate(combinations):
cameras = self.setUpBatchCameras(combination)
uv_point_batch = cameras.transform_points(p_3d)
self.assertClose(uv_point_batch, expected_res[i])
uv_point_batch = cameras.transform_points(p_3d.repeat(1, 1, 1)) uv_point_batch = cameras.transform_points(p_3d.repeat(1, 1, 1))
self.assertClose(uv_point_batch, expected_res) self.assertClose(uv_point_batch, expected_res[i].repeat(1, 1, 1))
def test_cuda(self): def test_cuda(self):
""" """
@ -1573,34 +1658,56 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase):
rep_3d = cameras.unproject_points(xy_depth) rep_3d = cameras.unproject_points(xy_depth)
expected_res = torch.tensor( expected_res = torch.tensor(
[ [
[3.0000, 2.0000, 1.0000], [[2.999442, 1.990583, 1.000000], [0.666728, 0.833142, 1.000000]],
[0.666667, 0.833333, 1.0000], [[2.997338, 2.005411, 1.000000], [0.666859, 0.834456, 1.000000]],
], [[3.002090, 1.985229, 1.000000], [0.666537, 0.832025, 1.000000]],
[[2.999999, 2.000000, 1.000000], [0.666667, 0.833333, 1.000000]],
[[2.999442, 1.990583, 1.000000], [0.666728, 0.833142, 1.000000]],
[[2.997338, 2.005411, 1.000000], [0.666859, 0.834456, 1.000000]],
[[3.002090, 1.985229, 1.000000], [0.666537, 0.832025, 1.000000]],
[[2.999999, 2.000000, 1.000000], [0.666667, 0.833333, 1.000000]],
]
) )
self.assertClose(rep_3d, expected_res) torch.set_printoptions(precision=6)
combinations = product([0, 1], repeat=3)
for i, combination in enumerate(combinations):
cameras = FishEyeCameras(
use_radial=combination[0],
use_tangential=combination[1],
use_thin_prism=combination[2],
focal_length=focal,
principal_point=principal_point,
radial_params=radial_params,
tangential_params=tangential_params,
thin_prism_params=thin_prism_params,
)
rep_3d = cameras.unproject_points(xy_depth)
self.assertClose(rep_3d, expected_res[i])
rep_3d = cameras.unproject_points(xy_depth.repeat(3, 1, 1))
self.assertClose(rep_3d, expected_res[i].repeat(3, 1, 1))
rep_3d = cameras.unproject_points(xy_depth.repeat(3, 1, 1)) # test case 2:
self.assertClose(rep_3d, expected_res.repeat(3, 1, 1)) # N transforms with points of (P, 3) -> (N, P, 3)
# N transforms with points of (1, P, 3) -> (N, P, 3)
# test case 2: cameras = FishEyeCameras(
# N transforms with points of (P, 3) -> (N, P, 3) use_radial=combination[0],
# N transforms with points of (1, P, 3) -> (N, P, 3) use_tangential=combination[1],
cameras = FishEyeCameras( use_thin_prism=combination[2],
focal_length=focal.repeat(2, 1), focal_length=focal.repeat(2, 1),
principal_point=principal_point.repeat(2, 1), principal_point=principal_point.repeat(2, 1),
radial_params=radial_params.repeat(2, 1), radial_params=radial_params.repeat(2, 1),
tangential_params=tangential_params.repeat(2, 1), tangential_params=tangential_params.repeat(2, 1),
thin_prism_params=thin_prism_params.repeat(2, 1), thin_prism_params=thin_prism_params.repeat(2, 1),
) )
rep_3d = cameras.unproject_points(xy_depth) rep_3d = cameras.unproject_points(xy_depth)
self.assertClose(rep_3d, expected_res.repeat(2, 1, 1)) self.assertClose(rep_3d, expected_res[i].repeat(2, 1, 1))
def test_unhandled_shape(self): def test_unhandled_shape(self):
""" """
Test error handling when shape of transforms Test error handling when shape of transforms
and points are not expected. and points are not expected.
""" """
cameras = self.setUpBatchCameras() cameras = self.setUpBatchCameras(None)
points = torch.rand(3, 3, 1) points = torch.rand(3, 3, 1)
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
cameras.transform_points(points) cameras.transform_points(points)
@ -1608,7 +1715,7 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase):
def test_getitem(self): def test_getitem(self):
# Check get item returns an instance of the same class # Check get item returns an instance of the same class
# with all the same keys # with all the same keys
cam = self.setUpBatchCameras() cam = self.setUpBatchCameras(None)
c0 = cam[0] c0 = cam[0]
self.assertTrue(isinstance(c0, FishEyeCameras)) self.assertTrue(isinstance(c0, FishEyeCameras))
self.assertEqual(cam.__dict__.keys(), c0.__dict__.keys()) self.assertEqual(cam.__dict__.keys(), c0.__dict__.keys())