首页 > 其他分享 >Ⅵ. Instant-NGP

Ⅵ. Instant-NGP

时间:2024-05-28 20:14:06浏览次数:21  
标签:采样 Voxel Instant voxel 特征值 NGP 128

NGP 改用了 Grid based 的方法。NeRF 采用了一个很大的神经网络(8层、每层256个神经元),直接计算每个采样点的 \(\sigma\) 和 \(c\) 就会很慢;NGP则是先找到所有包含这个点的Voxel,利用Voxel的顶点进行“三维内插”(trilinear)得到"特征值",把这个特征值丢到一个2层神经网络中;每个Voxel都会计算得到一个特征值,多个Voxel的做法在论文中称为 Multiresolution,所以特征值也不止一个。

一、NGP实验中的影响因素

(一)ablation study

ed15b9f69d5d236902f3b5db172fe3ed.png

(二)Dense Grid: single resolution

下面我们详细讲一下 Grid Based 的具体做法。

bcb5cffd1db4552816c896274270fba5.png
581b7f075c8a00569b5cb8aaf40c2597.png

输入坐标 xyz 找到对应的voxel(只找一个 voxel 即可),利用它周围八个顶点进行 trilinear 内插得到特征值,输入到神经网络中

如果这个点并不是处在 Voxel 的中心,比如极端一些,处在某个面上,那么“三维内插”时,非所在面的其他四个顶点对最后的特征值没有影响(不参与计算);那么在反向传播时,这四个点是没有梯度的,导致它们也没办法被优化(感觉也不用优化,本来这四个点对结果也没啥影响)。

下面是博主提出来的一个思考题:一条直线最多可以通过多少个立方体?比如\(2\times2\times2\)的立方体,最多可以通过4个,规律如下图所示。
6680e4d7c5e1f7cbf989ddb468080d6a.png

(三)Dense Grid: multi resolution

