PCL点云库学习笔记(输入输出)
输入输出(I\O)
一、概述
IO库包含用于读写点云数据(PCD)文件的类和函数,以及从各种传感设备捕获点云。
二、PCD 点云格式
1.为什么用一种新的文件格式?
PCD文件格式的出现,是因为现有的文件结构不支持由PCL库引进n维点类型机制处理过程中的某些扩展。PCD不是第一个支持3D点云数据的文件类型,尤其是计算机图形学和计算几何学领域,已经创建了很多格式来描述任意多边形和激光扫描仪获取的点云。
2.文件头
每个PCD文件都包含一个头,用于标识和声明文件中存储的点云数据的某些属性。PCD的头必须用ASCII编码。
·VERSION –指定PCD文件版本
·FIELDS –指定一个点可以有的每一个维度和字段的名字。
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
FIELDS j1 j2 j3 # moment invariants
·SIZE –用字节数指定每一个维度的大小。
unsigned char/char has 1 byte
unsigned short/short has 2 bytes
unsigned int/int/float has 4 bytes
double has 8 bytes
·TYPE –用一个字符指定每一个维度的类型。
I –表示有符号类型int8(char)、int16(short)和int32(int);
U – 表示无符号类型uint8(unsigned char)、uint16(unsigned short)和uint32(unsigned int);
F –表示浮点类型。
·COUNT –指定每一个维度包含的元素数目。
例如,x这个数据通常有一个元素,但是像VFH这样的特征描述子就有308个。实际上这是在给每一点引入n维直方图描述符的方法,把它们当做单个的连续存储块。默认情况下,如果没有COUNT,所有维度的数目被设置成1。
·WIDTH –用点的数量表示点云数据集的宽度。
它能确定无序的点云中点的个数;也能确定有序点云一行中点的数目。有序点云数据集,意味着点云是类似于图像(或者矩阵)的结构,数据分为行和列。
WIDTH 640 # 每行有640个点
·HEIGHT –用点的数目表示点云数据集的高度。
它表示有序点云数据集的高度(行的总数);对于无序数据集它被设置成1(被用来检查一个数据集是有序还是无序)。
有序点云例子:
WIDTH 640 # 像图像一样的有序结构,有640行和480列,
HEIGHT 480 # 这样该数据集中共有640*480=307200个点
无序点云例子:
WIDTH 307200
HEIGHT 1 # 有307200个点的无序点云数据集
·VIEWPOINT–指定数据集中点云的获取视点。
VIEWPOINT有可能在不同坐标系之间转换的时候应用,在辅助获取其他特征时也比较有用,例如曲面法线,在判断方向一致性时,需要知道视点的方位,
视点信息被指定为平移(tx ty tz)+四元数(qw qx qy qz)。默认值是:
VIEWPOINT 0 0 0 1 0 0 0
·POINTS–指定点云中点的总数。从0.7版本开始,该字段就有点多余了,因此有可能在将来的版本中将它移除。
·DATA –指定存储点云数据的数据类型。从0.7版本开始,支持两种数据类型:ascii和二进制。
注意:文件头最后一行(DATA)的下一个字节就被看成是点云的数据部分了,它会被解释为点云数据。PCD文件的文件头部分必须以上面的顺序精确指定,之间用换行隔开。
3.数据存储类型
在0.7版本中,.PCD文件格式用两种模式存储数据:
如果以ASCII形式,每一点占据一个新行.从PCL 1.0.1版本开始,用字符串“nan”表示NaN,此字符表示该点的值不存在或非法等。
p_1
p_2
...
p_n
如果以二进制形式,这里数据是数组(向量)pcl::PointCloud.points的一份完整拷贝,
4.例子
# .PCD v.7 - Point Cloud Data file format
VERSION .7
FIELDS x y z rgb
SIZE 4 4 4 4
TYPE F FFF
COUNT 1 1 1 1
WIDTH 213
HEIGHT 1
VIEWPOINT 0 0 0 1 0 0 0
POINTS 213
DATA ascii
0.93773 0.33763 0 4.2108e+06
0.90805 0.35641 0 4.2108e+06
三、从PCD文件读取点云数据
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
int main (int argc, char** argv)
{
//创建一个PointCloud <PointXYZ> Boost共享指针并对其进行初始化。
//从堆中分配了一个PointCloud<pcl::PointXYZ>对象,把这个对象的指针赋值给了cloud
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);
}
//Loaded 个数 data points from 文件 with the following fields: x y z
std::cout << "Loaded "
<< cloud->width * cloud->height //点的个数
<< " data points from test_pcd.pcd with the following fields: "
<< std::endl;
for (std::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);
}
编译中遇到的问题
error C4996: 'std::copy::_Unchecked_iterators::_Deprecate': Call to 'std::copy' with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'
解决办法:
在工程属性—>C/C++—>命令行—>其他选项 中添加: -D_SCL_SECURE_NO_WARNINGS
四、从PCD文件中写入点云数据
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
int main (int argc, char** argv)
{
pcl::PointCloud<pcl::PointXYZ> cloud;
// 写入点云数据
cloud.width = 5;
cloud.height = 1;
cloud.is_dense = false;
cloud.points.resize (cloud.width * cloud.height);
for (std::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);//储存在pcd文件中
std::cerr << "Saved " << cloud.points.size () << " data points to test_pcd.pcd." << std::endl;
for (std::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);
}
五、连接两个点云中的点
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
int main (int argc, char** argv)
{
if (argc != 2)
{
std::cerr << "please specify command line arg '-f' or '-p'" << std::endl;
exit(0);
}
pcl::PointCloud<pcl::PointXYZ> cloud_a, cloud_b, cloud_c;//三个输入点云
pcl::PointCloud<pcl::Normal> n_cloud_b;//两个输出点云,储存法线的点云
pcl::PointCloud<pcl::PointNormal> p_n_cloud_c;//储存xyz和法线的点云
// 为两个点云写入数据,points cloud_a 和cloud_b,或者cloud_a 和n_cloud_b
cloud_a.width = 5;//点个数
cloud_a.height = cloud_b.height = n_cloud_b.height = 1;//都为无序点云
cloud_a.points.resize (cloud_a.width * cloud_a.height);
if (strcmp(argv[1], "-p") == 0)//比较字符串,判断是否为连接xyz和法线,如果不是,创建cloud_b
{
cloud_b.width = 3;
cloud_b.points.resize (cloud_b.width * cloud_b.height);
}
else
{
n_cloud_b.width = 5;//如果是创建n_cloud_b
n_cloud_b.points.resize (n_cloud_b.width * n_cloud_b.height);
}
//写入cloud_a的点
for (std::size_t i = 0; i < cloud_a.points.size (); ++i)
{
cloud_a.points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);
cloud_a.points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);
cloud_a.points[i].z = 1024 * rand () / (RAND_MAX + 1.0f);
}
//写入cloud_b或者n_cloud_b的点
if (strcmp(argv[1], "-p") == 0)
for (std::size_t i = 0; i < cloud_b.points.size (); ++i)
{
cloud_b.points[i].x = 1024 * rand () / (RAND_MAX + 1.0f);
cloud_b.points[i].y = 1024 * rand () / (RAND_MAX + 1.0f);
cloud_b.points[i].z = 1024 * rand () / (RAND_MAX + 1.0f);
}
else
for (std::size_t i = 0; i < n_cloud_b.points.size (); ++i)
{
n_cloud_b.points[i].normal[0] = 1024 * rand () / (RAND_MAX + 1.0f);
n_cloud_b.points[i].normal[1] = 1024 * rand () / (RAND_MAX + 1.0f);
n_cloud_b.points[i].normal[2] = 1024 * rand () / (RAND_MAX + 1.0f);
}
//输出刚刚写入点云的点的数据
std::cerr << "Cloud A: " << std::endl;
for (std::size_t i = 0; i < cloud_a.points.size (); ++i)
std::cerr << " " << cloud_a.points[i].x << " " << cloud_a.points[i].y << " " << cloud_a.points[i].z << std::endl;
std::cerr << "Cloud B: " << std::endl;
if (strcmp(argv[1], "-p") == 0)
for (std::size_t i = 0; i < cloud_b.points.size (); ++i)
std::cerr << " " << cloud_b.points[i].x << " " << cloud_b.points[i].y << " " << cloud_b.points[i].z << std::endl;
else
for (std::size_t i = 0; i < n_cloud_b.points.size (); ++i)//输出法线点的方式不一样
std::cerr << " " << n_cloud_b.points[i].normal[0] << " " << n_cloud_b.points[i].normal[1] << " " << n_cloud_b.points[i].normal[2] << std::endl;
// Copy the point cloud data
if (strcmp(argv[1], "-p") == 0)
{
cloud_c = cloud_a;//连接点云到c
cloud_c += cloud_b;
std::cerr << "Cloud C: " << std::endl;
for (std::size_t i = 0; i < cloud_c.points.size (); ++i)
std::cerr << " " << cloud_c.points[i].x << " " << cloud_c.points[i].y << " " << cloud_c.points[i].z << " " << std::endl;
}
else
{
pcl::concatenateFields (cloud_a, n_cloud_b, p_n_cloud_c);//连接字段到p_n_cloud_c
std::cerr << "Cloud C: " << std::endl;
for (std::size_t i = 0; i < p_n_cloud_c.points.size (); ++i)
std::cerr << " " <<
p_n_cloud_c.points[i].x << " " << p_n_cloud_c.points[i].y << " " << p_n_cloud_c.points[i].z << " " <<
p_n_cloud_c.points[i].normal[0] << " " << p_n_cloud_c.points[i].normal[1] << " " << p_n_cloud_c.points[i].normal[2] << std::endl;
}
return (0);
}
编译遇到的问题,生成exe文件后,进入cmd进行操作