3 minute read

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:

Comparativa de la nube original (arriba izquierda) y la nube con indice de densidad de 0.7 (arriba derecha), indice de densidad 0.5 (abajo izquierda) e indice de densidad 0.3 (abajo derecha)

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:

Efecto del ruido gaussiano con σ (0.05 metros) aplicado al 70% de los puntos de la nube.

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.