NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis

NeRF是2020年ECCV的oral paper,仅使用多视角RGB图像,即可完成高度逼真的三维重建和新视角合成,效果不得不说很是惊艳。

简单的来说,NeRF的工作可以分为两部分:三维重建、渲染。三维重建本质上是一个2D到3D的建模过程,利用空间每个位置的3D点坐标(x,y,z)和观测视角(nx,ny,nz)作为输入,通过MLP网络建模空间每个位置对应的体密度σ和颜色c,形成了3D场景的隐式表示。渲染部分本质上是一个3D到2D的投影过程,利用重建得到的空间每个位置对应的体密度σ和颜色c,沿着光线进行整合,得到该视角下的2D图像像素值。


文章目录

  • 1、预备知识
    • 1.1、Why is it called Neural Radiation Field?
    • 1.2、相机模型
    • 1.3、相机外参
    • 1.4、NeRF中batches指代的含义
  • 2、NeRF处理流程
    • 2.1、利用COLMAP提取先验信息
    • 2.2、加载图像像素值、内参、外参、采样边界
    • 2.3、生成世界坐标系下的射线(光线起点、光线方向)
    • 2.4、在每条光线上进行采样,计算每个采样点的位置坐标、观测方向
    • 2.5、定义编码函数、NeRF网络结构,计算每个采样点的输出结果。
    • 2.6、根据光线上每个采样点的输出结果,进行体渲染,构建损失函数。
  • 3、NeRF额外的技巧
    • 3.1 ndc坐标系
    • 3.2 batching和no_batching处理
    • 3.3 chunk处理
    • 3.4 内参外参处理
    • 3.4 粗网络和细网络
  • 4、实验结果

1、预备知识

1.1、Why is it called Neural Radiation Field?

按我的理解,Neural代表神经网络,也就是用MLP网络层建模了三维空间结构信息。

Radiation Filed代表一种场论的思想(场论,物理学中把某个物理量在空间的一个区域内的分布称为场,如温度场、密度场、引力场、电场、磁场等),作者跳出了传统点云、网格、体素的建模方式,精细的从物理层面对三维空间的本质进行了思考。

作者假设三维空间中的每个坐标位置都对应存在一个粒子,粒子包含两部分信息:不透明度σ、颜色c。不透明度σ只与空间位置的坐标(x,y,z)有关,颜色c则与空间位置的坐标(x,y,z)和观测视角(nx,ny,nz)有关。通过对某一视角下的光线穿过的所有粒子进行整合,就能得到视觉感知下二维图像的颜色。

1.2、相机模型

相机成像过程可以用一个几何模型来描述,最为简单常用的是针孔相机模型。

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第1张图片

假设图像内参矩阵为:
NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第2张图片
内参矩阵K包含4个值,其中fx、fy是相机的水平和垂直焦距(对于理想的针孔相机,fx=fy)。焦距的物理含义是相机中心到成像平面的距离,长度以像素为单位。cx、cy是图像原点相对于相机光心的水平和垂直偏移量,cx、cy有时候可以用图像宽和高的1/2近似。

假设某个空间点在相机坐标系下的坐标为(X,Y,Z),则该点投影到像素平面上的索引坐标(u,v)为:

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第3张图片
值得注意的是,小孔成像会使得成像结果倒立。为方便数学建模和运算,实际处理过程中我们会将成像结果对称到相机前平面,采用正立的Visual Image用于后续计算。这一点非常重要,否则后续计算光线方向会很混乱。

常见的相机坐标系定义习惯如下。注意:在OpenCV/COLMAP的相机坐标系里相机朝向+z轴,在LLFF/NeRF的相机坐标系中里相机朝向-z轴。有时我们会按坐标系的xyz朝向描述坐标系,如OpenCV/COLMAP里使用的RDF表述X轴指向right,Y轴指向Down,Z轴指向Foward。

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第4张图片
不同的相机坐标系定义习惯会影响光线方向的计算。例如,如果采用OpenCV坐标系,由像素平面到相机坐标系的方向方向计算公式为:((i-cx)/fx,(j-cy)/fy, 1)。如果采用NeRF坐标系,由像素平面到相机坐标系的方向方向计算公式为:((i-cx)/fx, -(j-cy)/fy, -1)。

