
在单目场景重建领域Monocular Dynamic Gaussian Splatting 并不是简单地把 3DGS 从静态图片换成视频。静态场景用一组不带时间条件的高斯就能表达而一段普通单目视频既没有深度真值也没有多相机同步却要求把每一帧的三维内容还原出来。SMGSemantic Motion Graph就是一种针对这个问题的建模思路它把“哪里在动、如何动、哪些区域属于同一个语义整体”统一到一张运动图上再基于运动图驱动动态高斯泼溅优化。理解这类方法时很多人会陷入两个极端一是只关心跑通开源代码拿到重建视频就结束二是只讨论论文公式不关心模块之间的工程依赖。实际开发中更值得关注的是方法链路输入视频如何转成高斯属性运动信息如何绑定到图上语义分割结果如何约束优化以及某个模块失效时日志和渲染结果应该怎么定位。本文围绕 SMG 的技术动机、整体链路、环境准备、最小闭环、验证方法、失败排查和工程化建议展开。适合正在复现动态高斯泼溅论文、想把单目视频重建扩展成可落地项目或者需要对比动态场景表示方案的开发者阅读。1. 为什么单目动态高斯泼溅需要一张“语义运动图”1.1 动态高斯不只是“给每个高斯加一个速度”3D Gaussian Splatting 的核心是用一组带有位置、协方差、颜色、不透明度的高斯分布去近似场景表面。渲染时把高斯投影到二维图像平面并按照深度从前往后做 alpha 合成。静态场景中所有高斯只需要优化一次渲染任意固定视角都能得到较稳定的结果。一旦场景变成动态视频问题会立刻复杂。每个高斯不仅要在三维空间中移动还要在时间维度保持一致。理想情况下第 t 帧和第 tk 帧里的同一个高斯应该代表物体表面同一个物理点。如果只是训练大量独立的高斯并让它们分别匹配不同时刻经常会出现两个问题高斯身份漂移。前一帧属于人身体的高斯后一帧被优化到了背景上导致渲染出现闪烁和重影。时间不连续。每一帧单独优化帧与帧之间缺少平滑约束重建结果从视觉上看可能稳定但在光流、深度或分割图上存在跳变。因此动态高斯方法通常需要一个额外机制来描述“高斯如何随运动变化”。常见的做法包括耗时变参数、使用神经网络变形场、引入光流监督或者把场景划分为多个刚性部件。SMG 选择的是让语义信息参与运动建模用“语义运动图”把高斯之间的运动关系组织起来。1.2 “语义”在这里不是分割标签这么简单语义分割通常会输出一张逐像素标签图例如人、汽车、道路、背景。语义运动图中的“语义”要承担更多工作。它首先把三维空间中的高斯划分到不同语义组然后同一个语义组内部共享或约束运动参数。更关键的是语义有助于解决单目视频中的运动歧义。单目相机无法直接提供像素深度和遮挡后的三维结构如果只靠 RGB 重建一个移动的人和移动的静态物体可能被解释成同一种运动会导致相互作用部分错误。使用语义先验后人和椅子即便颜色接近也会被约束到不同运动组因为它们在语义上属于不同对象。语义运动图并不要求每个像素都必须属于一个刚体。它允许节点代表一个语义部件边代表部件之间的运动关系。例如骑自行车的人整个人和自行车可以分成两个节点它们之间的边记录相对位移和旋转关系。这样一来即使自行车的部分被遮挡只要能观测到人的运动也能利用边的约束估计出车座的大致运动范围。1.3 SMG 与普通逐帧高斯重建的差异很多动态重建方案采用“静态高斯 变形场”的路线。网络输入时间 t 和三维位置输出每个高斯在当前帧的位移和旋转。这种方案的优点是表达能力强能拟合非常复杂的非刚性运动缺点是容易出现模棱两可的输出。单目视频缺少足够的三维观测网络可能把形状变化和视角变化混在一起。SMG 的差异在于它不是从零预测每个高斯的细微运动而是先建立高层运动结构再让低层高斯服从这个结构。可以类比骨骼动画系统蒙皮顶点不独立运动而是受骨骼和权重控制。语义运动图更像是把场景中的对象、部件和关系显式建模然后用这些关系约束高斯的形变范围。这样设计带来的工程价值很直接模型需要的运动自由度和数据量都会下降。单目视频本来信息量有限如果每个高斯都享受完全独立的自由运动参数训练很容易过拟合加入语义图和分组约束后模型只需要估计少数运动节点和边的参数稳定性会明显提升。2. 理解 SMG 的整体技术链路2.1 一条典型的单目视频处理管线如果要落地 SMG 这类方案通常会把整个流程拆成四个阶段。第一阶段是数据准备。将单目视频抽取为帧序列用 COLMAP 或其他 SfM 工具估计相机位姿必要时得到稀疏点云作为高斯初始化参考。很多动态方法是建立在“每帧相机位姿已知”基础上的这一步不能省略。第二阶段是语义与运动信息提取。对视频帧运行语义分割模型得到逐帧语义掩码。如果有条件还可以估计光流或实例级分割帮助后续构建运动图。值得注意的是二维语义掩码必须反投影到三维空间或与高斯建立关联否则无法用于三维运动约束。第三阶段是构建语义运动图。节点来自语义对象或部件边来自对象间的运动关联。如果场景只有一个物体图可以非常简单如果场景是多个交互对象这一步需要处理跨帧匹配和遮挡关系。第四阶段是高斯泼溅优化。以语义运动图为先验或者约束条件优化高斯参数和运动参数并把 RGB 重建损失、正则项、语义一致性损失一起加入到总损失中。在实际代码实现中这四个阶段往往不是完全串行。语义分割会在开始时先跑一次然后在训练过程中根据渲染效果进行修正运动图也可能会在训练过程中动态合并或拆分节点。把这些逻辑封装成清晰的模块比把代码全部写在训练函数里更容易排查。2.2 关键模块的作用整个链路中最容易出问题的是“二维语义如何绑定到三维高斯”。高斯是一堆没有显式网格连接的点云式元素。二维分割图只能覆盖图像中的像素不能直接告诉某个三维高斯属于哪个节点。常用做法是先把高斯按当前时刻的相机位姿投影到图像平面再读取对应位置的语义标签给高斯分配一个概率分布。如果一段视频中物体高速运动高斯投影位置不准确标签绑定也会产生误差。运动图模块需要存储两类信息。节点信息通常包括每个语义区域的运动状态例如当前位移、旋转速度以及内部高斯列表或权重。边信息描述两个节点之间的相对运动关系例如“跟随”“分离”“在同一刚性平面”。训练时当前帧的变形会依据这些运动参数重新计算每个高斯的位置和朝向。渲染模块和标准动态高斯非常相似。它接收三维高斯在当前时刻的位置、颜色、不透明度和协方差将高斯投影到图像平面执行可微渲染输出预测图和透明度图。渲染结果一方面用于计算 RGB 损失另一方面可以用作语义分割一致性检查。2.3 为什么把运动约束建在“图”上图结构的最大优势是对运动关系做局部建模。如果使用全局变形场场景任意位置的运动都会受到网络容量的限制而且局部遮挡很难显式表达。图结构允许不同语义节点拥有不同的运动模式。举例来说人的躯干做刚体平移手臂做关节旋转衣服和头发做非刚性抖动这三个尺度差异很大。全局网络很难自然区分刚性和非刚性运动而图模型可以在边类型上显式标记刚性或柔性连接。另外图结构天然自带语义传播能力。当一个节点的运动被估计得比较准确时相邻节点的运动搜索范围可以相应缩小。尤其在单目视频中目标偶尔被遮挡或运动模糊语义图可以把可见部分的运动先验传播到被遮挡部分降低整体估计的不确定性。这里要注意不要把语义运动图理解为追求完全刚体分割。真实世界很少存在严格的刚体语义图中也要保留可变形边。工程实现时可以在边的类型上加一个权重或类型参数让模型自己学习刚性约束的强度。3. 工程复现前的环境与数据准备3.1 计算环境与依赖库SMG 这类基于 3D Gaussian Splatting 的动态方法对计算资源要求较高。训练阶段通常需要一张具备至少 16GB 显存的 NVIDIA GPU。如果只是阅读源码或做小规模验证24GB 会更从容因为动态方法在训练时除了存储高斯参数还要保存中间变形状态和梯度显存占用比静态 3DGS 高不少。推荐的内核环境以 CUDA、PyTorch 为主线。常见依赖如下表所示。依赖常见用途说明CUDA Toolkit编译自定义光栅化算子版本要与 PyTorch 和显卡驱动匹配PyTorch自动微分和神经网络定义优先使用对应 CUDA 版本的官方安装包diff-gaussian-rasterization可微的高斯泼溅光栅化论文复现经常需要源码编译OpenCV视频抽帧、图像读写、掩码处理注意与 numpy 版本兼容ffmpeg读取和输出视频抽帧和合成重建结果都会用到semantic segmentation 模型生成语义掩码可以是 Mask2Former、SAM 或专用模型不要一开始就追求最新版本。开源 3DGS 相关代码对 CUDA、PyTorch 版本比较敏感本地环境很新反而可能导致编译不通过或运行时符号不一致。落地前先确认目标代码仓库要求的版本再锁定环境。3.2 数据准备步骤单目视频输入看起来只有一段 MP4实际准备过程需要完成多个校验。第一步用 ffmpeg 把视频抽成帧序列命名规范建议是frame_%05d.png避免后续索引错位。ffmpeg -i input.mp4 -qscale:v 1 frames/frame_%05d.png第二步用 COLMAP 估计每帧相机位姿。不要直接拿视频帧作为所有后续模块的输入而不检查位姿质量。SfM 失败时可以先对帧做裁剪、校正或下采样排除过暗、过曝和大幅模糊的帧。第三步是语义掩码生成。对每一帧运行语义分割模型输出与原始图像分辨率一致的 PNG 掩码或概率图。保存格式建议是单通道整数标签而不是三通道可视化 JPG否则后续标签查询会引入额外解码误差。第四步是时间对齐。视频抽帧索引、语义掩码索引、相机位姿索引必须一致。一个小技巧是生成一个 JSON 文件记录每帧对应的相机位姿路径和语义掩码路径。{ frame_00001: { camera: colmap/frame_00001.txt, mask: masks/frame_00001.png }, frame_00002: { camera: colmap/frame_00002.txt, mask: masks/frame_00002.png } }3.3 数据质量如何影响训练单目动态重建对数据质量的要求比普通三维重建更高。拍摄时相机如果快速晃动SfM 很难稳定恢复位姿物体运动过快则容易出现运动模糊像素级语义掩码也会不准确。准备数据时优先选择相机缓慢环绕、物体运动速度适中、遮挡不要太多的视频片段。如果条件允许可以从不同机位多拍几段同一场景的视频用于交叉验证。单目方法通常不允许直接输入多视角视频训练但可以在测试阶段用另一视角作为额外精度验证。生产环境如果只能使用单视角建议保留一段不参与训练的视频作为人工观察样本确认动态区域不存在闪烁和漂移。4. 用最小训练闭环理解 SMG4.1 设计一个最简单的语义运动图数据结构在原始代码展开前可以先用一个极简的数据结构理解语义运动图的含义。假设当前场景有两个语义节点前景物体 A 和背景 B。A 每秒绕中心旋转并向前移动B 保持静止。每个语义节点需要记录自己包含的三维高斯索引和基础运动参数。dataclass class SemanticMotionNode: semantics_id: int gaussian_indices: List[int] translation: torch.Tensor rotation: torch.Tensor dataclass class MotionEdge: source_node: int target_node: int relation_type: str # rigid, nonrigid, free strength: float这里并不要求每个节点都拥有完全独立的旋转矩阵。实际计算时会根据当前时间 t 插值得到每帧的变换参数再结合每个高斯自己的局部偏移量计算最终位置。节点内的高斯数量可以很多但运动控制维度被压缩得很低。4.2 高斯姿态更新的计算公式给定第 i 个高斯在当前时间的中心位置 μ_i如果它属于语义节点 k且该节点估计出的刚性变换为 R_k(t) 和平移 T_k(t)则变换后的位置可以写作mu_i(t) R_k(t) * mu_i_base T_k(t) delta_i(t)其中 mu_i_base 是该高斯在标准状态下或初始帧中的位置delta_i(t) 表示相对于节点刚体运动的局部形变。对于背景静止节点R_k(t) 是单位矩阵T_k(t) 是零向量。对于人的躯干节点R_k(t) 和 T_k(t) 是全局动作。对于头发这类非刚体区域delta_i(t) 必须承担更多非刚性形变。实际工程中不建议直接对所有节点使用统一维度。合理的做法是把节点分成两类。第一类做刚性运动建模第二类在刚性运动基础上叠加一个轻量形变场。这样避免把简单的背景运动也交给形变网络处理也可以防止过拟合。4.3 训练循环中运动图和语义掩码如何参与优化最小训练闭环可以按以下流程组织。for iteration in range(max_iterations): # 采样当前时间附近的视频帧和相机位姿 frame_idx, camera sample_frame_and_camera(training_dataset) # 1. 根据时间 t 和运动图节点参数计算每个高斯的新位置 current_gaussians deform_gaussians_with_motion_graph( canonical_gaussians, motion_graph, frame_idx ) # 2. 执行可微高斯渲染 rendered_image, rendered_mask rasterize( current_gaussians, camera ) # 3. 渲染结果与真实图像比较 rgb_loss l1_loss(rendered_image, ground_truth_image) ssim_loss 1.0 - ssim(rendered_image, ground_truth_image) # 4. 语义掩码一致性 pred_segmentation, _ segment_model(rendered_image) semantic_loss cross_entropy(pred_segmentation, semantic_mask) # 5. 运动图需要运动平滑正则 motion_reg smoothness_loss(motion_graph.node_params) total_loss ( rgb_loss 0.2 * ssim_loss 0.5 * semantic_loss 0.01 * motion_reg ) total_loss.backward() optimizer.step()训练时要注意损失项之间的尺度差异。RGB L1 损失和 SSIM 损失的数量级通常相近语义交叉熵损失则可能明显偏大或偏小。推荐先只使用 RGB 损失训练少量迭代确认渲染能收敛到大致画面再加入语义和运动正则。这样能区分是基础渲染问题还是语义约束引入的问题。4.4 训练后期调整运动图结构的策略固定一张运动图直接训练到结束并不一定最优。初始时刻的语义分割可能把一个完整物体分裂成多个节点也可能把两个独立物体粘在一起。因此一些实现会交替执行“优化参数”和“更新图结构”两个过程。更新图结构时可以检查两个信号。其一是语义节点内部的残差如果某个节点内部高斯无法用当前运动参数解释说明节点划分过粗需要细分。其二是两个相邻节点之间的相对运动是否长时间保持近似刚性如果两个节点之间的旋转和平移差异接近零说明它们可能应当合并。这种做法非常适合工程实现但一定要设置触发条件避免每几次迭代就重建一次图导致训练振荡。通常建议固定图结构训练几百到几千步再在验证集上观察误差决定是否需要重建语义图。5. 运行验证与常见问题排查5.1 从渲染结果判断是否收敛动态高斯重建的结果不只在训练集上要重建清晰还应该在未见过的时刻或视角上保持合理。验证时可以分成三层。第一层看单帧重建质量。随机挑选若干训练帧比较重建图和真实图的颜色、纹理和边缘。如果单个静止帧非常清晰但连续播放时抖动问题大多出在运动建模而不是纹理拟合上。第二层观察视频时序稳定性。模型不能只在某个离散时间点上正确连续时间之间应平滑。把渲染帧按视频顺序拼接起来关注运动物体边缘是否有跳变、高斯是否出现突然弹出或消失。第三层做消融检查。关掉语义损失只保留 RGB 损失再打开语义损失对比同一段结果。这个检查能快速判断语义运动图是否真的抑制了运动漂移。如果打开语义约束后图像更模糊可能是语义掩码与三维高斯绑定不准确而不是语义约束本身无效。5.2 三种高频失败现象第一个高频问题是运动物体边缘出现大量漂浮高斯。原因是单目深度信息不足高斯被优化到错误的三维位置渲染时又从多个视角被看到产生“拖影”。解决思路是先确认 SfM 点云初始化是否正常再看该区域是否有语义掩码约束。如果物体在某一帧被完全遮挡则需要在运动图上引入更强的时序平滑。第二个高频问题是语义分割错误导致物体被错误拆分。例如一个人穿的下半身和背景颜色接近分割模型可能把腿部背景一起归入节点。此时腿部的运动参数会污染背景背景轻微移动也会跟着错误旋转。建议处理流程是先检查掩码可视化确认每一帧的语义标签是否符合预期再检查三维高斯投影后取到的标签是否存在大量噪声。第三个高频问题是训练损失下降但渲染视频抖动。通常是因为模型过度拟合了训练视角却无法表达真实运动子空间。降低高斯自由度的有效方式是把更多区域交给刚性节点或共享形变分支而不是继续扩大模型容量。此时可以尝试去掉非刚性 delta_i(t) 的一部分维度观察视频稳定性是否提升。现象常见原因检查方式处理建议物体边缘漂浮高斯深度估计不稳运动约束过弱查看对应帧掩码、深度图增大语义一致性权重约束形变幅度不同物体粘连为同一运动语义分割无法区分可视化二维掩码使用实例分割构建更细粒度节点训练 PSNR 高但视频闪烁运动参数过拟合训练帧渲染插帧检查相邻帧位置加入时间平滑正则降低运动自由度打开语义损失后画面变糊掩码和高斯绑定错位检查投影掩码覆盖区域修正相机位姿或标签绑定逻辑5.3 故障排查的顺序面对异常结果不要直接调损失权重。推荐的排查链路是从输入往输出走。先确认输入的帧序列、相机位姿、语义掩码三者是否对齐。这种错误最容易发生而且会产生“看起来系统正常但结果永远不对”的假象。随后检查相机位姿是否漂移。单目视频用 COLMAP 估计出来的位姿有时会在物体运动过快时出现错误最直接的方法是选取两帧图像做标准渲染对比投影边缘是否严重错位。再查语义掩码与高斯的绑定过程。确保高斯不是从任意随机位置读取标签而是先变换到当前帧坐标系再投影查询。最后才进入训练策略的调整例如损失权重、学习率和运动图节点的稀疏度。注意动态高斯训练过程非常依赖随机种子、高斯点数量、学习率甚至初始化点云。不要用单次训练结果判断方法无效应设置固定随机种子后做多个对照实验。6. 从实验复现到工程化落地6.1 学习环境与生产环境的差异在论文复现阶段可以把时间花在调参和观察渲染结果上。到产品化阶段同样的代码不能直接上线需要做大量工程改造。学习环境通常可以接受整个视频序列作为训练数据并且只关注重建质量。生产环境往往要求模型能够对新的视频片段做快速推理或增量更新。SMG 这类语义运动图方法因为是显式建模对象运动推理时具备增量扩展的基础但仍然需要解决新视频如何初始化语义图、旧模型如何融合新视频、异常运动如何处理等问题。训练与推理环境差异可以用下表概括。维度实验环境生产环境输入一段质量较好的采集视频多种分辨率、视角混杂的实际视频语义标签人工检查修正自动分割并做置信度过滤相机位姿COLMAP 离线估计可能依赖 SLAM 或传感器输入日志只关注 loss需要记录每个模块耗时、显存、异常指标回滚可重新训练必须有版本管理和特征回滚机制安全本地验证数据脱敏、权限控制、合规审查6.2 落地上需要补齐的工程能力图像序列管理需要做成统一数据类。不要在每个训练函数里乱传路径建议用一个 Dataset 类同时保存帧、位姿、分割掩码和时间戳。每次读取数据时校验数量一致避免数组越界带来的隐蔽错误。运动图结构需要持久化。训练完成后的结果不只是几万个高斯参数还应包括节点归属、节点运动参数、边的类型和训练使用的超参数。建议保存为字典或 JSON 结构并和渲染模型打包。否则一旦需要增量优化或局部修复将无法定位某个语义节点对应哪些高斯。代码中还要输出中间可视化数据。每隔固定步数导出不仅包括最终渲染图还包括高斯投影后的语义掩码、运动节点划分图、深度图。这样定位问题时不用重复运行完整训练。6.3 适合新项目使用的复现检查清单检查清单可以直接用于新项目配置和发布前验收。视频抽帧后是否覆盖目标运动完整周期。相机位姿是否在连续帧之间平滑是否有明显跳变。语义分割是否覆盖所有运动区域背景是否被错误标记。语义掩码是否和高斯正确绑定是否在遮挡帧出现大量错位。高斯初始点云是否来自 SfM而不是随机初始化后直接训练。训练损失是否同时出现在 RGB、语义和运动正则上。渲染视频是否只在训练帧表现好在插值帧出现闪烁。语义运动图节点是否能解释每个运动区域而不是只靠像素拟合。如果每一项都可以给出明确答复项目后续扩展会顺利很多。如果某一项无法确认就说明排查链路存在盲区。7. 扩展方向与实践建议SMG 给出的核心启发是把语义认知加入三维运动表示而不是把语义分割当作后处理插件。沿着这个方向继续做至少有三个明确扩展点。第一个扩展方向是实例级运动图。当场景中出现多个同类物体例如两辆颜色接近的车同向行驶单靠语义分割很难区分。对应做法是把语义图升级成实例运动图每个实例拥有独立节点信息。这样既能保持语义标签的一致性又不会把两个不同对象错误合并。第二个扩展方向是把运动图与传统动作捕捉或关节模型结合。人体、动物和车辆都有较强的结构先验。在语义节点内部加入骨骼或关节参数能让单目动态重建更稳定。这个思路工程上也很容易验证初期只需要把节点旋转矩阵从自由 3x3 矩阵替换成基于关节角的旋转表示。第三个扩展方向是面向实时交互。当前动态高斯训练仍然耗时但如果运动图可以作为中间表示渲染时不需要重新优化整个场景只需要根据新的交互指令改变运动图节点参数。这样就能把“重建”与“驱动”解耦新应用场景会扩大很多。对于刚开始接触这个方向的开发者建议先用一个小数据集完整跑通“视频 - 位姿 - 语义 - 高斯训练 - 渲染验证”的闭环再尝试修改运动图结构。不要在一开始就追求复杂的非刚性形变模块。单目动态高斯泼溅本身包含大量隐式约束语义运动图的价值正是在信息不足时用高层结构补足低层运动歧义。从简单语义分组和轻量形变开始逐步增加自由度才能更清楚看到每个模块对最终重建质量的影响。