一、PCL 点云PCD文件原始数据
在 PCL 点云库中,点云的原始数据可以包含多种字段,这些字段根据不同的应用场景和数据采集设备有所不同。最常见的点云数据存储在 PCD(Point Cloud Data)文件格式中,其主要字段包括:
# .PCD v0.7 - Point Cloud Data file format //注释
VERSION 0.7 //PCD文件版本
FIELDS x y z r g b intensity timestamp //每个点包含哪些维度,xyz表示XYZ三维坐标,rgb表示颜色(可以分开表示,也可以一个浮点数表示),intensity表示激光反射强度,timestamp表示时间戳,normal_x、normal_y、normal_z表示平面法线三维坐标,j1、j2、j3表示不变矩。
SIZE 4 4 4 1 1 1 1 8 //每个维度的数据占用字节大小
TYPE F F F U U U U F //每个维度的数据类型,I表示有符号类型int8(char)、int16(short)、int32(int),U表示无符号类型uint8(unsigned char)、uint16(unsigned short)、uint32(unsigned int),F表示浮点型
COUNT 1 1 1 1 1 1 1 1 //每个维度含有多少个元素(如果未提供COUNT属性,默认值为1)
WIDTH 32 //用点的数量表示点云数据集的宽度。有两种含义:1.无序数据集的点云中点的数量 2.有序点云数据集的宽度(一行中点的数量),有序点云数据集中,点云类似图片或矩阵的结构,分为行和列,这种数据通常来自于立体摄像机(stereo camera)、时间飞行摄像机(Time Of Flight camera,使用红外线或者光脉冲来估计光线从发射到检测到的时间延迟来测量距离),知道点的相邻关系,使算法计算更高 效。
HEIGHT 2172 //用点云数据集中点的数量表示点云数据集的高度。高度有如下两种含义:1.有序的点云数据集中,行的数量 2.无序点云数据集中,高度为1(可以用来判断一个数据集是有序的还是无序的)
VIEWPOINT 0 0 0 1 0 0 0 //指定数据集合中点的采集视点。可以用来后续可能的坐标转换,或者求平面法线坐标。格式是平移(tx ty tz) + 四元数(qw qx qy qz),默认是0 0 0 1 0 0 0。
POINTS 69504 //点云中点的总数(冗余字段)
DATA binary_compressed //点云数据的存储类型,0.7版本支持两种存储方式:ascii和binary。
1234567891011
# .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
激光点云XYZ坐标数据主要就是存储在DATA里面,我们可以从pcd文件读这个数据,或者在代码里赋值数据存为pcd文件。
而要使用上面数据,就要用到#include <pcl/point_types.h>和#include <pcl/point_cloud.h>两个头文件。
二、point_types 和 point_cloud 的总结
1. 概述:
在 PCL(Point Cloud Library)中,point_types
和 point_cloud
是两个核心元素,它们协同工作,为点云数据的表示、存储和操作提供了强大的支持。point_types
主要是 PCD(Point Cloud Data)文件的数量类型集合,其源代码主要由结构体组成,这些结构体定义了点云数据中每个点所包含的具体信息;而 point_cloud
主要为类,其中包含了存储点云数据的容器,其核心成员 points
是一个 std::vector
,用来存储具体的点数据。
2. point_types 的详细介绍:
(1) 主要功能:
point_types
提供了多种预定义的点云数据结构体,以满足不同的点云数据表示需求,涵盖了从简单的空间坐标信息到包含各种属性的复杂信息。这些结构体是 PCL 中存储点云数据的基本单元,允许开发者根据不同的应用场景和数据来源选择或自定义合适的点类型。- 源码解读:
在impl/point_types.hpp代码进行了实现:
(2) 常见的点类型结构体:
-
pcl::PointXYZ:
struct PointXYZ { float x; float y; float z; };
- 这是最基本的点类型,仅包含三维空间的坐标信息,使用浮点数表示
x
,y
,z
轴上的位置。适用于大多数只需要空间位置信息的场景,如简单的点云配准、三维空间的几何分析等。
- 这是最基本的点类型,仅包含三维空间的坐标信息,使用浮点数表示
-
pcl::PointXYZI:
struct PointXYZI { float x; float y; float z; float intensity; };
- 在
PointXYZ
的基础上添加了intensity
字段,通常用于存储激光雷达扫描的强度信息,该信息对于区分不同物体的反射特性非常有用,例如在识别不同材质或不同距离的物体时,强度信息可以作为重要依据。
- 在
-
pcl::PointXYZRGB:
struct PointXYZRGB { float x; float y; float z; uint8_t r; uint8_t g; uint8_t b; };
- 包含三维坐标信息和颜色信息,其中
r
,g
,b
分别表示红、绿、蓝分量,以 8 位无符号整数存储,适用于从 RGB-D 相机等设备获取的彩色点云数据,可用于基于颜色的点云处理任务,如彩色点云的可视化和分割。
- 包含三维坐标信息和颜色信息,其中
-
pcl::PointXYZRGBA:
struct PointXYZRGBA { float x; float y; float z; uint8_t r; uint8_t g; uint8_t b; uint8_t a; };
- 类似于
pcl::PointXYZRGB
,但多了a
表示透明度,可用于需要考虑透明度的应用,如某些可视化和渲染场景。
- 类似于
-
pcl::Normal:
struct Normal { float normal[3]; float curvature; };
- 存储点的法向量和曲率信息,
normal[3]
存储法向量的x
,y
,z
分量,curvature
存储曲率。在表面重建、特征提取和光照计算等任务中非常重要,因为法向量和曲率能够反映表面的几何特征。
- 存储点的法向量和曲率信息,
(3) 自定义点类型:
- 除了使用预定义的点类型,PCL 允许用户自定义点类型。以下是一个自定义点类型的示例:
struct MyPointType { float x; float y; float z; float custom_value; }; POINT_CLOUD_REGISTER_POINT_STRUCT(MyPointType, (float, x, x) (float, y, y) (float, z, z) (float, custom_value, custom_value) );
- 用户可以根据自己的需求定义结构体,并使用
POINT_CLOUD_REGISTER_POINT_STRUCT
宏将自定义点类型注册到 PCL 中,使其能够在各种点云操作中使用,这极大地增强了 PCL 的灵活性,可根据不同应用添加特殊的属性。
- 用户可以根据自己的需求定义结构体,并使用
3. point_cloud 的详细介绍:
(1) 主要功能:
point_cloud
中的核心类是pcl::PointCloud
,它是存储和操作点云数据的容器,为点云数据的管理提供了丰富的功能,包括存储、访问、修改和调整大小等操作。- 源码解读:
构造函数会初始化:header、points等变量,points因为是vectror,因此具备vector的所有操作方法。因此可以看到后续点云操作基本都是通过指针来操作这些属性,所以弄懂了这个,才知道后面会有哪些操作方法了。
(2) pcl::PointCloud 类的结构和使用:
- 基本结构:
template <typename PointT> class PointCloud { public: std::vector<PointT> points; int width; int height; // 其他成员和方法 };
points
是存储点云数据的std::vector
容器,存储的数据类型为PointT
,width
和height
用于描述点云的维度,对于无序点云,width
表示点的数量,height
通常为 1;对于有序点云,它们表示图像风格的组织方式。
(3) 代码示例:
创建和操作点云:
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
int main() {
// 创建不同类型的点云对象
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xyz(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_xyzi(new pcl::PointCloud<pcl::PointXYZI>);
// 初始化点云大小
cloud_xyz->width = 100;
cloud_xyzi->width = 50;
cloud_xyz->height = 1;
cloud_xyzi->height = 1;
// 调整点云的大小
cloud_xyz->points.resize(cloud_xyz->width * cloud_xyz->height);
cloud_xyzi->points.resize(cloud_xyzi->width * cloud_xyzi->height);
// 为点云添加数据
for (size_t i = 0; i < cloud_xyz->points.size(); ++i) {
cloud_xyz->points[i].x = i;
cloud_xyz->points[i].y = i * 2;
cloud_xyz->points[i].z = i * 3;
cloud_xyzi->points[i].x = i * 0.5;
cloud_xyzi->points[i].y = i * 1.5;
cloud_xyzi->points[i].z = i * 2.5;
cloud_xyzi->points[i].intensity = i;
}
// 输出点云的部分信息
std::cout << "PointCloud<pcl::PointXYZ> has " << cloud_xyz->points.size() << " points." << std::endl;
std::cout << "First point in PointCloud<pcl::PointXYZ>: " << cloud_xyz->points[0].x << " " << cloud_xyz->points[0].y << " " << cloud_xyz->points[0].z << std::endl;
std::cout << "PointCloud<pcl::PointXYZI> has " << cloud_xyzi->points.size() << " points." << std::endl;
std::cout << "First point in PointCloud<pcl::PointXYZI>: " << cloud_xyzi->points[0].x << " " << cloud_xyzi->points[0].y << " " << cloud_xyzi->points[0].z << " " << cloud_xyzi->points[0].intensity << std::endl;
return 0;
}
代码解释:
- 首先,我们使用
pcl::PointCloud<pcl::PointXYZ>::Ptr
和pcl::PointCloud<pcl::PointXYZI>::Ptr
创建不同类型的点云对象cloud_xyz
和cloud_xyzi
,并使用智能指针管理内存。 - 然后,设置
width
和height
来定义点云的维度,这里创建的是无序点云,所以height
为 1。 - 使用
resize
函数调整点云的大小,为存储点数据分配足够的空间。 - 通过
for
循环为点云中的每个点添加数据,根据点类型添加相应的数据,对于pcl::PointXYZ
只添加x
,y
,z
坐标,对于pcl::PointXYZI
还添加intensity
信息。 - 最后,输出点云的大小和第一个点的信息,用于验证数据添加是否正确。
从文件读取和保存点云:
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/io/pcd_io.h>
int main() {
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// 从文件中读取点云数据
if (pcl::io::loadPCDFile<pcl::PointXYZ>("input_cloud.pcd", *cloud) == -1) {
std::cerr << "Failed to load PCD file." << std::endl;
return -1;
}
// 输出点云的信息
std::cout << "Loaded " << cloud->points.size() << " points from input_cloud.pcd." << std::endl;
// 保存点云数据到另一个文件
if (pcl::io::savePCDFile<pcl::PointXYZ>("output_cloud.pcd", *cloud) == -1) {
std::cerr << "Failed to save PCD file." << std::endl;
return -1;
}
return 0;
}
代码解释:
- 创建
pcl::PointCloud<pcl::PointXYZ>
类型的点云对象cloud
。 - 使用
pcl::io::loadPCDFile
函数从input_cloud.pcd
文件中读取点云数据,pcl::io::loadPCDFile
的模板参数指定点云的类型,*cloud
表示将数据存储在cloud
指向的对象中。 - 输出读取的点云的点数。
- 使用
pcl::io::savePCDFile
函数将点云数据保存到output_cloud.pcd
文件中,确保存储成功。
复制和转换点云:
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/io/pcd_io.h>
#include <pcl/common/copy_pointcloud.h>
int main() {
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud1(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud2(new pcl::PointCloud<pcl::PointXYZ>);
// 假设 cloud1 已经初始化
pcl::copyPointCloud(*cloud1, *cloud2);
return 0;
}
代码解释:
- 创建两个
pcl::PointCloud<pcl::PointXYZ>
类型的点云对象cloud1
和cloud2
。 - 假设
cloud1
已经初始化(可以使用上述的创建和操作点云的方法或从文件读取),使用pcl::copyPointCloud
函数将cloud1
中的数据复制到cloud2
中,这在数据备份或不同处理步骤之间传递数据时非常有用。
4. 总结:
point_types
为 PCL 提供了丰富的点云数据表示方式,包括各种预定义的点类型和自定义点类型的机制,确保了点云数据的精确表示。point_cloud
中的pcl::PointCloud
类提供了存储和操作点云数据的容器,其points
成员存储具体的点数据,配合各种操作函数,方便开发者进行点云数据的操作和处理。- 两者紧密结合,为点云数据的处理、存储和转换提供了强大的基础,是 PCL 库中进行点云处理的核心组件,无论是创建新的点云、处理点云数据还是从文件中读写点云数据,都离不开它们的支持。通过使用这些组件,开发者可以更方便地实现各种高级点云处理任务,如点云滤波、配准、分割和特征提取等。
通过对 point_types
和 point_cloud
的深入理解和使用,开发者可以充分利用 PCL 库的强大功能,实现从简单的点云数据存储和操作到复杂的点云处理算法的开发。