1. 程式人生 > >通過八叉樹進行空間分割和搜尋

通過八叉樹進行空間分割和搜尋

轉自:https://blog.csdn.net/qq_25491201/article/details/51146085

一個octree是一個以樹基礎為的管理稀疏3-D資料的資料結構。每個中間的節點有8個子節點。在這次,我們將學習怎麼使用octree進行稀疏分割和近鄰搜尋。尤其,我們將解釋如何操作"體元近鄰搜尋",和"最近鄰搜尋"和"半徑近鄰搜尋".
我們將建立一個octree_search.cpp這個檔案
#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>);

  // Generate pointcloud data
  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);

  // Neighbors within 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;
  }

  // K nearest neighbor 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;
  }

  // Neighbors within radius search

  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;
  }

}
程式碼解釋
定義和例項化了一個PointCloud這個資料結構,並生成隨機點雲。
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);

  // Generate pointcloud data
  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);
  }
接下去我們創造了一個用下面的這個解析度為初始化的octree例項。octree保持了它的葉子節點的點下標。解析度引數描述了小體元的長度。octree的深度因此是一個解析度的函式和點雲的空間維度一樣。如果一個點雲的邊框盒子已知,它通過使用defineBoundingBox這個方法分配給octree。然後我們把一個指標分配給點雲並把所有的點加入到octree裡面。

  float resolution = 128.0f;

  pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> octree (resolution);

  octree.setInputCloud (cloud);
  octree.addPointsFromInputCloud ();

一旦點雲與octree相聯絡上,我們就能使用搜索的操作。我們第一個搜尋方法"Neighbors within Voxel Search"。它將被分配給相應葉子節點的體元的搜尋點並返回一個點下標的向量。這個下標和進入同一個體元裡面的點有關係。搜尋點與搜尋結果距離依賴於octree的解析度。


  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;
  }

接下去,顯示了一個K最近鄰搜尋。在這個例子裡面,K被設定為10,"K最近鄰搜尋"的方法的搜尋結果被寫入兩個獨立的向量裡面。第一個,pointIdxNKNSearch,將會包含搜尋結果(與相鄰點雲資料集相關的下標)。第二個下標向量保持相應的搜尋得到節點和最近鄰之間的平方距離。


  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;
  }

指定半徑內的鄰居搜尋和"最近鄰搜尋"相似。它的搜尋結果被寫成2個獨立的向量,描述了下標點和搜尋點的平方距離。

 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;
  }
下面是執行結果
Neighbors within voxel search at (974.82 188.793 138.779)
    903.656 82.8158 162.392
    1007.34 191.035 61.7727
    896.88 155.711 58.1942
K nearest neighbor search at (974.82 188.793 138.779) with K=10
    903.656 82.8158 162.392 (squared distance: 16853.1)
    903.18 247.058 54.3528 (squared distance: 15655)
    861.595 149.96 135.199 (squared distance: 14340.7)
    896.88 155.711 58.1942 (squared distance: 13663)
    995.889 116.224 219.077 (squared distance: 12157.9)
    885.852 238.41 160.966 (squared distance: 10869.5)
    900.807 220.317 77.1432 (squared distance: 10270.7)
    1002.46 117.236 184.594 (squared distance: 7983.59)
    1007.34 191.035 61.7727 (squared distance: 6992.54)
    930.13 223.335 174.763 (squared distance: 4485.15)
Neighbors within radius search at (974.82 188.793 138.779) with radius=109.783
    1007.34 191.035 61.7727 (squared distance: 6992.54)
    900.807 220.317 77.1432 (squared distance: 10270.7)
    885.852 238.41 160.966 (squared distance: 10869.5)
    1002.46 117.236 184.594 (squared distance: 7983.59)
    930.13 223.335 174.763 (squared distance: 4485.15)
一些額外的細節
PCL八叉樹元件裡面提供了幾種八叉樹的型別。他們通過他們的獨特的葉子節點特徵來區別。
OctreePointCloudPointVector(等於OctreePointCloud):這個octree可以包含每個葉子節點的一系列的點的下標。
OctreePointCloudSinglePoint:這個八叉樹類在每一個葉子節點裡麵包含一個點的下標。只有被分配到葉子節點裡面最多的下標將會被儲存。
OctreePointCloudOccupancy:這個八叉樹沒有儲存任何點的資訊在它的葉子節點上。
OctreePointCloudDensity:這個octree計算每個葉子節點體元的數量。它允許特殊的空間密度查詢。

如果八叉樹需要經常被建立,請看一下八叉樹的double buffering implementation(Octree2BufBase這個類)。這個類同時保持兩個並行的八叉樹結構在記憶體裡面。除了每個搜尋操作,它也使得空間改變檢測成為可能。更多,一個先進的記憶體管理減少了記憶體分配和回收操作在octree構建的時候。
總結:
PCL裡面的octree是一個進行空間分割和搜尋的有力的工具。

--------------------- 作者:Spongelady 來源:CSDN 原文:https://blog.csdn.net/qq_25491201/article/details/51146085?utm_source=copy 版權宣告:本文為博主原創文章,轉載請附上博文連結!