
项目名称:全角度三维视频拍摄 / 自由视角三维视频重建 项目来源:第二届 3D 视觉创新应用竞赛 + 首都师范大学舒克贝塔团队 相机型号:Astra Pro(Orbbec RGB-D 相机体系) 支持设备:Jetson Nano 源代码链接:https://github.com/3DCVdeveloper/3DReconstruction_Full-angle
01 一句话看懂:固定相机,也能生成“绕着物体看”的三维视频 👀
普通视频只能从相机所在的位置观看目标:角度不对、目标被遮挡、细节看不清时,后期很难补救。本项目进一步把“二维视频”拆解为三维结构、运动轨迹和姿态变化,再通过自由视角渲染,让用户从任意方向观察运动过程。
它做的不是简单地“录下一个物体在动”,而是把视频恢复为:
| 组成部分 | 对应含义 | 在项目中的体现 |
|---|---|---|
| 三维结构 | 目标本身的点云或网格 | COLMAP 输出 fused.ply |
| 运动轨迹 | 每一帧目标在空间中的位置 | 轨迹文件 transform_data.txt |
| 姿态变化 | 每一帧目标的旋转方向 | 四元数 qw, qx, qy, qz |
| 自由视角渲染 | 从任意角度观看运动过程 | Python 可视化 / UE4 渲染 |
样例数据已经包含 COLMAP 输出的 cameras.txt、images.txt、points3D.txt、fused.ply,以及 UE4 使用的 transform_data.txt。其中 UE4 示例包含 145 帧位姿记录,稠密点云 fused.ply 约 105,399 个顶点,项目已经跑通了从采集、分割、重建到可视化的完整实验链路。
02 这个项目到底难在哪里?📌
经典三维重建中的 SfM(Structure from Motion)和 MVS(Multi-View Stereo)通常默认一个前提:目标是静止的,相机在运动。比如我们拿手机绕着雕像拍一圈,算法可以根据多视角图像匹配特征点,估计相机位姿并重建雕像。
但本项目面对的是更现实的问题:相机可能固定,目标却在运动。比如坦克模型在桌面上行进、旋转,甚至炮塔和底盘之间还存在相对运动。直接把整段视频丢给传统重建算法,会出现几个典型问题:
| 难点 | 为什么麻烦 | 项目应对方式 |
|---|---|---|
| 背景静止、目标运动 | 背景特征会干扰相机位姿估计 | 先做像素级 Mask,剥离目标 |
| 多部件相对运动 | 整体刚体假设不成立 | 炮塔、底盘等部件分别 Mask 和重建 |
| RGB 重建尺度不确定 | 单靠图像无法确定真实尺寸 | 用深度信息或多相机外参校正尺度 |
| 点云还在重建坐标系 | 无法直接形成真实运动视频 | 用每帧位姿做坐标变换 |
项目的核心洞察是:只要先把目标从背景中分割出来,就可以站在“随目标一起运动的坐标系”里看问题。此时目标近似静止,而固定相机看到的每一帧,就像相机从不同角度观察同一个目标。这样,动态重建问题就被转化成了经典 SfM 问题。
原始问题:固定相机 + 运动目标 + 背景干扰
转化之后:静止目标 + 多个虚拟相机视角
这一转化非常关键,因为它让项目可以复用成熟的 COLMAP,而不是从零开发一套动态三维重建系统。
03 总体路线:Mask + COLMAP + 尺度校正 + 坐标变换 🧭
项目的主链路可以理解为一条数据流水线:

对应到工程模块,可以整理为:
| 阶段 | 输入 | 关键处理 | 输出 |
|---|---|---|---|
| 数据采集 | RGB 视频流、深度视频流 | Astra Pro / Orbbec 相机采集,ROS 录制与导出 | 按时间排序的 RGB/RGB-D 图像 |
| 目标分割 | 原始视频帧 | SiamMask 或 MiVOS 做像素级 Mask | 去背景目标图像、部件 Mask |
| 三维重建 | Mask 后的图像序列 | COLMAP 稀疏重建与稠密重建 | 点云、相机位姿、稀疏点 |
| 尺度校正 | 深度图或相机外参 | 消除 RGB SfM 的尺度歧义 | 统一尺度下的目标或部件 |
| 动态恢复 | 点云 + 每帧位姿 | 坐标变换、轨迹导出 | 可播放的三维运动序列 |
| 可视化 | 点云、轨迹、姿态 | Python 绘制或 UE4 驱动 Actor | 自由视角三维视频 |
04 技术一:像素级 Mask,让 COLMAP 只看该看的目标 ✂️

