def save( self, data: Pointclouds, path: Union[str, Path], path_manager: PathManager, binary: Optional[bool], decimal_places: Optional[int] = None, **kwargs, ) -> bool: if not endswith(path, self.known_suffixes): return False points = data.points_list()[0] features = data.features_list()[0] with _open_file(path, path_manager, "wb") as f: _save_ply( f=f, verts=points, verts_colors=features, verts_normals=torch.FloatTensor([]), faces=None, ascii=binary is False, decimal_places=decimal_places, ) return True
def test_point_mesh_face_distance(self): """ Test point_mesh_face_distance from pytorch3d.loss """ device = get_random_cuda_device() N, V, F, P = 4, 32, 16, 24 meshes, pcls = self.init_meshes_clouds(N, V, F, P, device=device) # clone and detach for another backward pass through the op verts_op = [verts.clone().detach() for verts in meshes.verts_list()] for i in range(N): verts_op[i].requires_grad = True faces_op = [faces.clone().detach() for faces in meshes.faces_list()] meshes_op = Meshes(verts=verts_op, faces=faces_op) points_op = [points.clone().detach() for points in pcls.points_list()] for i in range(N): points_op[i].requires_grad = True pcls_op = Pointclouds(points_op) # naive implementation loss_naive = torch.zeros(N, dtype=torch.float32, device=device) for i in range(N): points = pcls.points_list()[i] verts = meshes.verts_list()[i] faces = meshes.faces_list()[i] tris = verts[faces] num_p = points.shape[0] num_t = tris.shape[0] dists = torch.zeros((num_p, num_t), dtype=torch.float32, device=device) for p in range(num_p): for t in range(num_t): dist = self._point_to_tri_distance(points[p], tris[t]) dists[p, t] = dist min_dist_p, min_idx_p = dists.min(1) min_dist_t, min_idx_t = dists.min(0) loss_naive[i] = min_dist_p.mean() + min_dist_t.mean() loss_naive = loss_naive.mean() # Op loss_op = point_mesh_face_distance(meshes_op, pcls_op) # Compare forward pass self.assertClose(loss_op, loss_naive) # Compare backward pass rand_val = torch.rand(1).item() grad_dist = torch.tensor(rand_val, dtype=torch.float32, device=device) loss_naive.backward(grad_dist) loss_op.backward(grad_dist) # check verts grad for i in range(N): self.assertClose( meshes.verts_list()[i].grad, meshes_op.verts_list()[i].grad ) self.assertClose(pcls.points_list()[i].grad, pcls_op.points_list()[i].grad)
def test_point_mesh_edge_distance(self): """ Test point_mesh_edge_distance from pytorch3d.loss """ device = get_random_cuda_device() N, V, F, P = 4, 32, 16, 24 meshes, pcls = self.init_meshes_clouds(N, V, F, P, device=device) # clone and detach for another backward pass through the op verts_op = [verts.clone().detach() for verts in meshes.verts_list()] for i in range(N): verts_op[i].requires_grad = True faces_op = [faces.clone().detach() for faces in meshes.faces_list()] meshes_op = Meshes(verts=verts_op, faces=faces_op) points_op = [points.clone().detach() for points in pcls.points_list()] for i in range(N): points_op[i].requires_grad = True pcls_op = Pointclouds(points_op) # Cuda implementation: forward & backward loss_op = point_mesh_edge_distance(meshes_op, pcls_op) # Naive implementation: forward & backward edges_packed = meshes.edges_packed() edges_list = packed_to_list(edges_packed, meshes.num_edges_per_mesh().tolist()) loss_naive = torch.zeros(N, dtype=torch.float32, device=device) for i in range(N): points = pcls.points_list()[i] verts = meshes.verts_list()[i] v_first_idx = meshes.mesh_to_verts_packed_first_idx()[i] edges = verts[edges_list[i] - v_first_idx] num_p = points.shape[0] num_e = edges.shape[0] dists = torch.zeros((num_p, num_e), dtype=torch.float32, device=device) for p in range(num_p): for e in range(num_e): dist = self._point_to_edge_distance(points[p], edges[e]) dists[p, e] = dist min_dist_p, min_idx_p = dists.min(1) min_dist_e, min_idx_e = dists.min(0) loss_naive[i] = min_dist_p.mean() + min_dist_e.mean() loss_naive = loss_naive.mean() # NOTE that hear the comparison holds despite the discrepancy # due to the argmin indices returned by min(). This is because # we don't will compare gradients on the verts and not on the # edges or faces. # Compare forward pass self.assertClose(loss_op, loss_naive) # Compare backward pass rand_val = torch.rand(1).item() grad_dist = torch.tensor(rand_val, dtype=torch.float32, device=device) loss_naive.backward(grad_dist) loss_op.backward(grad_dist) # check verts grad for i in range(N): self.assertClose( meshes.verts_list()[i].grad, meshes_op.verts_list()[i].grad ) self.assertClose(pcls.points_list()[i].grad, pcls_op.points_list()[i].grad)