diff --git a/tests/test_pointclouds.py b/tests/test_pointclouds.py index 13d26af4..7f6a5466 100644 --- a/tests/test_pointclouds.py +++ b/tests/test_pointclouds.py @@ -17,7 +17,7 @@ from pytorch3d.structures.pointclouds import ( Pointclouds, ) -from .common_testing import needs_multigpu, TestCaseMixin +from .common_testing import TestCaseMixin class TestPointclouds(TestCaseMixin, unittest.TestCase): @@ -703,82 +703,6 @@ class TestPointclouds(TestCaseMixin, unittest.TestCase): self.assertEqual(cuda_device, cloud.device) self.assertIsNot(cloud, converted_cloud) - @needs_multigpu - def test_to_list(self): - cloud = self.init_cloud(5, 100, 10) - device = torch.device("cuda:1") - - new_cloud = cloud.to(device) - self.assertTrue(new_cloud.device == device) - self.assertTrue(cloud.device == torch.device("cuda:0")) - for attrib in [ - "points_padded", - "points_packed", - "normals_padded", - "normals_packed", - "features_padded", - "features_packed", - "num_points_per_cloud", - "cloud_to_packed_first_idx", - "padded_to_packed_idx", - ]: - self.assertClose( - getattr(new_cloud, attrib)().cpu(), getattr(cloud, attrib)().cpu() - ) - for i in range(len(cloud)): - self.assertClose( - cloud.points_list()[i].cpu(), new_cloud.points_list()[i].cpu() - ) - self.assertClose( - cloud.normals_list()[i].cpu(), new_cloud.normals_list()[i].cpu() - ) - self.assertClose( - cloud.features_list()[i].cpu(), new_cloud.features_list()[i].cpu() - ) - self.assertTrue(all(cloud.valid.cpu() == new_cloud.valid.cpu())) - self.assertTrue(cloud.equisized == new_cloud.equisized) - self.assertTrue(cloud._N == new_cloud._N) - self.assertTrue(cloud._P == new_cloud._P) - self.assertTrue(cloud._C == new_cloud._C) - - @needs_multigpu - def test_to_tensor(self): - cloud = self.init_cloud(5, 100, 10, lists_to_tensors=True) - device = torch.device("cuda:1") - - new_cloud = cloud.to(device) - self.assertTrue(new_cloud.device == device) - self.assertTrue(cloud.device == torch.device("cuda:0")) - for attrib in [ - "points_padded", - "points_packed", - "normals_padded", - "normals_packed", - "features_padded", - "features_packed", - "num_points_per_cloud", - "cloud_to_packed_first_idx", - "padded_to_packed_idx", - ]: - self.assertClose( - getattr(new_cloud, attrib)().cpu(), getattr(cloud, attrib)().cpu() - ) - for i in range(len(cloud)): - self.assertClose( - cloud.points_list()[i].cpu(), new_cloud.points_list()[i].cpu() - ) - self.assertClose( - cloud.normals_list()[i].cpu(), new_cloud.normals_list()[i].cpu() - ) - self.assertClose( - cloud.features_list()[i].cpu(), new_cloud.features_list()[i].cpu() - ) - self.assertTrue(all(cloud.valid.cpu() == new_cloud.valid.cpu())) - self.assertTrue(cloud.equisized == new_cloud.equisized) - self.assertTrue(cloud._N == new_cloud._N) - self.assertTrue(cloud._P == new_cloud._P) - self.assertTrue(cloud._C == new_cloud._C) - def test_split(self): clouds = self.init_cloud(5, 100, 10) split_sizes = [2, 3] diff --git a/tests/test_pointclouds_multigpu.py b/tests/test_pointclouds_multigpu.py new file mode 100644 index 00000000..f2d08b30 --- /dev/null +++ b/tests/test_pointclouds_multigpu.py @@ -0,0 +1,166 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +import unittest + +import numpy as np +import torch +from pytorch3d.structures.pointclouds import Pointclouds + +from .common_testing import needs_multigpu, TestCaseMixin + + +class TestPointclouds(TestCaseMixin, unittest.TestCase): + def setUp(self) -> None: + np.random.seed(42) + torch.manual_seed(42) + + @staticmethod + def init_cloud( + num_clouds: int = 3, + max_points: int = 100, + channels: int = 4, + lists_to_tensors: bool = False, + with_normals: bool = True, + with_features: bool = True, + min_points: int = 0, + requires_grad: bool = False, + ): + """ + Function to generate a Pointclouds object of N meshes with + random number of points. + + Args: + num_clouds: Number of clouds to generate. + channels: Number of features. + max_points: Max number of points per cloud. + lists_to_tensors: Determines whether the generated clouds should be + constructed from lists (=False) or + tensors (=True) of points/normals/features. + with_normals: bool whether to include normals + with_features: bool whether to include features + min_points: Min number of points per cloud + + Returns: + Pointclouds object. + """ + device = torch.device("cuda:0") + p = torch.randint(low=min_points, high=max_points, size=(num_clouds,)) + if lists_to_tensors: + p.fill_(p[0]) + + points_list = [ + torch.rand( + (i, 3), device=device, dtype=torch.float32, requires_grad=requires_grad + ) + for i in p + ] + normals_list, features_list = None, None + if with_normals: + normals_list = [ + torch.rand( + (i, 3), + device=device, + dtype=torch.float32, + requires_grad=requires_grad, + ) + for i in p + ] + if with_features: + features_list = [ + torch.rand( + (i, channels), + device=device, + dtype=torch.float32, + requires_grad=requires_grad, + ) + for i in p + ] + + if lists_to_tensors: + points_list = torch.stack(points_list) + if with_normals: + normals_list = torch.stack(normals_list) + if with_features: + features_list = torch.stack(features_list) + + return Pointclouds(points_list, normals=normals_list, features=features_list) + + @needs_multigpu + def test_to_list(self): + cloud = self.init_cloud(5, 100, 10) + device = torch.device("cuda:1") + + new_cloud = cloud.to(device) + self.assertTrue(new_cloud.device == device) + self.assertTrue(cloud.device == torch.device("cuda:0")) + for attrib in [ + "points_padded", + "points_packed", + "normals_padded", + "normals_packed", + "features_padded", + "features_packed", + "num_points_per_cloud", + "cloud_to_packed_first_idx", + "padded_to_packed_idx", + ]: + self.assertClose( + getattr(new_cloud, attrib)().cpu(), getattr(cloud, attrib)().cpu() + ) + for i in range(len(cloud)): + self.assertClose( + cloud.points_list()[i].cpu(), new_cloud.points_list()[i].cpu() + ) + self.assertClose( + cloud.normals_list()[i].cpu(), new_cloud.normals_list()[i].cpu() + ) + self.assertClose( + cloud.features_list()[i].cpu(), new_cloud.features_list()[i].cpu() + ) + self.assertTrue(all(cloud.valid.cpu() == new_cloud.valid.cpu())) + self.assertTrue(cloud.equisized == new_cloud.equisized) + self.assertTrue(cloud._N == new_cloud._N) + self.assertTrue(cloud._P == new_cloud._P) + self.assertTrue(cloud._C == new_cloud._C) + + @needs_multigpu + def test_to_tensor(self): + cloud = self.init_cloud(5, 100, 10, lists_to_tensors=True) + device = torch.device("cuda:1") + + new_cloud = cloud.to(device) + self.assertTrue(new_cloud.device == device) + self.assertTrue(cloud.device == torch.device("cuda:0")) + for attrib in [ + "points_padded", + "points_packed", + "normals_padded", + "normals_packed", + "features_padded", + "features_packed", + "num_points_per_cloud", + "cloud_to_packed_first_idx", + "padded_to_packed_idx", + ]: + self.assertClose( + getattr(new_cloud, attrib)().cpu(), getattr(cloud, attrib)().cpu() + ) + for i in range(len(cloud)): + self.assertClose( + cloud.points_list()[i].cpu(), new_cloud.points_list()[i].cpu() + ) + self.assertClose( + cloud.normals_list()[i].cpu(), new_cloud.normals_list()[i].cpu() + ) + self.assertClose( + cloud.features_list()[i].cpu(), new_cloud.features_list()[i].cpu() + ) + self.assertTrue(all(cloud.valid.cpu() == new_cloud.valid.cpu())) + self.assertTrue(cloud.equisized == new_cloud.equisized) + self.assertTrue(cloud._N == new_cloud._N) + self.assertTrue(cloud._P == new_cloud._P) + self.assertTrue(cloud._C == new_cloud._C)