Example #1
0
    def test_load_pointcloud_bad_order(self):
        """
        Ply file with a strange property order
        """
        file = "\n".join([
            "ply",
            "format ascii 1.0",
            "element vertex 1",
            "property uchar green",
            "property float x",
            "property float z",
            "property uchar red",
            "property float y",
            "property uchar blue",
            "end_header",
            "1 2 3 4 5 6",
        ])

        io = IO()
        pointcloud_gpu = io.load_pointcloud(StringIO(file), device="cuda:0")
        self.assertEqual(pointcloud_gpu.device, torch.device("cuda:0"))
        pointcloud = pointcloud_gpu.to(torch.device("cpu"))
        expected_points = torch.tensor([[[2, 5, 3]]], dtype=torch.float32)
        expected_features = torch.tensor([[[4, 1, 6]]], dtype=torch.float32)
        self.assertClose(pointcloud.points_padded(), expected_points)
        self.assertClose(pointcloud.features_padded(), expected_features)
Example #2
0
    def test_save_load_with_normals(self):
        points = torch.tensor([[0, 0, 0], [0, 0, 1], [0, 1, 0], [1, 0, 0]],
                              dtype=torch.float32)
        normals = torch.tensor([[0, 1, 0], [1, 0, 0], [1, 4, 1], [1, 0, 0]],
                               dtype=torch.float32)
        features = torch.rand_like(points)

        for do_features, do_normals in itertools.product([True, False],
                                                         [True, False]):
            cloud = Pointclouds(
                points=[points],
                features=[features] if do_features else None,
                normals=[normals] if do_normals else None,
            )
            device = torch.device("cuda:0")

            io = IO()
            with NamedTemporaryFile(mode="w", suffix=".ply") as f:
                io.save_pointcloud(cloud.cuda(), f.name)
                f.flush()
                cloud2 = io.load_pointcloud(f.name, device=device)
            self.assertEqual(cloud2.device, device)
            cloud2 = cloud2.cpu()
            self.assertClose(cloud2.points_padded(), cloud.points_padded())
            if do_normals:
                self.assertClose(cloud2.normals_padded(),
                                 cloud.normals_padded())
            else:
                self.assertIsNone(cloud.normals_padded())
                self.assertIsNone(cloud2.normals_padded())
            if do_features:
                self.assertClose(cloud2.features_packed(), features)
            else:
                self.assertIsNone(cloud2.features_packed())
Example #3
0
    def test_save_pointcloud(self):
        header = "\n".join([
            "ply",
            "format binary_little_endian 1.0",
            "element vertex 8",
            "property float x",
            "property float y",
            "property float z",
            "property float red",
            "property float green",
            "property float blue",
            "end_header",
            "",
        ]).encode("ascii")
        data = struct.pack("<" + "f" * 48, *range(48))
        points = torch.FloatTensor([0, 1, 2]) + 6 * torch.arange(8)[:, None]
        features = torch.FloatTensor([3, 4, 5]) + 6 * torch.arange(8)[:, None]
        pointcloud = Pointclouds(points=[points], features=[features])

        io = IO()
        with NamedTemporaryFile(mode="rb", suffix=".ply") as f:
            io.save_pointcloud(data=pointcloud, path=f.name)
            f.flush()
            f.seek(0)
            actual_data = f.read()
            reloaded_pointcloud = io.load_pointcloud(f.name)

        self.assertEqual(header + data, actual_data)
        self.assertClose(reloaded_pointcloud.points_list()[0], points)
        self.assertClose(reloaded_pointcloud.features_list()[0], features)

        with NamedTemporaryFile(mode="r", suffix=".ply") as f:
            io.save_pointcloud(data=pointcloud, path=f.name, binary=False)
            reloaded_pointcloud2 = io.load_pointcloud(f.name)
            self.assertEqual(f.readline(), "ply\n")
            self.assertEqual(f.readline(), "format ascii 1.0\n")
        self.assertClose(reloaded_pointcloud2.points_list()[0], points)
        self.assertClose(reloaded_pointcloud2.features_list()[0], features)
