【C++】 pcl库的输入输出(IO)
PCD文件格式
这是用gedit打开的.pcd文件的文件头
- VERSION:指定PCD文件版本
- FIELDS:指定一个点可以有的每一个维度和字段的名字
- SIZE:用字节数指定每一个维度的大小
unsigned char/char 1 byte
unsigned short/short 2 bytes
unsigned int/int/float 4 bytes
double 8 bytes
- TYPE:用一个字符指定一个维度的类型
I:int8(char)、int16(short)、int32(int)
U:uint8(unsigned char)、uint16(unsigned short)、uint32(unsigned int)
F:浮点类型
- COUNT:指定每一个维度包含的元素数目。
- WIDTH:如果是有序点云,用点的数量表示点云数据集的宽度(一行中点的数目)。如果是无序点云,表示点云中点的个数(等于POINTS)
- HEIGHT:如果是有序点云,用点的数量表示点云数据集的高度(行的总数)。如果是无序点云,被设置为1
- VIEWPOINT:指定数据集中点云的获取视点。VIEWPOINT有可能在不同坐标系之间转换的时候应用。视点信息被指定为平移(txtytz)+四元数(qwqxqyqz),默认值为VIEPOINT 0 0 0 1 0 0 0
- POINTS:指定点云中点的总数
- DATA:指定储存点云数据的数据类型。从0.7版本开始,支持两种数据类型:ASCII和二进制。
PCD文件IO操作
pcl::PCLPointCloud2与pcl::PointCloud的区别
PCLPointCloud2 v.s. PointCloud
PCL has replaced the “sensor_msgs::PointCloud2” type to “pcl::PCLPointCloud2”.
PointCLoud2 is a ROS message type.
=> So you had to use the PCLPointCloud2 type in PCL when you want to have interactions with ROS.
从.pcd里读取
#include <iostream>
#include <pcl/io/pcd_io.h> // PCD读写类相关的头文件
#include <pcl/point_types.h> // PCL中支持的点类型头文件
int main(int argc, char** argv)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); // 创建一个PointCloud<PointXYZ> boost共享指针,并进行实例化为cloud
if(pcl::io::loadPCDFile<pcl::PointXYZ>("test_pcd.pcd", *cloud) == -1)// 调用函数loadPCDFile,如果返回-1,则是打开点云文件失败
{
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);
}
其中函数loadPCDFile定义在pcl/io/pcd_io.h中,内容如下:
/** \brief Load any PCD file into a templated PointCloud type
* \param[in] file_name the name of the file to load
* \param[out] cloud the resultant templated point cloud
* \ingroup io
*/
template<typename PointT> inline int
loadPCDFile (const std::string &file_name, pcl::PointCloud<PointT> &cloud)
{
pcl::PCDReader p;
return (p.read (file_name, cloud));
}
用cmake编译:
CMakeLists.txt中内容如下:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
// 指定cmake的最小版本
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}-std=c++11")
// Enable C++11
project(pcd_read)
// 指定项目名
find_package(PCL 1.2 REQUIRED)
// 找到PCL包
include_directories(${PCL_INCLUDE_DIRS})
// 用于设定目录,这些设定的目录将被编译器用来查找 include 文件
link_directories(${PCL_LIBRARY_DIRS})
// 动态链接库或静态链接库的搜索路径
add_definitions(${PCL_DEFINITIONS})
// 用于添加编译器命令行标志(选项),通常的情况下我们使用其来添加预处理器定义
add_executable(pcd_read pcd_read.cpp)
// 将指定文件source编译成可执行文件,相当于g++的-o pcd_read pcd_read.cpp
target_link_libraries(pcd_read ${PCL_LIBRARIES})
// 用于指定 target 需要链接 item1 item2 …。这里 target 必须已经被创建,链接的 item 可以是已经存在的 target(依赖关系会自动添加)
运行结果:
向.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; // 设置点云width
cloud.height=1; // 设置点云height
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);
// rand()是随机产生一个从0到无限大的任意整数
// RAND_MAX是宏定义的一个字符常量
// 1.0f,f代表数字类型是浮点型
}
pcl::io::savePCDFileASCII("test_pcd.pcd", cloud); // 按ASCII码写入test_pcd.pcd
std::cout << "Saved " << cloud.points.size() << " data points to test_pcd.pcd." << 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);
}
运行结果:
两个点云中的数据或字段连接
连接两个.pcd文件成一个.pcd文件:进行连接操作前要确保两个数据集中的类型相同、维度相同。
连接两个不同点云的字段:两个数据集中点的数目必需一样(例如:点云A是N点的xyz点,点云B是N点的rgb点,则连接两个字段形成的点云C是N点的xyzrgb)。
Normal和PointNormal在point_types.h中的定义如下:
/** \brief Members: float normal[3], curvature
* \ingroup common
*/
struct Normal;
/** \brief Members: float x, y, z; float normal[3], curvature
* \ingroup common
*/
struct PointNormal;
可知,Normal是一个三维浮点型数组,这一个数组代表一个点的法线方向
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
int main(int argc, char** argv)
{
if(argc!=2) // 如果参数数量(包括可执行文件名称)不是2
{
std::cerr << "please specify command line arg '-f' or '-p'" << std::endl; // std::cerr专门用作输出错误信息
exit(0);
}
pcl::PointCloud<pcl::PointXYZ> cloud_a, cloud_b, cloud_c; // 新建三个点云cloud_a、cloud_b、cloud_c
pcl::PointCloud<pcl::Normal> n_cloud_b;
pcl::PointCloud<pcl::PointNormal> p_n_cloud_c;
cloud_a.width = 5; // cloud_a的宽度是5
cloud_a.height = cloud_b.height = n_cloud_b.height = 1; // a、b、c都是无序点云
cloud_a.points.resize(cloud_a.width * cloud_a.height);
// 如果参数是-p,则连接数据,需要相同的数据类型和维度,cloud_b也是PointXYZ
if(strcmp(argv[1], "-p")==0) // strcmp:如果两个字符串相等,返回0
{
cloud_b.width=3; // 都是width和height,width不需要一定相等
cloud_b.points.resize(cloud_b.width * cloud_b.height);
}
// 如果参数是-f,则连接字段,需要相同的数据数目,cloud_b是Normal
else
{
n_cloud_b.width=5; // 此时width必需相等
n_cloud_b.points.resize(n_cloud_b.width * n_cloud_b.height);
}
for(size_t i=0; i<cloud_a.points.size(); ++i) // cloud_a随机初始化
{
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);
}
if(strcmp(argv[1],"-p")==0) // cloud_b随机初始化
{
for(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(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::cout << "Cloud A: " << std::endl;
for(size_t i=0; i<cloud_a.points.size(); ++i)
{
std::cout << " " << cloud_a.points[i].x << " " << cloud_a.points[i].y << " "<<cloud_a.points[i].z << std::endl;
}
std::cout << "Cloud B: " << std::endl;
if(strcmp(argv[1],"-p")==0)
{
for(size_t i=0; i<cloud_b.points.size(); ++i)
{
std::cout<<" "<<cloud_b.points[i].x<<" "<<cloud_b.points[i].y<<" "<<cloud_b.points[i].z<<std::endl;
}
}
else
{
for(size_t i=0; i<n_cloud_b.points.size(); ++i)
{
std::cout<<" "<<n_cloud_b.points[i].normal[0]<<" "<<n_cloud_b.points[i].normal[1]<<" "<<n_cloud_b.points[i].normal[2]<<std::endl;
}
}
// 如果是连接数据
if(strcmp(argv[1],"-p")==0)
{
cloud_c=cloud_a;
cloud_c+=cloud_b;
// 就直接相加就好了
std::cout << "Cloud C: " << std::endl;
for(size_t i=0; i<cloud_c.points.size(); ++i)
std::cout<<" "<<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); // 调用函数concatenateFields
std::cout << "Cloud C: " << std::endl;
for(size_t i=0; i<p_n_cloud_c.points.size(); ++i)
std::cout<<" "<< 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);
}
-p执行结果如下:
-f执行结果如下:
结语
如果您有修改意见或问题,欢迎留言或者通过邮箱和我联系。
手打很辛苦,如果我的文章对您有帮助,转载请注明出处。