首页 > 其他分享 >PCL 法线微分分割(Don)

PCL 法线微分分割(Don)

时间:2024-08-16 17:54:48浏览次数:17  
标签:法线 Don PCL inside pcl 聚类 indices new cloud

1.原理

对于点云中的每个点云点P,使用一大一小两个半径计算该点的两个法向量,然后用两个单位法向量的差异计算Don特征。

如下图:

图1为大半径计算得到的法向量;图2为小半径计算得到的法向量;图3为一大一小两个法向量计算得出的Don特征。

步骤:

(1)建KD树,计算点云平均密度(即点云点之间的平均邻近距离)。

(2)用户依据平均密度设置两个尺度的Don半径,然后分别计算两个Don半径下的法向量。

(3)通过公式:

计算两个尺度下Don法向量的差异。

(4)得到对应的法线微分差异,得出Don结果。

2.使用场景

(1)寻找特征点(例如边缘点,角点等;因为法线微分分割之后,变化差异大的位置即边缘位置

(2)分割点云,得到点云的各个部分【往往用于分割具有多个平面的点云,如建筑物,道路面等】(在剔除Don计算出的边缘点之后,对其他点进行聚类,然后得到点云的不同部分)

3.注意事项

代码中的pcl::PointNormal不要写为pcl::Normal(因为我就写了...后面滤波报错了)

4.关键函数

(1)生成点云法线(在代码最初,生成点云法线时,最好使用K邻近搜索)

setKSearch()

(2)计算平均半径,用于辅助分割(以下代码可以搜索与当前点云点最邻近的点注意,参数设置为2,因为索引=0的点,是当前点云点本身

std::vector<int> pointIdxNKNSearch(2);                                                          // 保存最邻近点的索引(该方法最低设置为2,其中第一个值为当前点)
std::vector<float> pointNKNSquaredDistance(2);                                                  // 保存最邻近点的距离的平方(同上)                     
tree->nearestKSearch(cloud->points[i], 2, pointIdxNKNSearch, pointNKNSquaredDistance);
double dis = sqrt(pointNKNSquaredDistance[1]);

(3)大小尺度半径(大小尺度半径通过这个函数设置,以基于这两个函数,设置两个不同的值)

setRadiusSearch()

(4)Don法线微分分割(设置大小半径的参数)

pcl::DifferenceOfNormalsEstimation<pcl::PointXYZRGB, pcl::PointNormal, pcl::PointNormal> feature;
feature.setNormalScaleLarge(ne_sm);                         // 设置小尺寸半径
feature.setNormalScaleSmall(ne_lg);                         // 设置大尺寸半径

(5)条件滤波相关(用于过滤边缘点【保留边缘点同理】)条件滤波

pcl::ConditionOr<pcl::PointNormal>::Ptr filtersOr_keypoint(new pcl::ConditionOr<pcl::PointNormal>());
filtersOr_keypoint->addComparison(pcl::FieldComparison<pcl::PointNormal>::ConstPtr(new pcl::FieldComparison<pcl::PointNormal>("curvature", pcl::ComparisonOps::GT, 0.3)));       // 条件选择器,曲率值大于0.3
pcl::ConditionalRemoval<pcl::PointNormal> filters_keypoint;
filters_keypoint.setInputCloud(donCloud);
filters_keypoint.setCondition(filtersOr_keypoint);                                            // 设置条件选择器(或条件)
pcl::PointCloud<pcl::PointNormal>::Ptr cloud_filtered_keypoint(new pcl::PointCloud<pcl::PointNormal>);
filters_keypoint.filter(*cloud_filtered_keypoint);

(6)聚类相关(Don之后聚类可将去掉边缘的点云分成几部分;聚类得到的结果为聚类前点云点的索引)

 // 对去掉角点的内部点按照欧式聚类进行聚类,实现对物体不同面的分割功能
pcl::search::KdTree<pcl::PointNormal>::Ptr segtree(new pcl::search::KdTree<pcl::PointNormal>);
segtree->setInputCloud(cloud_filtered_inside);
std::vector<pcl::PointIndices> cluster_indices;                         // 聚类得到的索引集群(每个vector是一个集群)
pcl::EuclideanClusterExtraction<pcl::PointNormal> seg;                  // 欧式聚类
seg.setClusterTolerance(meanDis*5);                                     // 设置欧式聚类的阈值
seg.setMinClusterSize(50);                                              // 最小聚类点个数
seg.setMaxClusterSize(100000);                                          // 最大聚类点个数
seg.setSearchMethod(segtree);
seg.setInputCloud(cloud_filtered_inside);
seg.extract(cluster_indices);                                           // 得到聚类结果

5.代码

#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/search/kdtree.h>
#include <pcl/features/normal_3d_omp.h>
#include <pcl/filters/conditional_removal.h> //条件滤波
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/segmentation/impl/extract_clusters.hpp>
#include <pcl/features/don.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
#include <random>

int main()
{
    /****************Don法线微分分割********************/
    // 原始点云
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
    pcl::io::loadPCDFile("D:/code/csdn/data/two_tree.pcd", *cloud);   // 加载原始点云数据

    // 生成点云法线
    pcl::NormalEstimationOMP<pcl::PointXYZRGB, pcl::PointNormal> normEst;                              // 基于邻域的法线估计器(OMP型,可多线程处理)
    pcl::PointCloud<pcl::PointNormal>::Ptr normals(new pcl::PointCloud<pcl::PointNormal>);             // 法向量保存
    pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZRGB>());      // KD树
    normEst.setNumberOfThreads(4);
    normEst.setInputCloud(cloud);
    normEst.setSearchMethod(tree);
    normEst.setKSearch(50);                                                                            // 使用K领域搜索
     normEst.setRadiusSearch(0.3);                                                                   // 进行法向估计的领域半径(该半径范围内的点云拟合一个平面,生成点云法向量)
    normEst.compute(*normals);

    // 计算平均半径,用于辅助分割
    double totalDis = 0;
    for (int i = 0; i < cloud->size(); i++)                                                             // 搜索最邻近的两个点
    {
        std::vector<int> pointIdxNKNSearch(2);                                                          // 保存最邻近点的索引(该方法最低设置为2,其中第一个值为当前点)
        std::vector<float> pointNKNSquaredDistance(2);                                                  // 保存最邻近点的距离的平方(同上)                     
        tree->nearestKSearch(cloud->points[i], 2, pointIdxNKNSearch, pointNKNSquaredDistance);
        totalDis += sqrt(pointNKNSquaredDistance[1]);
    }
    double meanDis = totalDis / cloud->size();                                                          // 平均距离
    double scale_sm = meanDis * 2.0;                                                                    // 小尺度辅助半径
    double scale_lg = meanDis * 8.0;                                                                    // 大尺度辅助半径

    // 小尺度don半径
    pcl::NormalEstimationOMP<pcl::PointXYZRGB, pcl::PointNormal> normEst_sm;                                 // 基于邻域的法线估计器(OMP型,可多线程处理)
    pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree_sm(new pcl::search::KdTree<pcl::PointXYZRGB>());
    pcl::PointCloud<pcl::PointNormal>::Ptr ne_sm(new pcl::PointCloud<pcl::PointNormal>);
    normEst_sm.setNumberOfThreads(4);
    normEst_sm.setInputCloud(cloud);
    normEst_sm.setSearchMethod(tree_sm);
    normEst_sm.setRadiusSearch(scale_sm);
    normEst_sm.compute(*ne_sm);

    // 大尺度don半径
    pcl::NormalEstimationOMP<pcl::PointXYZRGB, pcl::PointNormal> normEst_lg;                                 // 基于邻域的法线估计器(OMP型,可多线程处理)
    pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree_lg(new pcl::search::KdTree<pcl::PointXYZRGB>());
    pcl::PointCloud<pcl::PointNormal>::Ptr ne_lg(new pcl::PointCloud<pcl::PointNormal>);
    normEst_lg.setNumberOfThreads(4);
    normEst_lg.setInputCloud(cloud);
    normEst_lg.setSearchMethod(tree_lg);
    normEst_lg.setRadiusSearch(scale_lg);
    normEst_lg.compute(*ne_lg);

    // Don法线微分分割
    pcl::DifferenceOfNormalsEstimation<pcl::PointXYZRGB, pcl::PointNormal, pcl::PointNormal> feature;
    feature.setInputCloud(cloud);
    feature.setNormalScaleLarge(ne_sm);                         // 设置小尺寸半径
    feature.setNormalScaleSmall(ne_lg);                         // 设置大尺寸半径

    pcl::PointCloud<pcl::PointNormal>::Ptr donCloud(new pcl::PointCloud<pcl::PointNormal>);
    pcl::copyPointCloud<pcl::PointXYZRGB, pcl::PointNormal>(*cloud, *donCloud);                  // 准备结果点云
    feature.computeFeature(*donCloud);

    // 应用一(寻找特征点)
    // 按照曲率进行条件滤波,删除小于给定曲率阈值的点云,得到特征点(曲率变化大的点,即角点)
    pcl::ConditionOr<pcl::PointNormal>::Ptr filtersOr_keypoint(new pcl::ConditionOr<pcl::PointNormal>());
    filtersOr_keypoint->addComparison(pcl::FieldComparison<pcl::PointNormal>::ConstPtr(new pcl::FieldComparison<pcl::PointNormal>("curvature", pcl::ComparisonOps::GT, 0.3)));       // 条件选择器,曲率值大于0.3
    pcl::ConditionalRemoval<pcl::PointNormal> filters_keypoint;
    filters_keypoint.setInputCloud(donCloud);
    filters_keypoint.setCondition(filtersOr_keypoint);                                            // 设置条件选择器(或条件)
    pcl::PointCloud<pcl::PointNormal>::Ptr cloud_filtered_keypoint(new pcl::PointCloud<pcl::PointNormal>);
    filters_keypoint.filter(*cloud_filtered_keypoint);

    // 应用二(分割点云)
    // 按照曲率进行条件滤波,删除大于给定曲率阈值的点云,得到内部点
    pcl::ConditionOr<pcl::PointNormal>::Ptr filtersOr_inside(new pcl::ConditionOr<pcl::PointNormal>());
    filtersOr_inside->addComparison(pcl::FieldComparison<pcl::PointNormal>::ConstPtr(new pcl::FieldComparison<pcl::PointNormal>("curvature", pcl::ComparisonOps::LT, 0.3)));       // 条件选择器,曲率值小于0.3
    pcl::ConditionalRemoval<pcl::PointNormal> filters_inside;
    filters_inside.setInputCloud(donCloud);
    filters_inside.setCondition(filtersOr_inside);                                            // 设置条件选择器(或条件)
    pcl::PointCloud<pcl::PointNormal>::Ptr cloud_filtered_inside(new pcl::PointCloud<pcl::PointNormal>);
    filters_inside.filter(*cloud_filtered_inside);
    // 对去掉角点的内部点按照欧式聚类进行聚类,实现对物体不同面的分割功能
    pcl::search::KdTree<pcl::PointNormal>::Ptr segtree(new pcl::search::KdTree<pcl::PointNormal>);
    segtree->setInputCloud(cloud_filtered_inside);
    std::vector<pcl::PointIndices> cluster_indices;                         // 聚类得到的索引集群(每个vector是一个集群)
    pcl::EuclideanClusterExtraction<pcl::PointNormal> seg;                  // 欧式聚类
    seg.setClusterTolerance(meanDis*5);                                     // 设置欧式聚类的阈值
    seg.setMinClusterSize(50);                                              // 最小聚类点个数
    seg.setMaxClusterSize(100000);                                          // 最大聚类点个数
    seg.setSearchMethod(segtree);
    seg.setInputCloud(cloud_filtered_inside);
    seg.extract(cluster_indices);                                           // 得到聚类结果

    /****************展示********************/
    // 整体曲率展示
    boost::shared_ptr<pcl::visualization::PCLVisualizer> pointView(new pcl::visualization::PCLVisualizer("Curvature"));
    pcl::visualization::PointCloudColorHandlerGenericField<pcl::PointNormal> handler(donCloud, "curvature");
    pointView->setBackgroundColor(1, 1, 1);
    pointView->addPointCloud(donCloud, handler);
    pointView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3);
    pointView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.5);
    /*
    while (!pointView->wasStopped())
    {
        pointView->spinOnce(100);
        boost::this_thread::sleep(boost::posix_time::microseconds(100000));
    }
    */

    // 内部点展示
    boost::shared_ptr<pcl::visualization::PCLVisualizer> insideView(new pcl::visualization::PCLVisualizer("KeyPoint"));
    pcl::visualization::PointCloudColorHandlerGenericField<pcl::PointNormal> handler_inside(cloud_filtered_inside, "curvature");
    insideView->setBackgroundColor(1, 1, 1);
    insideView->addPointCloud(cloud_filtered_inside, handler_inside);
    insideView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3);
    insideView->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_OPACITY, 0.5);
    /*
    while (!insideView->wasStopped())
    {
        insideView->spinOnce(100);
        boost::this_thread::sleep(boost::posix_time::microseconds(100000));
    }
    */

    // 聚类展示
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr clusters_cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
    clusters_cloud->resize(cloud_filtered_inside->points.size());
    for (size_t i = 0; i < cloud_filtered_inside->points.size(); i++)                   // 设置初始值,默认为白色
    {
        clusters_cloud->points[i].x = cloud_filtered_inside->points[i].x;
        clusters_cloud->points[i].y = cloud_filtered_inside->points[i].y;
        clusters_cloud->points[i].z = cloud_filtered_inside->points[i].z;
        clusters_cloud->points[i].r = 255;
        clusters_cloud->points[i].g = 255;
        clusters_cloud->points[i].b = 255;
    }

    for (size_t i = 0; i < cluster_indices.size(); i++)
    {
        std::random_device rand;                                // 生成随机颜色,每个聚类颜色一致
        std::mt19937 gen(rand());
        std::uniform_int_distribution<>dis(1, 254);
        int r = dis(gen);
        int g = dis(gen);
        int b = dis(gen);

        for (size_t j = 0; j < cluster_indices[i].indices.size(); j++)
        {
            clusters_cloud->points[cluster_indices[i].indices[j]].x = cloud_filtered_inside->points[cluster_indices[i].indices[j]].x;
            clusters_cloud->points[cluster_indices[i].indices[j]].y = cloud_filtered_inside->points[cluster_indices[i].indices[j]].y;
            clusters_cloud->points[cluster_indices[i].indices[j]].z = cloud_filtered_inside->points[cluster_indices[i].indices[j]].z;
            clusters_cloud->points[cluster_indices[i].indices[j]].r = r;
            clusters_cloud->points[cluster_indices[i].indices[j]].g = g;
            clusters_cloud->points[cluster_indices[i].indices[j]].b = b;
        }
    }

    boost::shared_ptr<pcl::visualization::PCLVisualizer> view_raw(new pcl::visualization::PCLVisualizer("cluster"));
    view_raw->addPointCloud<pcl::PointXYZRGB>(clusters_cloud, "cluster cloud");
    view_raw->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cluster cloud");
    while (!view_raw->wasStopped())
    {
        view_raw->spinOnce(100);
        boost::this_thread::sleep(boost::posix_time::microseconds(100000));
    }

    return 0;
}

