|
21 | 21 | PointsRasterizer,
|
22 | 22 | RasterizationSettings,
|
23 | 23 | )
|
| 24 | +from pytorch3d.renderer.fisheyecameras import FishEyeCameras |
24 | 25 | from pytorch3d.renderer.opengl.rasterizer_opengl import (
|
25 | 26 | _check_cameras,
|
26 | 27 | _check_raster_settings,
|
@@ -51,6 +52,9 @@ class TestMeshRasterizer(unittest.TestCase):
|
51 | 52 | def test_simple_sphere(self):
|
52 | 53 | self._simple_sphere(MeshRasterizer)
|
53 | 54 |
|
| 55 | + def test_simple_sphere_fisheye(self): |
| 56 | + self._simple_sphere_fisheye_against_perspective(MeshRasterizer) |
| 57 | + |
54 | 58 | def test_simple_sphere_opengl(self):
|
55 | 59 | self._simple_sphere(MeshRasterizerOpenGL)
|
56 | 60 |
|
@@ -155,6 +159,91 @@ def _simple_sphere(self, rasterizer_type):
|
155 | 159 |
|
156 | 160 | self.assertTrue(torch.allclose(image, image_ref))
|
157 | 161 |
|
| 162 | + def _simple_sphere_fisheye_against_perspective(self, rasterizer_type): |
| 163 | + device = torch.device("cuda:0") |
| 164 | + |
| 165 | + # Init mesh |
| 166 | + sphere_mesh = ico_sphere(5, device) |
| 167 | + |
| 168 | + # Init rasterizer settings |
| 169 | + R, T = look_at_view_transform(2.7, 0, 0) |
| 170 | + |
| 171 | + # Init Fisheye camera params |
| 172 | + focal = torch.tensor([[1.7321]], dtype=torch.float32) |
| 173 | + principal_point = torch.tensor([[0.0101, -0.0101]]) |
| 174 | + perspective_cameras = PerspectiveCameras( |
| 175 | + R=R, |
| 176 | + T=T, |
| 177 | + focal_length=focal, |
| 178 | + principal_point=principal_point, |
| 179 | + device="cuda:0", |
| 180 | + ) |
| 181 | + fisheye_cameras = FishEyeCameras( |
| 182 | + device=device, |
| 183 | + R=R, |
| 184 | + T=T, |
| 185 | + focal_length=focal, |
| 186 | + principal_point=principal_point, |
| 187 | + world_coordinates=True, |
| 188 | + use_radial=False, |
| 189 | + use_tangential=False, |
| 190 | + use_thin_prism=False, |
| 191 | + ) |
| 192 | + raster_settings = RasterizationSettings( |
| 193 | + image_size=512, blur_radius=0.0, faces_per_pixel=1, bin_size=0 |
| 194 | + ) |
| 195 | + |
| 196 | + # Init rasterizer |
| 197 | + perspective_rasterizer = rasterizer_type( |
| 198 | + cameras=perspective_cameras, raster_settings=raster_settings |
| 199 | + ) |
| 200 | + fisheye_rasterizer = rasterizer_type( |
| 201 | + cameras=fisheye_cameras, raster_settings=raster_settings |
| 202 | + ) |
| 203 | + |
| 204 | + #################################################################################### |
| 205 | + # Test rasterizing a single mesh comparing fisheye camera against perspective camera |
| 206 | + #################################################################################### |
| 207 | + |
| 208 | + perspective_fragments = perspective_rasterizer(sphere_mesh) |
| 209 | + perspective_image = perspective_fragments.pix_to_face[0, ..., 0].squeeze().cpu() |
| 210 | + # Convert pix_to_face to a binary mask |
| 211 | + perspective_image[perspective_image >= 0] = 1.0 |
| 212 | + perspective_image[perspective_image < 0] = 0.0 |
| 213 | + |
| 214 | + if DEBUG: |
| 215 | + Image.fromarray((perspective_image.numpy() * 255).astype(np.uint8)).save( |
| 216 | + DATA_DIR |
| 217 | + / f"DEBUG_test_perspective_rasterized_sphere_{rasterizer_type.__name__}.png" |
| 218 | + ) |
| 219 | + |
| 220 | + fisheye_fragments = fisheye_rasterizer(sphere_mesh) |
| 221 | + fisheye_image = fisheye_fragments.pix_to_face[0, ..., 0].squeeze().cpu() |
| 222 | + # Convert pix_to_face to a binary mask |
| 223 | + fisheye_image[fisheye_image >= 0] = 1.0 |
| 224 | + fisheye_image[fisheye_image < 0] = 0.0 |
| 225 | + |
| 226 | + if DEBUG: |
| 227 | + Image.fromarray((fisheye_image.numpy() * 255).astype(np.uint8)).save( |
| 228 | + DATA_DIR |
| 229 | + / f"DEBUG_test_fisheye_rasterized_sphere_{rasterizer_type.__name__}.png" |
| 230 | + ) |
| 231 | + |
| 232 | + self.assertTrue(torch.allclose(fisheye_image, perspective_image)) |
| 233 | + |
| 234 | + ################################## |
| 235 | + # 2. Test with a batch of meshes |
| 236 | + ################################## |
| 237 | + |
| 238 | + batch_size = 10 |
| 239 | + sphere_meshes = sphere_mesh.extend(batch_size) |
| 240 | + fragments = fisheye_rasterizer(sphere_meshes) |
| 241 | + for i in range(batch_size): |
| 242 | + image = fragments.pix_to_face[i, ..., 0].squeeze().cpu() |
| 243 | + image[image >= 0] = 1.0 |
| 244 | + image[image < 0] = 0.0 |
| 245 | + self.assertTrue(torch.allclose(image, perspective_image)) |
| 246 | + |
158 | 247 | def test_simple_to(self):
|
159 | 248 | # Check that to() works without a cameras object.
|
160 | 249 | device = torch.device("cuda:0")
|
@@ -412,6 +501,76 @@ def test_simple_sphere(self):
|
412 | 501 | image[image < 0] = 0.0
|
413 | 502 | self.assertTrue(torch.allclose(image, image_ref[..., 0]))
|
414 | 503 |
|
| 504 | + def test_simple_sphere_fisheye_against_perspective(self): |
| 505 | + device = torch.device("cuda:0") |
| 506 | + |
| 507 | + # Rescale image_ref to the 0 - 1 range and convert to a binary mask. |
| 508 | + sphere_mesh = ico_sphere(1, device) |
| 509 | + verts_padded = sphere_mesh.verts_padded() |
| 510 | + verts_padded[..., 1] += 0.2 |
| 511 | + verts_padded[..., 0] += 0.2 |
| 512 | + pointclouds = Pointclouds(points=verts_padded) |
| 513 | + R, T = look_at_view_transform(2.7, 0.0, 0.0) |
| 514 | + perspective_cameras = PerspectiveCameras( |
| 515 | + R=R, |
| 516 | + T=T, |
| 517 | + device=device, |
| 518 | + ) |
| 519 | + fisheye_cameras = FishEyeCameras( |
| 520 | + device=device, |
| 521 | + R=R, |
| 522 | + T=T, |
| 523 | + world_coordinates=True, |
| 524 | + use_radial=False, |
| 525 | + use_tangential=False, |
| 526 | + use_thin_prism=False, |
| 527 | + ) |
| 528 | + raster_settings = PointsRasterizationSettings( |
| 529 | + image_size=256, radius=5e-2, points_per_pixel=1 |
| 530 | + ) |
| 531 | + |
| 532 | + ################################# |
| 533 | + # 1. Test init without cameras. |
| 534 | + ################################## |
| 535 | + |
| 536 | + # Initialize without passing in the cameras |
| 537 | + rasterizer = PointsRasterizer() |
| 538 | + |
| 539 | + # Check that omitting the cameras in both initialization |
| 540 | + # and the forward pass throws an error: |
| 541 | + with self.assertRaisesRegex(ValueError, "Cameras must be specified"): |
| 542 | + rasterizer(pointclouds) |
| 543 | + |
| 544 | + ######################################################################################## |
| 545 | + # 2. Test rasterizing a single pointcloud with fisheye camera agasint perspective camera |
| 546 | + ######################################################################################## |
| 547 | + |
| 548 | + perspective_fragments = rasterizer( |
| 549 | + pointclouds, cameras=perspective_cameras, raster_settings=raster_settings |
| 550 | + ) |
| 551 | + fisheye_fragments = rasterizer( |
| 552 | + pointclouds, cameras=fisheye_cameras, raster_settings=raster_settings |
| 553 | + ) |
| 554 | + |
| 555 | + # Convert idx to a binary mask |
| 556 | + perspective_image = perspective_fragments.idx[0, ..., 0].squeeze().cpu() |
| 557 | + perspective_image[perspective_image >= 0] = 1.0 |
| 558 | + perspective_image[perspective_image < 0] = 0.0 |
| 559 | + |
| 560 | + fisheye_image = fisheye_fragments.idx[0, ..., 0].squeeze().cpu() |
| 561 | + fisheye_image[fisheye_image >= 0] = 1.0 |
| 562 | + fisheye_image[fisheye_image < 0] = 0.0 |
| 563 | + |
| 564 | + if DEBUG: |
| 565 | + Image.fromarray((perspective_image.numpy() * 255).astype(np.uint8)).save( |
| 566 | + DATA_DIR / "DEBUG_test_rasterized_perspective_sphere_points.png" |
| 567 | + ) |
| 568 | + Image.fromarray((fisheye_image.numpy() * 255).astype(np.uint8)).save( |
| 569 | + DATA_DIR / "DEBUG_test_rasterized_fisheye_sphere_points.png" |
| 570 | + ) |
| 571 | + |
| 572 | + self.assertTrue(torch.allclose(fisheye_image, perspective_image)) |
| 573 | + |
415 | 574 | def test_simple_to(self):
|
416 | 575 | # Check that to() works without a cameras object.
|
417 | 576 | device = torch.device("cuda:0")
|
|
0 commit comments