from __future__ import annotations
|
|
import tempfile
|
import unittest
|
from pathlib import Path
|
|
import numpy as np
|
import open3d as o3d
|
|
import sys
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
from run_pointcloud_understanding import classify_semantic_points, process_point_cloud # noqa: E402
|
|
|
def write_cloud(path: Path, points: np.ndarray) -> None:
|
cloud = o3d.geometry.PointCloud()
|
cloud.points = o3d.utility.Vector3dVector(points)
|
assert o3d.io.write_point_cloud(str(path), cloud)
|
|
|
class PointCloudUnderstandingTests(unittest.TestCase):
|
def test_raster_vector_and_mesh_are_created(self) -> None:
|
with tempfile.TemporaryDirectory() as directory:
|
root = Path(directory)
|
rng = np.random.default_rng(2)
|
ground = np.column_stack((rng.uniform(-4, 4, (600, 2)), rng.normal(0, 0.01, 600)))
|
roof = np.column_stack((rng.uniform(-1, 1, (350, 2)), rng.normal(2, 0.02, 350)))
|
source = root / "site.ply"
|
write_cloud(source, np.vstack((ground, roof)))
|
result = process_point_cloud(source, root / "output", voxel_size=0.2, ground_distance=0.1, elevated_threshold=0.6)
|
self.assertGreater(result["elevated_points"], 0)
|
self.assertGreater(result["elevated_footprint_count"], 0)
|
self.assertEqual(result["coordinate_basis"], "local_point_cloud_coordinates")
|
for field in ("dsm_file", "label_raster_file", "preview_file", "vector_file", "summary_file", "classified_point_cloud"):
|
self.assertTrue((root / "output" / result[field]).is_file())
|
|
def test_small_cloud_fails(self) -> None:
|
with tempfile.TemporaryDirectory() as directory:
|
root = Path(directory)
|
source = root / "small.ply"
|
write_cloud(source, np.zeros((4, 3)))
|
with self.assertRaises(ValueError):
|
process_point_cloud(source, root / "output")
|
|
def test_semantic_rules_keep_unknown_and_identify_strong_candidates(self) -> None:
|
ground = np.column_stack((np.linspace(0, 4, 20), np.zeros(20), np.zeros(20)))
|
structure = np.column_stack((10 + np.linspace(0, 0.8, 20), np.zeros(20), np.full(20, 3.0)))
|
vegetation = np.column_stack((20 + np.linspace(0, 0.8, 20), np.zeros(20), np.linspace(2.0, 3.5, 20)))
|
wire = np.column_stack((30 + np.arange(20), np.zeros(20), np.linspace(6.0, 6.1, 20)))
|
pole = np.column_stack((40 + np.zeros(20), np.zeros(20), np.linspace(1.0, 10.0, 20)))
|
points = np.vstack((ground, structure, vegetation, wire, pole))
|
heights = points[:, 2].copy()
|
colors = np.full((len(points), 3), 0.5)
|
colors[len(ground) + len(structure):len(ground) + len(structure) + len(vegetation)] = [0.15, 0.7, 0.15]
|
labels = classify_semantic_points(points, colors, heights, voxel_size=0.2)
|
self.assertIn(2, labels)
|
self.assertIn(5, labels)
|
self.assertIn(6, labels)
|
self.assertIn(15, labels)
|
self.assertIn(16, labels)
|
|
|
if __name__ == "__main__":
|
unittest.main()
|