From b0515e14615abe6e154f6dcf671ec8e54f29aaf4 Mon Sep 17 00:00:00 2001 From: Jiali Duan Date: Wed, 31 Aug 2022 13:50:50 -0700 Subject: [PATCH] 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 --- pytorch3d/renderer/fisheyecameras.py | 5 +- tests/test_cameras.py | 211 ++++++++++++++++++++------- 2 files changed, 161 insertions(+), 55 deletions(-) diff --git a/pytorch3d/renderer/fisheyecameras.py b/pytorch3d/renderer/fisheyecameras.py index 7ce0ffd0..3da558df 100644 --- a/pytorch3d/renderer/fisheyecameras.py +++ b/pytorch3d/renderer/fisheyecameras.py @@ -425,7 +425,7 @@ class FishEyeCameras(CamerasBase): # do Newton iterations to find xr_yr for _ in range(self.num_distortion_iters): # 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) 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) """ sh = list(th_radial_desired.shape) - # th = th_radial_desired.clone() th = th_radial_desired c = torch.tensor( [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] ) 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: temp1 = 2.0 * ( diff --git a/tests/test_cameras.py b/tests/test_cameras.py index fd5dd8f2..fca05e2e 100644 --- a/tests/test_cameras.py +++ b/tests/test_cameras.py @@ -33,6 +33,7 @@ import math import typing import unittest +from itertools import product import numpy as np import torch @@ -1389,7 +1390,7 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase): thin_prism_params, ) - def setUpBatchCameras(self) -> None: + def setUpBatchCameras(self, combination: None) -> None: super().setUp() focal, principal_point, p_3d = self.setUpSimpleCase() radial_params = torch.tensor( @@ -1412,8 +1413,12 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase): radial_params = torch.cat([radial_params, radial_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) - + if combination is None: + combination = [True, True, True] 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, @@ -1474,21 +1479,31 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase): def test_project_shape_broadcasts(self): focal, principal_point, p_3d = self.setUpSimpleCase() - # test case 1: - # 1 transform with points of shape (P, 3) -> (P, 3) - # 1 transform with points of shape (1, P, 3) -> (1, P, 3) - # 1 transform with points of shape (M, P, 3) -> (M, P, 3) - 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)) + torch.set_printoptions(precision=6) + combinations = product([0, 1], repeat=3) + for combination in combinations: + cameras = FishEyeCameras( + use_radial=combination[0], + use_tangential=combination[1], + use_thin_prism=combination[2], + focal_length=focal, + principal_point=principal_point, + ) + # test case 1: + # 1 transform with points of shape (P, 3) -> (P, 3) + # 1 transform with points of shape (1, P, 3) -> (1, P, 3) + # 1 transform with points of shape (M, P, 3) -> (M, P, 3) + 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) uv_point_batch = cameras.transform_points(points) @@ -1497,12 +1512,9 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase): # test case 2 # 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) - - # first camera transform params - cameras = self.setUpBatchCameras() + torch.set_printoptions(sci_mode=False) p_3d = torch.tensor( [ - [2.0, 3.0, 1.0], [2.0, 3.0, 1.0], [3.0, 2.0, 1.0], ] @@ -1510,22 +1522,95 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase): expected_res = torch.tensor( [ [ - [493.0993, 499.6489, 1.0], - [493.0993, 499.6489, 1.0], - [579.6489, 413.0993, 1.0], + [ + [800.000000, 960.000000, 1.000000], + [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], - [2134.5815, 1650.9565, 1.0], + [ + [800.000000, 960.000000, 1.000000], + [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) - self.assertClose(uv_point_batch, expected_res) + combinations = product([0, 1], repeat=3) + 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)) - self.assertClose(uv_point_batch, expected_res) + uv_point_batch = cameras.transform_points(p_3d.repeat(1, 1, 1)) + self.assertClose(uv_point_batch, expected_res[i].repeat(1, 1, 1)) def test_cuda(self): """ @@ -1573,34 +1658,56 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase): rep_3d = cameras.unproject_points(xy_depth) expected_res = torch.tensor( [ - [3.0000, 2.0000, 1.0000], - [0.666667, 0.833333, 1.0000], - ], + [[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]], + [[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)) - self.assertClose(rep_3d, expected_res.repeat(3, 1, 1)) - - # test case 2: - # N transforms with points of (P, 3) -> (N, P, 3) - # N transforms with points of (1, P, 3) -> (N, P, 3) - cameras = FishEyeCameras( - focal_length=focal.repeat(2, 1), - principal_point=principal_point.repeat(2, 1), - radial_params=radial_params.repeat(2, 1), - tangential_params=tangential_params.repeat(2, 1), - thin_prism_params=thin_prism_params.repeat(2, 1), - ) - rep_3d = cameras.unproject_points(xy_depth) - self.assertClose(rep_3d, expected_res.repeat(2, 1, 1)) + # test case 2: + # N transforms with points of (P, 3) -> (N, P, 3) + # N transforms with points of (1, P, 3) -> (N, P, 3) + cameras = FishEyeCameras( + use_radial=combination[0], + use_tangential=combination[1], + use_thin_prism=combination[2], + focal_length=focal.repeat(2, 1), + principal_point=principal_point.repeat(2, 1), + radial_params=radial_params.repeat(2, 1), + tangential_params=tangential_params.repeat(2, 1), + thin_prism_params=thin_prism_params.repeat(2, 1), + ) + rep_3d = cameras.unproject_points(xy_depth) + self.assertClose(rep_3d, expected_res[i].repeat(2, 1, 1)) def test_unhandled_shape(self): """ Test error handling when shape of transforms and points are not expected. """ - cameras = self.setUpBatchCameras() + cameras = self.setUpBatchCameras(None) points = torch.rand(3, 3, 1) with self.assertRaises(ValueError): cameras.transform_points(points) @@ -1608,7 +1715,7 @@ class TestFishEyeProjection(TestCaseMixin, unittest.TestCase): def test_getitem(self): # Check get item returns an instance of the same class # with all the same keys - cam = self.setUpBatchCameras() + cam = self.setUpBatchCameras(None) c0 = cam[0] self.assertTrue(isinstance(c0, FishEyeCameras)) self.assertEqual(cam.__dict__.keys(), c0.__dict__.keys())