目 录
1 nerf的粗糙介绍
1.1 初始
2 Nerf源码的解析
2.1 解析parser参数
2.2 load_blender_data
2.3 建立边界大小
2.4 数据合成
2.5 定义相机的内参矩阵
2.6 写入训练过程文件
2.7 创建nerf模型 create_nerf
2.7.1 输入的建立(位置编码 傅里叶变化)
2.7.2 Nerf模型的建立
2.7.3 network_fn的建立
2.7.4 Optimizer使用
2.7.5 加载预训练模型(关键)
2.7.6 打包所有内容为dict render_kwargs_train
2.8 如果args.render_only 则只进行渲染
2.9 训练重要
2.9.1 设置no batch
2.9.2 获取实际数据
2.9.3 获取相机的位置和方向信息
2.9.4 获取射线的起点和方向rays_o和rays_d
2.9.5 获取随机中1024个值(根据初始化设置的)
2.9.6 Render
2.9.7 损失函数计算以及梯度下降
2.9.8 保存模型
1 nerf的粗糙介绍
1.1 初始
摘要:我们提出了一种方法,通过优化基础的连续体场景函数,使用稀疏的输入视图集合,实现对复杂场景的新视角的合成,取得了最先进的结果。我们的算法使用一个全连接(非卷积)的深度网络来表示场景,其输入是一个连续的5D坐标(空间位置(x,y,z)和视角方向(θ,φ)),输出是该空间位置处的体密度和视角相关的辐射强度。我们通过查询沿相机射线的5D坐标来合成视图,并使用经典的体渲染技术将输出颜色和密度投影到图像中。由于体渲染是可微的,因此优化我们的表示所需的唯一输入是一组具有已知相机姿态的图像。我们描述了如何有效地优化神经辐射场以渲染具有复杂几何和外观的逼真新视图,并展示了优于先前神经渲染和视图合成工作的结果。视图合成结果最好作为视频查看,因此我们建议读者观看我们的补充视频以进行令人信服的比较。
2 Nerf源码的解析
代码地址:https://github.com/yenchenlin/nerf-pytorch
python run_nerf.py –config configs/lego.txt
–config configs/lego.txt 纯训练
–config configs/lego.txt –render_only 测试
2.1 解析parser参数
里面使用了configargparse,因此配置文件会覆盖掉argument默认的参数
差异:
import argparse 参数
import configargparse
–config configs/lego.txt
expname = fern_test 保存的二级目录 模型保存的路径
basedir = ./logs 保存结果的一级目录
datadir = ./data/nerf_llff_data/fern 训练数据路径
dataset_type = llff 数据类型
factor = 8 下采样因子
N_rand = 1024 模型输入的数量类似batch
N_samples = 64 采样个数
N_importance = 128 精细网络训练新增采样个数
use_viewdirs = True 是否使用相机视角
raw_noise_std = 1e0 扰动参数
–netdepth 全连接的层数 coarse网络
–netwidth 网络宽度 coarse网络
–netdepth_fine 全连接的层数 fine网络
–netwidth_fine 网络宽度 fine网络
–lrate 学习率
–lrate_decay 学习率衰减
–netchunk 网络中处理的点的数量
–no_batching 合成的数据集一般都是True, 每次只从一张图片中选取随机光线
真实的数据集一般都是False, 图形先混在一起
–no_reload 不加载权重
–ft_path 粗网络的权重文件的位置
–use_viewdirs 是否使用视角数据
–i_embed 0 使用位置编码,-1 不使用位置编码
–render_only 是否只渲染 默认为false
–render_factor 下采样的倍数
–precrop_iters 中心裁剪的训练轮数
–testskip 对于大的数据集,test和val数据集,只使用其中的一部分数据
–white_bkgd 白色背景
2.2 load_blender_data
2.1)使用json文件导入图片的路径以及相机变换矩阵(load_blender_data)
“transform_matrix”:变换矩阵,这是一个 4×4 的矩阵,描述了相机的姿态和位置。在这个矩阵中,每行代表了相机坐标系在世界坐标系中的表达方式。具体来说:
第一行 [a, b, c, d] 表示相机的 X 轴在世界坐标系中的方向,其中 [a, b, c] 是一个单位向量,表示相机的方向,而 d 表示相机在世界坐标系中的位置(x 坐标)。
第二行 [e, f, g, h] 表示相机的 Y 轴在世界坐标系中的方向,类似地,[e, f, g] 是一个单位向量,表示相机的方向,h 表示相机在世界坐标系中的位置(y 坐标)。
第三行 [i, j, k, l] 表示相机的 Z 轴在世界坐标系中的方向,[i, j, k] 是相机的方向,l 表示相机在世界坐标系中的位置(z 坐标)。
第四行相机坐标系原点在世界坐标系中的位置,
2.2)计算焦距
camera_angle_x 相机的水平视角角度
focal = .5 * W / np.tan(.5 * camera_angle_x)
2.3)使用np.linspace(-180,180,40+1)[:-1] 生成40个位置,间距为9的坐标,平移4个单位,绕x轴旋转-30°(phi),绕y轴旋转从-180°到171°(theta),生成测试的相机姿态
2.4)是否缩小为一半
2.4)返回(训练、验证、测试)图片imgs,实际的相机变换矩阵poses(相机坐标系到世界坐标系的变换矩阵),渲染的相机姿态render_poses,高、宽、相机焦距([H, W, focal]),索引号([np.arange(counts[i], counts[i+1]) for i in range(3)])
2.3 建立边界大小
2.4 数据合成
images = images[…,:3]*images[…,-1:] + (1.-images[…,-1:])
这是将透明度加权的颜色信息与不透明度进行相加,得到合成后的图像。在这个过程中,透明度越高的区域,颜色信息对最终结果的影响越小。
2.5 定义相机的内参矩阵
#定义相机的内参矩阵 W和H分别是图像的宽度和高度,focal是焦距
K = np.array([
[focal, 0, 0.5*W],
[0, focal, 0.5*H],
[0, 0, 1]
])
通过相机内参数矩阵K,我们可以进行相机标定(calibration),将三维空间中的点投影到图像平面上,并且可以恢复出世界坐标系中的三维结构。在计算机视觉中,这些信息对于进行三维重建、目标检测、图像配准等任务都是至关重要的。相机的内参矩阵将相机坐标系下的3D坐标映射到2D的图像平面。
此代码中主要用来计算射线的方向
2.6 写入训练过程文件
主要包括args.txt,config.txt
2.7 创建nerf模型 create_nerf
初始化Nerf模型create_nerf
2.7.1 输入的建立(位置编码 傅里叶变化)
输入的建立:输入值的embedding和大小 embed_fn, input_ch = get_embedder(args.multires, args.i_embed)
第一次get_embedder,max_freq = 9。
举一个例子 embed_fns后面1-20个参数返回的参数
sin(x*1) sin(x*2) sin(x*4) sin(x*8) … sin(x*512)
cos(x*1) cos(x*2) cos(x*4) cos(x*8) … cos(x*512)
2.7.2 Nerf模型的建立
加载coarse模型和fine模型,实际上是一样的模型
Model = NeRF()
输出:63->4 或者63->5
第一次渲染:1024*64*63->1024*64*4(使用视角方向) 否则输出5
第二次渲染:1024*192*63->1024*64*5(使用视角方向)
2.7.3 network_fn的建立
创造输入的值以及输入的维度,基本的维度([65536,63],根据args.multires而改变
这里主要是network_query_fn的建立
network_query_fn(进行模型的输入->输出)一般(1024,64,3)->(1024,64,4)
1 调用输入的embedding 转换输入值
2.7.4 Optimizer使用
optimizer = torch.optim.Adam(params=grad_vars, lr=args.lrate, betas=(0.9, 0.999))
1)torch.optim.Adam: 这是 PyTorch 中的 Adam 优化器类。Adam 是一种常用的优化算法,结合了动量方法和自适应学习率。它能够自适应地调整学习率,并且在不同参数上可以使用不同的学习率,从而能够更加高效地优化模型参数。
2) params=grad_vars: 这是 Adam 优化器的一个参数,表示需要被优化的参数,即梯度变量。grad_vars 应该是一个迭代器,包含了模型的所有可学习参数。
3) lr=args.lrate: 这是 Adam 优化器的另一个参数,表示学习率(learning rate)。学习率控制着每次参数更新的步长大小,是优化过程中一个重要的超参数。args.lrate 应该是事先定义好的学习率值。
4)betas=(0.9, 0.999): 这也是 Adam 优化器的一个参数,表示动量的衰减因子(betas)。Adam 算法使用了两个动量衰减因子,分别是 beta1 和 beta2,分别用于计算梯度的一阶矩和二阶矩的估计。这里设置的值 (0.9, 0.999) 是默认的建议值
2.7.5 加载预训练模型(关键)
本测试没有加载,使用时候一定要加载对模型
2.7.6 打包所有内容为dict render_kwargs_train
2.8 如果args.render_only 则只进行渲染
主要生成render_pose [40,4,4]将这个参数送入模型中进行
2.9 训练重要
2.9.1 设置no batch
use_batching = not args.no_batching
2.9.2 获取实际数据
从训练数据中随机选取一张图片
img_i = np.random.choice(i_train)
target = images[img_i] #[400,400,3]
target = torch.Tensor(target).to(device)
2.9.3 获取相机的位置和方向信息
pose = poses[img_i, :3,:4]
2.9.4 获取射线的起点和方向rays_o和rays_d
get_rays(H,W,K,torch.Tensor(pose)): 获取rays_o和rays_d
获取rays_o和rays_d 分别表示世界坐标系下的射线的起始点(所有起始点都是一样的)和方向。这两个张量的形状与输入图像的像素网格一致。
上述中使用torch.sum是将相机坐标系中的转到世界坐标系中的每一个分量在新的坐标系中每一个轴都有投影
2.9.5 获取随机中1024个值(根据初始化设置的)
构建(200,200,2)个坐标,即40000对坐标,随机选取1024个坐标对,获取选取的rays_o,rays_d,以及rgb值 rays:射线
2.9.6 Render
传入参数 H W K 32768 batch_rays(torch.stack([rays_o,rays_d],0)),
render_kwargs_train
2.9.6.1 产生viewdirs
对rays_d进行缩放 使其每个方向的长度都缩放到单位长度,使其成为单位长度,产生新的viewdirs
2.9.6.2 产生近裁和远裁剪面的距离,为了后续产生样本
产生相机视锥体的近裁和远裁剪面的距离
near:表示相机视锥体的近裁剪面的距离。这个值决定了相机视锥体的近端,超出这个距离的部分会被裁剪掉。
far:表示相机视锥体的远裁剪面的距离。这个值决定了相机视锥体的远端,超出这个距离的部分也会被裁剪掉。
2.9.6.3 合并输入与数据数据
整合数据rays = [rays_o,rays_d,near,far,viewdirs] (1024,11)
2.9.6.4 运行batchify_rays
rays[1024,11], chunk = 32768 kwargs如下:
2.9.6.5 运行render_rays
2.9.6.5.1 分层体积采样(coarse)
2.9.6.5.1.1 产生64组随机样本(实际维度1024*64*3)
将1064*3数据扩展为1024*64*3
t_vals = torch.linspace(0., 1., steps=N_samples) N_samples = 64
z_vals = 1./(1./near * (1.-t_vals) + 1./far * (t_vals))
计算了沿着射线的不同方向上的深度值
near是近裁剪面的深度值
far 是远裁剪面的深度值
t_vals 是一个张量 包含着沿着射线的不同方向上的参数值(通常是在区间[0,1]上)。
mids = .5 * (z_vals[…,1:] + z_vals[…,:-1]) #计算深度值之间的中点 mids,其实是插值
upper = torch.cat([mids, z_vals[…,-1:]], -1) #将 mids 和 z_vals[-1] 合并,得到 upper。
lower = torch.cat([z_vals[…,:1], mids], -1) #将z_vals[0] 和 mids 合并,得到 lower
t_rand = torch.rand(z_vals.shape) #生成均匀分布随机数,其中的元素是在区间[0, 1]内均匀分布的随机数。
z_vals = lower + (upper – lower) * t_rand 通过lower + (upper – lower) * t_rand 来扰动深度值,以获得最终的深度值。
2.9.6.5.1.2 计算射线上的各个点的坐标
沿着射线的不同方向上,在每个点上乘以对应的深度值,并加上射线的起始点,以得到射线上的各个点的坐标
pts = rays_o[…,None,:] + rays_d[…,None,:] * z_vals[…,:,None] [1024,64,3]
2.9.6.5.1.3 第一次调用network_query_fn(粗模型)
传入pts(1024,64,3),viewdirs(1024,3),model
1)进行embedding
2)合并embedded 后续还是会分开的63+27
其次进行模型推理
2.9.6.5.1.4 对推理结果进行转换体渲染技术,调用raw2output
输出的结果主要是粒子的rgb或者认为是物质的rgb,以及粒子的体密度,也可以成为不透明度,总共4维
合成权重函数建立
获取深度值差,为后续积分使用
合成权重使用
2.9.6.5.1.5 返回结果
返回估计的图片的颜色(光线所在的),视差图、光线所在的权重、光线所在样本的所有权重以及估计的距离图
2.9.6.5.2 分层体积采样(fine)(除了采样过程不一样,其他和粗网络一致)
PDF: 概率密度函数:连续型随机变量的概率密度函数(Probability Density Function,PDF是一个描述这个随机变量的输出值,在某个确定的取值点附近的可能性的函数。PDF 的函数图像中,横轴为随机变量的取值,纵轴为概率密度函数的值,而随机变量的取值落在某个区域内的概率为概率密度函数在这个区域上的积分。
CDF: 累积分布函数:当概率密度函数存在的时候,累积分布函数(Cumulative Distribution Function,CDF)是概率密度函数的积分。
新采样128个点 结合coarse的64个点进行fine网络训练,返回和coarse一样的值
2.9.6.5.3 render返回结果
2.9.7 损失函数计算以及梯度下降
使用了均方误差:它将两者之间的每个像素值之差的平方求和,并除以像素数量,然后取平均值,用于评估两个图像之间的差异程度。
mse2psnr 均方误差(MSE)转换为峰值信噪比(PSNR)的函数。PSNR 是衡量图像质量的常用指标,其数值越高表示图像质量越好。这个函数将 MSE 转换为 PSNR 值,没有使用到
计算损失 反向传播 更新参数。
2.9.8 保存模型
根据设置迭代次数,保存权重,特别说明,保存的是tar,具体参数见如下:(包括粗网络和精细网络)
其他结果保存比较简单,这里就不详细说明了。
参考1:https://mp.weixin.qq.com/s?__biz=MzU2NjU3OTc5NA==&mid=2247581359&idx=1&sn=57f4a60baa0dd3f9e11d9a7387002316&chksm=fca9b992cbde30842995e18809cf268a183f6e988c356728b664161e1f470a87891e66b92ef7&scene=27
参考2:https://zhuanlan.zhihu.com/p/593204605?utm_source=zhihu
参考3:https://zhuanlan.zhihu.com/p/664384159