コード例 #1
0
    def test_pcl_normals(self,
                         batch_size=3,
                         num_points=300,
                         neighborhood_size=50):
        """
        Tests the normal estimation on a spherical point cloud, where
        we know the ground truth normals.
        """
        device = torch.device("cuda:0")
        # run several times for different random point clouds
        for run_idx in range(3):
            # either use tensors or Pointclouds as input
            for use_pointclouds in (True, False):
                # get a spherical point cloud
                pcl, normals_gt = TestPCLNormals.init_spherical_pcl(
                    num_points=num_points,
                    batch_size=batch_size,
                    device=device,
                    use_pointclouds=use_pointclouds,
                )
                if use_pointclouds:
                    normals_gt = pcl.normals_padded()
                    num_pcl_points = pcl.num_points_per_cloud()
                else:
                    num_pcl_points = [pcl.shape[1]] * batch_size

                # check for both disambiguation options
                for disambiguate_directions in (True, False):
                    (
                        curvatures,
                        local_coord_frames,
                    ) = estimate_pointcloud_local_coord_frames(
                        pcl,
                        neighborhood_size=neighborhood_size,
                        disambiguate_directions=disambiguate_directions,
                    )

                    # estimate the normals
                    normals = estimate_pointcloud_normals(
                        pcl,
                        neighborhood_size=neighborhood_size,
                        disambiguate_directions=disambiguate_directions,
                    )

                    # TODO: temporarily disabled
                    if use_pointclouds:
                        # test that the class method gives the same output
                        normals_pcl = pcl.estimate_normals(
                            neighborhood_size=neighborhood_size,
                            disambiguate_directions=disambiguate_directions,
                            assign_to_self=True,
                        )
                        normals_from_pcl = pcl.normals_padded()
                        for nrm, nrm_from_pcl, nrm_pcl, np in zip(
                                normals, normals_from_pcl, normals_pcl,
                                num_pcl_points):
                            self.assertClose(nrm[:np], nrm_pcl[:np], atol=1e-5)
                            self.assertClose(nrm[:np],
                                             nrm_from_pcl[:np],
                                             atol=1e-5)

                    # check that local coord frames give the same normal
                    # as normals
                    for nrm, lcoord, np in zip(normals, local_coord_frames,
                                               num_pcl_points):
                        self.assertClose(nrm[:np],
                                         lcoord[:np, :, 0],
                                         atol=1e-5)

                    # dotp between normals and normals_gt
                    normal_parallel = (normals_gt * normals).sum(2)

                    # check that normals are on average
                    # parallel to the expected ones
                    for normp, np in zip(normal_parallel, num_pcl_points):
                        abs_parallel = normp[:np].abs()
                        avg_parallel = abs_parallel.mean()
                        std_parallel = abs_parallel.std()
                        self.assertClose(avg_parallel,
                                         torch.ones_like(avg_parallel),
                                         atol=1e-2)
                        self.assertClose(std_parallel,
                                         torch.zeros_like(std_parallel),
                                         atol=1e-2)

                    if disambiguate_directions:
                        # check that 95% of normal dot products
                        # have the same sign
                        for normp, np in zip(normal_parallel, num_pcl_points):
                            n_pos = (normp[:np] > 0).sum()
                            self.assertTrue((n_pos > np * 0.95)
                                            or (n_pos < np * 0.05))

                    if DEBUG and run_idx == 0 and not use_pointclouds:
                        import os
                        from pytorch3d.io.ply_io import save_ply

                        # export to .ply
                        outdir = "/tmp/pt3d_pcl_normals_test/"
                        os.makedirs(outdir, exist_ok=True)
                        plyfile = os.path.join(
                            outdir,
                            f"pcl_disamb={disambiguate_directions}.ply")
                        print(
                            f"Storing point cloud with normals to {plyfile}.")
                        pcl_idx = 0
                        save_ply(
                            plyfile,
                            pcl[pcl_idx].cpu(),
                            faces=None,
                            verts_normals=normals[pcl_idx].cpu(),
                        )
コード例 #2
0
ファイル: bm_points_normals.py プロジェクト: r23/pytorch3d
 def run():
     estimate_pointcloud_normals(
         points_padded, use_symeig_workaround=use_symeig_workaround)
     torch.cuda.synchronize()