PCL中outofcore模块---基于核外八叉树的大规模点云的显示
在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
{ ”version”:3 , ”bb_min”:[xxx,yyy,zzz], ”bb_max”:[xxx,yyy,zzz], ”bin”:”pathtodata.pcd ” }
可以使用OutofcoreOctreeNodeMetadata类直接访问此JSON数据。 此类将接口抽象到磁盘上的JSON数据,因此从理论上讲,该格式可以轻松更改为XML,YAML或其他所需格式。
{ ”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模块实现了该算法具有哪些特点
点云的插入
(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 使用不同的深度可视化的结果
这里使用了不同的分辨率的形式可视化,对于大规模的点云,根据不同的视角来显示点云,对于可视化的部分我们加载进来,对于不需要的部分,我们可以不用加载进来。这也是为什么能够提高可视化的效率。
该算法针对更大的点云采用上述的方法生成核外八叉树的形式,也可以很好实现点云的可视化
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交流群