COLMAP 输出文件格式完全指南:稀疏重建与稠密重建的读写规范
2026/9/15 11:02:14 网站建设 项目流程

COLMAP 输出文件格式完全指南:稀疏重建与稠密重建的读写规范

【免费下载链接】colmapCOLMAP - Structure-from-Motion and Multi-View Stereo项目地址: https://gitcode.com/GitHub_Trending/co/colmap

导读

doc/format.rst 是 COLMAP(Structure-from-Motion and Multi-View Stereo 的开源实现)官方的输出格式权威文档,完整定义了稀疏重建(Sparse Reconstruction)与稠密重建(Dense Reconstruction)的磁盘文件规范。无论你是要解析他人重建出的点云与相机位姿、编写自定义数据管线,还是需要把重建结果导出为第三方工具可用的格式,都必须精确理解这些文件布局。读完本文,你将掌握二进制与文本两套模型格式的逐字段含义、位姿与四元数的数学约定、稠密工作区的目录结构与深度图/一致性图编码,并能通过model_converter、GUI 菜单与 pycolmap 完成格式转换与程序化读写。

注意:本文以当前仓库 doc/format.rst 为骨架,结合源码 src/colmap/scene/reconstruction_io_binary.cc、src/colmap/scene/reconstruction_io_text.cc、src/colmap/scene/reconstruction.cc 及 CLI 文档 doc/cli.rst 展开。


一、总体约定:字节序、索引与标识符

在深入各文件格式之前,有三个贯穿所有格式的全局约定必须先建立起来,它们直接影响你对任何一行数据的解读。

1.1 二进制一律使用小端序

所有二进制数据均以little endian(小端)字节序存储。绝大多数 x86 处理器天然就是小端序,因此在主流平台上读取 COLMAP 二进制数据通常无需任何字节序转换;只有在跨架构(如大端序的某些服务器 CPU 或嵌入式平台)解析时才需要显式处理。二进制格式的解析入口有两处:

  • C++ API:src/colmap/scene/reconstruction_io.h 声明了全部读写函数;
  • Python API:pycolmap 提供了便捷的模型读取封装(后文有示例)。

从源码看,src/colmap/util/endian.h 提供了ReadBinaryLittleEndian/WriteBinaryLittleEndian辅助模板,reconstruction_io_binary.cc 中所有字段(uint64_tintdoubleuint8_t等)都是通过这些函数读写的。

1.2*_idx*_id的本质区别

这是一个极易踩坑的约定:

  • 任何以*_idx结尾的变量都是有序、连续、从零开始的下标(index)。最典型的是POINT2D_IDX——它指向images.txt中某幅图像第几个特征点的位置,必然是0, 1, 2, ...
  • 任何以*_id结尾的变量都是无序、不连续的标识符(identifier)。相机(CAMERA_ID)、图像(IMAGE_ID)、3D 点(POINT3D_ID)均属此类。

一个直接推论:最大POINT3D_ID不一定等于 3D 点总数。重建过程中会因滤除外点、剔除低质量点而删除若干 3D 点,导致 ID 出现空洞。所以在统计点数时,永远不要用max_id + 1,而要实际遍历计数。对应地,model_analyzer 工具输出的统计信息(Points、Observations 等)都以真实计数为准。

1.3 位姿的数学约定(读 images.txt 前必读)

COLMAP 中每幅图像的重建位姿定义为世界坐标系到相机坐标系的投影

  • 旋转用 Hamilton 约定的四元数(QW, QX, QY, QZ)表示(与 Eigen 库一致);
  • 平移向量为(TX, TY, TZ)
  • 投影/相机中心坐标 =-R^t * T,其中R^t是由四元数构成的 3×3 旋转矩阵的转置(即逆矩阵),T是平移向量;
  • 相机局部坐标系定义为:X 轴指向图像右方、Y 轴指向图像下方、Z 轴指向图像前方。

在 src/colmap/scene/reconstruction_io_text.cc 的WriteImagesText中可以看到,导出时正是按image_id, qw, qx, qy, qz, tx, ty, tz, camera_id, name的顺序写入的,与文档描述完全对应。


二、稀疏重建:两种格式、五类文件

