PCL学习---octree

1.Octree相关原理

2.PCL官方demo

本次运行了官方的基于八叉树的空间划分及搜索操作的demo
代码如下:

#include <pcl/point_cloud.h>
#include <pcl/octree/octree.h>
#include <iostream>
#include <vector>
#include <ctime>

int
main (int argc, char**argv)
{
    
    
srand ((unsigned int) time (NULL));
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// 随机生成1000个无序的点
cloud->width =1000;
cloud->height =1;
cloud->points.resize (cloud->width * cloud->height);
for (size_t i=0; i< cloud->points.size (); ++i)
  {
    
    
cloud->points[i].x =1024.0f* rand () / (RAND_MAX +1.0f);
cloud->points[i].y =1024.0f* rand () / (RAND_MAX +1.0f);
cloud->points[i].z =1024.0f* rand () / (RAND_MAX +1.0f);
  }
float resolution =128.0f;
pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> octree(resolution);
octree.setInputCloud (cloud);
octree.addPointsFromInputCloud ();
pcl::PointXYZ searchPoint;
searchPoint.x=1024.0f* rand () / (RAND_MAX +1.0f);
searchPoint.y=1024.0f* rand () / (RAND_MAX +1.0f);
searchPoint.z=1024.0f* rand () / (RAND_MAX +1.0f);
//Voxel Search
std::vector<int>pointIdxVec;
if (octree.voxelSearch (searchPoint, pointIdxVec))
  {
    
    
std::cout<<"Neighbors within voxel search at ("<<searchPoint.x
<<" "<<searchPoint.y
<<" "<<searchPoint.z<<")"
<<std::endl;
for (size_t i=0; i<pointIdxVec.size (); ++i)
std::cout<<"    "<< cloud->points[pointIdxVec[i]].x 
<<" "<< cloud->points[pointIdxVec[i]].y 
<<" "<< cloud->points[pointIdxVec[i]].z <<std::endl;
  }
//KNN Search
int K =10;
std::vector<int>pointIdxNKNSearch;
std::vector<float>pointNKNSquaredDistance;
std::cout<<"K nearest neighbor search at ("<<searchPoint.x
<<" "<<searchPoint.y
<<" "<<searchPoint.z
<<") with K="<< K <<std::endl;
if (octree.nearestKSearch (searchPoint, K, pointIdxNKNSearch, pointNKNSquaredDistance) >0)
  {
    
    
for (size_t i=0; i<pointIdxNKNSearch.size (); ++i)
std::cout<<"    "<<   cloud->points[ pointIdxNKNSearch[i] ].x 
<<" "<< cloud->points[pointIdxNKNSearch[i] ].y 
<<" "<< cloud->points[pointIdxNKNSearch[i] ].z 
<<" (squared distance: "<<pointNKNSquaredDistance[i] <<")"<<std::endl;
  }
//radiuSearch
std::vector<int>pointIdxRadiusSearch;
std::vector<float>pointRadiusSquaredDistance;
float radius =256.0f* rand () / (RAND_MAX +1.0f);
std::cout<<"Neighbors within radius search at ("<<searchPoint.x
<<" "<<searchPoint.y
<<" "<<searchPoint.z
<<") with radius="<< radius <<std::endl;
if (octree.radiusSearch (searchPoint, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance) >0)
  {
    
    
for (size_t i=0; i<pointIdxRadiusSearch.size (); ++i)
std::cout<<"    "<<   cloud->points[ pointIdxRadiusSearch[i] ].x 
<<" "<< cloud->points[pointIdxRadiusSearch[i] ].y 
<<" "<< cloud->points[pointIdxRadiusSearch[i] ].z 
<<" (squared distance: "<<pointRadiusSquaredDistance[i] <<")"<<std::endl;
  }
}

camkelist.txt的内容如下:

cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(octree_search)
find_package(PCL 1.2 REQUIRED)
include_directories(${
    
    PCL_INCLUDE_DIRS})
link_directories(${
    
    PCL_LIBRARY_DIRS})
