跳到主要内容

NeuS:用体渲染学习神经隐式表面

论文:NeuS: Learning Neural Implicit Surfaces by Volume Rendering for Multi-view Reconstruction
作者:Peng Wang, Lingjie Liu, Yuan Liu, Christian Theobalt, Taku Komura, Wenping Wang
arXiv:2106.10689v3
本地文件:/root/other/paper/2106.10689v3.pdf
官方页面:arXiv 2106.10689

这篇论文解决什么问题

NeuS 研究的是多视角 3D 表面重建:

多张已知相机位姿的 2D 图像
-> 学习一个连续 3D 表面
-> 提取高质量 mesh

它想结合两类方法的优点:

方法优点问题
IDR / DVR 这类 surface rendering用 SDF 或 occupancy 明确约束表面每条 ray 主要依赖一个交点,梯度太局部,复杂遮挡和突变深度容易卡住
NeRF 这类 volume rendering沿 ray 多点采样,优化更鲁棒,适合复杂可见性学的是 density field,缺少明确表面约束,提取 mesh 容易噪声大

NeuS 的核心想法是:

用 SDF 表示几何表面,但用 volume rendering 的方式从 2D 图像监督它。

这样既保留了 SDF 的清晰几何含义,又利用了体渲染沿 ray 多点传梯度的优化优势。

一句话版本

NeuS 学习两个连续函数:

f(x)sf(\mathbf{x})\rightarrow s c(x,v)rgbc(\mathbf{x},\mathbf{v})\rightarrow \mathbf{rgb}

其中 ff 是 SDF 网络,cc 是颜色网络。表面由零水平集给出:

S={xR3f(x)=0}\mathcal{S} = \{\mathbf{x}\in\mathbb{R}^3\mid f(\mathbf{x})=0\}

训练时,NeuS 沿相机 ray 采样很多点,把这些点的 SDF 转成可体渲染的 opacity / weight,再合成像素颜色,与输入图像做监督。

NeuS 整体架构重绘示意图

图源说明:根据 Wang et al., NeuS 的 neural SDF、color field 与 volume rendering 流程重绘,非论文原图。

模型架构:输入与输出

NeuS 的网络由两个主要 MLP 组成:

  1. SDF MLP:负责几何。
  2. Color MLP:负责外观。

补充材料中给出的实现细节是:SDF MLP 使用 8 个 hidden layers,每层 hidden size 为 256,激活函数使用 Softplus;Color MLP 使用 4 个 hidden layers,每层 hidden size 为 256。

输入

训练输入是多张带相机位姿的图像:

{Ik}\{\mathcal{I}_k\}

每次训练会随机采样一批像素,并生成对应 camera rays:

p(t)=o+tv\mathbf{p}(t)=\mathbf{o}+t\mathbf{v}

其中:

  • o\mathbf{o}:相机中心。
  • v\mathbf{v}:ray 的单位方向。
  • tt:沿 ray 的深度参数。
  • p(t)\mathbf{p}(t):ray 上的 3D 采样点。

对每个采样点,SDF 网络输入:

xR3\mathbf{x}\in\mathbb{R}^3

Color 网络输入通常包括:

x,v,n=f(x),z\mathbf{x},\quad \mathbf{v},\quad \mathbf{n}=\nabla f(\mathbf{x}),\quad \mathbf{z}

其中 z\mathbf{z} 是 SDF MLP 中间输出的 feature vector。

输出

SDF MLP 输出 signed distance:

s=f(x)s=f(\mathbf{x})

Color MLP 输出颜色:

c=c(x,v)\mathbf{c}=c(\mathbf{x},\mathbf{v})

然后 NeuS 通过 SDF 推导每个采样区间的 opacity αi\alpha_i,并进行 alpha compositing,得到渲染像素颜色:

C^=i=1nTiαici\hat{C} = \sum_{i=1}^{n}T_i\alpha_i\mathbf{c}_i

这里最终用于训练的输出是:

  • 每条 ray 的渲染颜色 C^\hat{C}
  • 每个 3D 点的 SDF 值 f(x)f(\mathbf{x})
  • 训练完成后提取的零水平集 mesh。