默认情况下 COLMAP 使用二进制格式保存稀疏模型(机器可读、速度快);同时提供文本格式选项(人类可读、速度慢)。两种格式都按rigscamerasframesimagespoints拆分为多个文件,包含这些文件的任意目录即构成一个稀疏模型。二进制文件扩展名为.bin,文本文件扩展名为.txt

加载时有一个重要优先级规则:当目录中同时存在二进制与文本文件时,COLMAP 优先加载二进制格式。这一逻辑在 reconstruction.cc 的Reconstruction::Read中实现——先检测cameras.bin/images.bin/points3D.bin是否存在,再回退到.txt版本。

此外还需了解向后兼容性:早期版本的 COLMAP 不支持 rig(多相机刚体),因此rigsframes文件可能缺失。COLMAP 的重建 I/O 例程完全向后兼容——读取无这些文件的模型时会自动初始化平凡(trivial)的 rig 和 frame;反过来,新版本输出的camerasimages文件也完全兼容旧版本读取。从 reconstruction_io_text.cc 可以看到is_legacy_reconstruction判定逻辑:当 rigs 与 frames 数量均为 0 时调用CreateOneRigPerCamera自动补全。

2.1 格式转换与模型导入导出

GUI 方式:

  • 导出当前选中模型:File > Export model
  • 导出数据集中全部模型:File > Export all(导出目录除模型文件外,还会附带便于重新导入的项目配置文件);
  • 导入模型(可视化或续跑重建):File > Import model,选择包含rigs/cameras/frames/images/points3D文件的文件夹;
  • 二进制与文本互转:先File > Import model加载,再File > Export model(二进制)或File > Export model as text(文本)导出;
  • 导出其他格式:File > Export as...可导出 VisualSfM 的 NVM、Bundler、PLY、VRML 等。

命令行方式:使用model_converter可执行程序。其参数在 src/colmap/exe/model.cc 的RunModelConverter中定义:

colmap model_converter \ --input_path /path/to/model \ --output_path /path/to/output \ --output_type BIN|TXT|NVM|Bundler|VRML|PLY|R3D|CAM \ [--skip_distortion]

其中output_type必选,可选值如上;skip_distortion默认false。结合 doc/cli.rst 的说明,model_converter的作用就是“将 COLMAP 导出格式转换为其他格式,如 PLY 或 NVM”。各导出实现位于 src/colmap/scene/reconstruction_io.h:ExportNVMExportBundlerExportPLYExportVRMLExportCamExportRecon3D等。值得注意的实现细节:

  • ExportNVM仅在导出畸变参数时支持SIMPLE_RADIAL模型;若设置skip_distortion=true则支持全部相机模型,但会退化为使用平均焦距(对双焦距或带畸变的模型不够精确);
  • ExportBundler仅当导出畸变参数时支持SIMPLE_PINHOLEPINHOLESIMPLE_RADIALRADIAL四种模型;
  • ExportVRML会生成<stem>.images.wrl<stem>.points3D.wrl两个文件。

Python 方式:稀疏模型可方便地通过 pycolmap 在 Python 中读取(示例见后文)。官方也提供了模型转换的纯 Python 脚本 python/examples/convert_legacy_rotation_averaging_format.py 作为参考实现。

2.2 文本格式逐文件详解

COLMAP 为每个重建模型导出以下文本文件:rigs.txtcameras.txtframes.txtimages.txtpoints3D.txt。所有文件都以#开头的行为注释(解析时被忽略),文件头注释简要描述了格式。文本写入在 reconstruction_io_text.cc 中实现,注意其中.imbue(std::locale::classic())precision(17)的设置——文本导出使用经典 locale 且保留 17 位有效数字,确保浮点精度不因文本化而丢失,这也是我们手工编辑文本模型时必须遵守的精度标准。

rigs.txt —— 刚体标定列表
# Rig calib list with one line of data per calib: # RIG_ID, NUM_SENSORS, REF_SENSOR_TYPE, REF_SENSOR_ID, SENSORS[] as (SENSOR_TYPE, SENSOR_ID, HAS_POSE, [QW, QX, QY, QZ, TX, TY, TZ]) # Number of rigs: 1 1 2 CAMERA 1 CAMERA 2 1 -0.9999701516465348 -0.0011120266840749639 -0.0075347911527510894 0.0012985125893421306 -0.19316906391350164 0.00085222218993398979 0.0070758955539026785 2 1 CAMERA 3