对于目标所在的区域,在第一次切分(single resolution)的基础上,继续细分(比如8→16),那么 xyz 也一定在某个更细化的 voxel 内,那么就能利用这个小 voxel 的顶点求得 \(f'\) 。

\(f, f'\) 这两个特征值都需要输入到 MLP 中,并且因为可能不止分一次,细分多少次就需要把多少个 \(f'\) concat 到一起作为输入,如下图所示。
52b1a17fc02cfe9dcd09892685ef0795.png
d5f4908933cbdb368611f85c8b21d3d7.png
这样做的参数总数并没有增加,因为我们只对感兴趣的区域进行细分,得到的效果反而更好~

(四)Hash Table: 创新点!

multiresolution 的切分越多、顶点越多、对GPU内存的需求越大。比如论文中Dense Grid切分了128块,那么共有\(128^3\)个Voxel块。
c88b21ee0f57a013b11cdb43eb627fdc.png
HashTable 的做法是:计算这些 voxel 编号的哈希值,放到 \(2^{14}\) 个buckets中。这样带来的优势是:从原来的 \(128^3=2^{21}\) ** 缩减到 \(2^{14}\)** ,减少了 \(2^7\) **** 倍

但是,\(2^{21}\)放到\(2^{14}\)个位置肯定存在哈希冲突;但是实验中也发现:只有2.57%的voxel是存在实际物体的“有用”voxel,其他的大部分都是空的“无用”voxel。因此即便存在哈希冲突,大概率造成影响的也只是对那些无用的voxel。

输入 xyz 后,就能知道 voxel 的编号,从而通过哈希函数计算哈希值,到哈希表中查找到所有 voxel(可能存在哈希冲突的其他 voxel 我们认为其影响不大),然后计算特征值,输入到 MLP 中。

二、Accelerated NeRF Ray Marching

Pytorch的“平行计算”的基础数据结构是“矩阵”,NeRF每条光线的采样点数是固定的,所以通过构建一个:“光线数x采样点数”的矩阵,就能按照行方向,计算矩阵的每一列,达到加速的目的,比 for-loop 快得多。如下图所示:
37207e0216accfbc90517f49182e4dcd.png

但是Voxel并不能这样做:如果固定 step 大小,每条射线的采样点数是不同的,如果再用矩阵的方式就需要 Padding 补零,这会造成大量浪费。
f57f6955254dda583f672acde39e95c7.png

(一)density bitfield

首先把空间进行128等分,得到\(128^3\)个Voxel,每个Voxel的值为0或1,分别代表该方块是否“有东西”,此时我们把它称为“Density bitfield”。

a4e22281aa2994741caff884f8ee0ded.png

那么射线经过为0的Voxel不会放置任何采样点(如上右图所示),只有经过为1的Voxel才会等间隔放置采样点。

这里有个问题:这个density-bitfield是如何得到的呢?见ngp_pl的训练部分。

博主提供了一个测试程序,形象化地展示了采样的整个过程,如下图所示:
820845582a04e4f55f9deeaee0771360.png
观察可以看到:只有物体所在的Voxel才会放置采样点,这个和上面的构想是一致的。

不同场景的bitfield大小是不同的,NGP会进行调整。默认是1(内部进行128等分),当调整 aabb_scale后(2的幂次),仍会进行128等分,剩下的步骤和上面是相同的。
2962bb46adcff11dad18b5a4a2232f70.png

(二)inference阶段的加速技巧

推论时有些像素所在的射线其实时没有用的(没有任何交点),NGP的做法是先“允许最多使用”1个采样点,那些没有任何交点的射线最后真正使用的只有0个采样点,那就可以把这些射线从渲染的对象中排除,减少了需要渲染的射线数目;然后再增加采样点,比如+2,再重复上述步骤,排除已经收敛的射线,把计算资源分配给还没收敛的射线。

fca11b7973a5fb808fc6cd8761ef34f2.png

采样间隔的step大小也是不固定的,而是采用了 exponential stepping 的方式:离光心越远,间隔越大(因为它们对最后的结果影响也小)。默认边长为1时,采样间隔为 \(\frac{\sqrt{3}}{1024}\) ,其中 \(\sqrt{3}\) 为正方体对角线的长度;对于其他边长,下一步的采样边长 \(dt=\frac{t}{256}\),其中 \(t\) 是当前点距光心的距离,并且要把这个值通过 clip 操作控制在 \(\frac{\sqrt{3}}{1024}\) 到 \(\frac{\sqrt{3}}{128}*aabb\_scale\)之内。

二、其他 Grid Based 算法

这种算法都需要自己实现 cuda 的 Kernel 来进行 Voxel 的计算。

  1. NSVF(2020年)
  2. instant NGP;
  3. plenoxels;
  4. plenoctree;
  5. direct voxel grid optimization;
  6. ReLU Fields: 把四周顶点的 RGB 和 \(\sigma\) 内插得到目标值 \(x\),然后套用 \(Relu(x)\) 并通过 clip(Relu(x), 0, 1) 把值限定在 0 到 1 之间。
  7. Volumetric Bundle Adjustment for online Photorealistic Scene Capture.不断细分的方式,如下图所示。

dfe1462b5819ef5941c908db28a5ef67.png

标签:采样,Voxel,Instant,voxel,特征值,NGP,128
From: https://www.cnblogs.com/7ytr5/p/18218726

相关文章

  • Ⅶ. ngp_pl
    作者实现的ngp_pl代码还存在的两个问题:收敛后的采样点数比NGP多(这导致计算量变大,渲染帧率下降);有些场景会失败;一、数据准备代码都在dataset文件夹下面。作者支持大部分算法的数据集格式,包括:NSVF:讲解的时候展示了nsvf.py这个文件,但是自己没找到它NeRF++COLMAPdata......
  • uni-app 微信 支付宝 小程序 使用 longpress 实现长按删除功能,非常简单 只需两步
    1、先看效果2、直接上代码ui结构<viewclass="bind"@longpress="deleteImage":data-index="index"><viewclass="bind_left">绑定设备</view><viewclass="bind_right"><viewc......
  • SwiftUI中的手势(TapGesture、LongPressGesture、GestureState的使用)
    手势操作在App中可谓是用途非常广泛了,常规的手势修饰符有TapGesture点击手势、LongPressGesture长按手势、DragGesture拖拽手势,MagnificationGesture放缩手势和RoationGesture旋转手势。和常规的修饰符方法类似,只要将手势修饰符添加到视图中,系统就会自动识别用户的操作,并......
  • python直接调用InstantID进行图片生成
    项目地址https://github.com/InstantID/InstantID克隆到本地,根据要求pip安装依赖模型文件上篇文章讲了如何下载https://www.cnblogs.com/qcy-blog/p/18202276我用的windows,所以改了一下示例infer.py源码,主要是修改了模型得绝对路径。importcv2importtorchimportnumpy......
  • ComfyUI使用InstantID实现换脸功能
    ComfyUI-InstantID:https://github.com/ZHO-ZHO-ZHO/ComfyUI-InstantID进入ComfyUI\custom_nodesgitclonehttps://github.com/ZHO-ZHO-ZHO/ComfyUI-InstantID.gitcdComfyUI-InstantIDpipinstall-rrequirements.txt需要下载的模型下载https://huggingface.co/Instant......
  • show-menu-by-longpress 企微群聊活码不支持
    微信小程序image的show-menu-by-longpress设置为true,并不支持识别 企业微信群聊活码 参考:https://developers.weixin.qq.com/community/develop/doc/0002e8177f09b82ca5f00d4c961c00https://developers.weixin.qq.com/miniprogram/dev/component/image.html#%E6%94%AF%E......
  • Jenkins发版时报错Failed to instantiate [io.seata.spring.annotation.GlobalTransac
    Failedtoinstantiate[io.seata.spring.annotation.GlobalTransactionScanner]:Factorymethod'globalTransactionScanner'threwexception;nestedexceptionisjava.lang.ExceptionInInitializerError一开始以为是seata配置有问题,但最近也没有动过,直接执行发版脚本就没事......
  • C++中StringPiece了解
    转自:https://blog.csdn.net/zxpoiu/article/details/1172580471.介绍使用c++string类不可避免会带来很多不必要的拷贝,拷贝多了必然影响性能。因此在很多高性能C++框架的实现中,都会使用StringPiece类作为string类的wrapper,该类只持有目标字符串的指针,而避免额外的拷贝。class......
  • C++多态与虚拟:Objects 实例化(Objects Instantiation)探究
    一、Objects的创建依据已有的classCPoint,我们可以产生一个或多个object(对象),或者说是产生一个instance(实体):CPointaPoint(7.2);//aPoint._x初始值为7.2aPoint.x(5.3);//aPoint._x现值为5.3这样的objects可能放在函数的stack之中(对象是在函数内部创建的,......
  • 树莓派wiringPi库详解
      树莓派wiringPi库详解 WiringPi/wiringPi/wiringSerial.c串口通信使用时需要包含头文件:#include<wiringSerial.h>intserialOpen(char*device,intbaud) device:串口的地址,在Linux中就是设备所在的目录。默认一般是"/dev/ttyAMA0",我的是这样的。baud:波特率返......