feat: initial commit - massive-cloud point cloud processing system
This commit is contained in:
141
alg/point_porj/archive/relative_code/distance_mesh.py
Normal file
141
alg/point_porj/archive/relative_code/distance_mesh.py
Normal file
@@ -0,0 +1,141 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user