当前位置:   article > 正文

PCL点云计算点的密度特征并进行提取_点密度 pcl

点密度 pcl

前言

托管GitHub的个人blog:点云计算点密度-sifan的blog

一、点云密度介绍

点云密度反应了点云的密集程度和分布,是点云的重要特征信息;其与采集设备的分辨率,被采集物体的表面材料有关。

广义上的点云密度有两种方法表示:

①距离密度,即给定需要计算点的数量,统计每个邻近点到该点的距离作为密度;

②点密度,即给定邻域半径,统计该点领域半径内的点数量作为点密度。


二、具体实现

主要步骤如下:

  1. 指定搜索半径radius和最小点密度阈值minPts。可以先用cloudcompare查看
  2. radiusSearch统计种子点的在radius局部点的数量,作为点的密度值densiities
  3. 遍历每个点的密度,满足密度阈值的点放入点集中。

2.1指定搜索半径和密度阈值

这一步可以指定搜索半径和阈值,可以把点云文件导入cloudcompare内查看,以便设置更合适的阈值。

  • 导入文件–>Tools–>Other–>Compute geometric features

在这里插入图片描述

  • 设置半径和勾选要计算的特征即可,这里我们勾选局部邻域内点的数量作为密度。

在这里插入图片描述

  • 拖动状态栏即可查看,设置合适的阈值。

在这里插入图片描述

2.2计算点的局部密度

首先构建kdtree,使用kdtree中的radiusSearch函数搜索种子点指定半径内点的索引nn_indices,然后将nn_indices.size()作为局部密度赋值给densiities

//首先初始化搜索领域半径radius和最小点minPts
float radius=0.8;
int minPts=90;
pcl::search::KdTree<pcl::PointXYZ>::Ptr treedense (new pcl::search::KdTree<pcl::PointXYZ>);
treedense->setInputCloud(cloud);
//初始化向量,储存局部密度
std::vector<int> nn_indices;//储存点云中每个点的最邻索引
std::vector<float> nn_dists; //储存每个点的最邻距离
std::vector<float> densiities(cloud->size(),0);//储存每个点的局部密度,初始化为零
                            
