Estimación de Correspondencias en Nubes de Puntos basada en Descriptores de Características FPFH

En el procesamiento de nubes de puntos 3D, la identificación de pares de puntos similares entre dos conjuntos de datos es un paso crítico para algoritmos de registro y reconocimiento. La clase pcl::registration::CorrespondenceEstimation de la bilbioteca PCL permite realizar esta búsqueda utilizando descriptores de características geométricas, lo que proporciona una mayor robustez en comparación con la búsqueda basada únicamente en coordenadas espaciales.

Uso de Descriptores para la Correspondencia

Para emplear descriptores en la búsqueda de correspondencias, se debe instanciar la clase de estimación utilizando el tipo de descriptor específico como parámetro de plantilla. Un ejemplo común es el uso de FPFH (Fast Point Feature Histograms):

pcl::registration::CorrespondenceEstimation<pcl::FPFHSignature33, pcl::FPFHSignature33> est;

El procedimiento general consta de tres etapas fundamentales:

  1. Cálculo de geometría local: Estimación de las normales de la superficie para ambas nubes de puntos.
  2. Extracción de características: Generación de descriptores (como FPFH, PFH o VFH) que codifican la información geométrica del entorno de cada punto.
  3. Determinación de pares: Búsqueda de los descriptores más cercanos en el espacio de características para establecer las correspondencias.

Implementación Técnica

El siguiente ejemplo demuestra cómo extraer descriptores FPFH y utilizarlos para encontrar pares de puntos correspondientes entre una nube de origen y una de destino.

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d_omp.h>
#include <pcl/features/fpfh_omp.h>
#include <pcl/registration/correspondence_estimation.h>
#include <pcl/visualization/pcl_visualizer.h>

// Función para obtener descriptores FPFH a partir de una nube de puntos
pcl::PointCloud<pcl::FPFHSignature33>::Ptr extraerDescriptores(pcl::PointCloud<pcl::PointXYZ>::Ptr nube)
{
    pcl::search::KdTree<pcl::PointXYZ>::Ptr arbol(new pcl::search::KdTree<pcl::PointXYZ>());
    
    // 1. Estimación de Normales
    pcl::PointCloud<pcl::Normal>::Ptr normales(new pcl::PointCloud<pcl::Normal>);
    pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> estimador_normales;
    estimador_normales.setInputCloud(nube);
    estimador_normales.setNumberOfThreads(4);
    estimador_normales.setSearchMethod(arbol);
    estimador_normales.setKSearch(15);
    estimador_normales.compute(*normales);

    // 2. Estimación de FPFH
    pcl::PointCloud<pcl::FPFHSignature33>::Ptr descriptores(new pcl::PointCloud<pcl::FPFHSignature33>);
    pcl::FPFHEstimationOMP<pcl::PointXYZ, pcl::Normal, pcl::FPFHSignature33> fpfh;
    fpfh.setNumberOfThreads(4);
    fpfh.setInputCloud(nube);
    fpfh.setInputNormals(normales);
    fpfh.setSearchMethod(arbol);
    fpfh.setKSearch(15);
    fpfh.compute(*descriptores);

    return descriptores;
}

int main()
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr nube_fuente(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::PointCloud<pcl::PointXYZ>::Ptr nube_destino(new pcl::PointCloud<pcl::PointXYZ>);

    // Carga de archivos PCD
    if (pcl::io::loadPCDFile("datos_fuente.pcd", *nube_fuente) == -1 || 
        pcl::io::loadPCDFile("datos_destino.pcd", *nube_destino) == -1)
    {
        PCL_ERROR("No se pudieron cargar los archivos.\n");
        return -1;
    }

    // Obtención de características para ambas nubes
    auto fpfh_fuente = extraerDescriptores(nube_fuente);
    auto fpfh_destino = extraerDescriptores(nube_destino);

    // Estimación de correspondencias basada en descriptores
    pcl::registration::CorrespondenceEstimation<pcl::FPFHSignature33, pcl::FPFHSignature33> estimador;
    estimador.setInputSource(fpfh_fuente);
    estimador.setInputTarget(fpfh_destino);

    pcl::Correspondences coincidencias;
    // Se determinan las correspondencias con un umbral de distancia máxima
    estimador.determineCorrespondences(coincidencias, 0.5);

    // Visualización de resultados
    pcl::visualization::PCLVisualizer::Ptr visor(new pcl::visualization::PCLVisualizer("Visor de Correspondencias"));
    visor->setBackgroundColor(0, 0, 0);
    
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color_fuente(nube_fuente, 0, 255, 0);
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color_destino(nube_destino, 255, 0, 0);
    
    visor->addPointCloud<pcl::PointXYZ>(nube_fuente, color_fuente, "fuente");
    visor->addPointCloud<pcl::PointXYZ>(nube_destino, color_destino, "destino");
    
    // Dibujar líneas entre puntos correspondientes
    visor->addCorrespondences<pcl::PointXYZ>(nube_fuente, nube_destino, coincidencias, "lineas_correspondencia");

    while (!visor->wasStopped())
    {
        visor->spinOnce(100);
    }

    return 0;
}

Parámetros Clave

Al utilizar determineCorrespondences, el segundo parámetro define la distancia máxima en el espacio de descriptores para considerar que dos puntos son candidatos a ser pares. Ajustar este valor es crucial: un valor demasiado bajo resultará en pocas correspondencias, mientras que uno demasiado alto aumentará el número de falsos positivos.

Además, el uso de implementaciones OMP (OpenMP) en el cálculo de normales y FPFH permite aprovechar la computación multihilo, reduciendo significativamente el tiempo de procesamiento en nubes de puntos densas.

Etiquetas: PCL Point-Cloud-Library FPFH Correspondence-Estimation computer-vision

Publicado el 7-19 21:57