4.1 为什么 Mask 是第一步?
在原始视频中,相机固定不动,背景也是静止的,只有目标在运动。如果不先分割,COLMAP 会同时看到两类矛盾信息:
- 背景特征:相机似乎没有运动
- 目标特征:相机似乎在围绕目标运动
这会让特征匹配和位姿估计变得混乱。Mask 的意义就是把背景特征“关掉”,让 COLMAP 只在目标区域内寻找匹配点。
4.2 项目中用了哪些分割方法?
| 方法 | 项目角色 | 适合场景 |
|---|---|---|
| SiamMask | 首帧框选 ROI,后续自动跟踪目标并输出 Mask 图 | 单目标、快速实验、开发板流程 |
| MiVOS | 交互式视频对象分割,支持多对象传播和修正 | 炮塔/底盘分割、遮挡修正、多部件目标 |
SiamMask 的工作方式可以简化为:
第 1 帧:人工框选目标 ROI
后续帧:模型根据外观相似性跟踪目标
输出:只保留目标区域,背景置黑的图像
项目脚本中的关键处理逻辑可以抽象成下面的伪代码:
# 1. 读取视频帧
frames = load_images(input_folder)
# 2. 在第一帧中选择目标区域
target_box = select_roi(frames[0])
# 3. 初始化跟踪器
tracker.init(frames[0], target_box)
# 4. 对每一帧输出 Mask 后的目标图像
for frame in frames[1:]:
mask = tracker.track(frame)
masked_frame = frame * mask
save(masked_frame)
对于多组分刚体,Mask 的粒度要更细。例如坦克不是整体刚体时,应拆成:
tank_chassis_mask → 底盘重建
tank_turret_mask → 炮塔重建
这样做的原因是:底盘和炮塔的运动不同,如果把它们当成一个整体输入 COLMAP,匹配点在不同帧之间不满足同一个刚体变换,重建质量会明显下降。
4.3 读者该记住什么?
Mask 不是美化图片,而是重建算法的“输入清洗”。它决定了后续 SfM 看到的是一个可被解释的刚体目标,还是一个背景与运动物体混杂的矛盾场景。
05 技术二:把“运动物体”反过来看成“运动相机” 🧠
5.1 核心思想
这是项目最重要的理论转折。对于固定相机来说,是目标在运动;但如果切换到目标自己的坐标系,目标就变成静止的,变化的是相机相对目标的视角。

可以用一个简单对照理解:
| 原始观察方式 | 坐标系转换后的理解 |
|---|---|
| 相机固定,坦克在运动 | 坦克固定,相机从不同角度观察 |
| 每帧是目标的不同姿态 | 每帧是一个虚拟相机视角 |
| 动态重建问题 | 静态目标的 SfM 问题 |
固定相机看到的第 k 帧
↓ 换到目标坐标系
等价于一个虚拟相机 pose_k 观察静态目标
5.2 为什么最终选择 COLMAP?
项目资料对比了 COLMAP、IDR、RGB-D 点云重建和 BundleFusion。最终采用 COLMAP,是一个工程上非常合理的选择。
| 方法 | 优点 | 局限 | 项目结论 |
|---|---|---|---|
| COLMAP | 成熟稳定,可输出位姿和带颜色点云 | 依赖纹理和 Mask 质量 | 作为主重建方案 |
| IDR | 曲面质量较高 | 计算量大,开发板难以承受 | 可作效果参考,不适合作主流程 |
| RGB-D 点云匹配 | 可利用深度 | 样例中精度不够理想 | 暂不作为主方案 |
| BundleFusion | RGB-D 重建代表方法之一 | 大量背景被 Mask 后容易崩溃 | 不适配当前数据形态 |
COLMAP 的典型输出可以理解为:
cameras.txt → 相机内参
images.txt → 每张图像对应的相机位姿 R, t
points3D.txt → 稀疏三维点
fused.ply → 稠密融合后的点云
images.txt 是后续“让点云动起来”的关键,因为里面记录了每一帧对应的相机姿态。项目正是利用这些姿态,把静态点云逐帧变换到真实运动位置。
5.3 COLMAP 输出数据的理解示例
COLMAP 中一帧图像的位姿可以抽象为:
IMAGE_ID, qw, qx, qy, qz, tx, ty, tz, CAMERA_ID, IMAGE_NAME
其中:
| 字段 | 含义 |
|---|---|
qw, qx, qy, qz |
四元数,表示旋转 |
tx, ty, tz |
平移向量 |
CAMERA_ID |
使用哪一个相机模型 |
IMAGE_NAME |
对应原始帧图像 |
后续 Matlab / Python 会把四元数转换为旋转矩阵,再对点云执行逐帧变换。
06 技术三:尺度消歧,解决多部件“拼不上”的硬伤 📏

