def test_downsample_mesh_voxel_grid(self): import point_cloud_utils as pcu import numpy as np # v is a nv by 3 NumPy array of vertices v = pcu.load_mesh_v( os.path.join(self.test_path, "duplicated_pcloud.ply")) bbmin, bbmax = v.min(0), v.max(0) bbsize = bbmax - bbmin vdown, _, _ = pcu.downsample_point_cloud_voxel_grid(bbsize / 128.0, v) self.assertLess(vdown.shape[0], v.shape[0])
def test_downsample_point_cloud_voxel_grid(self): import point_cloud_utils as pcu import numpy as np # v is a nv by 3 NumPy array of vertices # f is an nf by 3 NumPy array of face indexes into v # n is a nv by 3 NumPy array of vertex normals if they are specified, otherwise an empty array v, f, n = pcu.read_obj(os.path.join(self.test_path, "cube_twist.obj")) bbox = np.max(v, axis=0) - np.min(v, axis=0) bbox_diag = np.linalg.norm(bbox) vox_grid_size = 1.0 / 128.0 # Make sure we have normals self.assertEqual(n.shape, v.shape) # Vanilla case pts, nms, clr = pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v) self.assertIsNone(nms) self.assertIsNone(clr) self.assertGreater(pts.shape[0], 0) self.assertEqual(pts.shape[1], 3) # With normals pts, nms, clr = pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n) self.assertIsNone(clr) self.assertEqual(nms.shape, pts.shape) self.assertGreater(pts.shape[0], 0) self.assertEqual(pts.shape[1], 3) # With RBG colors c = np.random.rand(v.shape[0], 3) pts, nms, clr = pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, None, c) self.assertIsNone(nms) self.assertEqual(clr.shape, pts.shape) self.assertGreater(pts.shape[0], 0) self.assertEqual(pts.shape[1], 3) # With RBGA colors c = np.random.rand(v.shape[0], 4) pts, nms, clr = pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, None, c) self.assertIsNone(nms) self.assertEqual(clr.shape[0], pts.shape[0]) self.assertEqual(clr.shape[1], 4) self.assertGreater(pts.shape[0], 0) self.assertEqual(pts.shape[1], 3) # With normals and RGB colors c = np.random.rand(v.shape[0], 3) pts, nms, clr = pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n, c) self.assertEqual(nms.shape, pts.shape) self.assertEqual(clr.shape, pts.shape) self.assertGreater(pts.shape[0], 0) self.assertEqual(pts.shape[1], 3) # With normals and RBGA colors c = np.random.rand(v.shape[0], 4) pts, nms, clr = pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n, c) self.assertEqual(nms.shape, pts.shape) self.assertEqual(clr.shape[0], pts.shape[0]) self.assertEqual(clr.shape[1], 4) self.assertGreater(pts.shape[0], 0) self.assertEqual(pts.shape[1], 3) # With different voxel size per axis vox_grid_size = [1.0/128.0, 1.0/99.0, 1.0/222.0] c = np.random.rand(v.shape[0], 4) pts, nms, clr = pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n, c) self.assertEqual(nms.shape, pts.shape) self.assertEqual(clr.shape[0], pts.shape[0]) self.assertEqual(clr.shape[1], 4) self.assertGreater(pts.shape[0], 0) self.assertEqual(pts.shape[1], 3) # With bounding box dimensions vox_grid_size = np.array([1.0/128.0, 1.0/99.0, 1.0/222.0]) min_bound = np.min(v, axis=0) - 0.5 * np.array(vox_grid_size) max_bound = np.max(v, axis=0) + 0.5 * np.array(vox_grid_size) c = np.random.rand(v.shape[0], 4) pts, nms, clr = pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n, c, min_bound=min_bound, max_bound=max_bound) self.assertEqual(nms.shape, pts.shape) self.assertEqual(clr.shape[0], pts.shape[0]) self.assertEqual(clr.shape[1], 4) self.assertGreater(pts.shape[0], 0) self.assertEqual(pts.shape[1], 3) # Should raise if the voxel size is too small with self.assertRaises(ValueError): vox_grid_size = [1e-16, 1.0/99.0, 1.0/222.0] c = np.random.rand(v.shape[0], 4) pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n, c) # Should raise if the voxel size is negative with self.assertRaises(ValueError): vox_grid_size = [1.0/100.0, -1.0/99.0, 1.0/222.0] c = np.random.rand(v.shape[0], 4) pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n, c) # Invalid color dimension with self.assertRaises(ValueError): c = np.random.rand(v.shape[0], 2) pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n, c) # Invalid normal dimension with self.assertRaises(ValueError): c = np.random.rand(v.shape[0], 2) pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n[:, :1], c) # Invalid number of normals with self.assertRaises(ValueError): c = np.random.rand(v.shape[0], 3) pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n[1:, :], c) # Invalid number of colors with self.assertRaises(ValueError): c = np.random.rand(v.shape[0]//2, 3) pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n, c) # Negative bounding box with self.assertRaises(ValueError): min_bound = np.min(v, axis=0) - 0.5 * np.array(vox_grid_size) max_bound = np.max(v, axis=0) + 0.5 * np.array(vox_grid_size) pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n, c, max_bound=min_bound, min_bound=max_bound) # Badly shaped grid size with self.assertRaises(ValueError): vox_grid_size = [1.0/100.0, 1.0/99.0] min_bound = np.min(v, axis=0) - 0.5 * np.array(vox_grid_size) max_bound = np.max(v, axis=0) + 0.5 * np.array(vox_grid_size) pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n, c, max_bound=max_bound, min_bound=min_bound) # Badly shaped max bound with self.assertRaises(ValueError): vox_grid_size = [1.0/100.0, 1.0/99.0, 1.0/77.0] min_bound = np.min(v, axis=0) - 0.5 * np.array(vox_grid_size) max_bound = np.max(v, axis=0) + 0.5 * np.array(vox_grid_size) pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n, c, max_bound=max_bound[:1], min_bound=min_bound) # Badly shaped max bound with self.assertRaises(ValueError): vox_grid_size = [1.0/100.0, 1.0/99.0, 1.0/77.0] min_bound = np.min(v, axis=0) - 0.5 * np.array(vox_grid_size) max_bound = np.max(v, axis=0) + 0.5 * np.array(vox_grid_size) pcu.downsample_point_cloud_voxel_grid(vox_grid_size, v, n, c, max_bound=max_bound[:1], min_bound=(1.0, 1.0))