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交流群

posted @ 2019-12-09 15:38  Being_young  阅读(4602)  评论(1编辑  收藏  举报