Point Cloud Library (PCL)
Table of Contents
1. PCL 简介
The Point Cloud Library (or PCL) is a large scale, open project for 2D/3D image and point cloud processing. The PCL framework contains numerous state-of-the art algorithms including filtering, feature estimation, surface reconstruction, registration, model fitting and segmentation. These algorithms can be used, for example, to filter outliers from noisy data, stitch 3D point clouds together, segment relevant parts of a scene, extract keypoints and compute descriptors to recognize objects in the world based on their geometric appearance, and create surfaces from point clouds and visualize them -- to name a few.
参考:
What is PCL?: http://pointclouds.org/about/
PCL Tutorials: http://pointclouds.org/documentation/tutorials/index.php
1.1. 什么是 Point Cloud
A point cloud is a data structure used to represent a collection of multi-dimensional points and is commonly used to represent three-dimensional data. In a 3D point cloud, the points usually represent the X, Y, and Z geometric coordinates of an underlying sampled surface. When color information is present the point cloud becomes 4D.
Point clouds can be acquired from hardware sensors such as stereo cameras, 3D scanners, or time-of-flight cameras, or generated from a computer program synthetically. PCL supports natively the OpenNI 3D interfaces, and can thus acquire and process data from devices such as the Microsoft Kinect, or the Asus XTionPRO.
1.2. PCL dependencies
In order to compile every component of the PCL library we need to download and compile a series of 3rd party library dependencies:
PCL 3rd party library dependency | PCL 中用它做什么 | 是否必需 |
---|---|---|
Boost version >= 1.46.1 (http://www.boost.org/) | used for shared pointers, and threading. | mandatory |
Eigen version >= 3.0.0 (http://eigen.tuxfamily.org/) | used as the matrix backend for SSE optimized math. | mandatory |
FLANN version >= 1.6.8 (http://www.cs.ubc.ca/research/flann/) | used in kdtree for fast approximate nearest neighbors search. | mandatory |
Visualization ToolKit (VTK) version >= 5.6.1 (http://www.vtk.org/) | used in visualization for 3D point cloud rendering and visualization. | mandatory |
googletest version >= 1.6.0 (http://code.google.com/p/googletest/) | used to build test units. | optional |
QHULL version >= 2011.1 (http://www.qhull.org/) | used for convex/concave hull decompositions in surface. | optional |
OpenNI version >= 1.1.0.25 (http://www.openni.org/) | used to grab point clouds from OpenNI compliant devices. | optional |
Qt version >= 4.6 (http://qt.digia.com/) | used for developing applications with a graphical user interface (GUI) | optional |
1.3. PCL 安装
Ubuntu 中可以按下面方式安装提前编译好的 PCL:
sudo add-apt-repository ppa:v-launchpad-jochen-sprickerhof-de/pcl sudo apt-get update sudo apt-get install libpcl-all
其它系统中,PCL 的安装方式可参考:http://pointclouds.org/downloads/
2. PCL 基本使用
PCL 中提供了一系列用来处理 Point Cloud 的数据结构和算法。
2.1. PCL 中基本数据结构
PCL 中最重要的数据结构是 pcl::PointCloud ,用来表示 Point Cloud 数据的集合(可看作是 C++中的容器) ,它是一个模板类(需要指定具体的 Point Cloud 类型)。例如,要创建 4 个随机的 pcl::PointXYZ 类型(后文将介绍其它类型)的 Point Cloud,可以这样:
pcl::PointCloud<pcl::PointXYZ> cloud; cloud.push_back (pcl::PointXYZ (rand (), rand (), rand ())); cloud.push_back (pcl::PointXYZ (rand (), rand (), rand ())); cloud.push_back (pcl::PointXYZ (rand (), rand (), rand ())); cloud.push_back (pcl::PointXYZ (rand (), rand (), rand ()));
pcl::PointCloud 中重要的 public field 有:
PointCloud 中 public field | 说明 |
---|---|
width | specifies the width of the point cloud dataset in the number of points. WIDTH has two meanings: it can specify the total number of points in the cloud (equal with POINTS see below) for unorganized datasets; it can specify the width (total number of points in a row) of an organized point cloud dataset. Mandatory. |
height | specifies the height of the point cloud dataset in the number of points. HEIGHT has two meanings: it can specify the height (total number of rows) of an organized point cloud dataset; it is set to 1 for unorganized datasets (thus used to check whether a dataset is organized or not). Mandatory. |
points | the data array where all points of type PointT are stored. Mandatory. |
is_dense | specifies if all the data in points is finite (true), or whether it might contain Inf/NaN values (false). Mandatory. |
sensor_origin_ | specifies the sensor acquisition pose (origin/translation). Optional. |
sensor_orientation_ | specifies the sensor acquisition pose (rotation). Optional. |
2.1.1. Point Cloud 的类型
前面介绍过 pcl::PointCloud 是 Point Cloud 数据的容器,它是一个模板类,应用时需要指定具体的类型。
PCL 中定义了一些常用的 Point Cloud 类型。
Point Cloud 类型 | 说明 |
---|---|
pcl::PointXYZ | This is the simplest type of point and probably one of the most used; it stores only 3D xyz information. |
pcl::PointXYZI | This type of point is very similar to the previous one, but it also includes a field for the intensity of the point. Intensity can be useful when obtaining points based on a certain level of return from a sensor. There are two other standard identical point types to this one: the first one is pcl::InterestPoint, which has a field to store strength instead of intensity, and pcl::PointWithRange, which has a field to store the range instead of either intensity or strength. |
pcl::PointXYZRGBA | This type of point stores 3D information as well as color (RGB = Red, Green, Blue) and transparency (A = Alpha). |
pcl::PointXYZRGB | This type is similar to the previous point type, but it differs in that it lacks the transparency field. |
pcl::Normal | This is one of the most used types of points; it represents the surface normal at a given point and a measure of its curvature. |
pcl::PointNormal | This type is exactly the same as the previous one; it contains the surface normal and curvature information at a given point, but it also includes the 3D xyz coordinates of the point. Variants of this point are PointXYZRGBNormal and the PointXYZINormal, which, as the names suggest, include color (former) and intensity (latter). |
2.2. PCL 中的算法
PCL 中实现了很多处理 3D 数据的算法。使用算法的基本流程如下:
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::PointCloud<pcl::PointXYZ>::Ptr result(new pcl::PointCloud<pcl::PointXYZ>); pcl::Algorithm<pcl::PointXYZ> algorithm; // 应用时,把 pcl::Algorithm 替换为具体的算法名字。 algorithm.setInputCloud(cloud); algorithm.setParameter(1.0); algorithm.setAnotherParameter(0.33); algorithm.process(*result);
2.3. 第一个实例:PassThrough filter
In this tutorial we will learn how to perform a simple filtering along a specified dimension – that is, cut off values that are either inside or outside a given user range.
这个例子摘自:http://pointclouds.org/documentation/tutorials/passthrough.php#passthrough
// file passthrough.cpp #include <iostream> #include <pcl/point_types.h> #include <pcl/filters/passthrough.h> int main (int argc, char** argv) { pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>); // creates a PointCloud<PointXYZ> boost shared pointer and initializes it pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>); // Fill in the cloud data cloud->width = 5; cloud->height = 1; cloud->points.resize (cloud->width * cloud->height); for (size_t i = 0; i < cloud->points.size (); ++i) // 随机生成5个数据。 { cloud->points[i].x = 1024 * rand () / (RAND_MAX + 1.0f); cloud->points[i].y = 1024 * rand () / (RAND_MAX + 1.0f); cloud->points[i].z = 1024 * rand () / (RAND_MAX + 1.0f); } std::cerr << "Cloud before filtering: " << std::endl; for (size_t i = 0; i < cloud->points.size (); ++i) std::cerr << " " << cloud->points[i].x << " " << cloud->points[i].y << " " << cloud->points[i].z << std::endl; // Create the filtering object pcl::PassThrough<pcl::PointXYZ> pass; pass.setInputCloud (cloud); pass.setFilterFieldName ("z"); // 过滤数据,只选取 z 坐标在 (0.0, 1.0) 范围内的数据。 pass.setFilterLimits (0.0, 1.0); //pass.setFilterLimitsNegative (true); pass.filter (*cloud_filtered); std::cerr << "Cloud after filtering: " << std::endl; for (size_t i = 0; i < cloud_filtered->points.size (); ++i) std::cerr << " " << cloud_filtered->points[i].x << " " << cloud_filtered->points[i].y << " " << cloud_filtered->points[i].z << std::endl; return (0); }
2.3.1. 编译运行程序
新建 CMakeLists.txt,内容如下:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR) project(passthrough) find_package(PCL 1.2 REQUIRED) include_directories(${PCL_INCLUDE_DIRS}) link_directories(${PCL_LIBRARY_DIRS}) add_definitions(${PCL_DEFINITIONS}) add_executable (passthrough passthrough.cpp) target_link_libraries (passthrough ${PCL_LIBRARIES})
编译运行步骤如下:
$ mkdir build $ cd build $ cmake .. $ make $ ./passthrough Cloud before filtering: 0.352222 -0.151883 -0.106395 -0.397406 -0.473106 0.292602 -0.731898 0.667105 0.441304 -0.734766 0.854581 -0.0361733 -0.4607 -0.277468 -0.916762 Cloud after filtering: -0.397406 -0.473106 0.292602 -0.731898 0.667105 0.441304
从运行的结果可以看到,仅 z 坐标在 (0.0, 1.0) 范围内的数据被留下了,这和程序中的设置是一致的。
3. Point Cloud Data (PCD) file format
可以把 Point Cloud 数据保存为文件,PCL 定义了存储 Point Cloud 数据的文件格式,称为 PCD (Point Cloud Data)格式。PCD 文件的实例如下:
# .PCD v.7 - Point Cloud Data file format VERSION .7 FIELDS x y z rgb SIZE 4 4 4 4 TYPE F F F F COUNT 1 1 1 1 WIDTH 5 HEIGHT 1 VIEWPOINT 0 0 0 1 0 0 0 POINTS 5 DATA ascii 0.93773 0.33763 0 4.2108e+06 0.90805 0.35641 0 4.2108e+06 0.81915 0.32 0 4.2108e+06 0.97192 0.278 0 4.2108e+06 0.944 0.29474 0 4.2108e+06
文件头中各个字段含义可参考:http://pointclouds.org/documentation/tutorials/pcd_file_format.php#pcd-file-format
3.1. 写数据到 PCD 文件
// file pcd_write.cpp #include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> int main (int argc, char** argv) { pcl::PointCloud<pcl::PointXYZ> cloud; // Fill in the cloud data cloud.width = 5; cloud.height = 1; cloud.is_dense = false; cloud.points.resize (cloud.width * cloud.height); for (size_t i = 0; i < cloud.points.size (); ++i) { cloud.points[i].x = 1024 * rand () / (RAND_MAX + 1.0f); cloud.points[i].y = 1024 * rand () / (RAND_MAX + 1.0f); cloud.points[i].z = 1024 * rand () / (RAND_MAX + 1.0f); } pcl::io::savePCDFileASCII ("test_pcd.pcd", cloud); std::cerr << "Saved " << cloud.points.size () << " data points to test_pcd.pcd." << std::endl; for (size_t i = 0; i < cloud.points.size (); ++i) std::cerr << " " << cloud.points[i].x << " " << cloud.points[i].y << " " << cloud.points[i].z << std::endl; return (0); }
新建 CMakeLists.txt,内容如下:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR) project(pcd_write) find_package(PCL 1.2 REQUIRED) include_directories(${PCL_INCLUDE_DIRS}) link_directories(${PCL_LIBRARY_DIRS}) add_definitions(${PCL_DEFINITIONS}) add_executable (pcd_write pcd_write.cpp) target_link_libraries (pcd_write ${PCL_LIBRARIES})
编译完成后,运行程序可生成 PCD 文件 test_pcd.pcd:
$ ./pcd_write Saved 5 data points to test_pcd.pcd. 0.352222 -0.151883 -0.106395 -0.397406 -0.473106 0.292602 -0.731898 0.667105 0.441304 -0.734766 0.854581 -0.0361733 -0.4607 -0.277468 -0.916762 $ cat test_pcd.pcd # .PCD v0.7 - Point Cloud Data file format VERSION 0.7 FIELDS x y z SIZE 4 4 4 TYPE F F F COUNT 1 1 1 WIDTH 5 HEIGHT 1 VIEWPOINT 0 0 0 1 0 0 0 POINTS 5 DATA ascii 0.35222197 -0.15188313 -0.10639524 -0.3974061 -0.47310591 0.29260206 -0.73189831 0.66710472 0.44130373 -0.73476553 0.85458088 -0.036173344 -0.46070004 -0.2774682 -0.91676188
3.2. 从 PCD 文件加载数据
用 pcl::io::loadPCDFile 可以从 PCD 文件加载数据。实例如下:
// pcd_read.cpp #include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> int main (int argc, char** argv) { pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>); if (pcl::io::loadPCDFile<pcl::PointXYZ> ("test_pcd.pcd", *cloud) == -1) // load the file { PCL_ERROR ("Couldn't read file test_pcd.pcd \n"); return (-1); } std::cout << "Loaded " << cloud->width * cloud->height << " data points from test_pcd.pcd with the following fields: " << std::endl; for (size_t i = 0; i < cloud->points.size (); ++i) std::cout << " " << cloud->points[i].x << " " << cloud->points[i].y << " " << cloud->points[i].z << std::endl; return (0); }
假设这个例子中需要的文件 test_pcd.pcd 是从前节的例子中产生的,则这个例子的输出会为:
Loaded 5 data points from test_pcd.pcd with the following fields: x y z 0.35222 -0.15188 -0.1064 -0.39741 -0.47311 0.2926 -0.7319 0.6671 0.4413 -0.73477 0.85458 -0.036173 -0.4607 -0.27747 -0.91676