处理地形点云后,如何将PyVista的UnstructuredGrid转存为STL?
问题描述
使用PyVista处理3D扫描的.xyz点云数据,生成网格后导出STL用于Blender。单个连续区域的处理流程正常,但处理含多个不连续小区域的点云时,delaunay_2d()会生成连接不同区域的大面。尝试通过面面积阈值删除大面后,网格类型从可导出STL的PolyData变为UnstructuredGrid,无法直接导出,希望省去后续用MeshLab处理的步骤。
连续区域处理代码:
import numpy as np import pyvista as pv xyz_path = r"Path_to_file.xyz" stl_path = r"Path_to_file.stl" data = np.loadtxt(xyz_path) cloud = pv.PolyData(data) surf = cloud.delaunay_2d() surf.plot(show_edges=True) surf.save(stl_path)
不连续区域尝试的过滤代码(导致类型变为UnstructuredGrid):
import numpy as np import pyvista as pv xyz_path = r"Path_to_file.xyz" stl_path = r"Path_to_file.stl" data = np.loadtxt(xyz_path) cloud = pv.PolyData(data) surf = cloud.delaunay_2d() cell_sizes = surf.compute_cell_sizes(length=False, area=True, volume=False) areas = cell_sizes['Area'] max_area_threshold = 100 # 自定义面积阈值 valid_cells = areas < max_area_threshold filtered_mesh = surf.extract_cells(valid_cells) filtered_mesh.plot(show_edges=True, scalars='Area') # 原代码错误保存了未过滤的surf,且filtered_mesh类型不支持直接导出STL
解决方案
方法1:将UnstructuredGrid转换为PolyData
过滤后的UnstructuredGrid若仅包含三角形面,可直接转换为PolyData,支持STL导出:
import numpy as np import pyvista as pv xyz_path = r"Path_to_file.xyz" stl_path = r"Path_to_file.stl" data = np.loadtxt(xyz_path) cloud = pv.PolyData(data) surf = cloud.delaunay_2d() cell_sizes = surf.compute_cell_sizes(length=False, area=True, volume=False) areas = cell_sizes['Area'] max_area_threshold = 100 valid_cells = areas < max_area_threshold filtered_mesh = surf.extract_cells(valid_cells) # 转换为PolyData(若网格仅含三角形面,直接转换即可) polydata_mesh = pv.PolyData(filtered_mesh) # 或用extract_surface确保是纯表面网格 # polydata_mesh = filtered_mesh.extract_surface() polydata_mesh.plot(show_edges=True, scalars='Area') polydata_mesh.save(stl_path)
方法2:使用threshold过滤,保持PolyData类型
改用threshold方法基于单元面积过滤,该方法处理PolyData时会保留原类型:
import numpy as np import pyvista as pv xyz_path = r"Path_to_file.xyz" stl_path = r"Path_to_file.stl" data = np.loadtxt(xyz_path) cloud = pv.PolyData(data) surf = cloud.delaunay_2d() # 直接在原网格上计算单元面积(inplace=True修改原对象) surf.compute_cell_sizes(length=False, area=True, volume=False, inplace=True) # 过滤面积小于阈值的单元,返回仍为PolyData filtered_mesh = surf.threshold(scalars='Area', upper=100) filtered_mesh.plot(show_edges=True, scalars='Area') filtered_mesh.save(stl_path)
方法3:先聚类点云,分区域生成网格
针对多不连续区域,先对点云聚类拆分,再分别生成网格后合并,从根源避免跨区域大面:
import numpy as np import pyvista as pv xyz_path = r"Path_to_file.xyz" stl_path = r"Path_to_file.stl" data = np.loadtxt(xyz_path) cloud = pv.PolyData(data) # 聚类点云,tolerance需根据点云密度调整(单位与点云坐标一致) clustered = cloud.cluster_points(tolerance=5.0) labels = clustered['ClusterId'] unique_labels = np.unique(labels) # 逐个处理每个独立区域 mesh_list = [] for label in unique_labels: # 提取当前区域的点 region_points = clustered.extract_points(labels == label) # 生成该区域的网格 region_mesh = region_points.delaunay_2d() mesh_list.append(region_mesh) # 合并所有区域的网格为单个PolyData combined_mesh = pv.MultiBlock(mesh_list).combine() combined_mesh.save(stl_path)
内容的提问来源于stack exchange,提问作者Melch
相关产品推荐
相关产品推荐

