lee-romantic 's Blog
Everything is OK!
Toggle navigation
lee-romantic 's Blog
主页
About Me
归档
标签
点云数据格式bin转为pcd的问题
2023-03-31 18:27:54
62
0
0
lee-romantic
# 1、问题 常见点云存储方式有`pcd、ply、txt、bin`文件等。kitti数据集的点云是bin格式的,而pcl的数据格式为pcd格式。在使用点云库进行开发时,一般需要将bin格式的点云数据转为pcd格式。网上有很多介绍点云格式如何转换的博客,但是很多的bin2pcd函数的写法都存在小问题,这里记录下我发现的问题并给出解决办法。 # 2、PointXYZI的内存设计 首先介绍下比较PCL中常见的PointXYZI点类型。 `PointXYZI`是一个简单的XYZ坐标加`intensity`的point类型,理想情况下,这4个变量将新建单独一个结构体,并且满足存储对齐,然而由于PCL的大部分操作会把`data[4]`元素设置成0或1(用于变换),不能让intensity与XYZ在同一个结构体中。因为如果这样的话其内容将会被覆盖。 例如两个点的点积会把他们的第4个元素设置成0, 否则该点积没有意义,等等。因此,对于兼容存储对齐用3个额外的浮点数来填补`intensity`,这样在存储方面效率较低但是符合存储对齐要求,运行效率较高。 `PointXYZI`结构体源代码如下: ``` // PointXYZI占32个字节,8个浮点数的空间,但是只有四个有效,其余的为填充 struct PointXYZI { union { float data[4]; struct { float x; float y; float z; } } union { struct { float intensity; } float data_c[4]; } } ``` # 3、问题写法一 ## 3.1 问题发现 [方法一](https://blog.csdn.net/zzh516451964zzh/article/details/125916086)生成的pcd文件中,可以正常可视化,但是由于最后一个点存在`x=0,y=0,z=0,intensity=0`的情况,生成深度图时,会出现崩溃问题。 对于使用ascll编码的pcd文件是可以用记事本打开的, 我们打开导致崩溃的pcd文件,可以发现最后一个点是`0 0 0 0`,这将导致创建深度图时出现崩溃(原因未知,或许可以去PCL的github项目下提个issue? 至少不应该崩溃?):  导致奔溃的截图:  ## 3.2 问题原因 至于为什么会多出一个`x=0,y=0,z=0,intensity=0`的冗余点,就在于for循环中的eof判断。按照一般思维,应该就是到达文件尾,就eof()应返回true,但事实上,在读完最后一个数据时,eofbit仍然是false。只有当流再往下读取时,发现文件已经到结尾了,才会将标志eofbit修改为true。这也就是为什么使用`input.good() && !input.eof()`还是true,会出多现读一行的原因。可以使用`readfile.peek()==EOF`或者直接使用`while(read(...))`来判断是否到达文件结尾,这样就能避免多读一行。这个问题可以[参考](https://blog.csdn.net/qq_43458555/article/details/126942026) ``` void topcd(string infile,string outfile) { // load point cloud fstream input(infile.c_str(), ios::in | ios::binary); if (!input.good()) { cerr << "Could not read file: " << infile << endl; exit(EXIT_FAILURE); } input.seekg(0, ios::beg); pcl::PointCloud<PointXYZI>::Ptr points(new pcl::PointCloud<PointXYZI>); int i; for (i = 0; input.good() && !input.eof(); i++) { PointXYZI point; input.read((char*)&point.x, 3 * sizeof(float)); input.read((char*)&point.intensity, sizeof(float)); points->push_back(point); } cout << "Read point cloud with " << i << " points, writing to " << outfile << endl; pcl::PCDWriter writer; // Save DoN features writer.write<PointXYZI>(outfile, *points, false); } ``` # 4、问题写法二 ## 4.1 问题发现 [写法二](https://blog.csdn.net/SmallPink0713/article/details/129201592)也是差不多的,能正常可视化和创建深度图,但是打开生成的pcd文件后,发现点的数量减少了一半: ``` void topcd(string infile,string outfile) { // load point cloud fstream input(infile.c_str(), ios::in | ios::binary); if (!input.good()) { cerr << "Could not read file: " << infile << endl; exit(EXIT_FAILURE); } pcl::PointCloud<PointXYZI>::Ptr points(new pcl::PointCloud<PointXYZI>); pcl::PointXYZI point; // 这里实际上是有问题的,sizeof(pcl::PointXYZI)=32,而不是四个浮点数的大小16,会导致漏掉一半的点 while (input.read(char *)&point.x, sizeof(pcl::PointXYZI)) { points->push_back(point); } input.close(); pcl::PCDWriter writer; // Save DoN features writer.write<PointXYZI>(outfile, *points, false); } ``` 这种写法,使用了while循环避免了直接进行eof判断,不会多读一行导致在pcd文件末尾留下`x=0,y=0,z=0,intensity=0`的点,因此在创建深度图时并不会崩溃,貌似不会出现什么问题。 但是根据前面对于`PointXYZI`的内存模型的介绍,可以知道sizeof(PointXYZI)等于`8*sizeof(float)`,而不是四个浮点数的大小`4*sizeof(float)`, 而bin文件中却并未进行内存对齐,bin文件每个点存储的`xyz+intensity`四个浮点数。 因此,除非bin文件中,也像`PointXYZI`的内存设计一样存在内存对齐,否则直接使用`sizeof(PointXYZI)`进行`read`读取数据就会导致生成的pcd中点的数量减半,且所有的点的`intensity`都是0,这显然不是我们所希望的。 ## 4.2 问题解决 - 解决办法就是将 `while (input.read(char *)&point.x, sizeof(pcl::PointXYZI)) ` 改成 `while (input.read((char *)&point.x, 3 * sizeof(float)) && input.read((char *)&point.intensity, sizeof(float)))`。 - **注意**,如果直接改成`while (input.read(char *)&point.x, 4*sizeof(float))`将使得所有的点的`intensity`为0,这也是不对的。 ## 4.3 附完整bin2pcd代码 ``` // // Created by libo on 2023/3/23. // 使用PCL批量将点云.bin文件转.pcd // #include <pcl/point_types.h> #include <pcl/io/pcd_io.h> #include <pcl/common/io.h> #include <pcl/io/vtk_io.h> #include <vector> #include <iostream> #include <fstream> #include <string> using namespace pcl; using namespace std; void topcd(string infile,string outfile) { // load point cloud fstream input(infile.c_str(), ios::in | ios::binary); if (!input.good()) { cerr << "Could not read file: " << infile << endl; exit(EXIT_FAILURE); } // input.seekg(0, ios::beg); pcl::PointCloud<PointXYZI>::Ptr points(new pcl::PointCloud<PointXYZI>); // 正确写法一 int i; for (i = 0; input.good() && !(input.peek()==EOF); i++) { PointXYZI point; input.read((char*)&point.x, 3 * sizeof(float)); input.read((char*)&point.intensity, sizeof(float)); points->push_back(point); } cout << "Read point cloud with " << i << " points, writing to " << outfile << endl; // 正确写法二 // pcl::PointXYZI point; // while (input.read((char *)&point.x, 3 * sizeof(float)) && input.read((char *)&point.intensity, sizeof(float))) { // points->push_back(point); // } // input.close(); pcl::PCDWriter writer; // Save DoN features writer.write<PointXYZI>(outfile, *points, false); } int main(int argc, char** argv) { string binpath = R"(..\..\data_source\kitti\2011_09_26\2011_09_26_drive_0027_sync\velodyne_points\data\)"; string pcdpath = R"(..\..\data_source\kitti\2011_09_26\2011_09_26_drive_0027_sync\velodyne_points\pcd_data\)"; vector<boost::filesystem::path> stream(boost::filesystem::directory_iterator{binpath}, boost::filesystem::directory_iterator{}); sort(stream.begin(), stream.end()); auto streamIterator = stream.begin(); int j = 0; while(streamIterator != stream.end()){ string binfile((*streamIterator).string()); stringstream ss; string str; ss << setw(10) << setfill('0') << j; // 补零 ss >> str; string surfix = ".pcd"; string pcdfile = pcdpath + str + surfix; topcd(binfile,pcdfile); streamIterator++; j++; } return 0; } ``` # 5、参考文献 《点云库PCL学习教程》 [点云文件转换——bin文件转pcd](https://blog.csdn.net/SmallPink0713/article/details/129201592) [使用PCL批量将点云.bin文件转.pcd](https://blog.csdn.net/zzh516451964zzh/article/details/125916086) [C++ fstream流的eof()使用的问题](https://blog.csdn.net/qq_43458555/article/details/126942026)
上一篇:
C++ 头文件被多个cpp包含导致的重定义问题
下一篇:
C++ 左值右值、移动语义、完美转发
0
赞
62 人读过
新浪微博
微信
腾讯微博
QQ空间
人人网
提交评论
立即登录
, 发表评论.
没有帐号?
立即注册
0
条评论
More...
文档导航
没有帐号? 立即注册