PCL中outofcore模塊---基於核外八叉樹的大規模點雲的顯示


寫在前面
 
最近公眾號的活動讓更多的人加入交流群,嘗試提問更多的我問題,群主也在積極的招募更多的小伙伴與我一起分享,能夠相互促進。
 
這里總結群友經常問,經常提的兩個問題,並給出我的回答:
(1)啥時候能出教程,能夠講解PCL中的各種功能?
(2)如何解決大規模點雲的問題呢?
 
 
以下給出正式的解答以及計划安排
問題1:對於出PCL的教程,其實網上資料已經有很多,但是沒有十分系統的資料,對於該問題,我也在想該如何去做,本人將會在后期的計划中慢慢推出系統的學習教程,實現理論與代碼並行的PCL教程,並且正在與深度相機生產商商談合作,出教程售賣深度相機一並推出。(整個過程由於時間有限,努力籌划中)。那么有想參與進來的可以與群主私聊,參與資料整理,ppt制作等。
 
問題2:大規模點雲的問題,這個問題我已經和無數的人說過查看PCL庫中outofcore模塊,該模塊就是為了實現大規模點雲的載入與顯示,渲染等問題,但是總是有很多人在問,沒人去研究查看,更沒有人願意分享,太多人更願意做一個伸手就能得到事情。這里我也查閱了很多資料,百度上確實沒有很多的資料,但是還是能夠解釋一些基本的概念問題。這里將出一個全網唯一的關於使用點雲實現大規模點雲的載入與顯示的教程, 請各位在轉載的的時候注明出處。(因為網上有太多未經過允許轉載being_young文章的博客)
 
這里主要針對PCL庫中outofcore查詢外網文獻以及模塊的相關資料寫出以下內容,再次說明,文章出自“點雲PCL”,並且在博客園發出,未經允許,請勿轉載轉載。
 
 
什么是outofcore
outofcore,可以理解為使用內存映射的方法,來處理大規模點雲無法載入到內存的問題,並且這里我暫且將其翻譯為“核外八叉樹”,因為根據PCL中的實現方法,就是以八叉樹的方法實現了內存映射的算法。

在PCL中基於外存(out of core)的數據處理方法,借助於八叉樹理論在完成大規模點雲的前提處理,並使用一種八叉樹領域的搜索方法構建出散亂數據的拓撲結構。在可視化與計算機圖形學領域,基於外核的算法是涉及用來處理大數據量模型運行在有限內存中的方法,簡單來說,通過限制訪問到一個小的,處於高速緩存中的模型的字塊實現的。

首先我們看一下PCL 的Outofcore的模塊介紹,該模塊介紹是就是通過內存映射的方法以及八叉樹的數據結構實現大規模點雲的存儲,數據位於某些輔助存儲介質上基於目錄的八叉樹層次結構中,並且PCL——outofcore提供了構造和遍歷outofcore八叉樹的框架,其他的輔助函數在后面將會具體講解。

PCL中的outofcore模塊是由Urban Robotic整合起來,並且在PCL中實現了相關的例程,本文是在查閱了大量的相關資料的基礎上總結而成,其中難免會有一些理解錯誤,
該模塊翻譯成中文可以翻譯為核外八叉樹,主要針對點雲的處理,同時也可以擴展成為網格或者體素的表示形式,outofcore支持空間索引數據存儲和快速的數據訪問,並且只有部分數據從硬盤加載到運行的內存中,以便能夠快速的可視化。

所以該框架能夠滿足一下幾種條件:
(1)大數據,框架能夠處理大量的點雲或者大空間的數據
(2)支持非均勻數據,采集的點雲從分布,密度以及精度上都是變化的,
(3)支持數據查詢,能夠有效的搜索數據,查找數據等操作
(4)數據更新,在大量數據集中能夠實現數據的添加和刪除等操作,比如濾波操作,
(5)能夠保存數據質量,避免了簡化重采樣或者有損壓縮。


Out-of-core octree(核外八叉樹)其實就是運行內存不足以載入大量的數據情況下,采用內存映射的方法,並且將數據存儲為八叉樹的形式保存在硬盤上。