add_definitions(${
    
    PCL_DEFINITIONS})
add_executable(octree_search octree_search.cpp)
target_link_libraries(octree_search ${
    
    PCL_LIBRARIES})

运行结果如下:
在这里插入图片描述

3.源码分析

对于上述官方的demo中提到的函数,我找了这些函数的源码,以共大家更好的理解算法。

 /** \brief Provide a pointer to the input data set.
    * \param[in] cloud_arg the const boost shared pointer to a PointCloud message
    * \param[in] indices_arg the point indices subset that is to be used from \a cloud -
    * if 0 the whole point cloud is used
    */
   inline void
   setInputCloud(const PointCloudConstPtr& cloud_arg,
                 const IndicesConstPtr& indices_arg = IndicesConstPtr())
   {
    
    
     input_ = cloud_arg;
     indices_ = indices_arg;
   }
template <typename PointT,
           typename LeafContainerT,
           typename BranchContainerT,
           typename OctreeT>
 void
 pcl::octree::OctreePointCloud<PointT, LeafContainerT, BranchContainerT, OctreeT>::
     addPointsFromInputCloud()
 {
    
    
   if (indices_) {
    
         //有子集的情况下
     for (const int& index : *indices_) {
    
    
       assert((index >= 0) && (index < static_cast<int>(input_->size())));
  
       if (isFinite((*input_)[index])) {
    
    
         // add points to octree
         this->addPointIdx(index);
       }
     }
   }
   else {
    
      //没有子集,加载全部点云
     for (std::size_t i = 0; i < input_->size(); i++) {
    
    
       if (isFinite((*input_)[i])) {
    
    
         // add points to octree
         this->addPointIdx(static_cast<unsigned int>(i));
       }
     }
   }
 }
//voxelSearch() :Search for neighbors within a voxel at given point referenced by a point index.
template <typename PointT, typename LeafContainerT, typename BranchContainerT>
 bool
 OctreePointCloudSearch<PointT, LeafContainerT, BranchContainerT>::voxelSearch(
     const PointT& point, std::vector<int>& point_idx_data)
 {
    
    
   assert(isFinite(point) &&
          "Invalid (NaN, Inf) point coordinates given to nearestKSearch!");
   OctreeKey key;
   bool b_success = false;
  
   // generate key
   this->genOctreeKeyforPoint(point, key);  //找到point在八叉树中间的下x,y,z索引号值
  
   LeafContainerT* leaf = this->findLeaf(key);
  
   if (leaf) {
    
    
     (*leaf).getPointIndices(point_idx_data);  //返回该叶结点所有子节点的索引号
     b_success = true;
   }
  
   return (b_success);
 }

//genOctreeKeyforPoint():Generate octree key for voxel at a given point.
template <typename PointT,
           typename LeafContainerT,
           typename BranchContainerT,
           typename OctreeT>
 void
 pcl::octree::OctreePointCloud<PointT, LeafContainerT, BranchContainerT, OctreeT>::
     genOctreeKeyforPoint(const PointT& point_arg, OctreeKey& key_arg) const
 {
    
    
   // calculate integer key for point coordinates
   key_arg.x =
       static_cast<unsigned int>((point_arg.x - this->min_x_) / this->resolution_);
   key_arg.y =
       static_cast<unsigned int>((point_arg.y - this->min_y_) / this->resolution_);
   key_arg.z =
       static_cast<unsigned int>((point_arg.z - this->min_z_) / this->resolution_);
  
   assert(key_arg.x <= this->max_key_.x);
   assert(key_arg.y <= this->max_key_.y);
   assert(key_arg.z <= this->max_key_.z);
 }
 
//findLeaf(): 
  /** \brief Find leaf node
    *  \param key_arg: octree key addressing a leaf node.
    *  \return pointer to leaf node. If leaf node is not found, this pointer returns 0.
    */
   LeafContainerT*
   findLeaf(const OctreeKey& key_arg) const
   {
    
    
     LeafContainerT* result = nullptr;
     findLeafRecursive(key_arg, depth_mask_, root_node_, result);
     return result;
   }