Example #4
0
    def test_load_cloudcompare_pointcloud(self):
        """
        Test loading a pointcloud styled like some cloudcompare output.
        cloudcompare is an open source 3D point cloud processing software.
        """
        header = "\n".join([
            "ply",
            "format binary_little_endian 1.0",
            "obj_info Not a key-value pair!",
            "element vertex 8",
            "property double x",
            "property double y",
            "property double z",
            "property uchar red",
            "property uchar green",
            "property uchar blue",
            "property float my_Favorite",
            "end_header",
            "",
        ]).encode("ascii")
        data = struct.pack("<" + "dddBBBf" * 8, *range(56))
        io = IO()
        with NamedTemporaryFile(mode="wb", suffix=".ply") as f:
            f.write(header)
            f.write(data)
            f.flush()
            pointcloud = io.load_pointcloud(f.name)

        self.assertClose(
            pointcloud.points_padded()[0],
            torch.FloatTensor([0, 1, 2]) + 7 * torch.arange(8)[:, None],
        )
        self.assertClose(
            pointcloud.features_padded()[0],
            torch.FloatTensor([3, 4, 5]) + 7 * torch.arange(8)[:, None],
        )
Example #5
0
    def test_save_pointcloud(self):
        header = "\n".join([
            "ply",
            "format binary_little_endian 1.0",
            "element vertex 8",
            "property float x",
            "property float y",
            "property float z",
            "property float red",
            "property float green",
            "property float blue",
            "end_header",
            "",
        ]).encode("ascii")
        data = struct.pack("<" + "f" * 48, *range(48))
        points = torch.FloatTensor([0, 1, 2]) + 6 * torch.arange(8)[:, None]
        features_large = torch.FloatTensor([3, 4, 5
                                            ]) + 6 * torch.arange(8)[:, None]
        features = features_large / 255.0
        pointcloud_largefeatures = Pointclouds(points=[points],
                                               features=[features_large])
        pointcloud = Pointclouds(points=[points], features=[features])

        io = IO()
        with NamedTemporaryFile(mode="rb", suffix=".ply") as f:
            io.save_pointcloud(data=pointcloud_largefeatures, path=f.name)
            f.flush()
            f.seek(0)
            actual_data = f.read()
            reloaded_pointcloud = io.load_pointcloud(f.name)

        self.assertEqual(header + data, actual_data)
        self.assertClose(reloaded_pointcloud.points_list()[0], points)
        self.assertClose(reloaded_pointcloud.features_list()[0],
                         features_large)
        # Test the load-save cycle leaves file completely unchanged
        with NamedTemporaryFile(mode="rb", suffix=".ply") as f:
            io.save_pointcloud(
                data=reloaded_pointcloud,
                path=f.name,
            )
            f.flush()
            f.seek(0)
            data2 = f.read()
            self.assertEqual(data2, actual_data)

        with NamedTemporaryFile(mode="r", suffix=".ply") as f:
            io.save_pointcloud(data=pointcloud,
                               path=f.name,
                               binary=False,
                               decimal_places=9)
            reloaded_pointcloud2 = io.load_pointcloud(f.name)
            self.assertEqual(f.readline(), "ply\n")
            self.assertEqual(f.readline(), "format ascii 1.0\n")
        self.assertClose(reloaded_pointcloud2.points_list()[0], points)
        self.assertClose(reloaded_pointcloud2.features_list()[0], features)

        for binary in [True, False]:
            with NamedTemporaryFile(mode="rb", suffix=".ply") as f:
                io.save_pointcloud(data=pointcloud,
                                   path=f.name,
                                   colors_as_uint8=True,
                                   binary=binary)
                f.flush()
                f.seek(0)
                actual_data = f.read()
                reloaded_pointcloud3 = io.load_pointcloud(f.name)
            self.assertClose(reloaded_pointcloud3.features_list()[0], features)
            self.assertIn(b"property uchar green", actual_data)

            # Test the load-save cycle leaves file completely unchanged
            with NamedTemporaryFile(mode="rb", suffix=".ply") as f:
                io.save_pointcloud(
                    data=reloaded_pointcloud3,
                    path=f.name,
                    binary=binary,
                    colors_as_uint8=True,
                )
                f.flush()
                f.seek(0)
                data2 = f.read()
                self.assertEqual(data2, actual_data)