為了滿足數據查詢的要求,這里采用了八叉樹存儲結構【以前的文章介紹過八叉樹】八叉樹是基於空間驅動的分區方法,如果數據分布嚴重的不均勻,這種方法可能會有嚴重不平衡的缺點,在這種情況下提出了使用KDtree,需要較少的內存用於樹結構並且能夠快速的實現數據的訪問,但是一般pcl中的實現是主要使用了八叉樹只希望該模塊能夠支持快速的數據更新,並且八叉樹是非常適合的實現核外實現的算法,因為每個級別的分區都是相同的,因此不需要讀取額外的信息。

一般來說這種方法很少有開源的方案供大家使用,其中PCL中就是一個較好的實現了核外八叉樹模塊的算法,開源的模塊中只關注核外的八叉樹實現以及可視化的部分,並且樹的深度或者分辨率完全由用戶自行定義。

以上是PCL1.7版本以上outofcore模塊實現核外八叉樹的類,其中對cJSON的依賴關系作為pcl_outofcore的依賴項已經鏈接在一起,並且將函數已經封裝到兩個獨立的類OutofcoreOctreeNodeMetadata和OutofcoreOctreeMetadata

 
PCL中實現outofcore的文件概括介紹
 
outofcore模塊中實現核外八叉樹的四個主要的hpp文件
1.octree.hpp
2.  octree2.hpp
3.octree ram container.hpp
4.    octree disk container.hpp
頭文件:
(a) octree base.h
(b) octree base node.h
(c) octree abstract node container.h∗
(d) metadata.h∗: 實現metadata的抽象類
(e) outofcore node data.h∗:實現核外節點層級的數據文件的接口(tree.oct idx)
(f) outofcore base data.h∗:實現和外八叉樹高層級數據接口(tree.octree JSON file)
(g) OutofcoreDepthFirstIterator.h∗:核外八叉樹的深度優先迭代器,可以直接訪問外部節點的類
(h) OutofcoreIteratorBase.h∗: 基於pcl中octree模塊中迭代器的抽象迭代器的類
(i) octree disk container.h: 磁盤容器的IO
(j) octree ram container.h: 核外八叉樹的核心數據結構(不再需要了)
(k) outofcore node data.h: 包含主節點數據結構以及用於插入和查詢的遞歸方法
(l) boost.h: 包含pcl outofcore中所需的所有boost頭文件
(m) cJSON.h: 為了與PCL構建系統兼容,已進行了較小的修改
模板實現類的文件:
(a)octree base.hpp
(b) octree base node.hpp
(c) octree disk container.hpp
(d) octree ram container.hpp
(e) OutofcoreDepthFirstIterator.hpp
(f) OutofcoreIteratorBase.hpp
源文件
(a) cJSON.cpp
(b) outofcore node data.cpp
(c) outofcore base data.cpp
 
Outofcore節點的格式
 
在磁盤上每個內部節點和葉子節點最多可以包含兩個文件,
*.oct idx 以oct_idx為后綴的JSON數據文件,格式為:
{
”version”:3 ,
”bb_min”:[xxx,yyy,zzz],
”bb_max”:[xxx,yyy,zzz],
”bin”:”pathtodata.pcd ”
}

可以使用OutofcoreOctreeNodeMetadata類直接訪問此JSON數據。 此類將接口抽象到磁盤上的JSON數據,因此從理論上講,該格式可以輕松更改為XML,YAML或其他所需格式。

 
*.pcd pcd文件包含與該節點關聯的所有點雲的標准格式(v7 +)PCD文件。 該文件同樣也適用於所有葉子節點,但僅適用於內部(“分支”)節點(如果已構建LOD)。 通過OutofcoreOctreeDiskContainer類可以訪問此文件。
 
根節點包含了一個附件的文件
*.octree 其中包含有關八叉樹結構的高級信息。 格式為:
{
”name”:”test,
”version”:3,
”pointtype”:”urp”,        #(needs to be changed ∗)
”lod”:3,                  #depth of the tree
”numpts”:[X0,X1,X2, ... ,XD] , #total number of points at each LOD
”coordsystem”:”ECEF”           #the tree is not affected by this value
}

 

如果想使用PCL 中的outofcore模塊只需要添加
#include <pcl/outofcore/outofcore.h>
#include <pcl/outofcore/outofcore_impl.h>
具體實現后面將會更加具體例程的解釋
 

PCL中outofcore模塊實現了該算法具有哪些特點
點雲的插入
(1)addPointCloud
(2)addPointCloud and genLOD
使用addPointCloud的方法可以公開的訪問並且將點雲插入到外部的八叉樹中,NaN無效點將會被忽略,所有點將以全密度插入到葉子節點中,可以通過迭代的調用addPointClooud的方法將多個點雲插入到外部的八叉樹中,並且建議使用結構PointCloud2來表示點雲

