ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

COLMAP 输出文件格式完全指南:稀疏重建与稠密重建的读写规范 COLMAP 输出文件格式完全指南稀疏重建与稠密重建的读写规范【免费下载链接】colmapCOLMAP - Structure-from-Motion and Multi-View Stereo项目地址: https://gitcode.com/GitHub_Trending/co/colmap导读doc/format.rst 是 COLMAPStructure-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 APIsrc/colmap/scene/reconstruction_io.h 声明了全部读写函数Python APIpycolmap 提供了便捷的模型读取封装后文有示例。从源码看src/colmap/util/endian.h 提供了ReadBinaryLittleEndian/WriteBinaryLittleEndian辅助模板reconstruction_io_binary.cc 中所有字段uint64_t、int、double、uint8_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 使用二进制格式保存稀疏模型机器可读、速度快同时提供文本格式选项人类可读、速度慢。两种格式都按rigs、cameras、frames、images、points拆分为多个文件包含这些文件的任意目录即构成一个稀疏模型。二进制文件扩展名为.bin文本文件扩展名为.txt。加载时有一个重要优先级规则当目录中同时存在二进制与文本文件时COLMAP 优先加载二进制格式。这一逻辑在 reconstruction.cc 的Reconstruction::Read中实现——先检测cameras.bin/images.bin/points3D.bin是否存在再回退到.txt版本。此外还需了解向后兼容性早期版本的 COLMAP 不支持 rig多相机刚体因此rigs与frames文件可能缺失。COLMAP 的重建 I/O 例程完全向后兼容——读取无这些文件的模型时会自动初始化平凡trivial的 rig 和 frame反过来新版本输出的cameras与images文件也完全兼容旧版本读取。从 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.hExportNVM、ExportBundler、ExportPLY、ExportVRML、ExportCam、ExportRecon3D等。值得注意的实现细节ExportNVM仅在导出畸变参数时支持SIMPLE_RADIAL模型若设置skip_distortiontrue则支持全部相机模型但会退化为使用平均焦距对双焦距或带畸变的模型不够精确ExportBundler仅当导出畸变参数时支持SIMPLE_PINHOLE、PINHOLE、SIMPLE_RADIAL、RADIAL四种模型ExportVRML会生成stem.images.wrl与stem.points3D.wrl两个文件。Python 方式稀疏模型可方便地通过 pycolmap 在 Python 中读取示例见后文。官方也提供了模型转换的纯 Python 脚本 python/examples/convert_legacy_rotation_averaging_format.py 作为参考实现。2.2 文本格式逐文件详解COLMAP 为每个重建模型导出以下文本文件rigs.txt、cameras.txt、frames.txt、images.txt、points3D.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_IDrig 的唯一标识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_POSE1后面跟 7 个值描述该传感器相对 rig 的刚体变换四元数 平移若HAS_POSE0则没有位姿。上例中数据包含两个 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_PINHOLE、PINHOLE、SIMPLE_RADIAL、RADIAL、OPENCV、FULL_OPENCV等完整清单见 doc/cameras.rstWIDTH、HEIGHT传感器尺寸像素PARAMS[]变长参数序列数量取决于相机模型。例如SIMPLE_PINHOLE有 3 个参数单个焦距 f、主点 cx、cyPINHOLE有 4 个fx、fy、cx、cySIMPLE_RADIAL有 4 个f、cx、cy、径向畸变 k1。上例中3 台相机基于不同畸变模型但传感器尺寸相同3072×2304。第一台相机焦距 2559.81 像素、主点在像素位置(1536, 1152)。多幅图像可以共享同一相机内参——图像通过CAMERA_ID引用相机这是文本格式实现相机内参复用的机制。解析实现见ReadCamerasTextreconstruction_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该帧所属的 rigRIG_FROM_WORLD[QW, QX, QY, QZ, TX, TY, TZ]rig 坐标系到世界坐标系的刚体变换四元数 平移NUM_DATA_IDS及DATA_IDS[] as (SENSOR_TYPE, SENSOR_ID, DATA_ID)该帧同时曝光的所有传感器对应的数据如相机及其图像数据列表。上例中帧 1 是 rig 1 的一个实例两个相机数据帧 2 是 rig 2 的实例一个相机数据。对应解析逻辑见ReadFramesTextreconstruction_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_ID3D 点唯一标识注意不连续X, Y, Z3D 点在世界坐标系下的坐标R, G, B颜色0–255ERROR重投影误差单位像素仅在全局 BAbundle adjustment之后更新TRACK[] as (IMAGE_ID, POINT2D_IDX)该 3D 点的观测轨迹每对(IMAGE_ID, POINT2D_IDX)表示它在某幅图像的哪个特征点下标被观测到。POINT2D_IDX是images.txt中特征点列表的从零开始的下标。上例中点63390被 4 幅图像观测track length 4点63371被 2 幅图像观测。解析实现见ReadPoints3DTextreconstruction_io_text.cc。对第 1.2 节约定的印证示例中 3D 点 ID 为 63390、63376、63371最大 ID 远大于点数 3充分说明 ID 不连续且不能用于计数。2.3 二进制格式的内存布局二进制格式与文本格式字段一一对应只是以紧凑的小端二进制存储读取更快。文件仍为 5 个rigs.bin、cameras.bin、frames.bin、images.bin、points3D.bin。以 reconstruction_io_binary.cc 的实现为据各文件布局如下cameras.binuint64_t num_cameras 每个相机依次: camera_t camera_id (uint32) int model_id (int32CameraModelId 枚举) uint64_t width uint64_t height double[] params (数量由 CameraModelNumParams 决定)images.binuint64_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)注意WriteImagesBinaryreconstruction_io_binary.cc只写入已注册图像NumRegImages/RegImageIds。读取时存在向后兼容分支若 rigs 与 frames 均为 0则按传统重建处理为每幅图像自动创建帧CreateFrameForImage见第 146-154 行。points3D.binuint64_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_id、num_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其中rigs与frames文件若缺失则跳过向后兼容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.txt、images.txt、points3D.txtstereo立体重建中间结果内含consistency_graphs、depth_maps、normal_maps三个子目录以及patch-match.cfg、fusion.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深度图与法向图以文本头 二进制体的混合形式存储文本头定义图像尺寸格式为widthheightchannels其后紧跟按行优先row-major排列的float32二进制数据深度图channels1法向图channels3。即深度图体数据为width × height个 float32每像素一个深度值法向图体数据为width × height × 3个 float32每像素三个分量。文件名形如image1.jpg.photometric.binphotometric表示来自光度 PatchMatch。深度图与法向图可用 pycolmap 在 Python 中直接读取。3.2 一致性图Consistency Graphs一致性图定义了一幅图像中每个像素与哪些源图像保持一致光度一致性验证的结果供深度融合阶段使用。存储同样为混合格式文本部分与深度/法向图相同widthheightchannels头二进制部分是连续的int32值序列格式为row col N image_idx1 ... image_idxN其中(row, col)是像素在图像中的位置N是该像素一致的源图像数量其后是N个图像下标。这些下标是相对于images.txt中图像顺序的索引即注册图像的排列次序理解这一点对跨文件解析至关重要。四、用 pycolmap 程序化读写模型除 C API 外最便捷的读写方式是通过 pycolmapCOLMAP 的 Python 绑定源码位于 src/pycolmapPython 包入口见 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.ccmodel_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),仅供参考
返回列表