1.3、相机外参

相机外参是一个4x4的矩阵M,其作用是将世界坐标系的点P_world=[x, y, z, 1]变换到相机坐标系P_camera=M 左乘 P_world下,我们也把相机外参叫做world-to-camera(w2c)矩阵。

在位姿估计、三维重建中,我们最经常使用的是camera-to-world(c2w)矩阵,也就是相机外参的逆矩阵,用于将相机坐标系的点变换到世界坐标系。c2w矩阵是一个4x4的矩阵,左上角3x3是旋转矩阵R,右上角的3x1向量是平移向量T。

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第5张图片
c2w矩阵具有很强的物理意义:c2w矩阵的值直接描述了相机坐标系的朝向和原点。具体的,旋转矩阵的第一列到第三列分别表示了相机坐标系的X, Y, Z轴在世界坐标系下对应的方向;平移向量表示的是相机原点在世界坐标系的对应位置。

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第6张图片
如果这段描述还是有点抽象,可以尝试进行下列计算帮助自己理解。刚刚讲到c2w是将相机坐标系的向量变换到世界坐标系下,那我们如果将c2w作用到(即左乘)相机坐标系下的X轴[1,0,0,0],Y轴[0,1,0,0], Z轴[0,0,1,0],以及原点[0,0,0,1](注意方向向量的齐次坐标第四维等于0,点坐标第四维等于1),我们会得到它们在世界坐标系的坐标表示。

1.4、NeRF中batches指代的含义

在计算机视觉任务(例如图像分类、目标检测)中,一个样本通常指代一张图像,例如batch_size=16,代表一个批次计算16张图像构成的损失函数,然后进行梯度更新。

而在NeRF任务中,一个样本指代一束光线,例如batch_size=1024,代表一个批次计算1024条射线构成的损失损失函数,然后进行梯度更新。

假设NeRF训练时采用N张图像,每张图像尺寸为(H,W,3),则光线总数量为NxHxW,因为每个像素点都会发射出一条光线。


2、NeRF处理流程

2.1、利用COLMAP提取先验信息

COLMAP作用是为NeRF提供一些先验信息,辅助后续NeRF训练,这些先验信息十分重要,如果这些数值不够准确(例如相机位姿、最远最近距离),NeRF重建效果将大打折扣。

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第7张图片COLMAP提供的先验信息,主要包括:
(1)相机内参,主要包括fx、fy、cx、cy,图像尺寸H,W。
(2)相机外参,通常使用4x4的c2w矩阵存储,表示每张图像在拍摄时的位姿。
(3)最近最远距离,COLMAP对输入图像做稀疏三维重建后,会将重建好的点云的每个离散点投影到各个相机平面,由此可以计算得到稀疏点云在每个相机平面的最远最近距离,以此作为边界,辅助后续NeRF光线采样。

2.2、加载图像像素值、内参、外参、采样边界

针对fern文件夹,总共含有20张图像,取17张图像作为训练集,3张图像作为验证集。

对于训练集图像,每张图像尺寸为(378, 504, 3),加载得到一系列数据:
(1)images:尺寸(17,378,504,3),其中图像的像素值已经除以255。
(2)pose:尺寸(17,4,4),表示每张图像对应的c2w矩阵。
(3)cx、cy、fx、fy:每张图像的内参值应该是相同的。
(4)near、far:每张图像都包含各自对应的bds(最近最远距离)。取所有图像的最近边界的最小值乘以0.9作为采样起始位置,取所有图像的最远边界的最大值乘以1.0作为终止采样位置。

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第8张图片

2.3、生成世界坐标系下的射线(光线起点、光线方向)

利用内参数值cx、cy、fx、fy、H、W,即可生成每张图像在相机坐标系下的射线(光线起点、光线方向):

在这里插入图片描述
在相机坐标系下,每张图像生成的光线是相同的。光线起点都是(0,0,0),光线方向都由像素坐标系索引计算得到的,此时即得到在各自相机坐标系下的光线起点、光线方向:
rays_o尺寸:(17, 378, 504, 3)
rays_d尺寸:(17, 378, 504, 3)

