IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    三维点云处理入门(13):点云的特征描述

    52txr发表于 2024-03-19 19:44:00
    love 0

    点云的特征描述是点云处理和分析中的一个重要环节,主要效果是从原始的点云数据中提取有用的信息。在PCL(点云库)中,有几种常用的点云特征描述方法,例如PFH、FPFH、VFH等,主要用于对象识别、分类、配准和分析等任务。

    法线估计(Normal Estimation)

    这是最基本的点云特征之一,涉及计算点云中每个点的表面法线。法线是了解表面形状和方向的关键。

    PCL中的所有点云的法线都是指向视点的,视点坐标默认为(0,0,0),对视点进行设置,则可对法线进行定向。

    在PCL中,创建法线估计向量ne然后ne.compute(*normals);就完事了。下面用示例代码进行演示:

    #include <pcl/io/pcd_io.h>
    #include <pcl/features/normal_3d_omp.h>
    #include <omp.h>
    #include <pcl/visualization/cloud_viewer.h>
    #include <string>
    #include <atlstr.h>//CString头文件
    #include <boost/thread/thread.hpp>
    
    using namespace std;
    
    int main()
    {
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
        pcl::io::loadPCDFile("E:\\PCL1\\pcl1\\rabbit.pcd", *cloud);
    
        pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> ne;//创建法线估计向量
        ne.setNumberOfThreads(omp_get_max_threads());
        ne.setInputCloud(cloud);
        pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());//创建一个空的KdTree对象,并把它传递给法线估计向量,基于给出的输入数据集,KdTree将被建立
        ne.setSearchMethod(tree);
        ne.setViewPoint(10, 10, 10);//设置视点,官网上的pcl::flipNormalTowardsViewpoint (const PointT &point, float vp_x, float vp_y, float vp_z, Eigen::Vector4f &normal)是对“一个已知点”的法线进行手动定向
        //ne.setViewPoint(-10, -10, -10);
        pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);//存储输出数据
        //ne.setRadiusSearch(1);        //半径单位为毫米
        ne.setKSearch(20);
        ne.compute(*cloud_normals);
    
        //保存具有法线信息的点云文件
        CString strFilePath(_T("E:\\PCL1\\pcl1\\点云法线.pcd"));//将文件保存在电脑桌面上
        string saveFileName = CStringA(strFilePath);
        pcl::PCDWriter savePCDFile;
        savePCDFile.write(saveFileName, *cloud_normals);
    
        //可视化
        boost::shared_ptr<pcl::visualization::PCLVisualizer> m_viewer(new pcl::visualization::PCLVisualizer());
    
        int v1(0);
        m_viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1);
        m_viewer->addCoordinateSystem(v1);
        m_viewer->setBackgroundColor(128.0 / 255.0, 138.0 / 255.0, 135.0 / 255.0, v1);
        pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color(cloud, 0, 255, 0);
        m_viewer->addPointCloud(cloud, color, "cloud1", v1);
    
        int v2(0);
        m_viewer->createViewPort(0.5, 0.0, 1.0, 1.0, v2);
        m_viewer->addCoordinateSystem(v2);
        m_viewer->setBackgroundColor(128.0 / 255.0, 138.0 / 255.0, 135.0 / 255.0, v2);
        //显示点云及其法线
        pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> Cap_Cloud_Point(cloud, 255, 0, 0);
        m_viewer->addPointCloud(cloud, Cap_Cloud_Point, "cloud2", v2);
        m_viewer->addPointCloudNormals<pcl::PointXYZ, pcl::Normal>(cloud, cloud_normals, 2, 0.5, "normals", v2); //显示点云法线,第三个参数是法线显示的个数(每2个点显示1个),第四个参数是法线的长度(此处为0.5)
        while (!m_viewer->wasStopped())
        {
            m_viewer->spinOnce(100);
            boost::this_thread::sleep(boost::posix_time::microseconds(100000));
        }
        return 0;
    }

    代码运行的效果:

    法线估计

    主曲率(Principal Curvatures)

    主曲率是基于局部表面的形状来描述点的特征。它通常与法线估计一起使用。

    pcl::PrincipalCurvaturesEstimation是PCL中的点云曲率估计方法,其在法线计算的基础上对点云的曲率进行计算。

    主曲率计算是比较耗时的,我设置了较大的搜索半径(为0.5),耗时半分钟完成计算。

    #include <pcl/io/pcd_io.h>
    #include <pcl/features/normal_3d.h>
    #include <pcl/features/principal_curvatures.h>
    #include <ctime>//c++程序计时头文件
    #include <pcl/visualization/cloud_viewer.h>
    #include <boost/thread/thread.hpp>
    using namespace std;
    clock_t start_time, end_time;
    
    //计算点云的n个最大曲率点——函数声明
    void MaxCurvaturePoints(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in, int n, pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out);
    
    //曲率计算结构体
    typedef struct PCURVATURE {
        //POINT3F cPoint;
        int index;
        float curvature;
    }PCURVATURE;
    
    int main()
    {
        //读取点云数据
        pcl::PCDReader reader;
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
        reader.read("E:\\PCL1\\pcl1\\rabbit.pcd", *cloud);
    
        start_time = clock();//程序开始计时
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_MaxCurvature(new pcl::PointCloud<pcl::PointXYZ>);
        MaxCurvaturePoints(cloud, 5000, cloud_MaxCurvature);//点云的5000个最大曲率点
        end_time = clock();//程序结束用时
        double endtime = (double)(end_time - start_time) / CLOCKS_PER_SEC;
        cout << "Total time:" << endtime << "s" << endl;//s为单位
        cout << "Total time:" << endtime * 1000 << "ms" << endl;//ms为单位
    
        //可视化
        pcl::visualization::PCLVisualizer viewer("PCLVisualizer");
        viewer.initCameraParameters();
        viewer.setBackgroundColor(128.0 / 255.0, 138.0 / 255.0, 135.0 / 255.0);
        viewer.addPointCloud<pcl::PointXYZ>(cloud, "cloud");
        pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> color(cloud_MaxCurvature, 255, 0, 0);
        viewer.addPointCloud<pcl::PointXYZ>(cloud_MaxCurvature, color, "cloud_MaxCurvature");
    
        while (!viewer.wasStopped())
        {
            viewer.spinOnce(100);
            boost::this_thread::sleep(boost::posix_time::microseconds(100000));
        }
        return 0;
    }
    
    //计算点云的n个最大曲率点——函数实现
    void MaxCurvaturePoints(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in, int n, pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out)
    {
        //点云法线估计
        pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
        ne.setInputCloud(cloud_in);
        pcl::search::KdTree<pcl::PointXYZ>::Ptr tree_normals_curvatures(new pcl::search::KdTree<pcl::PointXYZ>());
        ne.setSearchMethod(tree_normals_curvatures);
        pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);
        //ne.setRadiusSearch(0.03);
        ne.setKSearch(30);
        ne.compute(*cloud_normals);//计算法线
    
        //计算点云曲率
        pcl::PrincipalCurvaturesEstimation<pcl::PointXYZ, pcl::Normal, pcl::PrincipalCurvatures> pc;
        pcl::PointCloud<pcl::PrincipalCurvatures>::Ptr cloud_curvatures(new pcl::PointCloud<pcl::PrincipalCurvatures>);
        pc.setInputCloud(cloud_in);
        pc.setInputNormals(cloud_normals);
        pc.setSearchMethod(tree_normals_curvatures);
        pc.setRadiusSearch(0.5);//设置搜索半径
        //pc.setKSearch(5);
        pc.compute(*cloud_curvatures);
    
        //计算点云的高斯曲率,获取曲率集
        std::vector<PCURVATURE> allCurvates;
        pcl::PointXYZ tempPoint;
        float curvature = 0.0;
        PCURVATURE pv;
        tempPoint.x = tempPoint.y = tempPoint.z = 0.0;
        for (int i = 0; i < cloud_curvatures->size(); i++) {
            //平均曲率
            curvature = ((*cloud_curvatures)[i].pc1 + (*cloud_curvatures)[i].pc2) / 2;
            //高斯曲率
            //curvature = (*cloud_curvatures)[i].pc1 * (*cloud_curvatures)[i].pc2;
            //pv.cPoint = tempPoint;
            pv.index = i;
            pv.curvature = curvature;
            allCurvates.insert(allCurvates.end(), pv);
        }
        //获取曲率最大的前n个点
        PCURVATURE temp;
        int maxIndex = 0;
        int count = 0;
        for (int i = 0; i < allCurvates.size(); i++) {
            float maxCurvature = -99999;
            for (int j = i + 1; j < allCurvates.size(); j++) {
                if (maxCurvature < allCurvates[j].curvature) {
                    maxCurvature = allCurvates[j].curvature;
                    maxIndex = j;
                }
            }
            if (maxCurvature > allCurvates[i].curvature) {
                temp = allCurvates[maxIndex];
                allCurvates[maxIndex] = allCurvates[i];
                allCurvates[i] = temp;
                count++;
            }
            if (count > n) {
                break;
            }
        }
        //提取最大曲率点
        for (int i = 0; i < n; i++)
        {
            int indexP = allCurvates[i].index;
            cloud_out->push_back(cloud_in->points[indexP]);
        }
    }

    运行结果:

    主曲率估计

    点特征直方图(Point Feature Histograms)

    表面法线和曲率估计是对某个点周围的几何特征的基本表示。虽计算速度快容易,但却无法获得太多信息,因为它们只使用很少的几个参数值来近似某个点的k-邻域特征。然而大多数场景会包含许多具有相同或非常相似的特征值的点,若采用点特征表示法将直接减少了它们的全局特征信息。

    想象一下你手里有一团由许多小点构成的云——这就是点云。在点云处理中,我们经常需要理解这些点如何相互关联,形成了什么样的形状或结构。这就是点特征直方图(PFH)发挥作用的地方。

    这个过程可以想象成在点云中的每个点周围画一个小球。在这个球内的所有点都被认为是那个中心点的邻居。然后,PFH会分析这些邻居点之间的相对位置和角度,以理解它们是如何相互排列的。

    点特征直方图(PFH)在PCL中的实现是pcl_features模块的一部分。默认PFH的实现使用5个区间分类(例如:四个特征值中的每个都使用5个区间来统计)。

    #include<iostream>
    #include<vector>
    #include <pcl/point_types.h>
    #include <pcl/features/pfh.h>
    #include <pcl/io/pcd_io.h>//点云文件pcd 读写
    #include <pcl/features/normal_3d.h>//法线特征
    #include <pcl/visualization/pcl_plotter.h>// 直方图的可视化 方法2
    
    using namespace std;
    int main()
    {
    
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_ptr(new pcl::PointCloud<pcl::PointXYZ>);
        //======【1】 读取点云文件 填充点云对象======
        pcl::PCDReader reader;
        reader.read("E:\\PCL1\\pcl1\\rabbit.pcd", *cloud_ptr);
        // =====【2】计算法线========创建法线估计类====================================
        pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
        ne.setInputCloud(cloud_ptr);
    
        pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
        ne.setSearchMethod(tree);//设置近邻搜索算法
        // 输出点云 带有法线描述
        pcl::PointCloud<pcl::Normal>::Ptr cloud_normals_ptr(new pcl::PointCloud<pcl::Normal>);
        pcl::PointCloud<pcl::Normal>& cloud_normals = *cloud_normals_ptr;
    
        ne.setRadiusSearch(0.2);//搜索临近点 
        // 计算表面法线特征
        ne.compute(cloud_normals);
    
        //=======【3】创建PFH估计对象pfh,并将输入点云数据集cloud和法线normals传递给它=================
        pcl::PFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::PFHSignature125> pfh;// phf特征估计其器
        pfh.setInputCloud(cloud_ptr);
        pfh.setInputNormals(cloud_normals_ptr);
        //如果点云是类型为PointNormal,则执行pfh.setInputNormals (cloud);
        //创建一个空的kd树表示法,并把它传递给PFH估计对象。
        //基于已给的输入数据集,建立kdtree
        pcl::search::KdTree<pcl::PointXYZ>::Ptr tree2(new pcl::search::KdTree<pcl::PointXYZ>());
        //pcl::KdTreeFLANN<pcl::PointXYZ>::Ptr tree2 (new pcl::KdTreeFLANN<pcl::PointXYZ> ()); //-- older call for PCL 1.5-
        pfh.setSearchMethod(tree2);//设置近邻搜索算法
        //输出数据集
        pcl::PointCloud<pcl::PFHSignature125>::Ptr pfh_fe_ptr(new pcl::PointCloud<pcl::PFHSignature125>());//phf特征
        //注意:此处使用的半径必须要大于估计表面法线时使用的半径!!!
        pfh.setRadiusSearch(0.8);
        //计算pfh特征值
        pfh.compute(*pfh_fe_ptr);
        cout << "phf feature size : " << pfh_fe_ptr->points.size() << endl;
        // 应该与input cloud->points.size ()有相同的大小,即每个点都有一个pfh特征向量
    
        // ========直方图可视化=============================
        pcl::visualization::PCLPlotter plotter;
        plotter.addFeatureHistogram(*pfh_fe_ptr, 300); //设置的很坐标长度,该值越大,则显示的越细致
        plotter.plot();
    
        return 0;
    }

    快速点特征直方图(Fast Point Feature Histogram)

    对于实时应用或接近实时应用中,密集点云的点特征直方图(PFH)的计算,是一个主要的性能瓶颈。此处为PFH计算方式的简化形式,称为快速点特征直方图。由于大大地降低了FPFH的整体复杂性,因此FPFH有可能使用在实时应用中。

    PFH和FPFH计算方式之间的主要区别总结如下:

    • FPFH没有对全互连,点的所有邻近点的计算参数进行统计,从图中可以看到,因此可能漏掉了一些重要的点对,而这些漏掉的对点可能对捕获查询点周围的几何特征有贡献。
    • PFH特征模型是对查询点周围的一个精确的邻域半径内,而FPFH还包括半径r范围以外的额外点对(不过在2r内)
    • 因为重新权重计算的方式,所以FPFH结合SPFH值,重新捕获邻近重要点对的几何信息;
    • 由于大大地降低了FPFH的整体复杂性,因此FPFH有可能使用在实时应用中;
    • 通过分解三元组,简化了合成的直方图。也就是简单生成d分离特征直方图,对每个特征维度来单独绘制,并把它们连接在一起。
    #include <vtkAutoInit.h>
    #include <iostream>
    #include <pcl/io/pcd_io.h>
    #include <pcl/point_types.h>
    #include <pcl/common/io.h>
    #include <pcl/features/normal_3d.h>
    #include <pcl/visualization/pcl_visualizer.h>
    #include <pcl/console/time.h>
    #include <pcl/features/fpfh.h>
    #include <pcl/visualization/pcl_plotter.h>
    using namespace pcl;
    
    int main(int argc, char** argv)
    {
        std::cout << "Starting program...\n";
    
        // 读取点云
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
        if (pcl::io::loadPCDFile<pcl::PointXYZ>("E:\\PCL1\\pcl1\\rabbit.pcd", *cloud) == -1)
        {
            PCL_ERROR("Couldn't read file rabbit.pcd\n");
            system("pause");
            return -1;
        }
        std::cout << "Loaded point cloud with " << cloud->size() << " points.\n";
    
        // 估计法线
        pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
        ne.setInputCloud(cloud);
        pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
        ne.setSearchMethod(tree);
        pcl::PointCloud<pcl::Normal>::Ptr cloud_normals(new pcl::PointCloud<pcl::Normal>);
        ne.setRadiusSearch(0.2);      // 使用半径在查询点周围0.2范围内的所有邻元素
        ne.compute(*cloud_normals);     // 计算法线
        std::cout << "Normals estimated.\n";
    
        // FPFH
        pcl::FPFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::FPFHSignature33> fpfh;
        fpfh.setInputCloud(cloud);
        fpfh.setInputNormals(cloud_normals);
        pcl::search::KdTree<PointXYZ>::Ptr tree1(new pcl::search::KdTree<pcl::PointXYZ>());
        fpfh.setSearchMethod(tree1);
        pcl::PointCloud<pcl::FPFHSignature33>::Ptr fpfhs(new pcl::PointCloud<pcl::FPFHSignature33>());
        fpfh.setRadiusSearch(0.8); //半径必须大于估计法线时使用的半径
        fpfh.compute(*fpfhs);
        std::cout << "FPFH features computed.\n";
    
        // 显示某点的fhfh特征
        pcl::visualization::PCLPlotter plotter;
        plotter.addFeatureHistogram<pcl::FPFHSignature33>(*fpfhs, "fpfh", 60);
        std::cout << "FPFH histogram prepared.\n";
    
        // 可视化
        pcl::visualization::PCLVisualizer viewer("PCL Viewer");
        viewer.setBackgroundColor(0.0, 0.0, 0.0);
        viewer.addPointCloudNormals<pcl::PointXYZ, pcl::Normal>(cloud, cloud_normals, 1, 0.4, "normals");
        viewer.addPointCloud(cloud, "cloud1");
        viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "cloud1");
        std::cout << "Visualization prepared. Starting visualization loop...\n";
    
        while (!viewer.wasStopped())
        {
            plotter.plot();
            viewer.spinOnce(100);
        }
    
        std::cout << "Program finished.\n";
        return 0;
    }
    

    由于程序执行的比较慢,我在代码中添加了一些调试信息:

    • 点云文件加载完成后。
    • 法线估计完成后。
    • FPFH特征计算完成后。
    • 准备FPFH直方图显示后。
    • 可视化窗口准备完成后。
    • 程序结束前。

    下面是运行结果:

    特征直方图

    SHOT(Signature of Histograms of OrienTations)

    SHOT描述符是用于三维形状描述的一种方法,特别适合用于三维物体识别。

    SHOT(Signature of Histogram of Orientation)是一种基于局部特征的描述子,在特征点处建立局部坐标系,将邻域点的空间位置信息和几何特征统计信息结合起来描述特征点。Tombari 等人将现有三维局部特征描述方法分为两类,即基于特征的描述方法与基于直方图的描述方法,并分析了两种方法的优势,提出基于特征的局部特征描述方法要比后者在特征的描述能力上更强,而基于直方图的局部特征描述方法在特征的鲁棒性上比前者更胜一筹。

    在PCL中,可以直接调用pcl::SHOTEstimation来完成SHOT计算。下面是一个示例代码:

    #include <pcl/io/pcd_io.h>
    #include <pcl/point_types.h>
    #include <pcl/features/normal_3d.h>
    #include <pcl/features/shot.h>
    
    int main(int argc, char** argv)
    {
        // 创建点云指针
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());
        pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>());
    
        // 加载点云文件
        if (pcl::io::loadPCDFile<pcl::PointXYZ>("E:\\PCL1\\pcl1\\rabbit.pcd", *cloud) == -1)
        {
            PCL_ERROR("Couldn't read file rabbit.pcd \n");
            return -1;
        }
        std::cout << "Loaded " << cloud->points.size() << " data points from rabbit.pcd" << std::endl;
    
        // 创建法线估计对象,并计算法线
        pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normalEstimation;
        normalEstimation.setInputCloud(cloud);
        pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
        normalEstimation.setSearchMethod(tree);
        normalEstimation.setRadiusSearch(0.3); // 设置搜索半径
        normalEstimation.compute(*normals);
        std::cout << "Computed " << normals->points.size() << " normals." << std::endl;
    
        // 检查是否所有点的法线都是有效的
        for (size_t i = 0; i < normals->points.size(); ++i)
        {
            if (!pcl::isFinite(normals->points[i]))
            {
                std::cout << "Normal for point " << i << " is not finite." << std::endl;
            }
        }
    
        // 创建SHOT估计对象
        pcl::SHOTEstimation<pcl::PointXYZ, pcl::Normal, pcl::SHOT352> shotEstimation;
        shotEstimation.setInputCloud(cloud);
        shotEstimation.setInputNormals(normals);
        shotEstimation.setRadiusSearch(0.8); // 对于每个点指定要使用的邻域大小
    
        // 计算SHOT特征
        pcl::PointCloud<pcl::SHOT352>::Ptr shots(new pcl::PointCloud<pcl::SHOT352>());
        shotEstimation.compute(*shots);
        std::cout << "Computed " << shots->points.size() << " SHOT descriptors." << std::endl;
    
        // 输出第一个点的SHOT特征
        if (shots->points.size() > 0)
        {
            std::cout << "SHOT features of first point: " << std::endl;
            for (size_t i = 0; i < shots->points[0].descriptorSize(); i++)
            {
                std::cout << shots->points[0].descriptor[i] << " ";
            }
            std::cout << std::endl;
        }
        else
        {
            std::cout << "No SHOT features computed." << std::endl;
        }
    
        return 0;
    }

    视点特征直方图(Viewpoint Feature Histogram)

    VFH用于物体识别和场景分析,是一种对物体整体形状进行描述的方法,通常用于场景中单个对象的识别。

    VFH源于FPFH,因为FPFH描述子便于识别,但是起步不具有伸缩不变性,所以在FPFH上添加视角点,以保持其尺度不变性。

    视角点方向的计算与FPFH中计算相对法线时同时进行。

    #include<pcl/visualization/pcl_plotter.h>
    #include <pcl/io/pcd_io.h>
    #include <pcl/filters/filter.h>
    #include <pcl/filters/voxel_grid.h>
    #include <pcl/console/parse.h>
    #include <pcl/kdtree/kdtree_flann.h>
    #include <pcl/filters/normal_space.h>
    #include <pcl/common/eigen.h>
    #include <pcl/features/normal_3d.h>
    #include <pcl/features/vfh.h>
    #include <pcl/visualization/histogram_visualizer.h>
    #include<iostream>
    #pragma comment(lib,"User32.lib")
    #pragma comment(lib, "gdi32.lib")
    using namespace std;
    using namespace pcl::visualization;
    using namespace pcl::console;
    
    int
    main(int argc, char* argv[])
    {
        /*if (argc < 2)
        {
            std::cout << ".exe source.pcd -r 0.005 -ds 5" << endl;
            return 0;
        }*/
        float voxel_re = 0.05, ds_N = 10;
        parse_argument(argc, argv, "-r", voxel_re);// 设置点云分辨率
        parse_argument(argc, argv, "-ds", ds_N);// 设置半径
    
                                                //调节下采样的分辨率以保持数据处理的速度。
                                                // 下采样 
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_src(new pcl::PointCloud<pcl::PointXYZ>);
        pcl::io::loadPCDFile("E:\\PCL1\\pcl1\\rabbit.pcd", *cloud_src);
        std::vector<int> indices1;
        pcl::removeNaNFromPointCloud(*cloud_src, *cloud_src, indices1);
        pcl::PointCloud<pcl::PointXYZ>::Ptr ds_src(new pcl::PointCloud<pcl::PointXYZ>);
        pcl::VoxelGrid<pcl::PointXYZ> grid;
        grid.setLeafSize(voxel_re, voxel_re, voxel_re);
        grid.setInputCloud(cloud_src);
        grid.filter(*ds_src);
    
        //计算法向量
        pcl::PointCloud<pcl::Normal>::Ptr norm_src(new pcl::PointCloud<pcl::Normal>);
        pcl::search::KdTree<pcl::PointXYZ>::Ptr tree_src(new pcl::search::KdTree<pcl::PointXYZ>());
        pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
        PCL_INFO("Normal Estimation - Source\n");
        ne.setInputCloud(ds_src);
        ne.setSearchSurface(cloud_src);
        ne.setSearchMethod(tree_src);
        ne.setRadiusSearch(ds_N * 2 * voxel_re);
        ne.compute(*norm_src);
    
        // 提取关键点
        pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints_src(new pcl::PointCloud<pcl::PointXYZ>);
        pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints_tgt(new pcl::PointCloud<pcl::PointXYZ>);
        grid.setLeafSize(ds_N * voxel_re, ds_N * voxel_re, ds_N * voxel_re);
        grid.setInputCloud(ds_src);
        grid.filter(*keypoints_src);
    
        //Feature-Descriptor 
        PCL_INFO("VFH - Feature Descriptor\n");
        //VFH    
        //VFH Source 
        pcl::VFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::VFHSignature308> vfh_est_src;
        pcl::search::KdTree<pcl::PointXYZ>::Ptr tree_vfh_src(new pcl::search::KdTree<pcl::PointXYZ>);
    
        vfh_est_src.setSearchSurface(ds_src);//输入完整点云数据
        vfh_est_src.setInputCloud(keypoints_src); // 输入关键点
        vfh_est_src.setInputNormals(norm_src);
        vfh_est_src.setRadiusSearch(2 * ds_N * voxel_re);
        vfh_est_src.setSearchMethod(tree_vfh_src);
        pcl::PointCloud<pcl::VFHSignature308>::Ptr vfh_src(new pcl::PointCloud<pcl::VFHSignature308>);
        vfh_est_src.compute(*vfh_src);
    
        //定义绘图器
        PCLPlotter* plotter = new PCLPlotter("My Plotter");
        //设置特性
        plotter->setShowLegend(true);
        std::cout << pcl::getFieldsList<pcl::VFHSignature308>(*vfh_src);
    
        //显示
        plotter->addFeatureHistogram<pcl::VFHSignature308>(*vfh_src, "vfh", 0, "vfh");
        plotter->setWindowSize(800, 600);
        plotter->spinOnce(30000);
    
        plotter->clearPlots();
        system("pause");
        return 0;
    }

    运行结果:

    视点特征直方图

    参考资料

    PCL点云库——点云最大曲率

    PCL点云库——点云法线估计

    PCL之点特征直方图(PFH)

    点云库PCL学习:特征描述与提取(FPFH)

    PCL:点云特征-VFH示例



沪ICP备19023445号-2号
友情链接