Example #1
0
    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
Example #2
0
    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)
Example #3
0
    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)