点云处理是自动驾驶、三维视觉和测绘建模等领域的核心基础,而点云读取和指定坐标的邻域点云提取,是进行点云分析的基础。本文将基于 Python 的 laspy 和 pointcloud 库,介绍从点云文件读取到邻域点云精准提取的全流程,代码简洁可直接复用。一、前置准备:laspy、pointcloud、scipy和 库laspy库主要用于 LAS/LAZ 格式点云文件的高效读写,pointcloud 专注点云处理。scipy 是数据分析 / 三维处理的常用库,核心通过构建 KD 树空间索引,实现半径邻域和K 近邻两种主流提取方式。Pandas库用于实现邻域点云的结构化存储。import laspyfrom pyntcloud import PyntClouddef read_las(filepath): las = laspy.read(filepath) # 2. 将所有字段转为Pandas DataFrame(保留全量信息) las_data = { "x": las.x, "y": las.y, "z": las.z, "intensity": las.intensity, # 强度 "classification": las.classification, # 分类 "return_number": las.return_number # 回波次数 } # 补充所有扩展字段(按需添加) for dim in las.point_format.dimension_names: if dim not in las_data: las_data[dim] = getattr(las, dim) # 3. 转为PyntCloud对象(全量数据无损失) cloud = PyntCloud(pd.DataFrame(las_data)) return cloud
邻域点云提取是点云分析的基础操作,核心需求是:给定一个目标二维/三维坐标,提取以该坐标为中心、指定范围内的所有点云。本次用 pointcloud 库实现半径邻域提取(最常用),步骤为 “构建点云索引→给定参数→提取邻域点云”,代码如下:def pyntcloud_batch_neighbors( cloud: PyntCloud, centers: list, search_type: str = "radius", radius: float = None, k: int = None, use_2d: bool = True) -> list[PyntCloud]: """ 批量查询多个中心的邻域点云(复用KDTree,避免重复构建) :param centers: 中心坐标列表,如 [(x1,y1), (x2,y2), ...] :radius:邻域范围 :return: 邻域点云列表,与centers一一对应 """ # 1. 仅构建1次KDTree(批量查询的核心优化) if use_2d: coords = cloud.points[["x", "y"]].values else: coords = cloud.points[["x", "y", "z"]].values kdtree = KDTree(coords) for idx, center in enumerate(centers): center = center[0:2] center_arr = np.array(center).reshape(1, 2 if use_2d else 3) if search_type == "radius": neighbor_idx = kdtree.query_ball_point(center_arr, radius)[0] else: _, neighbor_idx = kdtree.query(center_arr, k=k) neighbor_idx = neighbor_idx[0] # 筛选邻域点云 if len(neighbor_idx) == 0: empty_df = pd.DataFrame(columns=cloud.points.columns) # neighbor_clouds.append(PyntCloud(empty_df)) print(f"警告:中心{idx + 1}({center}) 邻域内无点云") else: neighbor_points = cloud.points.iloc[neighbor_idx].reset_index(drop=True) Neighbor_P = PyntCloud(neighbor_points) # Neighbor_P.to_file("Processed_SHP/" + "A_" + str(idx) + '.ply') pyntcloud2las(Neighbor_P,"Processed_SHP/" + "A_" + str(idx) + '.las') print(f"✅ 批量查询完成:共处理 {len(centers)} 个中心")
关键说明
pyntcloud2las为本文存储邻域点云的函数,实现代码如下:
def pyntcloud2las( pynt_cloud: PyntCloud, las_save_path: str, xyz_precision: int = 6, # 坐标保留小数位数(避免科学计数法) add_intensity: bool = True, # 是否添加默认强度字段 overwrite: bool = True) -> bool: """ 直接将PyntCloud格式点云保存为LAS格式(无中间文件) :param pynt_cloud: 输入的PyntCloud点云对象 :param las_save_path: LAS文件保存路径(如"output/neighbor.las") :param xyz_precision: 坐标保留小数位数(默认6位) :param add_intensity: 是否添加强度字段(LAS常用,默认填充0) :param overwrite: 是否覆盖已存在的LAS文件 :return: 保存成功返回True,失败返回False """ # 1. 校验输入点云是否为空 if len(pynt_cloud.points) == 0: print("错误:PyntCloud点云为空,无法保存为LAS!") return False # 2. 校验输出路径 if os.path.exists(las_save_path) and not overwrite: print(f"警告:LAS文件已存在,跳过 → {las_save_path}") return False os.makedirs(os.path.dirname(las_save_path), exist_ok=True) # 创建目录 try: # 3. 提取点云数据并预处理(适配LAS格式要求) points_df = pynt_cloud.points.copy() # 3.1 必选字段校验(LAS必须有x/y/z) required_fields = ["x", "y", "z"] if not all(f in points_df.columns for f in required_fields): print(f"错误:点云缺少必选字段!需包含:{required_fields}") return False # 3.2 坐标精度处理(取消科学计数法,保留指定小数位) points_df = points_df.round({ "x": xyz_precision, "y": xyz_precision, "z": xyz_precision }) # 转为LAS要求的float64类型 x = points_df["x"].astype(np.float64).values y = points_df["y"].astype(np.float64).values z = points_df["z"].astype(np.float64).values # 4. 初始化LAS文件(设置头文件,兼容主流软件) # point_format=3 支持更多字段,version=1.4 是常用版本 las_header = laspy.LasHeader(point_format=3, version="1.4") # 设置坐标偏移(用最小值偏移,减少文件体积,LAS优化) las_header.offsets = (np.min(x), np.min(y), np.min(z)) # 设置缩放因子(对应小数精度,0.000001=6位小数) las_header.scales = (10 ** -xyz_precision,) * 3 # 5. 写入LAS数据 las_file = laspy.LasData(las_header) las_file.x = x las_file.y = y las_file.z = z # 可选:添加强度字段(LAS标准字段,默认填充0) if add_intensity: las_file.intensity = np.zeros(len(x), dtype=np.uint16) # 可选:添加分类字段(默认分类为0,代表未分类) las_file.classification = np.zeros(len(x), dtype=np.uint8) # 6. 保存LAS文件 las_file.write(las_save_path) print(f"✅ PyntCloud点云已保存为LAS → {las_save_path}") print(f" 点数:{len(las_file.points)} | 坐标精度:{xyz_precision}位小数") return True except Exception as e: print(f"保存LAS失败:{e}") return False
下图左侧面板呈现的是未经处理的完整点云数据,涵盖了研究场景内全部点的 x、y、z 三维坐标及相关属性信息;右侧面板为依托本文编写的邻域查询代码(基于 KDTree 批量检索算法),从完整点云中提取的目标中心对应的局部邻域点云,直观反映了代码对邻域点云的精准提取效果。