Semana 24. Optimización de los metodos de submuestreo y adición de ruido gaussiano para nubes de puntos
Esta semana he trabajado en la optimización de dos algoritmos fundamentales para el procesamiento de nubes de puntos: el submuestreo aleatorio y la adición de ruido gaussiano controlado. Al igual que con los métodos TIN e IDW, he reemplazado los bucles por operaciones matriciales vectorizadas, logrando mejoras significativas en el rendimiento computacional, aunque en estos dos casos no fuese tan necesario.
Optimización del Método de Submuestreo Aleatorio
El algoritmo de submuestreo aleatorio ha sido completamente vectorizado, trabajando directamente con arrays NumPy de forma (N, 3) para las coordenadas y (N,) para las intensidades.
Los principales cambios incluyen:
-
Selección vectorizada de puntos: Uso de
np.random.default_rng().choice
para seleccionar todos los puntos de forma simultanea. -
Operaciones matriciales: Eliminación completa de bucles mediante indexación avanzada.
-
Control preciso del factor de reducción
La función optimizada queda así:
def subsample_point_cloud(points: np.ndarray, remissions: np.ndarray,
reduction_factor: float = 0.5) -> Tuple[np.ndarray, np.ndarray]:
"""
Reduce la nube de puntos mediante submuestreo aleatorio.
:param points: Nube de puntos original (N, 3)
:param remissions: Intensidades de los puntos originales (N,)
:param reduction_factor: Factor de reducción (0 < reduction_factor < 1)
:return: Puntos reducidos y sus intensidades
"""
print("\n=== INICIO DEL PROCESO DE SUBMUESTREO ALEATORIO ===")
print(f"Puntos iniciales: {points.shape}, Remisiones: {remissions.shape}")
# Validar factor de reducción
if reduction_factor <= 0 or reduction_factor >= 1:
raise ValueError("El factor de reducción debe estar entre 0 y 1")
# Calcular número de puntos a conservar
num_original_points = len(points)
num_subsampled_points = int(num_original_points * reduction_factor)
print(f"\n1. Reduciendo de {num_original_points} a {num_subsampled_points} puntos...")
# Generar índices aleatorios sin repetición
print("\n2. Generando índices aleatorios...")
rng = np.random.default_rng()
indices = rng.choice(num_original_points, size=num_subsampled_points, replace=False)
indices = np.sort(indices) # Ordenar para mantener cierta coherencia espacial
print("\nÍndices seleccionados (primeros 10):")
print(indices[:10])
# Seleccionar puntos e intensidades
print("\n3. Seleccionando puntos e intensidades...")
subsampled_points = points[indices]
subsampled_remissions = remissions[indices]
print("\nPuntos submuestreados (primeros 10):")
print(subsampled_points[:10])
print("\nRemisiones submuestreadas (primeras 10):")
print(subsampled_remissions[:10])
print("\n=== RESULTADOS FINALES ===")
print(f"Total puntos originales: {len(points)}")
print(f"Total puntos después del submuestreo: {len(subsampled_points)}")
print("\nPrimeros 5 puntos originales:")
print(points[:5])
print("\nPrimeros 5 puntos después del submuestreo:")
print(subsampled_points[:5])
return subsampled_points, subsampled_remissions
Los resultados de aplicar el submuestreo aleatorio variando el indice de densidad se muestra en el video a continuación:
Optimización de adición de Ruido Gaussiano
Para la adición de ruido gaussiano, las mejoras implementadas son:
-
Generación vectorizada de ruido: Uso de
np.random.normal
para generar todo el ruido en una operación. -
Modificación simultanea de puntos e intensidades
-
Control independiente de la desviación estandar espacial y de intensidad
La función optimizada:
def add_gaussian_noise(points: np.ndarray, remissions: np.ndarray,
std_dev: float, points_factor: float = 1.0) -> Tuple[np.ndarray, np.ndarray]:
"""
Añade ruido gaussiano a la nube de puntos.
:param points: Puntos originales (N, 3)
:param remissions: Intensidades originales (N,)
:param std_dev: Desviación estándar del ruido (metros)
:param points_factor: Porcentaje de puntos a modificar (0-1)
:return: Puntos con ruido e intensidades modificadas
"""
print("\n=== INICIO DEL PROCESO DE AÑADIR RUIDO GAUSSIANO ===")
print(f"Puntos iniciales: {points.shape}, Remisiones: {remissions.shape}")
# Validación de parámetros
if std_dev <= 0:
raise ValueError("La desviación estándar debe ser positiva")
if points_factor <= 0 or points_factor > 1:
raise ValueError("El factor de puntos debe estar entre 0 y 1")
num_points = len(points)
num_noisy_points = int(num_points * points_factor)
print(f"\n1. Seleccionando {num_noisy_points} puntos para añadir ruido...")
rng = np.random.default_rng()
selected_indices = rng.choice(num_points, size=num_noisy_points, replace=False)
print(f"\n2. Generando ruido gaussiano con σ={std_dev}...")
noise = rng.normal(0, std_dev, (num_noisy_points, 3))
print("\n3. Aplicando ruido a los puntos seleccionados...")
noisy_points = points.copy()
noisy_points[selected_indices] += noise
print("\n4. Ajustando intensidades con ruido proporcional...")
noisy_remissions = remissions.copy()
intensity_noise = rng.normal(1, std_dev*0.5, num_noisy_points)
noisy_remissions[selected_indices] *= np.clip(intensity_noise, 0.5, 1.5)
print("\n=== RESULTADOS ===")
print(f"Total puntos originales: {num_points}")
print(f"Puntos modificados: {num_noisy_points}")
print(f"Desviación estándar aplicada: {std_dev} metros")
return noisy_points, noisy_remissions
En el siguiente video se muestran los resultados de añadir ruido gaussiano en las nubes de puntos:
Tras probar tanto la densificación (utilizando IDW y TIN), el submuestreo aleatorio y apñadir ruido gaussiano, no se aprecia mejora o empeoramiento visual de la segmentación geometrica de las nubes de puntos.