點雲查詢
點雲的查詢使用:queryBoundingBox
該函數是為了outofcore構建的八叉樹為點雲查找提供的公共接口,該方法被重載,並且根據傳遞的參數,將返回位於指定深度的查詢邊界框內的所有點,或返回其並集將包含查詢邊界框內所有點的所有PCD文件列表。

深度級別(LOD level of Depth):多分辨率的核外八叉樹
構建LOD的方法: buildLOD, addPointCloud and genLOD
核外八叉樹的一個關鍵特性是所謂的“深度層次”簡稱LOD,按照習慣將八叉樹的根級成為0級,每一級都是i-1級別八倍采樣,(這里我理解為金字塔結構)深度級別是通過隨機下采樣每個級別的點數來構建,此百分比可以通過OutOfcoreCreeBase類中的setSamplePercent的方法來設置,這一參數也是可以在創建點雲的多分辨率表示的時候進行設置,可以達到快速的進行渲染的效果,渲染的過程中,可以基於某些查詢的方法,比如體素到視點的距離,訪問點雲所需要的分辨率,當然也可以通過所在層級以及深度,邊界框進行訪問

buildLOD:buildLOD使用多分辨率方法重新構建outofcore八叉樹的LOD。每個分支節點是其葉子的子采樣的並集。子采樣的百分比由setSamplePercent確定,默認為0.125^depth-maxdepth  LOD算法的細節請查看【5】

代碼實現以及注釋

實現從大規模點雲生成核外八叉樹的文件系統的代碼:

#include <pcl/common/time.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/PCLPointCloud2.h>   //建議在使用outofcore點雲的形式使用該形式的點雲頭文件
#include <pcl/io/pcd_io.h>
#include <pcl/pcl_macros.h>
#include <pcl/console/print.h>
#include <pcl/console/parse.h>

//添加outofcore的相關頭文件
#include <pcl/outofcore/outofcore.h>
#include <pcl/outofcore/outofcore_impl.h>

typedef pcl::PointXYZ PointT;

using namespace pcl;
using namespace pcl::outofcore;

using pcl::console::parse_argument;
using pcl::console::parse_file_extension_argument;
using pcl::console::find_switch;
using pcl::console::print_error;
using pcl::console::print_warn;
using pcl::console::print_info;

#include <boost/foreach.hpp>

typedef OutofcoreOctreeBase<> octree_disk;

const int OCTREE_DEPTH (0);
const int OCTREE_RESOLUTION (1);
/*
實現讀取點雲文件
*/
PCLPointCloud2::Ptr  getCloudFromFile (boost::filesystem::path pcd_path)
{
  PCLPointCloud2::Ptr cloud(new PCLPointCloud2);
  if (io::loadPCDFile (pcd_path.string (), *cloud) == -1)
  {
    PCL_ERROR ("Couldn't read file\n");
  }
  print_info ("(%d)\n", (cloud->width * cloud->height));
  return cloud;
}

