LingBot-Depth 2.0 处理点云数据的输入格式与输出结构

文章导读
LingBot-Depth 2.0 读取的不是原始 LAS 或 PCD 文件,而是一个已转为数组的张量。输出也不是直接可用的点云,而是与输入坐标绑定的深度张量。你首先要确认模型需要哪些通道、坐标系如何定义,再把格式转换做对,最后从输出张量中取出深度值。以下是通常可行的处理顺序。
📋 目录
  1. 确认模型输入的点云通道与坐标系统
  2. 将点云文件转换为模型可读的数组
  3. 执行推理并解析输出张量
  4. 校验解析结果与原始数据的对齐关系
A A

LingBot-Depth 2.0 读取的不是原始 LAS 或 PCD 文件,而是一个已转为数组的张量。输出也不是直接可用的点云,而是与输入坐标绑定的深度张量。你首先要确认模型需要哪些通道、坐标系如何定义,再把格式转换做对,最后从输出张量中取出深度值。以下是通常可行的处理顺序。

处理 LingBot-Depth 2.0 时,先加载官方示例数据,检查点云字段与坐标范围。输入通常为 N×C 数组,通道包含 xyz 及可选强度;输出为 B×H×W 或 B×N×D 张量。转换时使用 Open3D 或 laspy,推理后需按原坐标对齐校验,避免点序错位。操作顺序:确认规范→转换→推理→校验。

确认模型输入的点云通道与坐标系统

模型训练时用到的点云字段是固定的。通常至少包含 x、y、z 三个通道,部分变体还接收 intensity、rgb 或归一化坐标。坐标系统一般以模型训练数据的坐标系为准,常见的是相机坐标系:x 向右,y 向下,z 指向场景前方。如果拿到的点云来自激光扫描,其坐标系可能是 Z 轴向上,这时需要先旋转对齐。

建议先加载一份官方示例数据,直接查看数组形状和字段名,例如用 numpy 打印 shape 和 dtype,再读取几个点的坐标范围。这一步能确定输入张量的通道顺序,避免按错误顺序拼接导致推理结果无意义。

将点云文件转换为模型可读的数组

以 LAS 和 PCD 为例,通常用 laspy 和 open3d 读取。转换时只保留模型需要的字段,并按模型要求的通道顺序排列。如果模型使用相机坐标系,需要把 Z 轴向上的点云旋转到 z 向前,旋转矩阵由点云自身的坐标系与相机坐标系之间的外参决定。

import numpy as np
import laspy

# 读取 LAS 文件
las = laspy.read("scan.las")
points = np.vstack([las.x, las.y, las.z]).T  # N x 3

# 如果有 intensity,且模型接受该通道
if hasattr(las, "intensity"):
    intensity = las.intensity.reshape(-1, 1)
    points = np.hstack([points, intensity])  # N x 4

# 转换为 float32,模型通常要求这个精度
points = points.astype(np.float32)
# 这里先不旋转,需根据模型示例确定坐标系
import open3d as o3d
import numpy as np

# 读取 PCD 文件
pcd = o3d.io.read_point_cloud("scan.pcd")
points = np.asarray(pcd.points)  # N x 3

# 如果还有颜色字段,可加入
colors = np.asarray(pcd.colors)
if colors.shape[1] >= 3:
    points = np.hstack([points, colors])

points = points.astype(np.float32)

转换后建议保存为 .npy,方便后续重复加载,同时记录坐标范围,供校验阶段使用。

LingBot-Depth 2.0 处理点云数据的输入格式与输出结构

执行推理并解析输出张量

推理代码没有本质不同,关键在解析输出维度。假设模型是 PyTorch 实现,输入张量形状为 (1, N, C),输出可能是 (1, N, D) 或 (1, H, W)。前者表示每个输入点的深度估计,后者表示投影到图像平面后的深度图。

import torch
import numpy as np

model = torch.load("lingbot_depth.pt", map_location="cpu")
model.eval()

# points 已转换为 numpy 数组
input_tensor = torch.from_numpy(points).unsqueeze(0)  # (1, N, C)
if input_tensor.dtype != torch.float32:
    input_tensor = input_tensor.float()

with torch.no_grad():
    output_tensor = model(input_tensor)  # 形状可能是 (1,N,D) 或 (1,H,W)

print("输出形状:", output_tensor.shape)

# 按输出形状分情况处理
if output_tensor.ndim == 3:
    # (1, N, 1) 或 (1, N, 2),最后维包含深度信息
    depth = output_tensor[0, :, 0].numpy()
elif output_tensor.ndim == 4:
    # (1, 1, H, W) 深度图
    depth = output_tensor[0, 0].numpy()
else:
    raise ValueError("未识别的输出结构")

# 保存深度结果
np.save("depth.npy", depth)

如果输出是逐点深度,那么每个点对应一个深度值;如果是深度图,需要与对应的投影坐标关联。在没有相机内参的情况下,逐点输出更容易直接使用。

校验解析结果与原始数据的对齐关系

解析错误通常表现为点数不一致或坐标范围漂移。校验时先对比输入点数和输出点数,再检查坐标范围是否在原始点云的包围盒内。如果输出携带坐标,直接比较;如果是深度图,则检查投影后的点数与输入点数的比例。

# 假设原始点云为 points,解析后的深度为 depth
# 校验点数
if depth.ndim == 1 and len(depth) == len(points):
    print(f"点数一致: {len(depth)}")
else:
    print("点数不一致,检查点序或投影步骤")

# 校验坐标范围:如果输出是深度图,跳过此项
if depth.ndim == 1:
    # 用输出深度与原始点 z 坐标对比(需要确认坐标系一致)
    min_z = points[:, 2].min()
    max_z = points[:, 2].max()
    print(f"原始 Z 范围: [{min_z:.3f}, {max_z:.3f}]")
    print(f"深度值范围: [{depth.min():.3f}, {depth.max():.3f}]")

# 更深层校验:随机取若干点,计算输出深度与原始点坐标的差值
indices = np.random.choice(len(points), 100, replace=False)
if depth.ndim == 1:
    diffs = np.abs(depth[indices] - points[indices, 2])
    print(f"采样点深度差均值: {diffs.mean():.4f}")

若深度差均值明显偏离 0,说明坐标系对齐方式不对,需要检查模型训练时的坐标定义。建议在正式处理大量数据前,先用少量样本完成从文件到输出的全流程,确认每个环节的数组形状和数值范围都符合预期。