由于大多数点云文件都是las格式的,所以有必要解析下此文件格式并提取出需要的点云坐标信息。
las格式是ASPRS下属的LIDAR委员会制定了LIDAR数据的标准交换格式,较好地顾及了LIDAR数据的特点,其结构合理,便于扩展。从本质上来说,LAS格式是一种二进制文件格式。一个符合LAS标准的LIDAR文件分为三个部分:公用文件头块(PUBLICHEADERBLOCK)、变量长度记(VARIABLELENGTHRECORDS)和点数据记录(POINTDATARECORD)。
las目前有好几种版本,但主要都包含上述三个部分,此程序主要参考1.2版本。las1.2版本介绍可从这里下载。
1. 结构体定义:
1.1 PUBLICHEADERBLOCK:
公共头用来记录数据集的基本信息,包括LIDAR点总数、数据范围、LIDAR点格式、变长记录总数、LAS文件的生成方式等。
struct public_Header_Block
{
char file_signature[4];
unsigned short file_source_id;
unsigned short global_encoding;
unsigned long project_id_guid_data1;
unsigned short project_id_guid_data2;
unsigned short project_id_guid_data3;
unsigned char project_id_guid_data4[8];
unsigned char version_major;
unsigned char version_minor;
char systemID[32];
char generatingSoftware[32];
unsigned short fileCreationDay;
unsigned short fileCreationYear;
unsigned short headerSize;
unsigned long pointDataOffset;
unsigned long numberOfVariableRecords;
unsigned char pointDataFormatID;
unsigned short pointDataRecordLength;
unsigned long numberOfPointRecords;
unsigned long numberOfPointsByReturn[5];
double x_scale;
double y_scale;
double z_scale;
double x_offset;
double y_offset;
double z_offset;
double x_max;
double x_min;
double y_max;
double y_min;
double z_max;
double z_min;
};
1.2. VARIABLELENGTHRECORDS:
变长记录用来记录数据的投影信息、元数据信息以及用户自定义信息等,是LAS格式中最灵活的部分。每条变长记录包括固定的变长记录头和灵活的扩展域两部分。一条变长记录的长度=变长记录头长度+扩展域长度。
struct variable_Length_Record_Header
{
unsigned short reserved;
char user_ID[16];
unsigned short record_ID;
unsigned short record_Length_After_Header;
char description[32];
};
1.3. POINTDATARECORD:
点集记录部分保存了大量的L IDAR脚点信息,LAS支持的LIDAR点记录格式有100种,从Format0-Format99。在同一个LAS文件中,只有一种L IDAR点格式。而且要与公共头中的点格式一致。
struct point_Data_Record
{
long x;
long y;
long z;
unsigned short intensity;
unsigned char return_Number;//返回相关
unsigned char classification;//分类相关
unsigned char scanAngleRank;
unsigned char userData;
unsigned short pointSourceID;
double time; //GPS Time
unsigned short r;
unsigned short g;
unsigned short b;
};
2. 读取类函数定义:
使用c++对las文件读取,等于读取一个二进制文件,其中主要使用二进制文件读取的两个函数:infname.seekg()和infname.read(),前一个函数主要用于定位指针到指定位置,后者则按数据类型读取数据。
如下面几行代码为读取文件信息头里面第一个符号文件标志,如果成功应该为“LASF’’.
string str = filename + ".las";
ifstream infname(str, ios::binary | ios::in);
infname.seekg(0, ios::beg);
infname.read((char*)header.file_signature, sizeof(header.file_signature));
......
class ReadLas
{
public:
void read_Header();
void out_Header();
void read_var_Length_Record();
void out_var_Length_Record();
void read_Point_Data_Record();
void output_PointData(string outname);
public_Header_Block header;
vector<variable_Length_Record_Header> variableRecord;
vector<point_Data_Record> pointdataRecord;
vector<las_point> laspoint;
string filename;
};
3. 源码:
3.1 Read Public Header Block:
void ReadLas::read_Header()
{
string str = filename + ".las";
ifstream infname(str, ios::binary | ios::in);
infname.seekg(0, ios::beg);
infname.read((char*)header.file_signature, sizeof(header.file_signature));
infname.read((char*)&header.file_source_id, sizeof(header.file_source_id));
infname.read((char*)&header.global_encoding, sizeof(header.global_encoding));
infname.read((char*)&header.project_id_guid_data1, sizeof(header.project_id_guid_data1));
infname.read((char*)&header.project_id_guid_data2, sizeof(header.project_id_guid_data2));
infname.read((char*)&header.project_id_guid_data3, sizeof(header.project_id_guid_data3));
infname.read((char*)header.project_id_guid_data4, sizeof(header.project_id_guid_data4));
string project_id_guid_data4 = (char*)header.project_id_guid_data4;
infname.read((char*)&header.version_major, sizeof(header.version_major));
infname.read((char*)&header.version_minor, sizeof(header.version_minor));
infname.read((char*)header.systemID, sizeof(header.systemID));
infname.read((char*)header.generatingSoftware, sizeof(header.generatingSoftware));
infname.read((char*)&header.fileCreationDay, sizeof(header.fileCreationDay));
infname.read((char*)&header.fileCreationYear, sizeof(header.fileCreationYear));
infname.read((char*)&header.headerSize, sizeof(header.headerSize));
infname.read((char*)&header.pointDataOffset, sizeof(header.pointDataOffset));
infname.read((char*)&header.numberOfVariableRecords, sizeof(header.numberOfVariableRecords));
infname.read((char*)&header.pointDataFormatID, sizeof(header.pointDataFormatID));
infname.read((char*)&header.pointDataRecordLength, sizeof(header.pointDataRecordLength));
infname.read((char*)&header.numberOfPointRecords, sizeof(header.numberOfPointRecords));
infname.read((char*)header.numberOfPointsByReturn, sizeof(header.numberOfPointsByReturn));
infname.read((char*)&header.x_scale, sizeof(header.x_scale));
infname.read((char*)&header.y_scale, sizeof(header.y_scale));
infname.read((char*)&header.z_scale, sizeof(header.z_scale));
infname.read((char*)&header.x_offset, sizeof(header.x_offset));
infname.read((char*)&header.y_offset, sizeof(header.y_offset));
infname.read((char*)&header.z_offset, sizeof(header.z_offset));
infname.read((char*)&header.x_max, sizeof(header.x_max));
infname.read((char*)&header.x_min, sizeof(header.x_min));
infname.read((char*)&header.y_max, sizeof(header.y_max));
infname.read((char*)&header.y_min, sizeof(header.y_min));
infname.read((char*)&header.z_max, sizeof(header.z_max));
infname.read((char*)&header.z_min, sizeof(header.z_min));
}
3.2 read in variable length records:
void ReadLas::read_var_Length_Record()
{
string str = filename + ".las";
ifstream infname(str, ios::binary | ios::in);
infname.seekg(header.headerSize, ios::beg);
int num = header.numberOfVariableRecords;
for (int i = 0; i < num; i++)
{
variable_Length_Record_Header var_Len_Record;
infname.read((char*)&var_Len_Record.reserved, sizeof(var_Len_Record.reserved));
infname.read((char*)var_Len_Record.user_ID, sizeof(var_Len_Record.user_ID));
infname.read((char*)&var_Len_Record.record_ID, sizeof(var_Len_Record.record_ID));
infname.read((char*)&var_Len_Record.record_Length_After_Header, sizeof(var_Len_Record.record_Length_After_Header));
infname.read((char*)var_Len_Record.description, sizeof(var_Len_Record.description));
variableRecord.push_back(var_Len_Record);
infname.seekg(var_Len_Record.record_Length_After_Header, ios::cur);
}
};
3.3 read point data record:
void ReadLas::read_Point_Data_Record()
{
int c = header.pointDataOffset;
int n = header.numberOfPointRecords;
int formatlength;
switch (header.pointDataFormatID)
{
case 0:
formatlength = 20;
break;
case 1:
formatlength = 28;
break;
case 2:
formatlength = 26;
break;
case 3:
formatlength = 34;
break;
default:
std::cout << "Data format not supported." << std::endl;
}
//start to read point data to Vector<point_Data_Record>
string str = filename + ".las";
ifstream infname(str, ios::binary | ios::in);
infname.seekg(c + 0, ios::beg);//跳转指针到达点数据记录起始位置
for (int j = 0; j < n; j++)
{
point_Data_Record point;
//infname.seekg(c + 0, ios::beg);
infname.read((char*)&point.x, sizeof(point.x));
//infname.seekg(c + 4, ios::beg);
infname.read((char*)&point.y, sizeof(point.y));
//infname.seekg(c + 8, ios::beg);
infname.read((char*)&point.z, sizeof(point.z));
//infname.seekg(c + 12, ios::beg);
infname.read((char*)&point.intensity, sizeof(point.intensity));
//infname.seekg(c + 14, ios::beg);
infname.read((char*)&point.return_Number, sizeof(point.return_Number));
//infname.seekg(c + 15, ios::beg);
infname.read((char*)&point.classification, sizeof(point.classification));
//infname.seekg(c + 16, ios::beg);
infname.read((char*)&point.scanAngleRank, sizeof(point.scanAngleRank));
//infname.seekg(c + 17, ios::beg);
infname.read((char*)&point.userData, sizeof(point.userData));
//infname.seekg(c + 18, ios::beg);
infname.read((char*)&point.pointSourceID, sizeof(point.pointSourceID));
if (header.pointDataFormatID == 1)
{
//infname.seekg(c + 20, ios::beg);
infname.read((char*)&point.time, sizeof(point.time));
}
if (header.pointDataFormatID == 2)
{
//infname.seekg(c + 20, ios::beg);
infname.read((char*)&point.r, sizeof(point.r));
infname.read((char*)&point.g, sizeof(point.g));
infname.read((char*)&point.b, sizeof(point.b));
}
if (header.pointDataFormatID == 3)
{
//infname.seekg(c + 28, ios::beg);
infname.read((char*)&point.r, sizeof(point.r));
infname.read((char*)&point.g, sizeof(point.g));
infname.read((char*)&point.b, sizeof(point.b));
}
//infname.read((char*)&point.temp, formatlength - 28 );
pointdataRecord.push_back(point);
}
}