6.1 为什么 RGB 重建会有尺度问题?
只靠 RGB 图像做 SfM 时,算法能恢复目标形状和相机相对运动,但很难知道真实尺度。原因是:如果把目标和相机轨迹同时放大 k 倍,投影回二维图像后仍然可能看起来一样。
用公式直观理解:
X → 三维点
R, t → 相机旋转和平移
project → 投影到二维图像
project(R * X + t)
与
project(R * (kX) + kt)
在图像上可能等价
所以,单靠 RGB 图像,COLMAP 通常只能恢复“相对尺度”。
6.2 为什么多组分刚体尤其需要尺度校正?
如果只有一个整体刚体,整体缩放后仍然可以形成连贯视频。但如果目标被拆成多个部件分别重建,尺度问题就会非常明显:
底盘重建尺度:scale_a
炮塔重建尺度:scale_b
如果 scale_a != scale_b:
合成后会出现部件错位、比例失真、旋转中心不一致
项目采用两类额外信息消除尺度偏差:
| 校正方式 | 核心思想 | 适合情况 |
|---|---|---|
| 深度信息校正 | 利用 Astra Pro 的深度帧提供真实距离 | 单相机 RGB-D 采集 |
| 多相机外参约束 | 利用标定后的相机间固定空间关系 | 多相机、多目标、遮挡场景 |
6.3 相机标定与配准在这里做什么?
项目目录中的 calibration_registration 模块用于相机内参标定和多相机外参配准。它的目的可以概括为:
calibrate.py → 求单个相机自己的成像参数
register.py → 求相机 B 到相机 A 的旋转 R 和平移 t
一个简化的命令流程如下:
# 单相机标定:输入多张标定板图像,输出 calib.yml
python calibrate.py --dataset-dir ./camera_01 --template ./PlaneFiles/plane.json
# 双相机配准:输入两个相机的标定文件和同一标定板图像,输出外参关系
python register.py
--img-a ./camera_01/color.jpg
--img-b ./camera_02/color.jpg
--template ./PlaneFiles/plane.json
--calib-a ./camera_01/calib.yml
--calib-b ./camera_02/calib.yml
--out-dir ./registration_out
输出的外参可以理解为:
P_camera_A = R_AB * P_camera_B + t_AB
这让多台相机看到的目标可以被放到同一个世界坐标系中,也为多目标、多部件的尺度对齐提供依据。
07 技术四:坐标变换,真正把静态点云“动起来” 🔄