6.结果展示

(1)整体曲率展示

(2)去除边缘点之后的内部点展示

(3)聚类之后的内部点展示(将内部点分为几部分,用于分割平面,每个颜色是一个平面)

标签:法线,Don,PCL,inside,pcl,聚类,indices,new,cloud
From: https://blog.csdn.net/xhm01291212/article/details/141240806

相关文章

  • coca would have done 搭配
      have:585the:213to:190been:138i:135that:111it:110a:110and:93of:85in:72":66he:56you:53#:50had:45they:45she:37for:36on:36as:33if:33with:30we:30be:29but:28him:27his:25her:24think:24would:23more:23was:23-:22this:22th......
  • 启动应用程序出现PCLXL.DLL找不到问题解决
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个PCLXL.DLL文件(挑选合适的版本文件)把它放入......
  • C# httpclient上传文件
    ///<summary>///上传文件///</summary>///<paramname="file"></param>///<returns></returns>[HttpPost,Route("UploadFile")][NonAuthorize]publicasyncTask<Response<string>>UploadFile......
  • open3d python 法线估计
    测试效果废话Open3D中的法线估计是一个重要的功能,它可以帮助用户了解三维点云中每个点的局部表面方向。以下是对Open3D法线估计的详细解释:一、法线估计的基本原理法线估计通常基于局部表面拟合的方法。在点云数据中,每个点的局部邻域可以视为一个平面或曲面的近似。通......
  • httpclient&WebClient--4次迭代,让我的 Client 优化 100倍!
    4次迭代,让我的Client优化100倍!https://www.cnblogs.com/crazymakercircle/p/17136216.html 在大家的生产项目中,经常需要通过Client组件(HttpClient/OkHttp/JDKConnection)调用第三方接口。在一个高并发的中台生产项目中。有一个比较特殊的请求,一次请求,包含10个Web外部......
  • java httpclient发送中文乱码
    在使用Java的HttpClient发送请求时,如果遇到中文乱码问题,通常需要确保请求和响应的字符集都正确设置为UTF-8。以下是一些解决方法:指定请求数据的字符集为UTF-8格式:在使用UrlEncodedFormEntity或StringEntity时,确保传递正确的字符集参数。例如:StringEntityentity=newUrlEnco......
  • 在VSCode中使用PCL
    我要被气死了。前提条件:CMakeMSVC2022(务必装这个)VSCode以及相关的C++,CMake插件等安装步骤:去PCL的Release这里下载类似“PCL-1.14.1-AllInOne-msvc2022-win64.exe”名称的exe,安装过程中要求为所有用户写入环境变量。找个地方新建空文件夹,新建这些文件,注意文件名也要一......
  • Lyndon Word 小记
    1.定义一个字符串\(S\)被定义为LyndonWord当且仅当其严格小于所有真cyclicshift。LyndonWord的等价定义:是其所有后缀中最小的。2.性质性质1:LyndonWord无\(\text{Border}\)。不妨设\(w\)有\(\text{Border}\),则我们可以表示为\(w=xu=uy\),从而得到\(w......
  • Angular项目如何使用拦截器 httpClient 请求响应处理
    在Angular中,拦截器(Interceptor)是一种用于拦截和处理HTTP请求或响应的机制。HttpClient模块提供了一种方便的方式来创建拦截器,以便在发送请求或接收响应之前或之后执行一些操作。以下是如何在Angular项目中使用HttpClient拦截器的基本步骤:创建拦截器类:首先,你需要创建一个继承自H......
  • Apache HttpClient发送文件时中文名变问号
    使用ApacheHttpClient发送multipart/form-data,包含有中文名的文件,对方收到的文件名中文变成了问号解决方法:发送方需要设置mode为HttpMultipartMode.RFC6532发送端代码如下,其中关键行为builder.setMode(HttpMultipartMode.RFC6532);importorg.apache.http.HttpEntity;impor......