然后通过c2w外参矩阵,将每个相机坐标系的光线变换到世界坐标系下,光线起点变成了各自相机坐标系的位置,光线方向需要左乘对应相机坐标系的旋转矩阵:

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第9张图片
此时即得到在世界坐标系下的光线起点、光线方向:
rays_o尺寸:(17,378,504,3)
rays_d尺寸:(17,378,504,3)

2.4、在每条光线上进行采样,计算每个采样点的位置坐标、观测方向

对于每条光线,在最近采样位置near、最远采样位置far等分区间,每个区域随机选取一个数值t,由此得到采样点坐标:pt = rays_o + t x rays_d。

采样阶段,每条射线采样64个样本点,由此得到采样点的三维坐标:
pts尺寸:(17,378,504,64,3)
采样点的观测方向,定义为射线方向(ray_d)数值归一化的结果,由此得到观测方向:
view_dirs尺寸:(17,378,504,64,3)

将pts、view_dirs进行reshape,由此得到:
pts尺寸:(3238704,64,3)
view_dirs尺寸:(3238704,64,3)

每次只选取batch_size=1024条射线进行处理,即输入网络中的数据为:
pts尺寸:(1024,64,3)
view_dirs尺寸:(1024,64,3)

2.5、定义编码函数、NeRF网络结构,计算每个采样点的输出结果。

通过傅里叶变换,将输入位置pts编码为63维,将输入观测方向view_dirs编码为27维:

在这里插入图片描述
NeRF网络结构由多层MLP组成:

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第10张图片
输入数据维度:pts = (1024,64,3)、view_dirs = (1024,64,3)。
先进行reshape,得到:pts = (66536,3)、view_dirs = (66536,3)。
经过多个全连接层,输出得到:output_sigma = (66536,1)、output_rgb = (66536,3)。
最后重新reshape,得到:output_sigma =(1024,64,1)、output_rgb = (1024,64,3)。

经过MLP网络处理,即可得到每条射线64个采样点对应的体密度σ、颜色c。

2.6、根据光线上每个采样点的输出结果,进行体渲染,构建损失函数。

根据每条射线64个采样点对应的体密度σ、颜色c:
output_sigma =(1024,64,1)、output_rgb = (1024,64,3)

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第11张图片

利用离散化的体素渲染公式,对每条射线上的数值进行聚合,得到该射线投影到像素平面的RGB数值,与该位置真实的RGB数值做差,由此构建损失函数:
render_rgb = (1024,3)
gt_rgb = (1024,3)

在这里插入图片描述

3、NeRF额外的技巧

3.1 ndc坐标系

关于ndc坐标系,作者有一段解释:https://github.com/bmild/nerf/issues/18

(提问者):Thanks for sharing the great work! I noticed that in the codebase, there’s special handling of forward-facing data, specifically, this ndc_rays function. I wonder what is the physical intuition behind it. It’s a bit hard for me to digest this piece of code snippet. Any help is appreciated!

(作者):The general intuition is that NDC is a space where the z axis maps to inverse depth (disparity) rather than depth, which should be a good space for uniformly sampling points along rays when dealing with forward facing scenes (matches the sampling from the input cameras better). NDC is just what you get when you pass points through the OpenGL projection matrix, which is a linear transform in homogeneous space, so it maps rays to rays. However, the transformation is a little subtle since there is a divide by z step.Here is a pdf I wrote up deriving the equations in ndc_rays() we use to convert rays in regular camera space to NDC space: ndc_derivation.pdf. Here’s a great reference on the OpenGL projection matrix.

(提问者):Thanks a lot for the detailed explanation. If I understand it correctly, the final goal is just to sample points on each physical ray such that the inverse depths (1/z) of these sampled points are evenly spaced inside the interval (0,1/near_plane). As long as this goal is fulfilled, it doesn’t make a difference whether one adopts this NDC trick or not, right?

(作者):Note that we have an argument linspace to do linear sampling in disparity along the rays. The reason we use NDC instead of this for the forward facing scenes is that we find it actually negatively impacts the network itself if the input coordinates are very far from having a uniform distribution in space. So with linear disparity sampling, the far points end up being spaced much much farther apart than the near points. Especially with the positional encoding (where far apart points effectively make the higher frequency encodings useless), we find that this “confuses” the network and produces grainy results in the backgrounds. Hence the use of NDC when possible in unbounded scenes.

