19f11469da453.png

项目名称:全角度三维视频拍摄 / 自由视角三维视频重建 项目来源:第二届 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 + 尺度校正 + 坐标变换 🧭

项目的主链路可以理解为一条数据流水线:

19f1151ca7480.png

对应到工程模块,可以整理为:

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

04 技术一:像素级 Mask,让 COLMAP 只看该看的目标 ✂️

19f11565520e5.png

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 核心思想

这是项目最重要的理论转折。对于固定相机来说,是目标在运动;但如果切换到目标自己的坐标系,目标就变成静止的,变化的是相机相对目标的视角。

19f115a84043c.png

可以用一个简单对照理解:

原始观察方式 坐标系转换后的理解
相机固定,坦克在运动 坦克固定,相机从不同角度观察
每帧是目标的不同姿态 每帧是一个虚拟相机视角
动态重建问题 静态目标的 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 技术三:尺度消歧,解决多部件“拼不上”的硬伤 📏

19f115d3683e8.png

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 技术四:坐标变换,真正把静态点云“动起来” 🔄

19f115f93a3e4.png

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 展示场景:从单坦克到多目标联动 🚗

项目资料中记录了多个实验场景:

19f11631856a4.png
场景 配置 看点
坦克低清开发板 Solo 实例 单相机,约 130 张图像,SiamMask,COLMAP,Matlab 验证 Jetson Nano 侧基本流程
坦克高清渲染版 单相机,约 150 张图像,MiVOS,COLMAP,UE4 真实轨迹 + UE4 后期渲染
坦克和直升机联动 双相机,多目标,约 480 张图像 多相机降低遮挡影响,不同目标统一到同一坐标系
坦克行进中炮塔转动 单相机,多组分刚体 底盘和炮塔分别 Mask、分别重建,再校正尺度并合成

其中最值得关注的是“坦克行进中炮塔转动”。它已经不是单一刚体运动,而是多组分刚体运动。项目采用了:

分部件 Mask
    ↓
分部件 COLMAP 重建
    ↓
尺度校正
    ↓
各自坐标变换
    ↓
统一坐标系合成

这说明方案具有向复杂动态目标扩展的潜力。

10 应用价值:不只是好看,而是让视频变成可测量的三维证据 🧩

自由视角三维视频的价值在于:它把二维视频里的“看起来如何”,升级成三维空间中的“真实发生了什么”。

19f1163e2615c.png

工业检测 🏭

在流水线场景中,产品可能持续运动,传统固定相机容易受到角度限制。该项目路线可以把产品运动过程恢复到三维空间中,用于观察形变、装配状态、轨迹偏差和异常动作。

交通事故分析 🚦

事故现场常常只有固定监控视角。若能对车辆等刚体目标做三维重建和轨迹恢复,就可以辅助还原碰撞过程、相对位置和运动姿态,为复盘提供更强的空间证据。

装备与机器人测试 🤖

对于小型无人车、机械臂、移动平台等设备,三维视频可以记录其真实轨迹和姿态变化,辅助调试控制算法、定位误差和动作稳定性。

赛车与运动复盘 🏎️

对于赛车、模型车、运动器材等高速移动目标,自由视角三维视频可以让用户从不同角度回看关键动作,兼具展示、训练和留念价值。

11 亮点与局限:真正的价值在“可落地的折中” ⚖️

项目亮点

亮点 说明
思路巧妙 将运动刚体问题转化为静态目标 SfM 问题
工程完整 覆盖采集、Mask、重建、坐标变换、UE4 渲染
支持多目标 双相机方案可缓解遮挡,支持坦克和直升机联动
支持多组分刚体 可处理炮塔与底盘存在相对运动的复杂目标
适配边缘设备 以 Jetson Nano 为重要部署对象,强调低算力可运行

当前局限

局限 影响
依赖 Mask 质量 漏分、错分会影响 COLMAP 特征匹配
对纹理有要求 低纹理、反光、透明物体不利于 RGB 重建
实时性有限 COLMAP 和后处理流程更偏离线重建
多部件自动化不足 需要分别分割、重建、校正和合成
时间戳精度待完善 多相机或高速运动场景需要更精确同步

这些局限并不削弱项目价值,反而说明它的定位很清楚:在现有开源工具和低成本硬件条件下,构建一条可验证、可展示、可继续扩展的动态三维重建路径。

12 后续优化方向 🔮

  1. 将 Mask、COLMAP 调用、尺度校正和轨迹导出做成一键式流水线,降低使用门槛。
  1. 引入更稳健的视频分割模型,减少交互式修正成本。
  1. 使用更严格的时间戳同步机制,提升多相机、多目标动态重建精度。
  1. 将点云渲染升级为网格或神经渲染结果,提升自由视角观看质量。
  1. 在 Jetson Nano 上保留轻量预处理,把高算力重建放到 PC 或边缘服务器,形成端边协同架构。

13 总结:这是一次把“视频”升级成“空间记忆”的实验 🌌

全角度三维视频拍摄项目的核心价值,不在于单独用了某个炫目的算法,而在于它把多个成熟模块组合成了一条有说服力的技术链路:

RGB-D 采集 → 目标 Mask → COLMAP 重建 → 尺度校正 → 坐标变换 → UE4 自由视角渲染

它最关键的创新点,是用 Mask 将运动目标从复杂场景中剥离,再从随体坐标系的角度把动态重建问题转化为经典 SfM 问题;同时通过深度信息或多相机外参解决尺度问题,通过坐标变换把静态点云重新放回真实运动轨迹中。

19f1165d145cb.png

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

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注