点云las文件读取

由于大多数点云文件都是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);
 }
}
-------------本文结束感谢您的阅读-------------