NeRF-W 是神经辐射场在野外场景下的扩展,重点解决从互联网照片集合重建三维场景时的外观不一致问题。标准 NeRF 假设所有训练图像具有一致的辐射亮度,但在真实照片集中,不同视角往往来自不同时间、设备和光照条件,颜色差异可能非常大。NeRF-W 通过给每张图像引入外观嵌入和图像级参数,把场景固有颜色与图像特有的外观变化解耦,从而让几何重建更加稳定。

为什么外观不一致会破坏NeRF重建
标准NeRF将场景表示为一个连续的辐射场,输入空间位置和观察方向,输出颜色和体密度。训练时通过体渲染得到像素颜色,并与真实图像做均方误差损失。这个框架隐含假设所有输入图像的颜色分布一致,场景的辐射属性不随时间变化。但在野外场景中,照片来自不同设备、不同时间,曝光、白平衡、天气和光照差异非常大。同一个墙面可能在晴天下偏黄,在阴天偏蓝,直接训练时颜色头无法同时输出两种真实颜色。
网络为了降低训练误差,会尝试修改共享的密度场来拟合颜色差异。具体表现为:在高光或阴影区域产生额外的半透明密度,把颜色变化错误地建模为几何遮挡;或者生成漂浮在空中的雾状伪影,让远处背景的颜色混合出目标像素。这样重建出的几何表面不再干净,新视角合成时也会出现明显的颜色跳变和几何瑕疵。密度分支和颜色分支虽然可以分开设计,但体渲染的梯度传播使两者紧密耦合,只靠分离网络结构不足以完全消除干扰。
NeRF-W 的解决方案是把外观作为图像相关的条件变量引入。每张训练图像获得一个低维外观嵌入向量,颜色网络在预测 RGB 时额外接收这个向量。这样一来,颜色输出既依赖空间位置和观察方向,也依赖当前图像的外观状态。共享的密度场只负责表达场景几何和材质,图像特有的颜色偏移由外观嵌入负责。图像级参数可以进一步显式化曝光和白平衡等物理量,让外观变化更容易控制和解释。通过这种设计,几何优化不再被迫吸收外观噪声。
外观嵌入与图像级参数的原理
外观嵌入本质上是一张可学习的查找表。假设训练集有 N 张图像,嵌入表可以表示为 E ∈ R^{N×d},其中 d 是嵌入维度,通常在 8 到 32 之间。训练时根据当前光线的来源图像索引取出对应的向量 e_i,然后把它拼接到颜色网络的输入或中间层。外观嵌入不参与密度分支的计算,密度分支只接收位置编码,因此外观变化无法直接写入几何表示。颜色分支接收位置特征、方向编码和外观嵌入,输出 RGB 值。
图像级参数可以看作外观嵌入的显式版本。常见的图像级参数包括白平衡增益、曝光偏移和对比度缩放。比如每张图像学习一个三维向量表示红、绿、蓝通道的增益,把它与外观嵌入一起送入颜色网络。这种显式表达的好处是可解释性强,初始化时可以设为常量 1,训练时只在颜色损失驱动下小幅调整。外观嵌入则更加灵活,能够捕捉天气、季节、阴影等复杂的环境变化,但通常缺少明确的物理含义。两者可以同时使用:显式参数做全局颜色校正,外观嵌入学习残差变化。
外观嵌入的训练方式与网络权重一致,通过反向传播更新。由于嵌入数量通常远小于网络参数量,额外计算开销很低。例如 200 张图像、嵌入维度 16 时,仅增加 3200 个可学习参数。训练完成后,每个训练图像都有一个对应的嵌入向量,新视角合成可以选择训练集嵌入的平均值,或者在嵌入空间中进行插值,得到平滑的外观变化。这种机制让 NeRF-W 在面对外观不一致的数据时,几何重建质量明显优于标准 NeRF。
PyTorch实现一个带外观嵌入的颜色头
下面给出一个最小可运行的 NeRF-W 颜色分支实现框架。为了简化,位置和方向编码省略,重点展示外观嵌入如何参与颜色预测。密度网络只接收位置编码,颜色网络接收位置中间特征、方向编码和外观嵌入的拼接。
import torch
import torch.nn as nn
class NeRFW(nn.Module):
def __init__(self, pos_dim=60, dir_dim=24, app_dim=16, hidden_dim=256):
super().__init__()
self.app_dim = app_dim
# 密度分支只依赖位置
self.density_net = nn.Sequential(
nn.Linear(pos_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, 1)
)
# 颜色分支依赖位置、方向、外观嵌入
self.color_net = nn.Sequential(
nn.Linear(hidden_dim + dir_dim + app_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, 3),
nn.Sigmoid()
)
def forward(self, pos, view_dir, app_embedding):
x = self.density_net(pos)
sigma = torch.relu(x[..., 0])
h = x
c_input = torch.cat([h, view_dir, app_embedding], dim=-1)
rgb = self.color_net(c_input)
return rgb, sigma
代码中 app_embedding 的形状是 [batch_size, app_dim],它由训练图像索引从嵌入表中取出。嵌入表通常用 nn.Parameter 声明,初始化时使用标准差较小的随机值。密度分支的输出 sigma 完全不依赖外观嵌入,这是保证几何不被外观噪声污染的关键设计。颜色分支把位置特征、方向编码和外观嵌入拼接后送入全连接网络,最终输出归一化到 0 到 1 的 RGB 值。
实际训练时,需要把每张图的嵌入作为可学习参数一起优化。假设训练集有 200 张图像,外观嵌入维度为 16,则嵌入参数量仅为 3200,远小于网络权重,因此不会带来明显的计算负担。优化时可以为嵌入参数设置单独的学习率,例如使用与网络相同或略低的学习率,并在训练后期进行衰减,避免外观嵌入剧烈波动影响整体收敛。如果使用显式图像级参数,还可以在颜色输入中拼接一个三维白平衡向量,并用 softplus 保证增益为正。
训练技巧与常见问题
外观嵌入最大的风险是过拟合。如果嵌入维度太高或正则化不足,网络可能把训练图像的噪声、镜头污点甚至压缩伪影都编码进嵌入,新视角合成时嵌入无法泛化,输出颜色出现偏色或闪烁。为了避免这个问题,建议使用 8 到 32 维的嵌入,并对嵌入施加较小的权重衰减。对于图像数量少于 50 的小数据集,可以降低到 4 到 8 维,同时减少外观嵌入的学习率。
另一个常见问题是几何和外观仍然纠缠。虽然密度分支不接收外观嵌入,但颜色损失通过体渲染反向传播时仍会影响密度网络,特别是在高光和阴影边界处。NeRF-W 原论文引入了瞬态物体建模来处理这类问题:为每条光线输出一个瞬态颜色和不确定性,使用概率损失降低瞬态区域对几何的影响。这个机制可以显著减少漂浮伪影,但实现复杂度更高。对于一般场景,可以先使用外观嵌入,再逐步加入瞬态建模。
图像级参数的显式表达有助于提高可解释性。例如在数据预处理阶段读取照片的曝光时间和 ISO,将这些物理量归一化后作为额外输入;或者为每张图学习白平衡增益向量。显式参数能与外观嵌入互补:前者负责粗略的全局颜色校正,后者捕捉更复杂的局部或环境光照变化。二者串联使用时,训练收敛更快,新视角的颜色也更容易控制。
在推理阶段,通常需要为新视角选择合适的外观嵌入。如果没有用户指定,可以使用训练集嵌入的均值,或者从最近邻视角的嵌入插值得到。嵌入空间的平滑性保证插值结果合理,不会产生突变。对于需要特定光照效果的应用,可以直接优化或检索嵌入向量,实现一定程度的外观编辑和重光照。合理利用外观嵌入和图像级参数,能让 NeRF-W 在复杂野外数据上同时获得干净的几何和真实的新视角渲染结果。