02-点云输入/输出(I/O)*

数据文件格式

一个典型的PCD文件如下:

# .PCD v.5 - Point Cloud Data file format
VERSION .5
FIELDS x y z
SIZE 4 4 4
TYPE F F F
COUNT 1 1 1
WIDTH 397
HEIGHT 1
POINTS 397
DATA ascii
0.0054216 0.11349 0.040749
-0.0017447 0.11425 0.041273
-0.010661 0.11338 0.040916
0.026422 0.11499 0.032623
0.024545 0.12284 0.024255
0.034137 0.11316 0.02507
  • 格式描述
  1. VERSION .5 指定PCS文件版本

  2. FIELDS x y z 指定一个点的每一个维度和字段名字,例如

    FIELDS x y z # XYZ data

    FIELDS x y z rgb # XYZ + colors

    FIELDS x y z normal_x normal_y normal_z # XYZ + surface normals

  3. SIZE 4 4 4 指定每一个维度的字节数大小

  4. TYPE F F F 指定每一个维度的类型,I表示int,U表示uint,F表示浮点

  5. COUNT 1 1 1 指定每一个维度包含的元素数,如果没有COUNT,默认都为1

  6. WIDTH 397 点云数据集的宽度

  7. HEIGHT 1 点云数据集的高度

  8. VIEWPOINT 0 0 0 1 0 0 0 指定点云获取的视点和角度,在不同坐标系之间转换时使用(由3个平移+4个四元数构成)

  9. POINTS 397 总共的点数(显得多余)

  10. DATA ascii 存储点云数据的数据类型,ASCII和binary

  • 其他格式

PCD 不是第 一个支持 3D点云数据的文件类型,尤其是计算机图形学和计算几何学领域,已经创建了很多格式来描述任意多边形和激光扫描仪获取的点云。常见的有下面几种格式:

  1. PLY 是一种多边形文件格式 , 由 Stanford 大学的 Turk 等人设计开发;
  2. STL 是 3D Systems 公司创建的模型文件格式,主要应用于 CAD 、 CAM领域 ;
  3. OBJ 是从几何学上定义的文件格式,首先由 Wavefront Technologies 开发;
  4. 其他格式

以上所有格式都有其优缺点,因为他们是在不同时期为了满足不同的需求所创建的,那时很多当今流行的传感器和算法都还没有发明。

PCL 中 PCD 文件格式的正式发布是 0.7版本,之前也有0.5,0.6版本。

以及更多关于PCD文件格式详情

反序列化

我们可以对一个点云进行反序列化操作,将之保存到PointCloud对象中:load_pcd.cpp

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>

int
main(int argc, char **argv) {
    // 准备pcl::PointXYZ类型的点云
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    // 将pcd中的数据加载到cloud中
    if (pcl::io::loadPCDFile<pcl::PointXYZ>("./data/bunny.pcd", *cloud) == -1) //* load the file
    {
        PCL_ERROR ("Couldn't read file bunny.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);
}

序列化

以下代码,随机生成了5个点,并将之以ASCII形式保存(序列化)在test_pcd.pcd文件中:save_pcd.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);

    std::cout << rand() << std::endl;
    std::cout << rand() / (RAND_MAX + 1.0f) << std::endl;
    std::cout << 1024 * rand() / (RAND_MAX + 1.0f) << std::endl;

    // 随机生成5个点
    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);
}
  • 输出的文件内容如下
# .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.3974061 -0.47310591 0.29260206
-0.73189831 0.66710472 0.44130373
-0.73476553 0.85458088 -0.036173344
-0.46070004 -0.2774682 -0.91676188
0.1837492 0.96880913 0.5120554

有时为了节省空间,提高读写效率,还会以binary的格式进行序列化,即将save操作改为

pcl::io::savePCDFileBinary("test_pcd_binary.pcd", cloud);
// 或
pcl::io::savePCDFile("test_pcd_binary.pcd", cloud, true);
  • 此时输出的内容如下:
# .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 binary
�x˾�:��ϕ>�];�`�*?���>�<���Z?�*����P���j��(<>�x??

注意11行的DATA类型为binary,最后的内容由于是以二进制形式保存,故而不可以直接看到值,需要用程序读取。