COLMAP 数据解析

在这篇文章中,我们将重点解析 COLMAP 的输出数据,并用 Python 和 PyTorch 将它们转化为模型可以理解的张量(Tensors)。


数据集

我们首先下载 Mip-NeRF 360 数据集。当我们解压完数据集(例如经典的盆栽 bonsai 场景),会看到一个层级分明的目录结构。

图像数据 (Images)

首先是大量的图像文件。数据集中不仅包含接近 4K 的原始高分辨率图像,还贴心地提供了下采样(Downscaled)版本:缩小 2 倍 (images_2)、缩小 4 倍 (images_4)、缩小 8 倍 (images_8)。

COLMAP 输出文件

COLMAP 算法运行后,会输出三个核心的二进制(.bin)文件,它们是 3D 重建的灵魂:

  • cameras.bin:记录相机的内参(焦距、中心点等)。
  • images.bin:记录每张照片对应的相机外参(位置和姿态)。
  • points3d.bin:稀疏的 3D 点云数据。

为了方便在 Python 中操作,我们会用脚本将这些 .bin 文件先转换为 .txt 文本格式,最后再打包成 NumPy 的 .npy 格式(如 cameras.npypoints3d.npy)。


代码实现

接下来,我们将进入代码环节。用 Python 读取这些 .npy 文件并为渲染做准备。

加载 3D 点云

点云文件(points3d.npy)中存储了场景中每一个离散点的信息。每一行代表一个点,包含 6 个关键值:前三个是空间坐标 (x, y, z),后三个是颜色值 (R, G, B)

  1. # 1. Load point cloud
  2. pc_path = "datasets/out_colmap/bonsai/point_cloud.npy"
  3. print(f"Loading point cloud from {pc_path}...")
  4. pc_data = np.load(pc_path)  # shape: (N, 6)
  5.    
  6. # Extract XYZ and RGB
  7. xyz = pc_data[:, :3]   # shape: (N, 3)
  8. rgb = pc_data[:, 3:6]  # shape: (N, 3)
  9.    
  10. # Convert to PyTorch tensors
  11. PC = torch.from_numpy(xyz).float()
  12. PCColor = torch.from_numpy(rgb).float() / 255.0  # Normalize to [0, 1]

加载相机参数

这里的相机采用的是针孔相机模型 (Pinhole Camera Model)。对于光学中心点 c_xc_y,在理想状态下它们等于图像的 width / 2height / 2

  1. # 2. Load camera metadata
  2. meta_path = "datasets/out_colmap/bonsai/cam_meta.npy"
  3. print(f"Loading camera metadata from {meta_path}...")
  4. cam_meta = np.load(meta_path, allow_pickle=True).item()
  5. print("Camera metadata:", cam_meta)
  6.    
  7. height = cam_meta['height']
  8. width = cam_meta['width']
  9. fx = cam_meta['fx']
  10. fy = cam_meta['fy']
  11. cx = width / 2
  12. cy = height / 2

如果我们现在将准备好的 xyzrgb 喂给光栅化器去绘图,会发现图像上什么也没有。

此时屏幕毫无画面,并非点云数据丢失了,而是因为相机没有看向点云。目前我们仅仅加载了相机的内参(焦距、图像尺寸),但还没有配置相机的外参(位置和朝向)。我们将在下一篇文章中进行实现。