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