阶段输入计算输出
训练posed images、像素颜色、camera rays、可选 mask沿 ray 采样,预测 SDF 和 color,体渲染得到 C^\hat{C}渲染颜色、loss、更新 SDF/Color 网络
推理 / 重建训练好的 SDF 网络,大量 3D 查询点查询 f(x)f(\mathbf{x}),提取 f(x)=0f(\mathbf{x})=0三角网格 mesh
新视角渲染新相机 ray沿 ray 采样并体渲染新视角 RGB 图像

输出可以用来干什么

输出用途具体说明
f(x)f(\mathbf{x})判断点在内外f<0f<0 在内部,f>0f>0 在外部,f=0f=0 在表面
零水平集 f(x)=0f(\mathbf{x})=0表面重建用 Marching Cubes 提取 mesh
f(x)\nabla f(\mathbf{x})表面法向SDF 梯度可以作为 normal,用于颜色预测和渲染
C^\hat{C}2D 图像监督和真实像素颜色比较,反向传播训练网络
αi,Tiαi\alpha_i,T_i\alpha_i可见性建模决定 ray 上哪些采样点贡献最终像素颜色
color field c(x,v)c(\mathbf{x},\mathbf{v})新视角合成给定新相机视角,可以渲染 RGB 图像

NeuS 训练与推理数据流向重绘示意图

图源说明:根据 Wang et al., NeuS 的训练和推理流程重绘,非论文原图。

为什么不能直接照搬 NeRF 的 density

NeRF 学的是 volume density:

σ(x)\sigma(\mathbf{x})

体渲染权重通常写成:

w(t)=T(t)σ(t)w(t)=T(t)\sigma(t)

其中:

T(t)=exp(0tσ(u)du)T(t)=\exp\left(-\int_0^t\sigma(u)\,du\right)

如果直接把 SDF 附近的 bell-shaped density 当成 σ\sigma

σ(t)=ϕs(f(p(t)))\sigma(t)=\phi_s(f(\mathbf{p}(t)))
为什么直接把 SDF 附近的 bell-shaped density 当成 NeRF 的 σ\sigma 会有问题?

为了将体积渲染方法应用于 SDF 网络的训练,作者首先引入了一个概率密度函数:

ϕs(f(x))\phi_s(f(\mathbf{x}))

并将其命名为 S-density,也可以记作 S-密度

这里的含义是:先由 SDF 网络输出 signed distance:

f(x)f(\mathbf{x})

再把这个 SDF 值送进一个以 0 为中心的 logistic density:

ϕs()\phi_s(\cdot)

因为 ϕs\phi_s 在输入为 0 时最大,所以 ϕs(f(x))\phi_s(f(\mathbf{x})) 会在表面 f(x)=0f(\mathbf{x})=0 附近最大。直觉上,它像是在 SDF 零水平集附近放了一个 bell-shaped probability density,用来提示“表面最可能在这里”。

先把 NeRF 的体渲染权重拆开看:

w(t)=T(t)σ(t)w(t)=T(t)\sigma(t)

这里的 σ(t)\sigma(t) 表示 ray 上 tt 位置的体密度,越大表示这里越可能“吸收光线、贡献颜色”。但最终权重不是只看 σ(t)\sigma(t),还要乘上前面积累的透射率:

T(t)=exp(0tσ(u)du)T(t) = \exp\left( -\int_0^t \sigma(u)\,du \right)

T(t)T(t) 的意思是:光线从相机走到 tt 之前,还没有被前面内容挡住的概率。

如果直接令:

σ(t)=ϕs(f(p(t)))\sigma(t)=\phi_s(f(\mathbf{p}(t)))

直觉上看很自然,因为 ϕs\phi_s 是一个在 f=0f=0 附近达到最大值的 bell-shaped density。也就是说,越靠近 SDF 零水平集,σ\sigma 越大;离表面远,σ\sigma 越小。

问题在于,体渲染真正使用的是:

w(t)=T(t)ϕs(f(p(t)))w(t)=T(t)\phi_s(f(\mathbf{p}(t)))

而不是单独的:

ϕs(f(p(t)))\phi_s(f(\mathbf{p}(t)))

