def test_all_empty_meshes(self): """ Check sample_points_from_meshes raises an exception if all meshes are invalid. """ device = torch.device("cuda:0") verts1 = torch.tensor([], dtype=torch.float32, device=device) faces1 = torch.tensor([], dtype=torch.int64, device=device) meshes = Meshes(verts=[verts1, verts1, verts1], faces=[faces1, faces1, faces1]) with self.assertRaises(ValueError) as err: sample_points_from_meshes(meshes, num_samples=100, return_normals=True) self.assertTrue("Meshes are empty." in str(err.exception))
def init_cube_point_cloud(batch_size: int = 10, n_points: int = 100000, rotate_y: bool = True): """ Generate a random point cloud of `n_points` whose points of which are sampled from faces of a 3D cube. """ # create the cube mesh batch_size times meshes = TestPointsToVolumes.init_cube_mesh(batch_size) # generate point clouds by sampling points from the meshes pcl = sample_points_from_meshes(meshes, num_samples=n_points, return_normals=False) # colors of the cube sides clrs = [ [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0], [0.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 0.0, 1.0], ] # init the color tensor "rgb" rgb = torch.zeros_like(pcl) # color each side of the cube with a constant color clri = 0 for dim in (0, 1, 2): for offs in (0.0, 1.0): current_face_verts = (pcl[:, :, dim] - offs).abs() <= 1e-2 for bi in range(batch_size): rgb[bi, current_face_verts[bi], :] = torch.tensor( clrs[clri]).type_as(pcl) clri += 1 if rotate_y: # uniformly spaced rotations around y axis R = init_uniform_y_rotations(batch_size=batch_size) # rotate the point clouds around y axis pcl = torch.bmm(pcl - 0.5, R) + 0.5 return pcl, rgb
def test_sampling_output(self): """ Check outputs of sampling are correct for different meshes. For an ico_sphere, the sampled vertices should lie on a unit sphere. For an empty mesh, the samples and normals should be 0. """ device = torch.device("cuda:0") # Unit simplex. verts_pyramid = torch.tensor( [ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], ], dtype=torch.float32, device=device, ) faces_pyramid = torch.tensor( [[0, 1, 2], [0, 2, 3], [0, 1, 3], [1, 2, 3]], dtype=torch.int64, device=device, ) sphere_mesh = ico_sphere(9, device) verts_sphere, faces_sphere = sphere_mesh.get_mesh_verts_faces(0) verts_empty = torch.tensor([], dtype=torch.float32, device=device) faces_empty = torch.tensor([], dtype=torch.int64, device=device) num_samples = 10 meshes = Meshes( verts=[verts_empty, verts_sphere, verts_pyramid], faces=[faces_empty, faces_sphere, faces_pyramid], ) samples, normals = sample_points_from_meshes( meshes, num_samples=num_samples, return_normals=True ) samples = samples.cpu() normals = normals.cpu() self.assertEqual(samples.shape, (3, num_samples, 3)) self.assertEqual(normals.shape, (3, num_samples, 3)) # Empty meshes: should have all zeros for samples and normals. self.assertTrue( torch.allclose(samples[0, :], torch.zeros((1, num_samples, 3))) ) self.assertTrue( torch.allclose(normals[0, :], torch.zeros((1, num_samples, 3))) ) # Sphere: points should have radius 1. x, y, z = samples[1, :].unbind(1) radius = torch.sqrt(x ** 2 + y ** 2 + z ** 2) self.assertTrue(torch.allclose(radius, torch.ones((num_samples)))) # Pyramid: points shoudl lie on one of the faces. pyramid_verts = samples[2, :] pyramid_normals = normals[2, :] self.assertTrue( torch.allclose( pyramid_verts.lt(1).float(), torch.ones_like(pyramid_verts) ) ) self.assertTrue( torch.allclose( (pyramid_verts >= 0).float(), torch.ones_like(pyramid_verts) ) ) # Face 1: z = 0, x + y <= 1, normals = (0, 0, 1). face_1_idxs = pyramid_verts[:, 2] == 0 face_1_verts, face_1_normals = ( pyramid_verts[face_1_idxs, :], pyramid_normals[face_1_idxs, :], ) self.assertTrue( torch.all((face_1_verts[:, 0] + face_1_verts[:, 1]) <= 1) ) self.assertTrue( torch.allclose( face_1_normals, torch.tensor([0, 0, 1], dtype=torch.float32).expand( face_1_normals.size() ), ) ) # Face 2: x = 0, z + y <= 1, normals = (1, 0, 0). face_2_idxs = pyramid_verts[:, 0] == 0 face_2_verts, face_2_normals = ( pyramid_verts[face_2_idxs, :], pyramid_normals[face_2_idxs, :], ) self.assertTrue( torch.all((face_2_verts[:, 1] + face_2_verts[:, 2]) <= 1) ) self.assertTrue( torch.allclose( face_2_normals, torch.tensor([1, 0, 0], dtype=torch.float32).expand( face_2_normals.size() ), ) ) # Face 3: y = 0, x + z <= 1, normals = (0, -1, 0). face_3_idxs = pyramid_verts[:, 1] == 0 face_3_verts, face_3_normals = ( pyramid_verts[face_3_idxs, :], pyramid_normals[face_3_idxs, :], ) self.assertTrue( torch.all((face_3_verts[:, 0] + face_3_verts[:, 2]) <= 1) ) self.assertTrue( torch.allclose( face_3_normals, torch.tensor([0, -1, 0], dtype=torch.float32).expand( face_3_normals.size() ), ) ) # Face 4: x + y + z = 1, normals = (1, 1, 1)/sqrt(3). face_4_idxs = pyramid_verts.gt(0).all(1) face_4_verts, face_4_normals = ( pyramid_verts[face_4_idxs, :], pyramid_normals[face_4_idxs, :], ) self.assertTrue( torch.allclose( face_4_verts.sum(1), torch.ones(face_4_verts.size(0)) ) ) self.assertTrue( torch.allclose( face_4_normals, ( torch.tensor([1, 1, 1], dtype=torch.float32) / torch.sqrt(torch.tensor(3, dtype=torch.float32)) ).expand(face_4_normals.size()), ) )
def sample_points(): sample_points_from_meshes( meshes, num_samples=num_samples, return_normals=True ) torch.cuda.synchronize()