(提问者):I see. Looks like it’s important for the coordinates of 3d points sampled along physical rays to uniformly distribute in the cube [-1, 1]^3, so that positional encoding can work properly (btw, my understanding is that the sin, cos basis used in positional encoding are fundamental frequencies of this bounded cube; if the size of the bounded cube changes, the fundamental frequencies might also need to adjust accordingly). For unbounded scenes, because the Euclidean coordinates of the sampled 3d points are not bounded, we need to apply NDC trick to map the unbounded coordinates to be bounded. Thanks again for the clarification!

关于射线ndc变换那块的数学公式我理解的也不是很清楚,查阅相关资料,感觉ndc变换的作用就是将视锥体空间变换到了标准化空间,这么做有几个好处:

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第12张图片
(1)ndc变换后再采样,采样点分布更合理,网络学习三维空间结构的效果会更好。
(2)对于unbounded空间,原始采样方法难以处理,而ndc变换后只需要对t由0到1采样即可,由于使用的是深度的逆,很适合处理前向无界情形。

具体理解可看:https://blog.csdn.net/qq_40514113/article/details/131746384?spm=1001.2014.3001.5502

3.2 batching和no_batching处理

在训练NeRF时,有no_batching=True or False两种设置可以选择。
no_batching=True,代表每次从训练图像中随机选取一张图像,再从该图像中随机选取光线。
no_batching=False,代表将所有训练图像的光线排序在一起,每次按顺序选取一定数量,等到所有光线都被采样过,将这些光线打乱,重新遍历。

3.3 chunk处理

由于渲染部分消耗的计算机资源较多,而且每张图像的光线数量又巨大,因此设置了chunk处理方式来避免显存不足。

假设batch_size=1024,chunk=256,电脑性能实在不行,每次连一个batch的光线都处理不了,那么每次电脑就处理1个chunk的光线,累积处理4次后就把这个batch全部处理完了,此时再计算损失函数,进行梯度更新,这样的话每次梯度更新按的还是batch_size=1024计算的。

3.4 内参外参处理

对于相机内参,一般由于拍摄图像的尺寸都较大,都需要对图像进行缩放,然后相应调整fx、fy、cx、cy、H、W的值。

对于相机外参,对于平移分量、near、far也会进行缩放处理,调整到一个固定的尺度;对于相机c2w矩阵,一般会进行中心化调整,让相机平均位姿尽可能的以世界坐标系原点为中心,以世界坐标系的x轴、y轴、z轴为方向,这样有利于后续可视化和理解。

3.4 粗网络和细网络

NeRF为了使网络三维重建的效果更好,定义了两个相同的网络结构:

粗网络直接输入均匀采样的64个样本点,回归得到体密度、颜色,再进行渲染,得到粗阶段预测得到的RGB图、weights权重。

根据计算得到的weights权重,对输入的采样点进行重要性采样,得到新的128个样本点,再与原先64个采样点合并,得到192个采样点,输入细网络。

细网络回归得到这192个采样点的体密度、颜色,再进行渲染,得到细阶段预测得到的RGB图、weights权重。

在计算网络损失时,粗阶段的RGB图、细阶段的RGB图都被用来构建损失。

4、实验结果

fern文件夹,基于17张RGB图像进行隐式重建,NeRF渲染出的RGB图、深度图的效果如下:

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第13张图片

NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis_第14张图片
效果还是相当惊艳的,渲染出的RGB图像完全难以分辨是虚拟生成的还是现实拍摄的,而且得到的深度图细节非常丰富,比起MVS范式确实是降维的打击。

参考文献:
1、NeRF源码:https://github.com/yenchenlin/nerf-pytorch
2、NeRF坐标系讲解:https://zhuanlan.zhihu.com/p/593204605
3、NeRF原理讲解:https://zhuanlan.zhihu.com/p/523208661
4、NDC变换讲解:https://github.com/Kai-46/nerfplusplus/issues/29

你可能感兴趣的:(计算机视觉,人工智能,深度学习)