字段含义:

  • RIG_ID:rig 的唯一标识;
  • NUM_SENSORS:该 rig 包含的传感器数量;
  • REF_SENSOR_TYPE/REF_SENSOR_ID:参考传感器(基准传感器)的类型与 ID,本例为CAMERA 1
  • 其余每个非参考传感器为(SENSOR_TYPE, SENSOR_ID, HAS_POSE, [QW, QX, QY, QZ, TX, TY, TZ]):若HAS_POSE=1,后面跟 7 个值描述该传感器相对 rig 的刚体变换(四元数 + 平移);若HAS_POSE=0则没有位姿。

上例中数据包含两个 rig:第一个 rig 有 2 个相机(CAMERA 2带位姿),第二个 rig 有 1 个相机(CAMERA 3)。对应读取逻辑见 reconstruction_io_text.cc 的ReadRigsText

cameras.txt —— 相机内参列表
# Camera list with one line of data per camera: # CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[] # Number of cameras: 3 1 SIMPLE_PINHOLE 3072 2304 2559.81 1536 1152 2 PINHOLE 3072 2304 2560.56 2560.56 1536 1152 3 SIMPLE_RADIAL 3072 2304 2559.69 1536 1152 -0.0218531

字段含义:

  • CAMERA_ID:相机唯一标识;
  • MODEL:相机畸变模型名称(如SIMPLE_PINHOLEPINHOLESIMPLE_RADIALRADIALOPENCVFULL_OPENCV等,完整清单见 doc/cameras.rst);
  • WIDTHHEIGHT:传感器尺寸(像素);
  • PARAMS[]:变长参数序列,数量取决于相机模型。例如SIMPLE_PINHOLE有 3 个参数(单个焦距 f、主点 cx、cy),PINHOLE有 4 个(fx、fy、cx、cy),SIMPLE_RADIAL有 4 个(f、cx、cy、径向畸变 k1)。

上例中,3 台相机基于不同畸变模型但传感器尺寸相同(3072×2304)。第一台相机焦距 2559.81 像素、主点在像素位置(1536, 1152)多幅图像可以共享同一相机内参——图像通过CAMERA_ID引用相机,这是文本格式实现相机内参复用的机制。解析实现见ReadCamerasText(reconstruction_io_text.cc),它会用CameraModelNameToId将模型名映射为枚举、再依据CameraModelNumParams校验参数个数(VerifyParams)。

frames.txt —— 帧列表(rig 实例)
# Frame list with one line of data per frame: # FRAME_ID, RIG_ID, RIG_FROM_WORLD[QW, QX, QY, QZ, TX, TY, TZ], NUM_DATA_IDS, DATA_IDS[] as (SENSOR_TYPE, SENSOR_ID, DATA_ID) # Number of frames: 151 1 1 0.99801363919752195 0.040985139360073107 0.041890917712361225 -0.023111584553400576 -5.2666546897987896 -0.17120007823690631 0.12300519697527648 2 CAMERA 1 1 CAMERA 2 2 2 2 0.99816472047267968 0.037605501383281774 0.043101511724657163 -0.019881568259519072 -5.1956060695789192 -0.20794508616745555 0.14967533910764824 1 CAMERA 3 3

字段含义:

  • FRAME_ID:帧的唯一标识;
  • RIG_ID:该帧所属的 rig;
  • RIG_FROM_WORLD[QW, QX, QY, QZ, TX, TY, TZ]:rig 坐标系到世界坐标系的刚体变换(四元数 + 平移);
  • NUM_DATA_IDSDATA_IDS[] as (SENSOR_TYPE, SENSOR_ID, DATA_ID):该帧同时曝光的所有传感器对应的数据(如相机及其图像数据)列表。