//nn_in和nn_dists,在算法中用于计算每个点的局部密度,对于每个点通过tree->ra函数找到其在领域半径内的所有最邻点的索引和距离
for (size_t i = 0; i < cloud->size(); i++){
        treedense->radiusSearch(i,radius,nn_indices,nn_dists);//1.要查找最邻的点的索引,2.指定半径,3.储存最邻点索引的向量
        densiities[i]=nn_indices.size();//赋值给densities
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2.3筛选点并进行聚类

创建visited用于记录点是否已经访问过,当点已经被访问过,或点的密度小于阈值则跳过该点。然后创建一个队列queue用于记录当前聚类的点,queue为先入先出结构。最后得到点云满足阈值条件的的密度聚类cluster。将其放入clusters中。

std::vector<bool>visited(cloud->size(),false);//用于记录每个点是否被访问过,初始化为每个点都没有访问过
for (size_t i = 0; i < cloud->size(); i++)
{
      if (visited[i] || densiities[i] < minPts)//该点已经被访问,或点的密度小于minpts,
       {
            continue; 
       }
            
       //visited[i]为true且densities[i]>=minPts
       pcl::PointCloud<pcl::PointXYZ> cluster;//用于储存同一族的点,点云对象
       std::vector<int>singlegapindices;
       std::queue<int>q;//queue先进先出的数据结构
       //使用一个队列q保存当前聚类的点,初始时加入起始点i
       q.push(i);
       visited[i]=true;//标记起始点已经被访问

       //基于广度优先搜索算法对每个聚类进行拓展,BFS从起始点开始逐层搜索
       while (!q.empty())
       {
             //循环内部,每次取出队列中一个idx,并将其加入到当前聚类中
             //然后查找其半径范围内的邻居点,并将未访问过且密度大于等于minpt的点放入队列中
             int idx =q.front();//front返回queue中的第一元素的引用
             q.pop();//删除q中第一个元素用于退出循环
             cluster.push_back((*cloud)[idx]);
             singlegapindices.push_back(idx);
             std::vector<int> n_indices;
             std::vector<float> n_dists;
             treedense->radiusSearch(idx, radius, n_indices, n_dists);
             for (size_t j = 0; j < n_indices.size(); j++) 
             {
                   int neighbor_idx = n_indices[j];
                   if (!visited[neighbor_idx] && densiities[neighbor_idx] >= minPts) //没有被访问过且点的密度大于minpt
                   {
                           q.push(neighbor_idx);//加入到队列中
                           visited[neighbor_idx] = true;//标记已访问
                   }
             }
         }
		 clusters.push_back(cluster);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

2.4代码

总的实现代码。

void SegByLocalDensity(const pcl::PointCloud<pcl::PointXYZ>::Ptr &cloud,
    					std::vector<pcl::PointCloud<pcl::PointXYZ>>& clusters,
						std::vector<std::vector<int>>& gappointindices)
{
    	//首先初始化搜索领域半径radius和最小点minPts
		float radius=0.8;
        int minPts=90;
        pcl::search::KdTree<pcl::PointXYZ>::Ptr treedense (new pcl::search::KdTree<pcl::PointXYZ>);
        treedense->setInputCloud(cloud);
        //初始化向量,储存局部密度
        std::vector<int> nn_indices;//储存点云中每个点的最邻索引
        std::vector<float> nn_dists; //储存每个点的最邻距离
        std::vector<float> densiities(cloud->size(),0);//储存每个点的局部密度,并初始化为零
                            
    
        for (size_t i = 0; i < cloud->size(); i++)
        {
                treedense->radiusSearch(i,radius,nn_indices,nn_dists);//1.要查找最邻的点的索引,2.指定半径,3.储存最邻点索引的向量
                densiities[i]=nn_indices.size();//赋值给densities
        }
        
        //基于广度优先搜索
        std::vector<bool>visited(cloud->size(),false);//用于记录每个点是否被访问过,初始化为每个点都没有访问过
        for (size_t i = 0; i < cloud->size(); i++)
        {
           if (visited[i] || densiities[i]< minPts)//||或逻辑,只有当该点已经被访问,且点的密度大于minpts,才执行下面的处理
           {
                     continue; 
           }
            
            //visited[i]为true且densities[i]>=minPts
            pcl::PointCloud<pcl::PointXYZ> cluster;//用于储存同一族的点,点云对象
            std::vector<int>singlegapindices;
            std::queue<int>q;//queue先进先出的数据结构
            //使用一个队列q保存当前聚类的点,初始时加入起始点i
            q.push(i);
            visited[i]=true;//标记起始点已经被访问

            //基于广度优先搜索算法对每个聚类进行拓展,BFS从起始点开始逐层搜索
            while (!q.empty())//如果q中没有元素则返回true
            {
                    //循环内部,每次取出队列中一个idx,并将其加入到当前聚类中
                    //然后查找其半径范围内的邻居点,并将未访问过且密度大于等于minpt的点放入队列中
                    int idx =q.front();//front返回queue中的第一元素的引用
                    q.pop();//删除q中第一个元素-----------------用于退出循环
                    cluster.push_back((*cloud)[idx]);
                    singlegapindices.push_back(idx);
                    std::vector<int> n_indices;
                    std::vector<float> n_dists;
                    treedense->radiusSearch(idx, radius, n_indices, n_dists);
                    for (size_t j = 0; j < n_indices.size(); j++) 
                    {
                            int neighbor_idx = n_indices[j];
                            if (!visited[neighbor_idx] && densiities[neighbor_idx] >= minPts) //没有被访问过且点的密度大于minpt
                            {
                                    q.push(neighbor_idx);//加入到队列中
                                    visited[neighbor_idx] = true;//标记已访问
                            }
                    }
            }

            clusters.push_back(cluster);
            gappointindices.emplace_back(singlegapindices);
         
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

2.5原始图像

在这里插入图片描述

2.6处理后的图像

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/122660
推荐阅读
相关标签
  

闽ICP备14008679号