int outofcoreProcess (std::vector<boost::filesystem::path> pcd_paths, boost::filesystem::path root_dir, 
                  int depth, double resolution, int build_octree_with, bool gen_lod, bool overwrite, bool multiresolution)
{
  // Bounding box min/max pts
  PointT min_pt, max_pt;
  // Iterate over all pcd files resizing min/max
  for (size_t i = 0; i < pcd_paths.size (); i++)
  {
    // Get cloud
    PCLPointCloud2::Ptr cloud = getCloudFromFile (pcd_paths[i]);
    PointCloud<PointXYZ>::Ptr cloudXYZ (new PointCloud<PointXYZ>);

    fromPCLPointCloud2 (*cloud, *cloudXYZ);
    PointT tmp_min_pt, tmp_max_pt;

    if (i == 0)
    {
      getMinMax3D (*cloudXYZ, min_pt, max_pt);  //獲取點雲數據在XYZ軸上的最大最小值
    }
    else
    {
      getMinMax3D (*cloudXYZ, tmp_min_pt, tmp_max_pt);

      // Resize new min
      if (tmp_min_pt.x < min_pt.x)
        min_pt.x = tmp_min_pt.x;
      if (tmp_min_pt.y < min_pt.y)
        min_pt.y = tmp_min_pt.y;
      if (tmp_min_pt.z < min_pt.z)
        min_pt.z = tmp_min_pt.z;

      // Resize new max
      if (tmp_max_pt.x > max_pt.x)
        max_pt.x = tmp_max_pt.x;
      if (tmp_max_pt.y > max_pt.y)
        max_pt.y = tmp_max_pt.y;
      if (tmp_max_pt.z > max_pt.z)
        max_pt.z = tmp_max_pt.z;
    }
  }

  std::cout << "Bounds: " << min_pt << " - " << max_pt << std::endl;

  // The bounding box of the root node of the out-of-core octree must be specified
  const Eigen::Vector3d bounding_box_min (min_pt.x, min_pt.y, min_pt.z);
  const Eigen::Vector3d bounding_box_max (max_pt.x, max_pt.y, max_pt.z);

  //specify the directory and the root node's meta data file with a
  //".oct_idx" extension (currently it must be this extension)
  boost::filesystem::path octree_path_on_disk (root_dir / "tree.oct_idx");

  print_info ("Writing: %s\n", octree_path_on_disk.c_str ());
  //make sure there isn't an octree there already
  if (boost::filesystem::exists (octree_path_on_disk))
  {
    if (overwrite)
    {
      boost::filesystem::remove_all (root_dir);
    }
    else
    {
      PCL_ERROR ("There's already an octree in the default location. Check the tree directory\n");
      return (-1);
    }
  }

  octree_disk *outofcore_octree;

  //create the out-of-core data structure (typedef'd above)
  if (build_octree_with == OCTREE_DEPTH)
  {
    outofcore_octree = new octree_disk (depth, bounding_box_min, bounding_box_max, octree_path_on_disk, "ECEF");
  }
  else
  {
    outofcore_octree = new octree_disk (bounding_box_min, bounding_box_max, resolution, octree_path_on_disk, "ECEF");
  }

  uint64_t total_pts = 0;

  // Iterate over all pcd files adding points to the octree
  for (size_t i = 0; i < pcd_paths.size (); i++)
  {

    PCLPointCloud2::Ptr cloud = getCloudFromFile (pcd_paths[i]);

    boost::uint64_t pts = 0;
    
    if (gen_lod && !multiresolution)
    {
      print_info ("  Generating LODs\n");
      pts = outofcore_octree->addPointCloud_and_genLOD (cloud);
    }
    else
    {
      pts = outofcore_octree->addPointCloud (cloud, false);
    }
    
    print_info ("Successfully added %lu points\n", pts);
    print_info ("%lu Points were dropped (probably NaN)\n", cloud->width*cloud->height - pts);
    
//    assert ( pts == cloud->width * cloud->height );
    
    total_pts += pts;
  }

  print_info ("Added a total of %lu from %d clouds\n",total_pts, pcd_paths.size ());
  

  double x, y;
  outofcore_octree->getBinDimension (x, y);

  print_info ("  Depth: %i\n", outofcore_octree->getDepth ());
  print_info ("  Resolution: [%f, %f]\n", x, y);

  if(multiresolution)
  {
    print_info ("Generating LOD...\n");
    outofcore_octree->setSamplePercent (0.25);
    outofcore_octree->buildLOD ();
  }

  //free outofcore data structure; the destructor forces buffer flush to disk
  delete outofcore_octree;

  return 0;
}

void
printHelp (int, char **argv)
{
  print_info ("This program is used to process pcd fiels into an outofcore data structure viewable by the");
  print_info ("pcl_outofcore_viewer\n\n");
  print_info ("%s <options> <input>.pcd <output_tree_dir>\n", argv[0]);
  print_info ("\n");
  print_info ("Options:\n");
  print_info ("\t -depth <resolution>           \t Octree depth\n");
  print_info ("\t -resolution <resolution>      \t Octree resolution\n");
  print_info ("\t -gen_lod                      \t Generate octree LODs\n");
  print_info ("\t -overwrite                    \t Overwrite existing octree\n");
  print_info ("\t -multiresolution              \t Generate multiresolutoin LOD\n");
  print_info ("\t -h                            \t Display help\n");
  print_info ("\n");
}

