LAS(Light Detection and Ranging)格式是一种广泛使用的用于存储点云数据的格式。点云数据通常由三维空间中的大量点组成,每个点带有其在空间中的坐标(X, Y, Z)以及其他可能的属性,如颜色(红、绿、蓝通道值),强度,分类等。
处理LAS格式的点云数据通常涉及以下步骤:
- 读取LAS文件:解析LAS文件的头部信息和数据点。
- 访问点云数据:根据需要读取和处理点云中的特定点或区域。
- 数据过滤:根据特定标准(如空间位置、分类代码等)过滤点云数据。
- 数据分析:执行所需的分析,如计算点云的统计数据、检测特征或生成三维模型。
- 数据可视化:将点云数据渲染为三维模型,以便于观察和分析。
- 数据导出:将处理后的点云数据导出为其他格式,如PLY、TXT等
那么,如何实现对las格式的点云数据进行分类,对于很多点云处理的初学者来说,是一个好奇又难解决的问题。采取CC或者PCM等点云处理软件打开LAS格式的点云数据,我们往往发现,点云数据本身已经被赋予了地物实体原本具备的一般色彩特性。因此,笔者猜测,LAS格式存储的点云数据可能本身就存储了包括地物类型的某些属性。
在此,笔者先推荐一些点云数据方向的前沿文章:
(2023年新疆大学、中科院等点云分类最新综述) Deep ...
基于laspy的点云数据存取及基于Open3D的点云数据可视化 ...
不同格式点云存储结构(txt、pcd、las、ply)整理以及基本的 ...
那么,对于LAS格式的点云数据,存在一种统一的分类编码系统。这种分类编码由美国摄影测量与遥感协会(ASPRS)定义,并被广泛接受和使用。根据ASPRS的标准,LAS文件中的点云数据可以按照不同的类别进行分类,每个类别有一个特定的数值编码。这些分类代码帮助用户识别点云中的不同类型的地物,例如地面、植被、建筑物等。分类代码通常是以5位二进制数或其等效的十进制数值表示的,可以用来表示多达32种不同的类别,这些分类代码在LAS文件的点记录部分的“Classification”字段中明确指定。一些常见的分类代码包括但不限于:
- 0: 未分类(Unclassified)
- 1: 地面(Ground)
- 2: 低矮植被(Low Vegetation)
- 3: 中等高度植被(Medium Vegetation)
- 4: 高植被(High Vegetation)
- 5: 建筑物(Buildings)
- 6: 水体(Water)
- 7: 道路(Road Surfaces)
- 8-9: 重叠点(Overlapping Points)
因此,根据上面的概述,我们可以较为容易地实现以下的python代码去实现点云数据:
import laspy
# 定义一个函数来根据分类代码分类点云
def classify_point_cloud(las_file_path, output_dir):
# 读取LAS文件
with laspy.open(las_file_path, mode='rd') as las:
points = las.read()
# 创建字典来存储不同分类的点云
classified_points = {
"Unclassified": [],
"Ground": [],
"Low Vegetation": [],
"Medium Vegetation": [],
"High Vegetation": [],
"Buildings": [],
"Water": [],
"Road Surfaces": []
}
# 遍历点云数据,根据分类代码将点分配到相应的列表
for point in points:
classification = point.classification
if classification == 0:
classified_points["Unclassified"].append(point)
elif classification == 1:
classified_points["Ground"].append(point)
elif classification == 2:
classified_points["Low Vegetation"].append(point)
elif classification == 3:
classified_points["Medium Vegetation"].append(point)
elif classification == 4:
classified_points["High Vegetation"].append(point)
elif classification == 5:
classified_points["Buildings"].append(point)
elif classification == 6:
classified_points["Water"].append(point)
elif classification == 7:
classified_points["Road Surfaces"].append(point)
# 将不同分类的点云写入单独的LAS文件
for category, points_list in classified_points.items():
if points_list:
output_file_path = f"{output_dir}/classified_{category}.las"
with laspy.open(output_file_path, mode='w', header=las.header) as out_file:
out_file.write(points_list)
print(f"Classified points of category '{category}' saved to {output_file_path}")
# 使用示例
las_file_path = 'path_to_your_las_file.las' # 替换为你的LAS文件路径
output_dir = 'classified_output' # 输出目录,如果不存在则需要创建
classify_point_cloud(las_file_path, output_dir)
正如上述代码所示,直接调用python中带有的函数库Laspy是处理LAS格式点云文件的方式。
但是上述代码存在致命缺陷:由于点云数据庞大的数据量(比如一颗树就可能由上百万以上的点构成),因此这种遍历算法耗时过长,因此可以作为处理小规模的点云数据。
笔者认为提高点云数据处理效率通常涉及到减少磁盘I/O操作、优化内存使用、并行处理以及利用高效的数据结构和算法等多个方面。以下是一些提升Python中点云分类代码效率的方法:
-
避免数据复制:在处理大型点云数据时,避免将所有点云数据加载到内存中,应逐个处理点云。
-
使用生成器:使用生成器表达式代替列表推导,尤其是在处理大型数据集时。
-
减少文件读写:将所有分类后的点云一次性写入文件,而不是分批写入。
-
并行处理:如果你的机器有多个CPU核心,可以考虑使用并行处理。不过,由于
laspy
是逐个点读取和写入,它的并行化可能比较困难。如果数据量非常大,可能需要考虑使用专门的点云处理库,如PCL(Point Cloud Library),它对并行处理有更好的支持。 -
优化数据结构:使用合适的数据结构,例如,如果分类操作可以简单地通过映射来完成,那么使用一个字典来映射分类代码到输出文件可能更快。
-
减少数据转换:避免不必要的数据类型转换。
-
利用就地操作:如果可能,使用就地操作来更新数据,而不是创建新的数据副本。
因此,为了提高算法效率, 笔者首先为每个分类创建了一个输出文件路径的字典。然后,迭代原始的 LasReader
对象来处理每个点。对于每个点,检查它的分类并根据分类打开对应的输出文件,然后将该点写入到该文件中。这里使用了with语句来确保文件会在写入后正确关闭。
import os
import laspy
# 定义分类的描述
classification_description = {
1: "Ground",
2: "Low Vegetation",
3: "Medium Vegetation",
4: "High Vegetation",
5: "Buildings",
6: "Water",
7: "Road Surfaces",
0: "Unclassified"
}
def classify_point_cloud(las_file_path, output_dir):
# 确保输出目录存在
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# 读取原始的LAS文件
with laspy.open(las_file_path, mode='r') as las_reader:
header = las_reader.header
# 初始化分类点云字典
classified_points = {cls_code: [] for cls_code in classification_description}
# 分类点云数据
for point in las_reader:
cls_code = point.classification
if cls_code in classified_points:
classified_points[cls_code].append(point)
# 为每个分类写入到不同的文件
for cls_code, points in classified_points.items():
if points: # 如果分类中有至少一个点
description = classification_description[cls_code]
output_file_path = os.path.join(output_dir, f'classified_{description}.las')
with laspy.open(output_file_path, mode='w', header=header) as las_writer:
las_writer.write(points) # 写入整个分类的点云
# 使用示例
las_file_path = 'path_to_your_las_file.las' # 替换为你的LAS文件路径
output_dir = 'classified_output' # 输出目录
classify_point_cloud(las_file_path, output_dir)
然而,笔者上述代码存在少许问题没有解决:las_reader不被允许直接迭代,改为las_reader.read()后,后续代码任存在变量不可被变换的问题。需要后续继续调整和改进。
最后,署名(作者:余心远)