算法更新: delivery v1.2.1、平整度v1.5、测距v2.2、整车配准v1.3 适配器: vehicle-registration 新增、各算法 manifest 和 adapter 更新 后端: taskCadFiles/source 字段迁移、derivedPointClouds 注册、取消任务联动 前端: ElapsedTime 时长显示、两步算法流程、canRun 逻辑修复 文档: 新增5份算法性能分析报告
141 lines
4.6 KiB
Python
141 lines
4.6 KiB
Python
import open3d as o3d
|
||
import numpy as np
|
||
from scipy.spatial import KDTree
|
||
import copy
|
||
|
||
|
||
def compute_point_to_mesh_distance(point_cloud, mesh):
|
||
"""
|
||
计算点云到 Mesh 的距离
|
||
|
||
Args:
|
||
point_cloud: Open3D 点云对象
|
||
mesh: Open3D 网格对象
|
||
|
||
Returns:
|
||
distances: 每个点到最近三角形面的距离
|
||
"""
|
||
# 方法1:使用 Open3D 内置函数(新版本)
|
||
try:
|
||
distances = mesh.compute_point_cloud_distance(point_cloud)
|
||
return np.asarray(distances)
|
||
except AttributeError:
|
||
pass
|
||
|
||
# 方法2:使用 mesh 顶点最近邻(近似)
|
||
mesh_points = np.asarray(mesh.vertices)
|
||
points = np.asarray(point_cloud.points)
|
||
|
||
tree = KDTree(mesh_points)
|
||
distances, indices = tree.query(points)
|
||
|
||
return distances
|
||
|
||
|
||
def compute_statistics(distances):
|
||
"""计算距离统计信息"""
|
||
stats = {
|
||
'mean': np.mean(distances),
|
||
'rmse': np.sqrt(np.mean(distances ** 2)),
|
||
'max': np.max(distances),
|
||
'min': np.min(distances),
|
||
'std': np.std(distances),
|
||
'median': np.median(distances),
|
||
}
|
||
|
||
# 分位数
|
||
percentiles = [50, 75, 90, 95, 99]
|
||
for p in percentiles:
|
||
stats[f'percentile_{p}'] = np.percentile(distances, p)
|
||
|
||
return stats
|
||
|
||
|
||
def visualize_distance(point_cloud, mesh, distances, max_dist=None):
|
||
"""可视化距离(按距离着色)"""
|
||
pcd_colored = copy.deepcopy(point_cloud)
|
||
|
||
if max_dist is None:
|
||
max_dist = np.percentile(distances, 95)
|
||
|
||
# 生成颜色(红:远,蓝:近)
|
||
colors = np.zeros((len(distances), 3))
|
||
norm_dist = np.clip(distances / (max_dist + 1e-8), 0, 1)
|
||
colors[:, 0] = norm_dist # 红色通道(远)
|
||
colors[:, 2] = 1 - norm_dist # 蓝色通道(近)
|
||
|
||
pcd_colored.colors = o3d.utility.Vector3dVector(colors)
|
||
|
||
# Mesh 着色为灰色
|
||
mesh_colored = copy.deepcopy(mesh)
|
||
mesh_colored.paint_uniform_color([0.5, 0.5, 0.5])
|
||
|
||
# 可视化
|
||
o3d.visualization.draw_geometries(
|
||
[mesh_colored, pcd_colored],
|
||
window_name="Distance to Mesh (Blue=Near, Red=Far)",
|
||
width=1024,
|
||
height=768
|
||
)
|
||
|
||
|
||
def print_statistics(stats):
|
||
"""打印统计信息"""
|
||
print("=" * 60)
|
||
print("点云到 Mesh 距离统计")
|
||
print("=" * 60)
|
||
print(f"平均距离: {stats['mean']:.6f} m ({stats['mean'] * 1000:.3f} mm)")
|
||
print(f"RMSE: {stats['rmse']:.6f} m ({stats['rmse'] * 1000:.3f} mm)")
|
||
print(f"中位数: {stats['median']:.6f} m ({stats['median'] * 1000:.3f} mm)")
|
||
print(f"标准差: {stats['std']:.6f} m ({stats['std'] * 1000:.3f} mm)")
|
||
print(f"最大距离: {stats['max']:.6f} m ({stats['max'] * 1000:.3f} mm)")
|
||
print(f"最小距离: {stats['min']:.6f} m ({stats['min'] * 1000:.3f} mm)")
|
||
print("\n距离分位数:")
|
||
print(f" 50%: {stats['percentile_50']:.6f} m ({stats['percentile_50'] * 1000:.3f} mm)")
|
||
print(f" 75%: {stats['percentile_75']:.6f} m ({stats['percentile_75'] * 1000:.3f} mm)")
|
||
print(f" 90%: {stats['percentile_90']:.6f} m ({stats['percentile_90'] * 1000:.3f} mm)")
|
||
print(f" 95%: {stats['percentile_95']:.6f} m ({stats['percentile_95'] * 1000:.3f} mm)")
|
||
print(f" 99%: {stats['percentile_99']:.6f} m ({stats['percentile_99'] * 1000:.3f} mm)")
|
||
|
||
|
||
# ========== 主程序 ==========
|
||
if __name__ == "__main__":
|
||
# 读取文件(使用原始字符串避免转义问题)
|
||
file_path = r"E:\point_distance\data\40_noisy.ply"
|
||
mesh_path = r"E:\point_distance\data\40.stl"
|
||
|
||
print("读取点云...")
|
||
source_pcd = o3d.io.read_point_cloud(file_path)
|
||
|
||
print("读取 Mesh...")
|
||
mesh = o3d.io.read_triangle_mesh(mesh_path)
|
||
|
||
# 检查读取是否成功
|
||
if len(source_pcd.points) == 0:
|
||
print("错误:点云读取失败,请检查文件路径")
|
||
exit()
|
||
|
||
if len(mesh.vertices) == 0:
|
||
print("错误:Mesh 读取失败,请检查文件路径")
|
||
exit()
|
||
|
||
print(f"\n点云点数: {len(source_pcd.points)}")
|
||
print(f"Mesh 顶点数: {len(mesh.vertices)}")
|
||
print(f"Mesh 面数: {len(mesh.triangles)}")
|
||
|
||
# 可选:Mesh 法线计算(用于更好的可视化)
|
||
mesh.compute_vertex_normals()
|
||
|
||
# 计算距离
|
||
print("\n计算点云到 Mesh 的距离...")
|
||
distances = compute_point_to_mesh_distance(source_pcd, mesh)
|
||
|
||
# 计算统计信息
|
||
stats = compute_statistics(distances)
|
||
|
||
# 打印统计信息
|
||
print_statistics(stats)
|
||
|
||
# 可视化
|
||
print("\n可视化...")
|
||
visualize_distance(source_pcd, mesh, distances) |