From 0ca839cc327c1b90faf933f256355a72ec206954 Mon Sep 17 00:00:00 2001 From: Jeremy Reizenstein Date: Fri, 7 May 2021 05:03:10 -0700 Subject: [PATCH] avoid running tests twice Summary: Avoid test files explicitly importing TestCase objects from each other, because doing so causes the tests to be discovered twice by unittest discover. This means moving a few static functions out of their classes. I noticed this while trying to fix failures from yesterday. Reviewed By: nikhilaravi Differential Revision: D28194679 fbshipit-source-id: ac6e6585603bd4ef9c098cdd56891d94f8923ba6 --- tests/test_mesh_edge_loss.py | 4 +- tests/test_meshes.py | 260 ++++++++++++------------ tests/test_sample_points_from_meshes.py | 64 +++--- tests/test_texturing.py | 8 +- 4 files changed, 165 insertions(+), 171 deletions(-) diff --git a/tests/test_mesh_edge_loss.py b/tests/test_mesh_edge_loss.py index 4577bf4a..f21a05b3 100644 --- a/tests/test_mesh_edge_loss.py +++ b/tests/test_mesh_edge_loss.py @@ -6,7 +6,7 @@ import torch from common_testing import TestCaseMixin from pytorch3d.loss import mesh_edge_loss from pytorch3d.structures import Meshes -from test_sample_points_from_meshes import TestSamplePoints +from test_sample_points_from_meshes import init_meshes class TestMeshEdgeLoss(TestCaseMixin, unittest.TestCase): @@ -92,7 +92,7 @@ class TestMeshEdgeLoss(TestCaseMixin, unittest.TestCase): @staticmethod def mesh_edge_loss(num_meshes: int = 10, max_v: int = 100, max_f: int = 300): - meshes = TestSamplePoints.init_meshes(num_meshes, max_v, max_f, device="cuda:0") + meshes = init_meshes(num_meshes, max_v, max_f, device="cuda:0") torch.cuda.synchronize() def compute_loss(): diff --git a/tests/test_meshes.py b/tests/test_meshes.py index 9156598d..5d2d5a6d 100644 --- a/tests/test_meshes.py +++ b/tests/test_meshes.py @@ -10,128 +10,126 @@ from common_testing import TestCaseMixin from pytorch3d.structures.meshes import Meshes +def init_mesh( + num_meshes: int = 10, + max_v: int = 100, + max_f: int = 300, + lists_to_tensors: bool = False, + device: str = "cpu", + requires_grad: bool = False, +): + """ + Function to generate a Meshes object of N meshes with + random numbers of vertices and faces. + + Args: + num_meshes: Number of meshes to generate. + max_v: Max number of vertices per mesh. + max_f: Max number of faces per mesh. + lists_to_tensors: Determines whether the generated meshes should be + constructed from lists (=False) or + a tensor (=True) of faces/verts. + + Returns: + Meshes object. + """ + device = torch.device(device) + + verts_list = [] + faces_list = [] + + # Randomly generate numbers of faces and vertices in each mesh. + if lists_to_tensors: + # If we define faces/verts with tensors, f/v has to be the + # same for each mesh in the batch. + f = torch.randint(1, max_f, size=(1,), dtype=torch.int32) + v = torch.randint(3, high=max_v, size=(1,), dtype=torch.int32) + f = f.repeat(num_meshes) + v = v.repeat(num_meshes) + else: + # For lists of faces and vertices, we can sample different v/f + # per mesh. + f = torch.randint(max_f, size=(num_meshes,), dtype=torch.int32) + v = torch.randint(3, high=max_v, size=(num_meshes,), dtype=torch.int32) + + # Generate the actual vertices and faces. + for i in range(num_meshes): + verts = torch.rand( + (v[i], 3), + dtype=torch.float32, + device=device, + requires_grad=requires_grad, + ) + faces = torch.randint(v[i], size=(f[i], 3), dtype=torch.int64, device=device) + verts_list.append(verts) + faces_list.append(faces) + + if lists_to_tensors: + verts_list = torch.stack(verts_list) + faces_list = torch.stack(faces_list) + + return Meshes(verts=verts_list, faces=faces_list) + + +def init_simple_mesh(device: str = "cpu"): + """ + Returns a Meshes data structure of simple mesh examples. + + Returns: + Meshes object. + """ + device = torch.device(device) + + verts = [ + torch.tensor( + [[0.1, 0.3, 0.5], [0.5, 0.2, 0.1], [0.6, 0.8, 0.7]], + dtype=torch.float32, + device=device, + ), + torch.tensor( + [[0.1, 0.3, 0.3], [0.6, 0.7, 0.8], [0.2, 0.3, 0.4], [0.1, 0.5, 0.3]], + dtype=torch.float32, + device=device, + ), + torch.tensor( + [ + [0.7, 0.3, 0.6], + [0.2, 0.4, 0.8], + [0.9, 0.5, 0.2], + [0.2, 0.3, 0.4], + [0.9, 0.3, 0.8], + ], + dtype=torch.float32, + device=device, + ), + ] + faces = [ + torch.tensor([[0, 1, 2]], dtype=torch.int64, device=device), + torch.tensor([[0, 1, 2], [1, 2, 3]], dtype=torch.int64, device=device), + torch.tensor( + [ + [1, 2, 0], + [0, 1, 3], + [2, 3, 1], + [4, 3, 2], + [4, 0, 1], + [4, 3, 1], + [4, 2, 1], + ], + dtype=torch.int64, + device=device, + ), + ] + return Meshes(verts=verts, faces=faces) + + class TestMeshes(TestCaseMixin, unittest.TestCase): def setUp(self) -> None: np.random.seed(42) torch.manual_seed(42) - @staticmethod - def init_mesh( - num_meshes: int = 10, - max_v: int = 100, - max_f: int = 300, - lists_to_tensors: bool = False, - device: str = "cpu", - requires_grad: bool = False, - ): - """ - Function to generate a Meshes object of N meshes with - random numbers of vertices and faces. - - Args: - num_meshes: Number of meshes to generate. - max_v: Max number of vertices per mesh. - max_f: Max number of faces per mesh. - lists_to_tensors: Determines whether the generated meshes should be - constructed from lists (=False) or - a tensor (=True) of faces/verts. - - Returns: - Meshes object. - """ - device = torch.device(device) - - verts_list = [] - faces_list = [] - - # Randomly generate numbers of faces and vertices in each mesh. - if lists_to_tensors: - # If we define faces/verts with tensors, f/v has to be the - # same for each mesh in the batch. - f = torch.randint(1, max_f, size=(1,), dtype=torch.int32) - v = torch.randint(3, high=max_v, size=(1,), dtype=torch.int32) - f = f.repeat(num_meshes) - v = v.repeat(num_meshes) - else: - # For lists of faces and vertices, we can sample different v/f - # per mesh. - f = torch.randint(max_f, size=(num_meshes,), dtype=torch.int32) - v = torch.randint(3, high=max_v, size=(num_meshes,), dtype=torch.int32) - - # Generate the actual vertices and faces. - for i in range(num_meshes): - verts = torch.rand( - (v[i], 3), - dtype=torch.float32, - device=device, - requires_grad=requires_grad, - ) - faces = torch.randint( - v[i], size=(f[i], 3), dtype=torch.int64, device=device - ) - verts_list.append(verts) - faces_list.append(faces) - - if lists_to_tensors: - verts_list = torch.stack(verts_list) - faces_list = torch.stack(faces_list) - - return Meshes(verts=verts_list, faces=faces_list) - - @staticmethod - def init_simple_mesh(device: str = "cpu"): - """ - Returns a Meshes data structure of simple mesh examples. - - Returns: - Meshes object. - """ - device = torch.device(device) - - verts = [ - torch.tensor( - [[0.1, 0.3, 0.5], [0.5, 0.2, 0.1], [0.6, 0.8, 0.7]], - dtype=torch.float32, - device=device, - ), - torch.tensor( - [[0.1, 0.3, 0.3], [0.6, 0.7, 0.8], [0.2, 0.3, 0.4], [0.1, 0.5, 0.3]], - dtype=torch.float32, - device=device, - ), - torch.tensor( - [ - [0.7, 0.3, 0.6], - [0.2, 0.4, 0.8], - [0.9, 0.5, 0.2], - [0.2, 0.3, 0.4], - [0.9, 0.3, 0.8], - ], - dtype=torch.float32, - device=device, - ), - ] - faces = [ - torch.tensor([[0, 1, 2]], dtype=torch.int64, device=device), - torch.tensor([[0, 1, 2], [1, 2, 3]], dtype=torch.int64, device=device), - torch.tensor( - [ - [1, 2, 0], - [0, 1, 3], - [2, 3, 1], - [4, 3, 2], - [4, 0, 1], - [4, 3, 1], - [4, 2, 1], - ], - dtype=torch.int64, - device=device, - ), - ] - return Meshes(verts=verts, faces=faces) - def test_simple(self): - mesh = TestMeshes.init_simple_mesh("cuda:0") + mesh = init_simple_mesh("cuda:0") # Check that faces/verts per mesh are set in init: self.assertClose(mesh._num_faces_per_mesh.cpu(), torch.tensor([1, 2, 7])) @@ -168,7 +166,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): # Check if correct errors are raised when verts/faces are on # different devices - mesh = TestMeshes.init_mesh(10, 10, 100) + mesh = init_mesh(10, 10, 100) verts_list = mesh.verts_list() # all tensors on cpu verts_list = [ v.to("cuda:0") if random.uniform(0, 1) > 0.5 else v for v in verts_list @@ -192,7 +190,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): # Define the test mesh object either as a list or tensor of faces/verts. for lists_to_tensors in (False, True): N = 10 - mesh = TestMeshes.init_mesh(N, 100, 300, lists_to_tensors=lists_to_tensors) + mesh = init_mesh(N, 100, 300, lists_to_tensors=lists_to_tensors) verts_list = mesh.verts_list() faces_list = mesh.faces_list() @@ -361,7 +359,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): def test_clone(self): N = 5 - mesh = TestMeshes.init_mesh(N, 10, 100) + mesh = init_mesh(N, 10, 100) for force in [0, 1]: if force: # force mesh to have computed attributes @@ -386,7 +384,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): def test_detach(self): N = 5 - mesh = TestMeshes.init_mesh(N, 10, 100, requires_grad=True) + mesh = init_mesh(N, 10, 100, requires_grad=True) for force in [0, 1]: if force: # force mesh to have computed attributes @@ -425,7 +423,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): # Note that we don't test with random meshes for this case, as the # definition of Laplacian is defined for simple graphs (aka valid meshes) - meshes = TestMeshes.init_simple_mesh("cuda:0") + meshes = init_simple_mesh("cuda:0") lapl_naive = naive_laplacian_packed(meshes) lapl = meshes.laplacian_packed().to_dense() @@ -443,7 +441,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): return Meshes(verts=new_verts_list, faces=new_faces_list) N = 5 - mesh = TestMeshes.init_mesh(N, 30, 100, lists_to_tensors=True) + mesh = init_mesh(N, 30, 100, lists_to_tensors=True) all_v = mesh.verts_packed().size(0) verts_per_mesh = mesh.num_verts_per_mesh() for force, deform_shape in itertools.product([False, True], [(all_v, 3), 3]): @@ -577,7 +575,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): N = 5 for test in ["tensor", "scalar"]: for force in (False, True): - mesh = TestMeshes.init_mesh(N, 10, 100, lists_to_tensors=True) + mesh = init_mesh(N, 10, 100, lists_to_tensors=True) if force: # force mesh to have computed attributes mesh.verts_packed() @@ -686,7 +684,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): def test_extend_list(self): N = 10 - mesh = TestMeshes.init_mesh(5, 10, 100) + mesh = init_mesh(5, 10, 100) for force in [0, 1]: if force: # force some computes to happen @@ -721,7 +719,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): mesh.extend(N=-1) def test_to(self): - mesh = TestMeshes.init_mesh(5, 10, 100, device=torch.device("cuda:0")) + mesh = init_mesh(5, 10, 100, device=torch.device("cuda:0")) device = torch.device("cuda:1") new_mesh = mesh.to(device) @@ -729,7 +727,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): self.assertTrue(mesh.device == torch.device("cuda:0")) def test_split_mesh(self): - mesh = TestMeshes.init_mesh(5, 10, 100) + mesh = init_mesh(5, 10, 100) split_sizes = [2, 3] split_meshes = mesh.split(split_sizes) self.assertTrue(len(split_meshes[0]) == 2) @@ -756,9 +754,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): N = 10 for lists_to_tensors in (False, True): for force in (True, False): - mesh = TestMeshes.init_mesh( - N, 100, 300, lists_to_tensors=lists_to_tensors - ) + mesh = init_mesh(N, 100, 300, lists_to_tensors=lists_to_tensors) num_verts_per_mesh = mesh.num_verts_per_mesh() if force: # force mesh to have computed attributes @@ -1166,7 +1162,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): num_meshes = 10 max_v = 100 max_f = 300 - mesh_cpu = TestMeshes.init_mesh(num_meshes, max_v, max_f, device="cpu") + mesh_cpu = init_mesh(num_meshes, max_v, max_f, device="cpu") device = torch.device("cuda:0") mesh_cuda = mesh_cpu.to(device) @@ -1187,7 +1183,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): def compute_packed_with_init( num_meshes: int = 10, max_v: int = 100, max_f: int = 300, device: str = "cpu" ): - mesh = TestMeshes.init_mesh(num_meshes, max_v, max_f, device=device) + mesh = init_mesh(num_meshes, max_v, max_f, device=device) torch.cuda.synchronize() def compute_packed(): @@ -1200,7 +1196,7 @@ class TestMeshes(TestCaseMixin, unittest.TestCase): def compute_padded_with_init( num_meshes: int = 10, max_v: int = 100, max_f: int = 300, device: str = "cpu" ): - mesh = TestMeshes.init_mesh(num_meshes, max_v, max_f, device=device) + mesh = init_mesh(num_meshes, max_v, max_f, device=device) torch.cuda.synchronize() def compute_padded(): diff --git a/tests/test_sample_points_from_meshes.py b/tests/test_sample_points_from_meshes.py index 714aab10..bf883fc7 100644 --- a/tests/test_sample_points_from_meshes.py +++ b/tests/test_sample_points_from_meshes.py @@ -33,41 +33,41 @@ DEBUG = False DATA_DIR = get_tests_dir() / "data" +def init_meshes( + num_meshes: int = 10, + num_verts: int = 1000, + num_faces: int = 3000, + device: str = "cpu", + add_texture: bool = False, +): + device = torch.device(device) + verts_list = [] + faces_list = [] + texts_list = [] + for _ in range(num_meshes): + verts = torch.rand((num_verts, 3), dtype=torch.float32, device=device) + faces = torch.randint( + num_verts, size=(num_faces, 3), dtype=torch.int64, device=device + ) + texts = torch.rand((num_verts, 3), dtype=torch.float32, device=device) + verts_list.append(verts) + faces_list.append(faces) + texts_list.append(texts) + + # create textures + textures = None + if add_texture: + textures = TexturesVertex(texts_list) + meshes = Meshes(verts=verts_list, faces=faces_list, textures=textures) + + return meshes + + class TestSamplePoints(TestCaseMixin, unittest.TestCase): def setUp(self) -> None: super().setUp() torch.manual_seed(1) - @staticmethod - def init_meshes( - num_meshes: int = 10, - num_verts: int = 1000, - num_faces: int = 3000, - device: str = "cpu", - add_texture: bool = False, - ): - device = torch.device(device) - verts_list = [] - faces_list = [] - texts_list = [] - for _ in range(num_meshes): - verts = torch.rand((num_verts, 3), dtype=torch.float32, device=device) - faces = torch.randint( - num_verts, size=(num_faces, 3), dtype=torch.int64, device=device - ) - texts = torch.rand((num_verts, 3), dtype=torch.float32, device=device) - verts_list.append(verts) - faces_list.append(faces) - texts_list.append(texts) - - # create textures - textures = None - if add_texture: - textures = TexturesVertex(texts_list) - meshes = Meshes(verts=verts_list, faces=faces_list, textures=textures) - - return meshes - def test_all_empty_meshes(self): """ Check sample_points_from_meshes raises an exception if all meshes are @@ -298,9 +298,7 @@ class TestSamplePoints(TestCaseMixin, unittest.TestCase): def test_outputs(self): for add_texture in (True, False): - meshes = TestSamplePoints.init_meshes( - device=torch.device("cuda:0"), add_texture=add_texture - ) + meshes = init_meshes(device=torch.device("cuda:0"), add_texture=add_texture) out1 = sample_points_from_meshes(meshes, num_samples=100) self.assertTrue(torch.is_tensor(out1)) diff --git a/tests/test_texturing.py b/tests/test_texturing.py index a9043881..3f9fddd7 100644 --- a/tests/test_texturing.py +++ b/tests/test_texturing.py @@ -15,7 +15,7 @@ from pytorch3d.renderer.mesh.textures import ( pack_rectangles, ) from pytorch3d.structures import Meshes, list_to_packed, packed_to_list -from test_meshes import TestMeshes +from test_meshes import init_mesh def tryindex(self, index, tex, meshes, source): @@ -173,7 +173,7 @@ class TestTexturesVertex(TestCaseMixin, unittest.TestCase): def test_extend(self): B = 10 - mesh = TestMeshes.init_mesh(B, 30, 50) + mesh = init_mesh(B, 30, 50) V = mesh._V tex_uv = TexturesVertex(verts_features=torch.randn((B, V, 3))) tex_mesh = Meshes( @@ -379,7 +379,7 @@ class TestTexturesAtlas(TestCaseMixin, unittest.TestCase): def test_extend(self): B = 10 - mesh = TestMeshes.init_mesh(B, 30, 50) + mesh = init_mesh(B, 30, 50) F = mesh._F tex_uv = TexturesAtlas(atlas=torch.randn((B, F, 2, 2, 3))) tex_mesh = Meshes( @@ -667,7 +667,7 @@ class TestTexturesUV(TestCaseMixin, unittest.TestCase): def test_extend(self): B = 5 - mesh = TestMeshes.init_mesh(B, 30, 50) + mesh = init_mesh(B, 30, 50) V = mesh._V num_faces = mesh.num_faces_per_mesh() num_verts = mesh.num_verts_per_mesh()