int
main (int argc, char* argv[])
{
  // Check for help (-h) flag
  if (argc > 1)
  {
    if (find_switch (argc, argv, "-h"))
    {
      printHelp (argc, argv);
      return (-1);
    }
  }

  // If no arguments specified
  if (argc - 1 < 1)
  {
    printHelp (argc, argv);
    return (-1);
  }

  if (find_switch (argc, argv, "-debug"))
  {
    pcl::console::setVerbosityLevel ( pcl::console::L_DEBUG );
  }
  
  // Defaults
  int depth = 4;
  double resolution = .1;
  bool gen_lod = false;
  bool multiresolution = false;
  bool overwrite = false;
  int build_octree_with = OCTREE_DEPTH;

  // If both depth and resolution specified
  if (find_switch (argc, argv, "-depth") && find_switch (argc, argv, "-resolution"))
  {
    PCL_ERROR ("Both -depth and -resolution specified, please specify one (Mutually exclusive)\n");
  }
  // Just resolution specified (Update how we build the tree)
  else if (find_switch (argc, argv, "-resolution"))
  {
    build_octree_with = OCTREE_RESOLUTION;
  }

  // Parse options
  parse_argument (argc, argv, "-depth", depth);
  parse_argument (argc, argv, "-resolution", resolution);
  gen_lod = find_switch (argc, argv, "-gen_lod");
  overwrite = find_switch (argc, argv, "-overwrite");

  if (gen_lod && find_switch (argc, argv, "-multiresolution"))
  {
    multiresolution = true;
  }
  

  // Parse non-option arguments for pcd files
  std::vector<int> file_arg_indices = parse_file_extension_argument (argc, argv, ".pcd");

  std::vector<boost::filesystem::path> pcd_paths;
  for (size_t i = 0; i < file_arg_indices.size (); i++)
  {
    boost::filesystem::path pcd_path (argv[file_arg_indices[i]]);
    if (!boost::filesystem::exists (pcd_path))
    {
      PCL_WARN ("File %s doesn't exist", pcd_path.string ().c_str ());
      continue;
    }
    pcd_paths.push_back (pcd_path);

  }

  // Check if we should process any files
  if (pcd_paths.size () < 1)
  {
    PCL_ERROR ("No .pcd files specified\n");
    return -1;
  }

  // Get root directory
  boost::filesystem::path root_dir (argv[argc - 1]);

  // Check if a root directory was specified, use directory of pcd file
  if (root_dir.extension () == ".pcd")
    root_dir = root_dir.parent_path () / (root_dir.stem().string() + "_tree").c_str();

  return outofcoreProcess (pcd_paths, root_dir, depth, resolution, build_octree_with, gen_lod, overwrite, multiresolution);
}

 

可視化的代碼如下(這些代碼的編譯與實現建議在PCL1.7版本以上)

// C++
#include <iostream>
#include <string>

// PCL
#include <pcl/common/time.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/PCLPointCloud2.h>
#include <pcl/io/pcd_io.h>
#include <pcl/io/vtk_lib_io.h>
#include <pcl/pcl_macros.h>
#include <pcl/console/print.h>
#include <pcl/console/parse.h>

// PCL - visualziation
//#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/visualization/common/common.h>
#include <pcl/visualization/vtk/vtkVertexBufferObjectMapper.h>

//#include "vtkVBOPolyDataMapper.h"

// PCL - outofcore
#include <pcl/outofcore/outofcore.h>
#include <pcl/outofcore/outofcore_impl.h>

#include <pcl/outofcore/visualization/axes.h>
#include <pcl/outofcore/visualization/camera.h>
#include <pcl/outofcore/visualization/grid.h>
#include <pcl/outofcore/visualization/object.h>
#include <pcl/outofcore/visualization/outofcore_cloud.h>
#include <pcl/outofcore/visualization/scene.h>
#include <pcl/outofcore/visualization/viewport.h>

using namespace pcl;
using namespace pcl::outofcore;

using pcl::console::parse_argument;
using pcl::console::find_switch;
using pcl::console::print_error;
using pcl::console::print_warn;
using pcl::console::print_info;

//typedef PCLPointCloud2 PointT;
typedef PointXYZ PointT;

typedef OutofcoreOctreeBase<OutofcoreOctreeDiskContainer<PointT>, PointT> octree_disk;
typedef OutofcoreOctreeBaseNode<OutofcoreOctreeDiskContainer<PointT>, PointT> octree_disk_node;

//typedef octree_base<OutofcoreOctreeDiskContainer<PointT> , PointT> octree_disk;
typedef boost::shared_ptr<octree_disk> OctreeDiskPtr;
//typedef octree_base_node<octree_disk_container<PointT> , PointT> octree_disk_node;
typedef Eigen::aligned_allocator<PointT> AlignedPointT;