上例中,帧 1 是 rig 1 的一个实例(两个相机数据),帧 2 是 rig 2 的实例(一个相机数据)。对应解析逻辑见ReadFramesText(reconstruction_io_text.cc)。帧与图像的关系是:图像通过image_to_frame映射关联到帧,从而支持多相机(rig)场景的重建。

images.txt —— 位姿与特征点(每幅图像两行)
# Image list with two lines of data per image: # IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME # POINTS2D[] as (X, Y, POINT3D_ID) # Number of images: 2, mean observations per image: 2 1 0.851773 0.0165051 0.503764 -0.142941 -0.737434 1.02973 3.74354 1 P1180141.JPG 2362.39 248.498 58396 1784.7 268.254 59027 1784.7 268.254 -1 2 0.851773 0.0165051 0.503764 -0.142941 -0.737434 1.02973 3.74354 1 P1180142.JPG 1190.83 663.957 23056 1258.77 640.354 59070

第一行(每幅图像):

  • IMAGE_ID:图像唯一标识;
  • QW, QX, QY, QZ, TX, TY, TZ:图像位姿,含义见前文 1.3 节的数学约定(世界→相机,Hamilton 四元数);
  • CAMERA_ID:引用的相机内参标识;
  • NAME:图像文件名,相对于项目选定的基础图像文件夹

第二行:该图像全部特征点,格式为X Y POINT3D_ID三元组重复出现。

  • X, Y:特征点像素坐标;
  • POINT3D_ID:该特征点关联的 3D 点标识;若为-1表示该特征点未关联任何重建 3D 点

上例中两幅图像都引用CAMERA_ID = 1(共享内参)。第一幅图像有 3 个特征点、第二幅有 2 个;两幅图像均观察到 2 个 3D 点,而第一幅图像的最后一个特征点POINT3D_ID = -1,即它在重建中不关联 3D 点。写入逻辑在 reconstruction_io_text.cc,读取时每两行解析一幅图像(ReadImagesText,第 187-281 行),其中-1会被映射为kInvalidPoint3DId

points3D.txt —— 3D 点与轨迹
# 3D point list with one line of data per point: # POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[] as (IMAGE_ID, POINT2D_IDX) # Number of points: 3, mean track length: 3.3334 63390 1.67241 0.292931 0.609726 115 121 122 1.33927 16 6542 15 7345 6 6714 14 7227 63376 2.01848 0.108877 -0.0260841 102 209 250 1.73449 16 6519 15 7322 14 7212 8 3991 63371 1.71102 0.28566 0.53475 245 251 249 0.612829 118 4140 117 4473

字段含义:

  • POINT3D_ID:3D 点唯一标识(注意不连续);
  • X, Y, Z:3D 点在世界坐标系下的坐标;
  • R, G, B:颜色(0–255);
  • ERROR:重投影误差(单位:像素),仅在全局 BA(bundle adjustment)之后更新
  • TRACK[] as (IMAGE_ID, POINT2D_IDX):该 3D 点的观测轨迹,每对(IMAGE_ID, POINT2D_IDX)表示它在某幅图像的哪个特征点(下标)被观测到。POINT2D_IDXimages.txt中特征点列表的从零开始的下标

上例中,点63390被 4 幅图像观测(track length 4),点63371被 2 幅图像观测。解析实现见ReadPoints3DText(reconstruction_io_text.cc)。对第 1.2 节约定的印证:示例中 3D 点 ID 为 63390、63376、63371,最大 ID 远大于点数 3,充分说明 ID 不连续且不能用于计数。

2.3 二进制格式的内存布局

二进制格式与文本格式字段一一对应,只是以紧凑的小端二进制存储,读取更快。文件仍为 5 个:rigs.bincameras.binframes.binimages.binpoints3D.bin。以 reconstruction_io_binary.cc 的实现为据,各文件布局如下:

cameras.bin

uint64_t num_cameras 每个相机依次: camera_t camera_id (uint32) int model_id (int32,CameraModelId 枚举) uint64_t width uint64_t height double[] params (数量由 CameraModelNumParams 决定)

images.bin

uint64_t num_reg_images 每个已注册图像依次: image_t image_id (uint32) double qw, qx, qy, qz (cam_from_world 旋转) double tx, ty, tz (cam_from_world 平移) camera_t camera_id (uint32) char[] name (以 '\0' 结尾的字符串) uint64_t num_points2D 每个特征点: double x, double y, point3D_t point3D_id (uint32)