沿 ray 从相机走向物体表面时,ϕs(f(p(t)))\phi_s(f(\mathbf{p}(t))) 会在表面附近逐渐升高;但与此同时,T(t)T(t) 会因为前面积累了 density 而持续下降。

所以 w(t)w(t) 是两个趋势相乘:

沿 ray 靠近表面时的变化影响
ϕs(f(p(t)))\phi_s(f(\mathbf{p}(t)))越靠近 f=0f=0 越大希望权重峰值靠近表面
T(t)T(t)随着前方 density 积累逐渐变小把后面的权重压低

结果就是:即使 ϕs(f)\phi_s(f) 自己在 f=0f=0 处最大,乘上 T(t)T(t) 以后,w(t)w(t) 的最大值也可能提前出现在表面之前。

一个很粗略的数值直觉:

位置ϕs(f)\phi_s(f)T(t)T(t)w(t)=T(t)ϕs(f)w(t)=T(t)\phi_s(f)
表面之前0.800.900.72
真正表面1.000.650.65
表面之后0.800.400.32

单看 ϕs(f)\phi_s(f),真正表面最大;但乘上 T(t)T(t) 以后,最大权重反而落到了表面之前。这就是 naive S-density 会产生几何 bias 的核心原因。

NeuS 要解决的正是这个错位问题:它不是直接把 ϕs(f)\phi_s(f) 当成 NeRF density,而是用 SDF 的 CDF 比值构造 opacity,让渲染权重的峰值更稳定地对齐到 f=0f=0 的真实表面附近。

传统 NeRF 本身会用 SDF 吗?

传统 NeRF 一般不使用 SDF 表示几何。它学习的是一个 radiance field,网络通常输入 3D 位置和观察方向:

(x,d)(\mathbf{x},\mathbf{d})

然后输出:

(σ(x), c(x,d))(\sigma(\mathbf{x}),\ \mathbf{c}(\mathbf{x},\mathbf{d}))

也就是:

  • σ(x)\sigma(\mathbf{x}):volume density / opacity density,表示这个空间点有多“不透明”。
  • c(x,d)\mathbf{c}(\mathbf{x},\mathbf{d}):从方向 d\mathbf{d} 看过去的颜色。

所以在传统 NeRF 里,density σ\sigma 通常就是网络直接预测出来的标量;它不是由 SDF 再转换出来的。

这也是 NeRF 和 NeuS 的关键区别:

方法几何网络输出表面在哪里体渲染里用的 density / opacity 从哪里来
NeRF直接输出 σ(x)\sigma(\mathbf{x})没有显式零水平集,通常从 density 场中后处理提取网络直接预测
NeuS输出 SDF f(x)f(\mathbf{x})明确是 f(x)=0f(\mathbf{x})=0由 SDF 的 CDF / density 构造 opacity

因此,NeuS 不是“传统 NeRF 直接应用到 SDF”那么简单,而是把 NeRF 的 volume rendering 优化方式借过来,同时把几何表示换成更适合表面重建的 SDF。

会出现一个问题:权重 w(t)w(t) 的最大值不一定落在真正表面 f=0f=0 的位置,而可能偏到表面之前。

这就是论文说的 bias:渲染权重峰值和真实零水平集不对齐,最终会带来几何误差。

NeuS weight bias 直觉重绘示意图

图源说明:根据 Wang et al., NeuS 中 naive S-density 与 NeuS weight construction 的 bias 分析重绘,非论文原图。

SDF 如何变成可体渲染的权重

NeuS 先定义 logistic CDF:

Φs(x)=11+esx\Phi_s(x)=\frac{1}{1+e^{-sx}}

它的导数是 logistic density:

ϕs(x)=Φs(x)=sesx(1+esx)2\phi_s(x) = \Phi_s'(x) = \frac{se^{-sx}}{(1+e^{-sx})^2}

把 SDF 值代入后得到 S-density:

ϕs(f(x))\phi_s(f(\mathbf{x}))

这个分布在 f(x)=0f(\mathbf{x})=0 附近最大。参数 ss 控制峰的尖锐程度,1/s1/s 可以理解成标准差量级;训练越收敛,表面越清晰,分布越集中。

NeuS 从 SDF 到 opacity 的重绘示意图