// VTK
#include <vtkActor.h>
#include <vtkActorCollection.h>
#include <vtkActor2DCollection.h>
#include <vtkAppendPolyData.h>
#include <vtkAppendFilter.h>
#include <vtkCamera.h>
#include <vtkCameraActor.h>
#include <vtkCellArray.h>
#include <vtkCellData.h>
#include <vtkCommand.h>
#include <vtkConeSource.h>
#include <vtkCubeSource.h>
#include <vtkDataSetMapper.h>
#include <vtkHull.h>
#include <vtkInformation.h>
#include <vtkInformationStringKey.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkLODActor.h>
#include <vtkMath.h>
#include <vtkMatrix4x4.h>
#include <vtkMutexLock.h>
#include <vtkObjectFactory.h>
#include <vtkPolyData.h>
#include <vtkProperty.h>
#include <vtkTextActor.h>
#include <vtkRectilinearGrid.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>
#include <vtkUnsignedCharArray.h>

#include <vtkInteractorStyleRubberBand3D.h>
#include <vtkParallelCoordinatesInteractorStyle.h>

// Boost
#include <boost/date_time.hpp>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>

// Globals
vtkSmartPointer<vtkRenderWindow> window;

class KeyboardCallback : public vtkCommand
{
public:
  vtkTypeMacro(KeyboardCallback, vtkCommand)
  ;

  static KeyboardCallback *
  New ()
  {
    return new KeyboardCallback;
  }

  void
  Execute (vtkObject *caller, unsigned long vtkNotUsed(eventId), void* vtkNotUsed(callData))
  {
    vtkRenderWindowInteractor *interactor = vtkRenderWindowInteractor::SafeDownCast (caller);
    vtkRenderer *renderer = interactor->FindPokedRenderer (interactor->GetEventPosition ()[0],
                                                           interactor->GetEventPosition ()[1]);

    std::string key (interactor->GetKeySym ());
    bool shift_down = interactor->GetShiftKey();

    cout << "Key Pressed: " << key << endl;

    Scene *scene = Scene::instance ();
    OutofcoreCloud *cloud = static_cast<OutofcoreCloud*> (scene->getObjectByName ("my_octree"));

    if (key == "Up" || key == "Down")
    {
      if (key == "Up" && cloud)
      {
        if (shift_down)
        {
          cloud->increaseLodPixelThreshold();
        }
        else
        {
          cloud->setDisplayDepth (cloud->getDisplayDepth () + 1);
        }
      }
      else if (key == "Down" && cloud)
      {
        if (shift_down)
        {
          cloud->decreaseLodPixelThreshold();
        }
        else
        {
          cloud->setDisplayDepth (cloud->getDisplayDepth () - 1);
        }
      }
    }

    if (key == "o")
    {
      cloud->setDisplayVoxels(1-static_cast<int> (cloud->getDisplayVoxels()));
    }

    if (key == "Escape")
    {
      Eigen::Vector3d min (cloud->getBoundingBoxMin ());
      Eigen::Vector3d max (cloud->getBoundingBoxMax ());
      renderer->ResetCamera (min.x (), max.x (), min.y (), max.y (), min.z (), max.z ());
    }
  }
};

void
renderTimerCallback(vtkObject* caller, unsigned long int vtkNotUsed(eventId), void* vtkNotUsed(clientData), void* vtkNotUsed(callData))
{
  vtkRenderWindowInteractor *interactor = vtkRenderWindowInteractor::SafeDownCast (caller);
  interactor->Render ();
}

void
renderStartCallback(vtkObject* vtkNotUsed(caller), unsigned long int vtkNotUsed(eventId), void* vtkNotUsed(clientData), void* vtkNotUsed(callData))
{
  //std::cout << "Start...";
}

void
renderEndCallback(vtkObject* vtkNotUsed(caller), unsigned long int vtkNotUsed(eventId), void* vtkNotUsed(clientData), void* vtkNotUsed(callData))
{
  //std::cout << "End" << std::endl;
}