注意WriteImagesBinary(reconstruction_io_binary.cc)只写入已注册图像NumRegImages/RegImageIds)。读取时存在向后兼容分支:若 rigs 与 frames 均为 0,则按传统重建处理,为每幅图像自动创建帧(CreateFrameForImage),见第 146-154 行。

points3D.bin

uint64_t num_points3D 每个 3D 点依次: point3D_t point3D_id (uint32) double x, y, z uint8_t r, g, b double error uint64_t track_length track_length 对: image_t image_id (uint32), point2D_t point2D_idx (uint32)

rigs.bin / frames.bin结构与文本对应:rigs 先写uint64_t num_rigs,再按rig_idnum_sensors、参考传感器(int type, uint32 id)、非参考传感器(int type, uint32 id, uint8_t has_pose, [7 × double])写出;frames 先写帧数量,再写frame_id, rig_id, rig_from_world(7 × double), num_data_ids, 每个 data_id 为 (int type, uint32 sensor_id, uint64 data_id)。完整读写实现见 reconstruction_io_binary.cc 与第 266-379 行。

模型目录的读取与写入入口Reconstruction::ReadBinary/ReadText在 reconstruction.cc,其中rigsframes文件若缺失则跳过(向后兼容);WriteBinary/WriteText(第 1010-1026 行)则总是写出全部 5 个文件。


三、稠密重建:工作区目录结构与产物

COLMAP 稠密重建使用如下固定工作区目录结构

+── images │ +── image1.jpg │ +── image2.jpg │ +── ... +── sparse │ +── cameras.txt │ +── images.txt │ +── points3D.txt +── stereo │ +── consistency_graphs │ │ +── image1.jpg.photometric.bin │ │ +── image2.jpg.photometric.bin │ │ +── ... │ +── depth_maps │ │ +── image1.jpg.photometric.bin │ │ +── image2.jpg.photometric.bin │ │ +── ... │ +── normal_maps │ │ +── image1.jpg.photometric.bin │ │ +── image2.jpg.photometric.bin │ │ +── ... │ +── patch-match.cfg │ +── fusion.cfg +── fused.ply +── meshed-poisson.ply +── meshed-delaunay.ply +── textured │ +── mesh.ply │ +── texture.png +── run-colmap-geometric.sh +── run-colmap-photometric.sh

各目录/文件的角色:

  • images:去畸变(undistorted)后的图像;
  • sparse:使用去畸变相机的稀疏重建(文本格式,通常为cameras.txtimages.txtpoints3D.txt);
  • stereo:立体重建中间结果,内含consistency_graphsdepth_mapsnormal_maps三个子目录,以及patch-match.cfgfusion.cfg两个配置文件(PatchMatch 立体匹配与深度融合阶段各自读取它们);
  • fused.ply:深度融合的输出点云;
  • meshed-poisson.ply / meshed-delaunay.ply:泊松(Poisson)与 Delaunay 两种网格化(meshing)算法的输出网格;
  • texturedmesh_texturer生成的纹理化网格——mesh.ply(含逐面 UV 坐标)与texture.png(纹理图集);
  • run-colmap-geometric.sh / run-colmap-photometric.sh:执行稠密重建的示例命令行脚本(几何/光度两种 PatchMatch 变体)。

从结构可以推断,稠密重建的典型流程是:undistorter(生成 images 与 sparse)→patch_match_stereo(生成 stereo 下的深度/法向/一致性图)→stereo_fusion(融合为 fused.ply)→poisson_mesher/delaunay_mesher(网格化)→mesh_texturer(纹理化)。各阶段的可执行程序与参数可查阅 doc/cli.rst。

3.1 深度图与法向图(Depth and Normal Maps)

深度图与法向图以文本头 + 二进制体的混合形式存储:

  • 文本头定义图像尺寸,格式为width&height&channels&
  • 其后紧跟按行优先(row-major)排列的float32二进制数据
  • 深度图channels=1,法向图channels=3