//getPointIndices()
 /** \brief Retrieve point indices from container. This container stores only a single
    * point index
    * \param[out] data_vector_arg vector of point indices to be stored within
    * data vector
    */
   void
   getPointIndices(std::vector<int>& data_vector_arg) const
   {
    
    
     if (data_ >= 0)
       data_vector_arg.push_back(data_);
   }
//nearestKSearch()
/*
Search for k-nearest neighbors at given query point.

Parameters
[in]	p_q	the given query point
[in]	k	the number of neighbors to search for
[out]	k_indices	the resultant indices of the neighboring points (must be resized to k a priori!)
[out]	k_sqr_distances	the resultant squared distances to the neighboring points (must be resized to k a priori!)
Return numbers of neighbors found
*/
template <typename PointT, typename LeafContainerT, typename BranchContainerT>
 int
 OctreePointCloudSearch<PointT, LeafContainerT, BranchContainerT>::nearestKSearch(
     const PointT& p_q,
     int k,
     std::vector<int>& k_indices,
     std::vector<float>& k_sqr_distances)
 {
    
    
   assert(this->leaf_count_ > 0);
   assert(isFinite(p_q) &&
          "Invalid (NaN, Inf) point coordinates given to nearestKSearch!");
  
   k_indices.clear();
   k_sqr_distances.clear();
  
   if (k < 1)
     return 0;
  
   prioPointQueueEntry point_entry;
   std::vector<prioPointQueueEntry> point_candidates;
  
   OctreeKey key;
   key.x = key.y = key.z = 0;
  
   // initialize smallest point distance in search with high value
   double smallest_dist = std::numeric_limits<double>::max();
  
   getKNearestNeighborRecursive(
       p_q, k, this->root_node_, key, 1, smallest_dist, point_candidates);
  
   unsigned int result_count = static_cast<unsigned int>(point_candidates.size());
  
   k_indices.resize(result_count);
   k_sqr_distances.resize(result_count);
  
   for (unsigned int i = 0; i < result_count; ++i) {
    
    
     k_indices[i] = point_candidates[i].point_idx_;
     k_sqr_distances[i] = point_candidates[i].point_distance_;
   }
  
   return static_cast<int>(k_indices.size());
 }

