前情提要:在实践中有些3D相机只能输出exr格式的文件,为了实现3D点云的可视化,需要将其转为ply文件格式,因此找到了一下代码,可以实现该功能。
import OpenEXR
import numpy as np
from plyfile import PlyData, PlyElement
def read_exr(file_path):
# 打开EXR文件
exr_file = OpenEXR.InputFile(file_path)
# 查看所有通道
channels = exr_file.header()['channels']
print("可用通道:", channels) # 输出文件中所有通道的信息
# 读取数据窗口信息
dw = exr_file.header()['dataWindow']
width = dw.max.x - dw.min.x + 1
height = dw.max.y - dw.min.y + 1
# 选择一个合适的通道名
depth_channel = 'Y' # 从可用通道中选择一个,如'Y'
if depth_channel not in channels:
raise ValueError(f"错误:通道 '{depth_channel}' 不在图像中。请检查通道名称。")
# 读取深度数据(假设存储在'Y'通道中)直接不使用PixelType对象
depth_str = exr_file.channel(depth_channel) # 直接读取
depth_array = np.frombuffer(depth_str, dtype=np.float32)
depth_array.shape = (height, width)
exr_file.close()
return depth_array
def depth_to_point_cloud(depth_array, fx, fy, cx, cy):
height, width = depth_array.shape
xx, yy = np.meshgrid(np.arange(width), np.arange(height))
# 计算三维坐标
X = (xx - cx) * depth_array / fx
Y = (yy - cy) * depth_array / fy
Z = depth_array
# 只提取有效的深度值
mask = Z > 0 # 过滤掉无效点
points = np.array([X[mask], Y[mask], Z[mask]]).T # 将XYZ坐标合并为点云
return points
def write_ply(file_path, points):
# 创建PLY元素
vertex = np.array([(p[0], p[1], p[2]) for p in points], dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4')])
ply_data = PlyData([PlyElement.describe(vertex, 'vertex')], text=True)
# 写入PLY文件
ply_data.write(file_path)
if __name__ == '__main__':
# 使用示例
exr_file_path = 'E:/logs/depth.exr' # 输入EXR深度图的路径
ply_file_path = 'E:/logs/depth.ply' # 输出PLY点云文件的路径
# 摄像机内参(根据你的相机标定信息调整)
fx, fy = 1.37599402e+03, 1.37599548e+03 # 焦距,假设为525像素
cx, cy = 9.56831726e+02, 5.94493591e+02 # 主点坐标,假设为图像中心
# 1. 读取深度图
depth_array = read_exr(exr_file_path)
# 2. 深度图转换为点云
points = depth_to_point_cloud(depth_array, fx, fy, cx, cy)
# 3. 写入PLY文件
write_ply(ply_file_path, points)
print(f"点云已保存到 {ply_file_path}")