即深度图体数据为width × height个 float32(每像素一个深度值),法向图体数据为width × height × 3个 float32(每像素三个分量)。文件名形如image1.jpg.photometric.binphotometric表示来自光度 PatchMatch)。深度图与法向图可用 pycolmap 在 Python 中直接读取。

3.2 一致性图(Consistency Graphs)

一致性图定义了一幅图像中每个像素与哪些源图像保持一致(光度一致性验证的结果),供深度融合阶段使用。存储同样为混合格式:

  • 文本部分与深度/法向图相同(width&height&channels&头);
  • 二进制部分是连续的int32值序列,格式为:
<row> <col> <N> <image_idx1> ... <image_idxN>

其中(row, col)是像素在图像中的位置,N是该像素一致的源图像数量,其后是N个图像下标。这些下标是相对于images.txt中图像顺序的索引(即注册图像的排列次序),理解这一点对跨文件解析至关重要。


四、用 pycolmap 程序化读写模型

除 C++ API 外,最便捷的读写方式是通过 pycolmap(COLMAP 的 Python 绑定,源码位于 src/pycolmap,Python 包入口见 python/pycolmap/init.py)。典型用法:

import pycolmap # 读取稀疏模型(自动探测二进制/文本,二进优先) reconstruction = pycolmap.Reconstruction("path/to/sparse/0") # 遍历相机 for camera_id, camera in reconstruction.cameras.items(): print(camera_id, camera.model, camera.width, camera.height, camera.params) # 遍历已注册图像,读取位姿与特征点 for image_id, image in reconstruction.images.items(): if image.registered: qvec = image.qvec # 四元数 (QW, QX, QY, QZ) tvec = image.tvec # 平移 (TX, TY, TZ) name = image.name points2D = image.points2D # (x, y, point3D_id) # 遍历 3D 点 for point3D_id, point in reconstruction.points3D.items(): xyz = point.xyz rgb = point.color error = point.error track = point.track # 观测轨迹 # 写回文本格式(也可用 WriteBinary) reconstruction.write_text("path/to/output")

官方 Python 示例还包括模型可视化脚本 python/examples/visualize_model.py 与自定义增量重建管线 python/examples/custom_incremental_pipeline.py,可参考其如何加载模型目录。此外,benchmark/reconstruction/evaluation 下的评估脚本大量使用 pycolmap 读取重建模型计算几何精度,是阅读格式解析用法的良好范例。


五、实用速查:常见操作对照表

目标GUI 操作CLI 命令(示意)
导出当前选中模型File > Export model
导出全部模型File > Export all
导入模型File > Import modelcolmap model_converter --input_path ...
二进制 → 文本File > Export model as textcolmap model_converter --output_type TXT
文本 → 二进制File > Export modelcolmap model_converter --output_type BIN
导出 NVM / Bundler / PLY / VRML / R3D / CAMFile > Export as...colmap model_converter --output_type NVM
打印模型统计信息colmap model_analyzer --path ...(见 src/colmap/exe/model.cc)

model_analyzer输出 Rigs、Cameras、Frames、Registered frames、Images、Registered images、Points、Observations、Mean track length、Mean observations per image、Mean reprojection error 等统计量;加--verbose可逐条列出相机与已注册图像详情。


结语

COLMAP 的文件格式设计体现了工程化的取舍:二进制格式面向机器读写效率,文本格式面向人工检查与调试,二者字段语义完全一致并共享同一套Reconstruction数据结构(src/colmap/scene/reconstruction.h);稠密重建产物则通过固定的工作区布局把稀疏、立体、融合、网格化、纹理化各阶段的输出组织为一条清晰的流水线。理解*_idx*_id的语义差异、位姿的四元数约定、二进制布局的字段顺序以及稠密文件的混合编码,是安全解析、转换和再加工 COLMAP 输出数据的基石。本文所述的每一种格式细节都可以在 reconstruction_io_binary.cc、reconstruction_io_text.cc 与 reconstruction.cc 中找到对应实现,遇到边界情况时以源码为准。

【免费下载链接】colmapCOLMAP - Structure-from-Motion and Multi-View Stereo项目地址: https://gitcode.com/GitHub_Trending/co/colmap

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询