7.1 为什么还需要坐标变换?
COLMAP 输出的是一个静态点云,以及每张图像对应的相机位姿。要生成三维视频,不能只显示一个静态模型,而是要根据每一帧的位姿,把点云放回真实运动轨迹中。
核心公式可以写成:
X_world(k) = R_k * X_model + t_k
其中:
| 符号 | 含义 |
|---|---|
X_model |
COLMAP 重建坐标系中的点云坐标 |
R_k |
第 k 帧的旋转矩阵 |
t_k |
第 k 帧的平移向量 |
X_world(k) |
第 k 帧在世界/相机坐标系中的点云位置 |
7.2 项目代码里的变换逻辑
Matlab 脚本中的核心逻辑可以简化为:
% 读取 COLMAP 输出的四元数和平移
rot1 = data_T{j}(1:3, 1:3);
trans1 = data_T{j}(4, 1:3);
% 对点云中每一个点做刚体变换
loc = rot1 * ptCloud.Location' + trans1';
pt = pointCloud(loc');
% 计算该帧目标质心
barycenter = mean(pt.Location);
如果用更通用的 Python 伪代码表示,就是:
for frame_id in frames:
R = pose[frame_id].rotation_matrix
t = pose[frame_id].translation_vector
transformed_points = []
for X in point_cloud:
X_world = R @ X + t
transformed_points.append(X_world)
render(transformed_points)
7.3 UE4 为什么使用四元数?
旋转可以用欧拉角、旋转矩阵或四元数表示。UE4 示例中使用四元数,是因为四元数适合表示连续旋转,也能减少欧拉角可能出现的万向节锁问题。
项目导出的 transform_data.txt 每一行可以理解为:
qw, qx, qy, qz, center_x, center_y, center_z
本地样例前几行数据形态类似:
0.999690, 0.000990, -0.024678, 0.003076, -2.246228, 0.612343, 4.487050
0.999518, 0.003747, -0.030764, 0.001695, -2.079275, 0.619062, 4.517896
0.999084, 0.003610, -0.042596, -0.002034, -1.952475, 0.625035, 4.543966
UE4 中的控制逻辑可以概括为:
// 每一帧取出当前时刻对应的位姿
NewLocation.X = scale * trans_vec[PoseIndex][4];
NewLocation.Y = scale * trans_vec[PoseIndex][5];
NewLocation.Z = scale * trans_vec[PoseIndex][6];
// 用四元数恢复旋转
FQuat InputQuat;
InputQuat.W = trans_vec[PoseIndex][0];
InputQuat.X = trans_vec[PoseIndex][1];
InputQuat.Y = trans_vec[PoseIndex][2];
InputQuat.Z = trans_vec[PoseIndex][3];
NewRotation = InputQuat.Rotator();
SetActorLocationAndRotation(NewLocation, NewRotation);
这一段的意义是:点云模型不再只是静态展品,而是被每帧的“位置 + 姿态”驱动,形成真实轨迹上的动态三维对象。
08 工程实现:从采集到渲染的闭环 🛠️
项目并不是单一脚本,而是一套多工具协作的工程流程。
| 模块 | 对应文件/目录 | 作用 |
|---|---|---|
| 项目说明 | README.md |
描述输入、输出和算法阶段 |
| 分割脚本 | demo1/siammask_demo.py |
首帧 ROI 初始化,后续帧跟踪并输出 Mask |
| COLMAP 模型读取 | demo1/read_write_model.py、solo.py |
读取相机、图像、点云并做可视化 |
| 标定配准 | calibration_registration/calibrate.py、register.py |
相机内参标定、多相机外参注册 |
| 动态变换 | prepare_transform_data.m、testTrans.m、testMerge.m |
读取 COLMAP 位姿,计算每帧位置和姿态 |
| 点云导出 | prepare_xyzrgb_data.m |
将 PLY 点云转换为 UE4 可读数据 |
| UE4 渲染 | UE4/ue4_code/tank_dummy.cpp |
根据轨迹文件驱动点云模型运动 |
可以把它理解成一个“离线重建 + 渲染播放”的系统:
Jetson Nano / PC
负责相机采集、数据整理、初步 Mask
COLMAP / Matlab / Python
负责重建点云、解析位姿、生成轨迹文件
Unreal Engine 4
负责将点云和轨迹渲染成更具视觉冲击力的自由视角视频
09 展示场景:从单坦克到多目标联动 🚗
项目资料中记录了多个实验场景:

| 场景 | 配置 | 看点 |
|---|---|---|
| 坦克低清开发板 Solo 实例 | 单相机,约 130 张图像,SiamMask,COLMAP,Matlab | 验证 Jetson Nano 侧基本流程 |
| 坦克高清渲染版 | 单相机,约 150 张图像,MiVOS,COLMAP,UE4 | 真实轨迹 + UE4 后期渲染 |
| 坦克和直升机联动 | 双相机,多目标,约 480 张图像 | 多相机降低遮挡影响,不同目标统一到同一坐标系 |
| 坦克行进中炮塔转动 | 单相机,多组分刚体 | 底盘和炮塔分别 Mask、分别重建,再校正尺度并合成 |
其中最值得关注的是“坦克行进中炮塔转动”。它已经不是单一刚体运动,而是多组分刚体运动。项目采用了:
分部件 Mask
↓
分部件 COLMAP 重建
↓
尺度校正
↓
各自坐标变换
↓
统一坐标系合成
这说明方案具有向复杂动态目标扩展的潜力。
10 应用价值:不只是好看,而是让视频变成可测量的三维证据 🧩
自由视角三维视频的价值在于:它把二维视频里的“看起来如何”,升级成三维空间中的“真实发生了什么”。

工业检测 🏭
在流水线场景中,产品可能持续运动,传统固定相机容易受到角度限制。该项目路线可以把产品运动过程恢复到三维空间中,用于观察形变、装配状态、轨迹偏差和异常动作。
交通事故分析 🚦
事故现场常常只有固定监控视角。若能对车辆等刚体目标做三维重建和轨迹恢复,就可以辅助还原碰撞过程、相对位置和运动姿态,为复盘提供更强的空间证据。
装备与机器人测试 🤖
对于小型无人车、机械臂、移动平台等设备,三维视频可以记录其真实轨迹和姿态变化,辅助调试控制算法、定位误差和动作稳定性。
赛车与运动复盘 🏎️
对于赛车、模型车、运动器材等高速移动目标,自由视角三维视频可以让用户从不同角度回看关键动作,兼具展示、训练和留念价值。
11 亮点与局限:真正的价值在“可落地的折中” ⚖️
项目亮点
| 亮点 | 说明 |
|---|---|
| 思路巧妙 | 将运动刚体问题转化为静态目标 SfM 问题 |
| 工程完整 | 覆盖采集、Mask、重建、坐标变换、UE4 渲染 |
| 支持多目标 | 双相机方案可缓解遮挡,支持坦克和直升机联动 |
| 支持多组分刚体 | 可处理炮塔与底盘存在相对运动的复杂目标 |
| 适配边缘设备 | 以 Jetson Nano 为重要部署对象,强调低算力可运行 |
当前局限
| 局限 | 影响 |
|---|---|
| 依赖 Mask 质量 | 漏分、错分会影响 COLMAP 特征匹配 |
| 对纹理有要求 | 低纹理、反光、透明物体不利于 RGB 重建 |
| 实时性有限 | COLMAP 和后处理流程更偏离线重建 |
| 多部件自动化不足 | 需要分别分割、重建、校正和合成 |
| 时间戳精度待完善 | 多相机或高速运动场景需要更精确同步 |
这些局限并不削弱项目价值,反而说明它的定位很清楚:在现有开源工具和低成本硬件条件下,构建一条可验证、可展示、可继续扩展的动态三维重建路径。
12 后续优化方向 🔮
- 将 Mask、COLMAP 调用、尺度校正和轨迹导出做成一键式流水线,降低使用门槛。
- 引入更稳健的视频分割模型,减少交互式修正成本。
- 使用更严格的时间戳同步机制,提升多相机、多目标动态重建精度。
- 将点云渲染升级为网格或神经渲染结果,提升自由视角观看质量。
- 在 Jetson Nano 上保留轻量预处理,把高算力重建放到 PC 或边缘服务器,形成端边协同架构。
13 总结:这是一次把“视频”升级成“空间记忆”的实验 🌌
全角度三维视频拍摄项目的核心价值,不在于单独用了某个炫目的算法,而在于它把多个成熟模块组合成了一条有说服力的技术链路:
RGB-D 采集 → 目标 Mask → COLMAP 重建 → 尺度校正 → 坐标变换 → UE4 自由视角渲染
它最关键的创新点,是用 Mask 将运动目标从复杂场景中剥离,再从随体坐标系的角度把动态重建问题转化为经典 SfM 问题;同时通过深度信息或多相机外参解决尺度问题,通过坐标变换把静态点云重新放回真实运动轨迹中。

对于竞赛项目而言,这套方案的亮点非常明确:它不是只停留在论文式构想,而是用 Astra Pro / Orbbec 相机、Jetson Nano、COLMAP、Matlab、Python 和 UE4 跑通了从拍摄到渲染的闭环。它让固定视角视频获得了三维空间表达能力,也为工业检测、事故复盘、机器人测试和运动回放提供了一条低成本但很有想象力的实现路径。