前段时间课题组需要写一个点云数据降采样的小程序,要求读写内容都是LAS文件。本来CGAL可以做到,但是发现CGAL中写LAS文件的头文件好像有点问题,经过查阅资料决定用liblas解决。
踩了很多坑,好在最后问题终于解决了。这里把代码记录下来,以供后续查阅。
虽然采用的是pcl中的均匀采样,可是出来的结果貌似并不是很均匀,这个程序在release模式下运行没问题,但是debug模式总是会报一个错误,目前还没找到什么好的办法可以解决。。。。。。。不过目前的需求是能用就行~。
#include <liblas\liblas.hpp>
#include <fstream>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/keypoints/uniform_sampling.h>
#include <pcl/common/common.h>
#include <algorithm>
using namespace pcl;
void las2las(std::string fname)
{
//打开las文件
std::ifstream ifs(fname, std::ios::in | std::ios::binary);
liblas::ReaderFactory f;
liblas::Reader reader = f.CreateWithStream(ifs);
//读取las文件信息头
liblas::Header const& header = reader.GetHeader();
int nbPoints = header.GetPointRecordsCount();
//设置初始偏移量(这一步没有的话会报错,好像是一个什么内存上的问题,偏移量写入输出部分的文件头。保证两份数据在相同坐标体系下)
double x_setoff = header.GetOffsetX();
double y_setoff = header.GetOffsetY();
double z_setoff = header.GetOffsetZ();
//转换为pcl格式
pcl::PointCloud<pcl::PointXYZRGB>::Ptr in_cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
in_cloud->resize(nbPoints);
int i = 0;
while (reader.ReadNextPoint())
{
//坐标信息
in_cloud->points[i].x = reader.GetPoint().GetX();
in_cloud->points[i].y = reader.GetPoint().GetY();
in_cloud->points[i].z = reader.GetPoint().GetZ();
//颜色信息
uint16_t r1 = reader.GetPoint().GetColor().GetRed();
uint16_t g1 = reader.GetPoint().GetColor().GetGreen();
uint16_t b1 = reader.GetPoint().GetColor().GetBlue();
uint32_t r2 = ceil(((float)r1 / 65536)*(float)256);
uint32_t g2 = ceil(((float)g1 / 65536)*(float)256);
uint32_t b2 = ceil(((float)b1 / 65536)*(float)256);
uint32_t rgb = ((int)r2) << 16 | ((int)g2) << 8 | ((int)b2);
in_cloud->points[i].rgb = *reinterpret_cast<float*>(&rgb);
i++;
}
//均匀采样(pcl),这里的采样方法可以任意替换,只要代码没有问题,基本不会报错
pcl::PointCloud<pcl::PointXYZRGB>::Ptr filteredCloud(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::UniformSampling<pcl::PointXYZRGB> filter;
filter.setInputCloud(in_cloud);
filter.setRadiusSearch(0.15f); //本来希望将点云密度设置为0.1m,可是这里设置为0.15效果貌似更好点。
filter.filter(*filteredCloud);
//写入las文件
fname.replace(fname.find("in"), 2, "out");
std::ofstream ofs = std::ofstream(fname, std::ios::out | std::ios::binary);
//设置文件头、格式、偏移量、缩放因子、点数、缩放因子
liblas::Header f_header;
f_header.SetVersionMajor(1);
f_header.SetVersionMinor(2);
f_header.SetDataFormatId(liblas::PointFormatName::ePointFormat3);
f_header.SetOffset(x_setoff,y_setoff,z_setoff); //这里注意偏移量要从读取部分的头文件获取
f_header.SetScale(0.001, 0.001, 0.001);
int out_p_n = filteredCloud->size();
f_header.SetPointRecordsCount(out_p_n);
liblas::Writer writer(ofs, f_header);
liblas::Point point(&f_header);
//转换为las
for (size_t i = 0; i < filteredCloud->size(); ++i)
{
double x = filteredCloud->points[i].x;
double y = filteredCloud->points[i].y;
double z = filteredCloud->points[i].z;
point.SetX(x);
point.SetY(y);
point.SetZ(z);
uint32_t r = (uint32_t)filteredCloud->points[i].r;
uint32_t g = (uint32_t)filteredCloud->points[i].g;
uint32_t b = (uint32_t)filteredCloud->points[i].b;
liblas::Color pointColor(r, g, b);
point.SetColor(pointColor);
writer.WritePoint(point);
}
writer.SetHeader(f_header);
ofs.flush();
ofs.close();
std::cout << fname << " " <<"finished!"<<std::endl;
in_cloud->clear();
filteredCloud->clear();
}
int main()
{
测试
//las2las("E:/temp/in/Tile_1321222320002302112.las");
//这里是读取txt中的全部文件名。有一个小插件可以获取文件夹下所有文件名,并保存在一个txt中
std::ifstream ifs;
ifs.open("G:/Production_3 (2)_in/dir.txt");
std::vector<std::string> dir;
while (!ifs.eof())
{
std::string str;
std::getline(ifs, str);
dir.push_back(str);
}
ifs.close();
//std::cout << dir.size() << std::endl;
for (size_t i = 0; i < dir.size()-1; ++i)
{
las2las(dir[i]);
}
return EXIT_SUCCESS;
}