图源说明:根据 Wang et al., NeuS 的 S-density、opacity 与 volume rendering 公式重绘,非论文原图。

NeuS 的 opaque density

NeuS 不直接令 σ=ϕs(f)\sigma=\phi_s(f),而是定义 opaque density:

ρ(t)=max(dΦsdt(f(p(t)))Φs(f(p(t))),0)\rho(t) = \max\left( \frac{ -\frac{d\Phi_s}{dt}(f(\mathbf{p}(t))) }{ \Phi_s(f(\mathbf{p}(t))) }, 0 \right)

然后仍然走 volume rendering:

w(t)=T(t)ρ(t)w(t)=T(t)\rho(t) T(t)=exp(0tρ(u)du)T(t)=\exp\left(-\int_0^t\rho(u)\,du\right)

这套构造的目标是同时满足:

  • unbiased:权重峰值落在 f=0f=0 的表面交点附近。
  • occlusion-aware:如果 ray 穿过多个表面,前面的表面贡献更大,后面的表面会被遮挡。

离散 alpha 公式

实际训练时沿 ray 采样离散点:

pi=o+tiv\mathbf{p}_i=\mathbf{o}+t_i\mathbf{v}

NeuS 使用下面的 opacity:

αi=max(Φs(f(p(ti)))Φs(f(p(ti+1)))Φs(f(p(ti))),0)\alpha_i = \max\left( \frac{ \Phi_s(f(\mathbf{p}(t_i))) - \Phi_s(f(\mathbf{p}(t_{i+1}))) }{ \Phi_s(f(\mathbf{p}(t_i))) }, 0 \right)

然后:

Ti=j=1i1(1αj)T_i=\prod_{j=1}^{i-1}(1-\alpha_j) C^=i=1nTiαici\hat{C} = \sum_{i=1}^{n}T_i\alpha_i c_i

可以把 TiαiT_i\alpha_i 理解成“第 ii 个采样区间真正被看到并贡献颜色的概率权重”。

为什么 alpha 公式里是两个 CDF 相减?

Φs(f)\Phi_s(f) 可以理解成 SDF 值经过 sigmoid 后的累计量。沿 ray 从外部走向内部时,ff 会从正数逐渐变成负数,因此 Φs(f)\Phi_s(f) 会下降。

两个相邻 section point 的差:

Φs(fi)Φs(fi+1)\Phi_s(f_i)-\Phi_s(f_{i+1})

衡量的是这个小区间里“穿过表面附近概率质量”的多少。

再除以:

Φs(fi)\Phi_s(f_i)

相当于在“当前还没有被前面完全遮挡”的条件下,计算这个区间产生 opacity 的比例。

最后取:

max(,0)\max(\cdot,0)

是为了避免 ray 上某些 SDF 增大的区间产生负 opacity。

所以这个公式不是随便构造的,它是在把 SDF 的零水平集附近概率质量转成 volume rendering 能用的 opacity。

训练目标

训练时没有 3D ground truth SDF 监督,主要靠 2D 图像颜色监督:

L=Lcolor+λLreg+βLmask\mathcal{L} = \mathcal{L}_{color} +\lambda\mathcal{L}_{reg} +\beta\mathcal{L}_{mask}

color loss

渲染颜色和真实像素颜色做差:

Lcolor=1mkR(C^k,Ck)\mathcal{L}_{color} = \frac{1}{m} \sum_k \mathcal{R}(\hat{C}_k,C_k)

论文使用 L1 loss。

Eikonal regularization

SDF 的一个重要性质是:

f(x)21\|\nabla f(\mathbf{x})\|_2\approx 1

所以加入:

Lreg=1nmk,i(f(p^k,i)21)2\mathcal{L}_{reg} = \frac{1}{nm} \sum_{k,i} \left( \|\nabla f(\hat{\mathbf{p}}_{k,i})\|_2-1 \right)^2

它让网络更像真正的 signed distance function,而不只是任意隐式函数。

optional mask loss

如果有 foreground mask,可以用:

Lmask=BCE(Mk,O^k)\mathcal{L}_{mask} = \operatorname{BCE}(M_k,\hat{O}_k)

其中:

O^k=iTk,iαk,i\hat{O}_k = \sum_iT_{k,i}\alpha_{k,i}

表示该 ray 上累计的不透明度。

采样策略

NeuS 类似 NeRF 使用 hierarchical sampling:

  1. 先沿 ray 均匀采样。
  2. 根据 coarse probability 进行 importance sampling。
  3. 用更集中在表面附近的采样点计算最终颜色。

补充材料给出的设置是:先均匀采样 64 个点,再进行 4 次 importance sampling,每次额外采样 16 个点,总共 128 个点。

和前面几篇论文的关系

方法表示输入输出主要用途
DeepSDFSDFlatent code + query pointsigned distance单物体形状表示、补全、插值
PointNet点集特征point cloudclass / point labels / global feature点云分类、分割、encoder
ConvONetoccupancy fieldpoint cloud / voxel + query pointoccupancy probability从观测重建物体或场景
NeuSneural SDF + color fieldposed multi-view imagesSDF、rendered color、mesh多视角表面重建

NeuS 和 DeepSDF 都用 SDF,但训练信号不同:

  • DeepSDF 通常需要 3D SDF samples。
  • NeuS 主要从 2D posed images 通过可微渲染学习 SDF。

NeuS 和 NeRF 都用 volume rendering,但几何表示不同:

  • NeRF 学 density,适合新视角合成,但 mesh 不够干净。
  • NeuS 学 SDF,表面就是 f=0f=0,更适合高质量 mesh extraction。

实验结论

论文在 DTU 和 BlendedMVS 上比较了 IDR、NeRF、COLMAP、UNISURF 等方法。

主要结论:

  • 在 DTU 上,无论有 mask 还是无 mask,NeuS 的 Chamfer distance 都优于对比方法。
  • 对 thin structures、self-occlusion、abrupt depth change,NeuS 比 surface rendering 方法更稳定。
  • NeRF 的体渲染优化虽然稳,但 density field 提取出的 mesh 噪声更明显。
  • Ablation 显示,weight construction 很关键;naive S-density 方案会引入几何 bias。
  • Eikonal regularization 和 geometric initialization 对输出“真正像 SDF”很重要。

局限

论文指出,一个明显失败场景是 textureless regions。原因是没有纹理的区域对多视角重建来说本身存在歧义,图像监督提供的约束不足。

另一个限制是 NeuS 使用单个全局尺度参数 ss 控制 logistic density 的标准差。不同空间位置的局部几何复杂度不同,理想情况下可能需要 spatially varying sharpness。

我的理解

NeuS 最核心的贡献不是“用 SDF”,也不是“用体渲染”,而是把二者连接得更正确:

让 SDF 的零水平集能通过 volume rendering 从 2D 图像中稳定学习,同时避免直接把 S-density 当 NeRF density 带来的几何偏移。

读 NeuS 时要抓住三条线:

  1. 表示:几何是 SDF,表面是 f(x)=0f(\mathbf{x})=0
  2. 监督:没有直接 3D SDF 标签,而是通过渲染颜色和图像颜色比较。
  3. 关键公式:用 Φs(f)\Phi_s(f) 构造 αi\alpha_i,让权重既 occlusion-aware 又尽量 unbiased。

阅读时要抓住的主线

  1. 为什么 surface rendering 的梯度太局部?
  2. 为什么 NeRF 的 density field 不适合直接提取高质量 mesh?
  3. SDF 的零水平集为什么适合表示表面?
  4. S-density ϕs(f)\phi_s(f) 为什么在表面附近最大?
  5. 为什么 naive σ=ϕs(f)\sigma=\phi_s(f) 会产生 bias?
  6. NeuS 的 αi\alpha_i 公式如何把 SDF 转成体渲染 opacity?
  7. Eikonal regularization 为什么能让网络更像真正 SDF?

后续问题

  1. NeuS 和 VolSDF 的 SDF-to-density 设计有什么区别?
  2. NeuS 的 bias-free 是一阶近似意义下的,二阶误差实际影响有多大?
  3. 为什么后续很多方法会在 NeuS 基础上引入 hash grid、proposal network 或 multi-resolution feature?
  4. 如果输入是稀疏视角,NeuS 会在哪些地方退化?