5 minute read

Auto-interpolación

El primero de los algoritmos, se basa en interpolar los valores de un punto de la nube original. Al inicializar n_neighbor a 1, se toman los valores (x,y,z,r) del punto seleccionado aleatoriamente de la nube original y se crea un nuevo punto que tendrá un valor de r igual al punto seleccionado y las cordenadas x,y,z serán las coordenadas del punto seleccionado más un valor aleatorio entre -0.1 y 0.1.

El criterio para seleccinar los puntos que se interpolarán, es contar las veces que se toma un punto y elegir aleatoriamente entre los que menos se han seleccionado.

Este proceso se repite las veces necesaria para obtener un numero de puntos en la nube densificada igual al numero de puntos de la nube original multiplicado por el indice de densificación.

def densify_point_cloud(points: np.ndarray, remissions: np.ndarray, 
                                       density_factor: float = 5.0) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
    """
    Densifica la nube de puntos utilizando interpolación basada en vecinos más cercanos.
    """
    # Usar NearestNeighbors para encontrar los vecinos más cercanos
    nn = NearestNeighbors(n_neighbors=2)
    nn.fit(points)
    
    # Establecer el número de nuevos puntos a crear
    num_new_points = int((len(points) * (density_factor-1)))
    
    # Cuenta de las veces que se usa cada punto
    point_usage_count = {}

    # Almacenar los puntos y las intensidades generadas
    new_points = []
    new_remissions = []
    
    for _ in range(num_new_points):
        # Elegir un punto aleatorio de la nube de puntos original
        index = np.random.randint(0, len(points))
        point = points[index]

        # Elegir un punto que haya sido usado menos veces
        if not point_usage_count:  # Si el diccionario está vacío
            index = np.random.randint(0, len(points))

        else:
            # Encontrar el mínimo número de veces que se ha usado cualquier punto
            min_usage = min(point_usage_count.values())
            # Obtener todos los índices que tienen el mínimo uso
            least_used_indices = [idx for idx, count in point_usage_count.items() if count == min_usage]
            # Si hay puntos que aún no se han usado, incluirlos
            unused_indices = [i for i in range(len(points)) if i not in point_usage_count]
            candidate_indices = least_used_indices + unused_indices
            # Seleccionar aleatoriamente entre los candidatos
            index = np.random.choice(candidate_indices)

        point = points[index]

        # Incrementar contador para el punto seleccionado
        if index in point_usage_count:
            point_usage_count[index] += 1
        else:
            point_usage_count[index] = 1

        
        # Obtener los 2 vecinos más cercanos (el primero es él mismo)
        distances,indices = nn.kneighbors([point], n_neighbors=2)

        # Seleccionar el segundo vecino más cercano para interpolar
        neighbor_point = points[indices[0][1]]

        print(f"\nPunto original seleccionado [{index}]: {point}")
        print(f"Punto vecino encontrado [{indices[0][0]}]: {neighbor_point}")
        print(f"¿Son el mismo punto?: {np.array_equal(point, neighbor_point)}")
        
        # Interpolar entre el punto original y el vecino seleccionado
        displacement = np.random.uniform(-0.1, 0.1, size=(3,))  # Pequeño desplazamiento aleatorio
        interpolated_point = (point + neighbor_point) / 2 + displacement
        
        # Las intensidades de los nuevos puntos serán las medias de las intensidades del punto original y el vecino
        interpolated_remission = (remissions[index] + remissions[indices[0][1]]) / 2
        
        # Agregar el nuevo punto y su intensidad
        new_points.append(interpolated_point)
        new_remissions.append(interpolated_remission)
 
    # Mostrar el uso de los puntos
    print("\nInformación de puntos usados como referencia:")
    print("Formato: [Punto ID] (x, y, z) - usado N veces - Tipo")
    for point_idx, count in point_usage_count.items():
        coord = points[point_idx]
        print(f"[{point_idx}] ({coord[0]:.3f}, {coord[1]:.3f}, {coord[2]:.3f}) - usado {count} veces - ORIGINAL")

    # También podemos mostrar los nuevos puntos generados
    print("\nPuntos nuevos generados:")
    for i, new_point in enumerate(new_points):
        print(f"[NEW_{i}] ({new_point[0]:.3f}, {new_point[1]:.3f}, {new_point[2]:.3f}) - INTERPOLADO")

    # Convertir las listas a arrays
    new_points = np.array(new_points)
    new_remissions = np.array(new_remissions)

    combined_points = np.vstack([points, new_points])
    combined_remissions = np.concatenate([remissions, new_remissions])    
    
    # Retornar los puntos originales junto con los nuevos puntos
    return combined_points, combined_remissions