int
outofcoreViewer (boost::filesystem::path tree_root, int depth, bool display_octree=true, unsigned int gpu_cache_size=512)
{
  cout << boost::filesystem::absolute (tree_root) << endl;

  // Create top level scene
  Scene *scene = Scene::instance ();

  // Clouds
  OutofcoreCloud *cloud = new OutofcoreCloud ("my_octree", tree_root);
  cloud->setDisplayDepth (depth);
  cloud->setDisplayVoxels (display_octree);
  OutofcoreCloud::cloud_data_cache.setCapacity(gpu_cache_size*1024);
  scene->addObject (cloud);

//  OutofcoreCloud *cloud2 = new OutofcoreCloud ("my_octree2", tree_root);
//  cloud2->setDisplayDepth (depth);
//  cloud2->setDisplayVoxels (display_octree);
//  scene->addObject (cloud2);

  // Add Scene Renderables
  Grid *grid = new Grid ("origin_grid");
  Axes *axes = new Axes ("origin_axes");
  scene->addObject (grid);
  scene->addObject (axes);

  // Create smart pointer with arguments
//  Grid *grid_raw = new Grid("origin_grid");
//  vtkSmartPointer<Grid> grid;
//  grid.Take(grid_raw);

  // Create window and interactor
  vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New ();
  window = vtkSmartPointer<vtkRenderWindow>::New ();
  window->SetSize (1000, 500);

  interactor->SetRenderWindow (window);
  interactor->Initialize ();
  interactor->CreateRepeatingTimer (100);

  // Viewports
  Viewport octree_viewport (window, 0.0, 0.0, 0.5, 1.0);
  Viewport persp_viewport (window, 0.5, 0.0, 1.0, 1.0);

  // Cameras
  Camera *persp_camera = new Camera ("persp", persp_viewport.getRenderer ()->GetActiveCamera ());
  Camera *octree_camera = new Camera ("octree", octree_viewport.getRenderer ()->GetActiveCamera ());
  scene->addCamera (persp_camera);
  scene->addCamera (octree_camera);
  octree_camera->setDisplay(true);

  // Set viewport cameras
  persp_viewport.setCamera (persp_camera);
  octree_viewport.setCamera (octree_camera);

  // Render once
  window->Render ();

  // Frame cameras
  Eigen::Vector3d min (cloud->getBoundingBoxMin ());
  Eigen::Vector3d max (cloud->getBoundingBoxMax ());
  octree_viewport.getRenderer ()->ResetCamera (min.x (), max.x (), min.y (), max.y (), min.z (), max.z ());
  persp_viewport.getRenderer ()->ResetCamera (min.x (), max.x (), min.y (), max.y (), min.z (), max.z ());

  cloud->setRenderCamera(octree_camera);

  // Interactor
  // -------------------------------------------------------------------------
  vtkSmartPointer<vtkInteractorStyleTrackballCamera> style = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New ();
  style->SetAutoAdjustCameraClippingRange(false);
  interactor->SetInteractorStyle (style);

  // Callbacks
  // -------------------------------------------------------------------------
  vtkSmartPointer<vtkCallbackCommand> render_start_callback = vtkSmartPointer<vtkCallbackCommand>::New();
  render_start_callback->SetCallback(renderStartCallback);
  window->AddObserver(vtkCommand::StartEvent, render_start_callback);

  vtkSmartPointer<vtkCallbackCommand> render_end_callback = vtkSmartPointer<vtkCallbackCommand>::New();
  render_end_callback->SetCallback(renderEndCallback);
  window->AddObserver(vtkCommand::EndEvent, render_end_callback);

  vtkSmartPointer<KeyboardCallback> keyboard_callback = vtkSmartPointer<KeyboardCallback>::New ();
  interactor->AddObserver (vtkCommand::KeyPressEvent, keyboard_callback);

  interactor->CreateRepeatingTimer(1000);
  vtkSmartPointer<vtkCallbackCommand> render_timer_callback = vtkSmartPointer<vtkCallbackCommand>::New();
  render_timer_callback->SetCallback(renderTimerCallback);
  interactor->AddObserver(vtkCommand::TimerEvent, render_timer_callback);

  interactor->Start ();

  return 0;
}

void
print_help (int, char **argv)
{
  print_info ("This program is used to visualize outofcore data structure");
  print_info ("%s <options> <input_tree_dir> \n", argv[0]);
  print_info ("\n");
  print_info ("Options:\n");
  print_info ("\t -depth <depth>                \t Octree depth\n");
  print_info ("\t -display_octree               \t Toggles octree display\n");
//  print_info ("\t -mem_cache_size <size>        \t Size of pointcloud memory cache in MB (Defaults to 1024MB)\n");
  print_info ("\t -gpu_cache_size <size>        \t Size of pointcloud gpu cache in MB (512MB)\n");
  print_info ("\t -lod_threshold <pixels>       \t Bounding box screen projection threshold (10000)\n");
  print_info ("\t -v                            \t Print more verbosity\n");
  print_info ("\t -h                            \t Display help\n");
  print_info ("\n");

  exit (1);
}

