谁是我邻居--kdTree&OcTree

由于分割工作需要对点云的邻近点进行操作,不断对比和访问某个点的邻居,所以决定点云的相邻关系是非常重要的。对于Scan来说,邻居关系是天然的。但对于很多杂乱点云,或者滤波,分割后的点云来说,邻居关系就已经被破坏了。确定一个点云之间的相邻关系可以通过“树”来完成,目前比较主流的方法包括:kdTree和OcTree,这两种方法各有特点。

1.1.kdTree---一种递归的邻近搜索策略

  关于kdTree到底是怎么工作的https://en.wikipedia.org/wiki/K-d_tree这里有非常详细的说明,我不再赘述。但是kdTree实际上包括两个部分:1.建立kdTree,2.在kdTree中查找。建立kdTree实际上是一个不断划分的过程,首先选择最sparse的维度,然后找到该维度上的中间点,垂直该维度做第一次划分。此时k维超平面被一分为二,在两个子平面中再找最sparse的维度,依次类推知道最后一个点也被划分。那么就形了一个不断二分的树。如图所示。

  

  显然,一般情况下一个点的邻近点只需要在其父节点和子节点中搜索即可,大大缩小了邻近点的搜索规模。并且kdtree可以有效的对插入点进行判断其最近点在哪个位置。对于低层次视觉来说kdTree算法是非常重要的。在很多情况下需要给出某个点,再查k临近点的编号,或者差某半径范围内的点。PCL已经实现了kdtree算法,其调用接口如下:

复制代码
  #include 
  #include 



   //创建kdtree 结构
  pcl::KdTreeFLANN kdtree;
  //传入点云
  kdtree.setInputCloud (cloud);
  //设置输入点
  pcl::PointXYZ searchPoint;
   //k邻近搜索
   int K = 10;
   //设置两个容器,第一个放点的标号,第二个点到SearchPoint的距离
   std::vector<int> pointIdxNKNSearch(K);
   std::vector<float> pointNKNSquaredDistance(K);
   //进行搜索,注意,此函数有返回值>0为找到,<0则没找到
   kdtree.nearestKSearch (searchPoint, K, pointIdxNKNSearch, pointNKNSquaredDistance)
    


   //    基于距离的搜索    //
  //两个未知大小的容器,作用同上
  std::vector<int> pointIdxRadiusSearch;
  std::vector<float> pointRadiusSquaredDistance;
  // 搜索半径
  float radius = 3;
  //搜索,效果同上
  kdtree.radiusSearch (searchPoint, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance)
复制代码

   显然,我们还需要一个算法把Idx里的点云数据提取出来进行重新着色之类的工作,代码可以写作:

  

复制代码
    pcl::PointCloud::Ptr Npoints(new pcl::PointCloud);
    Npoints->height=1;
    Npoints->width=searchindice.size();
    Npoints->resize (searchindice.size());
    //注意此清空操作非常极其以及特别重要,否则Npoints中会有莫名奇妙的点。
    Npoints->clear();
    int PointNUM = 0;
    for(int i=0;ii)
    {   
        PointNUM = searchindice[i];
        Npoints->push_back(cloud->points[PointNUM]);
    //    cout<points[PointNUM].x<<"  "<points[PointNUM].y<<"  "<points[PointNUM].z<<"  "<
    }

    pcl::visualization::PointCloudColorHandlerCustom Npoints_color_handler (Npoints, 0, 255, 0);
    viewer->addPointCloud(Npoints,Npoints_color_handler,"Npoints");
    viewer->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "Npoints");
复制代码

 

1.2 OcTree

  OcTree是一种更容易理解也更自然的思想。对于一个空间,如果某个角落里有个盒子我们却不知道在哪儿。但是"神"可以告诉我们这个盒子在或者不在某范围内,显而易见的方法就是把空间化成8个卦限,然后询问在哪个卦限内。再将存在的卦限继续化成8个。意思大概就是太极生两仪,两仪生四象,四象生八卦,就这么一直划分下去,最后一定会确定一个非常小的空间。对于点云而言,只要将点云的立方体凸包用octree生成很多很多小的卦限,那么在相邻卦限里的点则为相邻点。

 

  显然,对于不同点云应该采取不同的搜索策略,如果点云是疏散的,分布很广泛,且每什么规律(如lidar测得的点云或双目视觉捕捉的点云)kdTree能更好的划分,而octree则很难决定最小立方体应该是多少。太大则一个立方体里可能有很多点云,太小则可能立方体之间连不起来。如果点云分布非常规整,是某个特定物体的点云模型,则应该使用ocTree,因为很容易求解凸包并且点与点之间相对距离无需再次比对父节点和子节点,更加明晰。典型的例子是斯坦福的兔子。

你可能感兴趣的:(PCL)