目录
ply 可视化:
在线可视化:
open3d可视化ply 功能很强
mayavi可视化失败:
three.js 加载显示ply
open3d生成可以可视化:
生成ply代码,不是3dgs:
智驾自动生成dtype_full
智驾导入ply
安装ply
pip install plyfile
ply 可视化:
在线可视化:
4D Video Web Viewer - 4DV.ai
Mark Kellogg, Senior Software Engineer
open3d可视化ply 功能很强
import open3d as o3d;
import numpy as np;pcdsync = o3d.io.read_point_cloud("cube.ply")# print(pcdsync)
# print(np.asarray(pcdsync.points))
o3d.visualization.draw_geometries([pcdsync], zoom=0.3412, front=[0.4257, -0.2125, -0.8795], lookat=[2.6172, 2.0475, 1.532], up=[-0.0694, -0.9768, 0.2024])
mayavi可视化失败:
from plyfile import PlyData
from mayavi import mlab# 读取ply文件,并展示3d模型
ply = PlyData.read("sync.ply")
vtx = ply['vertex']
mlab.points3d(vtx['x'], vtx['y'], vtx['z'])
mlab.show()
three.js 加载显示ply
three.js加载PLY格式模型(vue中使用three.js56)_threejs ply-CSDN博客
open3d生成可以可视化:
import open3d as o3d;
import numpy as np;# Generate some neat n times 3 matrix using a variant of sync function
x = np.linspace(-3, 3, 501)
mesh_x, mesh_y = np.meshgrid(x, x)
z = np.sinc((np.power(mesh_x, 2) + np.power(mesh_y, 2)))
z_norm = (z - z.min()) / (z.max() - z.min())
xyz = np.zeros((np.size(mesh_x), 3))
xyz[:, 0] = np.reshape(mesh_x, -1)
xyz[:, 1] = np.reshape(mesh_y, -1)
xyz[:, 2] = np.reshape(z_norm, -1)
print('xyz')
print(xyz)# Pass xyz to Open3D.o3d.geometry.PointCloud and visualize
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(xyz)
o3d.io.write_point_cloud("sync.ply", pcd)pcdsync = o3d.io.read_point_cloud("sync.ply")# print(pcdsync)
# print(np.asarray(pcdsync.points))
o3d.visualization.draw_geometries([pcdsync], zoom=0.3412, front=[0.4257, -0.2125, -0.8795], lookat=[2.6172, 2.0475, 1.532], up=[-0.0694, -0.9768, 0.2024])
生成ply代码,不是3dgs:
import torchimport numpy as np
from plyfile import PlyData, PlyElement# 创建立方体的8个顶点数据
num_vertices = 8# 顶点坐标 (立方体)
xyz = np.array([[0, 0, 0],[1, 0, 0],[1, 1, 0],[0, 1, 0],[0, 0, 1],[1, 0, 1],[1, 1, 1],[0, 1, 1]
], dtype=np.float32)# 顶点颜色 (RGB)
colors = np.array([[255, 0, 0], # 红色[0, 255, 0], # 绿色[0, 0, 255], # 蓝色[255, 255, 0], # 黄色[255, 0, 255], # 品红[0, 255, 255], # 青色[128, 128, 128],# 灰色[255, 165, 0] # 橙色
], dtype=np.uint8)# 法线向量 (示例数据)
normals = np.array([[-1, -1, -1],[1, -1, -1],[1, 1, -1],[-1, 1, -1],[-1, -1, 1],[1, -1, 1],[1, 1, 1],[-1, 1, 1]
], dtype=np.float32)# 创建结构化数组类型
dtype = [('x', 'f4'),('y', 'f4'),('z', 'f4'),('red', 'u1'),('green', 'u1'),('blue', 'u1'),('nx', 'f4'),('ny', 'f4'),('nz', 'f4'),
]# 创建并填充结构化数组
vertex_data = np.zeros(num_vertices, dtype=dtype)# 填充数据
vertex_data['x'] = xyz[:, 0]
vertex_data['y'] = xyz[:, 1]
vertex_data['z'] = xyz[:, 2]
vertex_data['red'] = colors[:, 0]
vertex_data['green'] = colors[:, 1]
vertex_data['blue'] = colors[:, 2]
vertex_data['nx'] = normals[:, 0]
vertex_data['ny'] = normals[:, 1]
vertex_data['nz'] = normals[:, 2]# 创建PLY元素
vertex_element = PlyElement.describe(vertex_data, 'vertex')# 保存为PLY文件
PlyData([vertex_element], text=True).write('cube.ply')print("PLY文件已生成: cube.ply")
智驾自动生成dtype_full
import numpy as np
from plyfile import PlyElementdef generate_dtype(variables, names):"""根据输入变量的维度自动生成结构化数组的dtypeArgs:variables: 变量列表 [xyz, normals, f_dc, f_rest, opacities, scale, rotation]names: 对应的属性前缀 ['xyz', 'normals', 'f_dc', 'f_rest', 'opacity', 'scale', 'rotation']Returns:np.dtype: 结构化数组类型描述"""dtype = []# 特殊字段名称映射 (处理xyz和normals的特殊情况)special_names = {'xyz': ['x', 'y', 'z'],'normals': ['nx', 'ny', 'nz']}for var, name in zip(variables, names):# 获取变量维度 (处理二维数组)if var.ndim == 1:cols = 1else:cols = var.shape[1]# 处理特殊命名字段if name in special_names:assert cols == len(special_names[name]), f"{name}维度不匹配"dtype += [(n, 'f4') for n in special_names[name]]# 处理通用命名字段else:if cols == 1:# 单列直接使用属性名dtype.append((name, 'f4'))else:# 多列添加数字后缀dtype += [(f"{name}_{i}", 'f4') for i in range(cols)]return np.dtype(dtype)# 输入数据示例 (根据实际shape修改)
xyz = np.random.rand(100, 3) # (N,3)
normals = np.random.rand(100, 3) # (N,3)
f_dc = np.random.rand(100, 3) # (N,3)
f_rest = np.random.rand(100, 45) # (N,45)
opacities = np.random.rand(100, 1) # (N,1)
scale = np.random.rand(100, 3) # (N,3)
rotation = np.random.rand(100, 4) # (N,4)# 自动生成dtype
variables = [xyz, normals, f_dc, f_rest, opacities.squeeze(), scale, rotation]
names = ['xyz', 'normals', 'f_dc', 'f_rest', 'opacity', 'scale', 'rotation']dtype_full = generate_dtype(variables, names)# 验证输出
print("生成的dtype结构:")
print(dtype_full.descr)# 验证字段总数 (3+3+3+45+1+3+4=62)
assert len(dtype_full) == 62, f"字段数错误,应为62,当前{len(dtype_full)}"# 使用示例
elements = np.empty(xyz.shape[0], dtype=dtype_full)# 填充数据 (注意顺序必须与variables列表一致!)
attributes = np.concatenate([xyz, normals, f_dc, f_rest, opacities, scale, rotation
], axis=1)elements[:] = list(map(tuple, attributes))
el = PlyElement.describe(elements, 'vertex')
智驾导入ply
import os
import numpy as np
import torch
from plyfile import PlyData
import pickledef load_ply_to_model(ply_dir):model_state_dict = {}keys = ['RigidNodes', 'Background', 'Ground']for key in keys:ply_path = os.path.join(ply_dir, f'{key}.ply')ply_data = PlyData.read(ply_path)vertex_data = ply_data['vertex'].data# 提取各属性xyz = np.stack([vertex_data['x'], vertex_data['y'], vertex_data['z']], axis=1)# 处理f_dc(3个特征)f_dc = np.stack([vertex_data[f'f_dc_{i}'] for i in range(3)], axis=1)# 处理f_rest(45个特征)f_rest = np.stack([vertex_data[f'f_rest_{i}'] for i in range(45)], axis=1)# 处理opacity(1个特征)opacity = vertex_data['opacity']# 处理scale(3个特征)scale = np.stack([vertex_data[f'scale_{i}'] for i in range(3)], axis=1)# 处理rotation(4个特征)rotation = np.stack([vertex_data[f'rot_{i}'] for i in range(4)], axis=1)# 转换为张量并调整形状bg_dict = {'_means': torch.tensor(xyz, dtype=torch.float32),'_scales': torch.tensor(scale, dtype=torch.float32),'_quats': torch.tensor(rotation, dtype=torch.float32),'_features_dc': torch.tensor(f_dc, dtype=torch.float32).unsqueeze(-1), # (N,3,1)'_features_rest': torch.tensor(f_rest.reshape(-1, 15, 3), dtype=torch.float32).transpose(1, 2), # (N,3,15)'_opacities': torch.tensor(opacity, dtype=torch.float32).unsqueeze(-1) # (N,1)}model_state_dict[key] = bg_dict# 加载Sky权重sky_path = os.path.join(ply_dir, 'sky_weights.pkl')with open(sky_path, 'rb') as f:model_state_dict['Sky'] = pickle.load(f)return model_state_dictif __name__ == '__main__':ply_dir = '/shared_disk/users/lbg/serch_2025/reconic/Reconic_api/out_ply'model_state_dict = load_ply_to_model(ply_dir)# 验证输出形状for key in ['RigidNodes', 'Background', 'Ground']:print(f"\n--- {key} ---")data = model_state_dict[key]print(f"Means shape: {data['_means'].shape}")print(f"Scales shape: {data['_scales'].shape}")print(f"Quats shape: {data['_quats'].shape}")print(f"Features DC shape: {data['_features_dc'].shape}")print(f"Features Rest shape: {data['_features_rest'].shape}")print(f"Opacities shape: {data['_opacities'].shape}")