int
main (int argc, char* argv[])
{

  // Check for help (-h) flag
  if (argc > 1)
  {
    if (find_switch (argc, argv, "-h"))
    {
      print_help (argc, argv);
      return (-1);
    }
  }

  // If no arguments specified
  if (argc - 1 < 1)
  {
    print_help (argc, argv);
    return (-1);
  }

  if (find_switch (argc, argv, "-v"))
    console::setVerbosityLevel (console::L_DEBUG);

  // Defaults
  int depth = 4;
//  unsigned int mem_cache_size = 1024;
  unsigned int gpu_cache_size = 512;
  unsigned int lod_threshold = 10000;
  bool display_octree = find_switch (argc, argv, "-display_octree");

  // Parse options
  parse_argument (argc, argv, "-depth", depth);
//  parse_argument (argc, argv, "-mem_cache_size", mem_cache_size);
  parse_argument (argc, argv, "-gpu_cache_size", gpu_cache_size);
  parse_argument (argc, argv, "-lod_threshold", lod_threshold);

  // Parse non-option arguments
  boost::filesystem::path tree_root (argv[argc - 1]);

  // Check if a root directory was specified, use directory of pcd file
  if (boost::filesystem::is_directory (tree_root))
  {
    boost::filesystem::directory_iterator diterend;
    for (boost::filesystem::directory_iterator diter (tree_root); diter != diterend; ++diter)
    {
      const boost::filesystem::path& file = *diter;
      if (!boost::filesystem::is_directory (file))
      {
        if (boost::filesystem::extension (file) == octree_disk_node::node_index_extension)
        {
          tree_root = file;
        }
      }
    }
  }

  return outofcoreViewer (tree_root, depth, display_octree, gpu_cache_size);
}

 

根據上面的處理的代碼的實現提示:
使用深度參數化的方法
pcl_outofcore_process −depth 4 −gen_lod data01.pcd data02.pcd myoutofcoretree
要創建葉節點體素大小不超過50 cm的多分辨率outofcore八叉樹,使用如下:
pcl _outofcoreprocess −resolution 0. 50 −genlod data01.pcd data02.pcd myotheroutofcoretree

在PCL中使用OutCore算法可以使用自帶的工具里的可執行文件 構造過程可以通過深度或分辨率參數化。如果指定了分辨率(即最大葉尺寸),則自動計算深度。如果設置的樹太深:LOD的構建可能需要很長時間

pcl_outofcore_viewer 使用不同的深度可視化的結果


這里使用了不同的分辨率的形式可視化,對於大規模的點雲,根據不同的視角來顯示點雲,對於可視化的部分我們加載進來,對於不需要的部分,我們可以不用加載進來。這也是為什么能夠提高可視化的效率。

下面這是使用了真實的PCD點雲數據,來做實驗例程。該PCD文件是一個大規模的街景點雲,該點雲有200MB
 
 
 
該點雲直接可視化的結果,我們可以看到點雲的數量以及加載的時間
 
 
我們分別使用了生成了不同的深度和不同分辨率的核外八叉樹文件
 
 
使用我們outofcore_viewer可視化的結果
 
 

該算法針對更大的點雲采用上述的方法生成核外八叉樹的形式,也可以很好實現點雲的可視化

 

References
[1] PCL Urban Robotics Code Sprint Blog. http://www.pointclouds.org/blog/urcs.
[2] Point Cloud Library Documentation: Module outofcore. https://docs.pointclouds.org/trunk/
group__outofcore.html.
[3] Urban Robotics. http://www.urbanrobotics.net.
[4] Adam Stambler. osgpcl: OpenSceneGraph Point Cloud Library Cloud Viewer. https://github.com/
adasta/osgpcl, 2012.
[5] Claus Scheiblauer and Michael Wimmer. Out-of-core selection and editing of huge point clouds. Computers & Graphics, 35(2):342–351, April 2011.

 
 

這里的文章均為“點雲PCL”博主原創,未經允許,請勿轉載,謝謝大家的理解與合作

請關注公眾號,加入微信或者QQ交流群


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM