from __future__ import annotations import copy import numpy as np import open3d as o3d class GaussianNoiseAdder: """Add Gaussian noise to an Open3D point cloud.""" def __init__(self, noise_std: float = 4.0, seed: int | None = None) -> None: self.noise_std = noise_std self.seed = seed def apply(self, pointcloud: o3d.geometry.PointCloud) -> o3d.geometry.PointCloud: noisy = copy.deepcopy(pointcloud) points = np.asarray(noisy.points) rng = np.random.default_rng(self.seed) noise = rng.normal(0, self.noise_std, points.shape) noisy.points = o3d.utility.Vector3dVector(points + noise) return noisy def noise_level(self, clean: o3d.geometry.PointCloud, noisy: o3d.geometry.PointCloud) -> float: clean_points = np.asarray(clean.points) noisy_points = np.asarray(noisy.points) if clean_points.shape != noisy_points.shape: raise ValueError("Clean and noisy point clouds must have the same point count.") return float(np.mean(np.linalg.norm(noisy_points - clean_points, axis=1)))