//getKNearestNeighborRecursive()
/*
Recursive search method that explores the octree and finds the K nearest neighbors.
Parameters
[in]	point	query point
[in]	K	amount of nearest neighbors to be found
[in]	node	current octree node to be explored
[in]	key	octree key addressing a leaf node.
[in]	tree_depth	current depth/level in the octree
[in]	squared_search_radius	squared search radius distance
[out]	point_candidates	priority queue of nearest neigbor point candidates
Returns squared search radius based on current point candidate set found */

 template <typename PointT, typename LeafContainerT, typename BranchContainerT>
 double
 OctreePointCloudSearch<PointT, LeafContainerT, BranchContainerT>::
     getKNearestNeighborRecursive(
         const PointT& point,
         unsigned int K,
         const BranchNode* node,
         const OctreeKey& key,
         unsigned int tree_depth,
         const double squared_search_radius,
         std::vector<prioPointQueueEntry>& point_candidates) const
 {
    
    
   std::vector<prioBranchQueueEntry> search_heap;
   search_heap.resize(8);
  
   OctreeKey new_key;
  
   double smallest_squared_dist = squared_search_radius;
  
   // get spatial voxel information
   double voxelSquaredDiameter = this->getVoxelSquaredDiameter(tree_depth);
  
   // iterate over all children
   for (unsigned char child_idx = 0; child_idx < 8; child_idx++) {
    
    
     if (this->branchHasChild(*node, child_idx)) {
    
    
       PointT voxel_center;
  
       search_heap[child_idx].key.x = (key.x << 1) + (!!(child_idx & (1 << 2)));
       search_heap[child_idx].key.y = (key.y << 1) + (!!(child_idx & (1 << 1)));
       search_heap[child_idx].key.z = (key.z << 1) + (!!(child_idx & (1 << 0)));
  
       // generate voxel center point for voxel at key
       this->genVoxelCenterFromOctreeKey(
           search_heap[child_idx].key, tree_depth, voxel_center);
  
       // generate new priority queue element
       search_heap[child_idx].node = this->getBranchChildPtr(*node, child_idx);
       search_heap[child_idx].point_distance = pointSquaredDist(voxel_center, point);
     }
     else {
    
    
       search_heap[child_idx].point_distance = std::numeric_limits<float>::infinity();
     }
   }
  
   std::sort(search_heap.begin(), search_heap.end());
  
   // iterate over all children in priority queue
   // check if the distance to search candidate is smaller than the best point distance
   // (smallest_squared_dist)
   while ((!search_heap.empty()) &&
          (search_heap.back().point_distance <
           smallest_squared_dist + voxelSquaredDiameter / 4.0 +
               sqrt(smallest_squared_dist * voxelSquaredDiameter) - this->epsilon_)) {
    
    
     const OctreeNode* child_node;
  
     // read from priority queue element
     child_node = search_heap.back().node;
     new_key = search_heap.back().key;
  
     if (tree_depth < this->octree_depth_) {
    
    
       // we have not reached maximum tree depth
       smallest_squared_dist =
           getKNearestNeighborRecursive(point,
                                        K,
                                        static_cast<const BranchNode*>(child_node),
                                        new_key,
                                        tree_depth + 1,
                                        smallest_squared_dist,
                                        point_candidates);
     }
     else {
    
    
       // we reached leaf node level
       std::vector<int> decoded_point_vector;
  
       const LeafNode* child_leaf = static_cast<const LeafNode*>(child_node);
  
       // decode leaf node into decoded_point_vector
       (*child_leaf)->getPointIndices(decoded_point_vector);
  
       // Linearly iterate over all decoded (unsorted) points
       for (const int& point_index : decoded_point_vector) {
    
    
  
         const PointT& candidate_point = this->getPointByIndex(point_index);
  
         // calculate point distance to search point
         float squared_dist = pointSquaredDist(candidate_point, point);
  
         // check if a closer match is found
         if (squared_dist < smallest_squared_dist) {
    
    
           prioPointQueueEntry point_entry;
  
           point_entry.point_distance_ = squared_dist;
           point_entry.point_idx_ = point_index;
           point_candidates.push_back(point_entry);
         }
       }
  
       std::sort(point_candidates.begin(), point_candidates.end());
  
       if (point_candidates.size() > K)
         point_candidates.resize(K);
  
       if (point_candidates.size() == K)
         smallest_squared_dist = point_candidates.back().point_distance_;
     }
     // pop element from priority queue
     search_heap.pop_back();
   }
  
   return (smallest_squared_dist);
 }

//radiusSearch()
/*
Search for all neighbors of query point that are within a given radius.
Parameters
[in]	p_q	the given query point
[in]	radius	the radius of the sphere bounding all of p_q's neighbors
[out]	k_indices	the resultant indices of the neighboring points
[out]	k_sqr_distances	the resultant squared distances to the neighboring points
[in]	max_nn	if given, bounds the maximum returned neighbors to this value
Returns
number of neighbors found in radius */

template <typename PointT, typename LeafContainerT, typename BranchContainerT>
 int
 OctreePointCloudSearch<PointT, LeafContainerT, BranchContainerT>::radiusSearch(
     const PointT& p_q,
     const double radius,
     std::vector<int>& k_indices,
     std::vector<float>& k_sqr_distances,
     unsigned int max_nn) const
 {
    
    
   assert(isFinite(p_q) &&
          "Invalid (NaN, Inf) point coordinates given to nearestKSearch!");
   OctreeKey key;
   key.x = key.y = key.z = 0;
  
   k_indices.clear();
   k_sqr_distances.clear();
  
   getNeighborsWithinRadiusRecursive(p_q,
                                     radius * radius,
                                     this->root_node_,
                                     key,
                                     1,
                                     k_indices,
                                     k_sqr_distances,
                                     max_nn);
  
   return (static_cast<int>(k_indices.size()));
 }