Visualización del aumento de la densidad con auto-interpolación:

auto-interpolación

1-NN

El siguiente algoritmo, 1-NN, sigue el mismo funcionamiento que el anterior. En este caso el valor de n_neighbor es 2. El primer paso es común con el algoritmo anterior. Se selecciona aleatoriamente un punto de la nube original. De este punto se selecciona el vecino más cercano. El nuevo punto tendrá unas coordenadas x,y,z, que se han generado calculando la media de las posiciones de los dos puntos, y se añade un desplazamiento aleatorio entre -0.1 y 0.1, para evitar coincidencias entre los puntos generados. Para el valor de r, también se calcula la media del valor de r del punto original seleccionado y su vecino mas cercano.

El proceso se repite tantas veces como sea necesario para lograr la densidad que se indique, es decir que el numero final de puntos sea igual al nuero original multiplicado por el factor de densificación.

Aumento de la densidad con 1-NN:

1NN

TIN

En este caso se utiliza la triangulación de Delaunay para realizar la interpolación de los puntos. Este algoritmo consiste en crear un triańgulo usando las coordenadas x,y de los puntos de entrada, creando una malla triangular que conecta todos los puntos. Usando esta triangulación, el algoritmo LinearNDInterpolator creaa dos interpoladores lineales, uno para las coordenadas z y otro para los calores de intensidad.

    # Crear la triangulación de Delaunay
    tri = Delaunay(points[:, :2]) 
    
    # Crear un interpolador lineal basado en la triangulación
    z_interpolator = LinearNDInterpolator(tri, points[:,2])
    remission_interpolator = LinearNDInterpolator(tri, remissions)

El proceso de densificación determina cuantos puntos nuevos se van a crear basandose en el factor de densificación density_factor. Genera coordenadas x,y aleatorias dentro de los limites de la nube de puntos original. Verifica si cada nuevo punto cae dentro de la triangualación, para los puntos validos, los que estan dentro de la trianguñación, interpola la coordenada z y los valores de intensidad usando los interpoladores lineales. Por ultimo se añaden los nuevos puntos a la nube depuntos, combinando los puntos originales y los nuevos.

 # Establecer el número de nuevos puntos a crear
    target_point = int(len(points) * density_factor)
    
    # Generar nuevos puntos dentro del área de la triangulación
    x_min, x_max = points[:, 0].min(), points[:, 0].max()
    y_min, y_max = points[:, 1].min(), points[:, 1].max()
    
    new_points = []
    new_remissions = []
    
    while len(new_points) + len(points) < target_point:
        # Generar un punto aleatorio dentro del rango (x,y)
        x = np.random.uniform(x_min, x_max)
        y = np.random.uniform(y_min, y_max)

        # Verificar si el punto está dentro de la triangulación
        if tri.find_simplex(np.array([[x, y]])) >= 0:
            # Interpolar el valor de z y la intensidad
            z_interp = z_interpolator(x, y)
            remission_interp = remission_interpolator(x, y)

            if not np.isnan(z_interp):
                new_points.append([x,y,z_interp])
                new_remissions.append(remission_interp)

    
    combined_points = np.vstack([points, new_points])
    combined_remissions = np.concatenate([remissions, new_remissions])

    
    # Retornar los puntos originales junto con los nuevos puntos
    return combined_points, combined_remissions
TIN