//getNeighborsWithinRadiusRecursive()
/*
Recursive search method that explores the octree and finds neighbors within a given radius.

Parameters
[in]	point	query point
[in]	radiusSquared	squared search radius
[in]	node	current octree node to be explored
[in]	key	octree key addressing a leaf node.
[in]	tree_depth	current depth/level in the octree
[out]	k_indices	vector of indices found to be neighbors of query point
[out]	k_sqr_distances	squared distances of neighbors to query point
[in]	max_nn	maximum of neighbors to be found
*/

template <typename PointT, typename LeafContainerT, typename BranchContainerT>
 void
 OctreePointCloudSearch<PointT, LeafContainerT, BranchContainerT>::
     getNeighborsWithinRadiusRecursive(const PointT& point,
                                       const double radiusSquared,
                                       const BranchNode* node,
                                       const OctreeKey& key,
                                       unsigned int tree_depth,
                                       std::vector<int>& k_indices,
                                       std::vector<float>& k_sqr_distances,
                                       unsigned int max_nn) const
 {
    
    
   // get spatial voxel information
   double voxel_squared_diameter = this->getVoxelSquaredDiameter(tree_depth);
  
   // iterate over all children
   for (unsigned char child_idx = 0; child_idx < 8; child_idx++) {
    
    
     if (!this->branchHasChild(*node, child_idx))
       continue;
  
     const OctreeNode* child_node;
     child_node = this->getBranchChildPtr(*node, child_idx);
  
     OctreeKey new_key;
     PointT voxel_center;
     float squared_dist;
  
     // generate new key for current branch voxel
     new_key.x = (key.x << 1) + (!!(child_idx & (1 << 2)));
     new_key.y = (key.y << 1) + (!!(child_idx & (1 << 1)));
     new_key.z = (key.z << 1) + (!!(child_idx & (1 << 0)));
  
     // generate voxel center point for voxel at key
     this->genVoxelCenterFromOctreeKey(new_key, tree_depth, voxel_center);
  
     // calculate distance to search point
     squared_dist = pointSquaredDist(static_cast<const PointT&>(voxel_center), point);
  
     // if distance is smaller than search radius
     if (squared_dist + this->epsilon_ <=
         voxel_squared_diameter / 4.0 + radiusSquared +
             sqrt(voxel_squared_diameter * radiusSquared)) {
    
    
  
       if (tree_depth < this->octree_depth_) {
    
    
         // we have not reached maximum tree depth
         getNeighborsWithinRadiusRecursive(point,
                                           radiusSquared,
                                           static_cast<const BranchNode*>(child_node),
                                           new_key,
                                           tree_depth + 1,
                                           k_indices,
                                           k_sqr_distances,
                                           max_nn);
         if (max_nn != 0 && k_indices.size() == static_cast<unsigned int>(max_nn))
           return;
       }
       else {
    
    
         // we reached leaf node level
         const LeafNode* child_leaf = static_cast<const LeafNode*>(child_node);
         std::vector<int> decoded_point_vector;
  
         // decode leaf node into decoded_point_vector
         (*child_leaf)->getPointIndices(decoded_point_vector);
  
         // Linearly iterate over all decoded (unsorted) points
         for (const int& index : decoded_point_vector) {
    
    
           const PointT& candidate_point = this->getPointByIndex(index);
  
           // calculate point distance to search point
           squared_dist = pointSquaredDist(candidate_point, point);
  
           // check if a match is found
           if (squared_dist > radiusSquared)
             continue;
  
           // add point to result vector
           k_indices.push_back(index);
           k_sqr_distances.push_back(squared_dist);
  
           if (max_nn != 0 && k_indices.size() == static_cast<unsigned int>(max_nn))
             return;
         }
       }
     }
   }
 }

猜你喜欢

转载自blog.csdn.net/weixin_37617732/article/details/111300975