WU-CVGL / BAD-Gaussians

[ECCV 2024] "BAD-Gaussians: Bundle Adjusted Deblur Gaussian Splatting". ⚡Train a scene from real-world blurry images in minutes!
https://lingzhezhao.github.io/BAD-Gaussians/
Apache License 2.0
137 stars 4 forks source link

关于本篇论文中,渲染细节的相关问题 #15

Open 520jz opened 1 month ago

520jz commented 1 month ago

亲爱的作者您好!原谅我的菜,我想问一下您这篇论文中一些渲染方面的细节问题,正如您论文中所所说:在初始pose以及截止pose之间进行插值,以获得沿轨迹的每个相机pose,然后将对应的pose的image渲染出来生成一些列的虚拟清晰图像,然后将这些虚拟清晰图像进行混合得到渲染后的blur-image,最后将渲染的blur-image和GT-blur-image做损失,以联合更新并优化pose以及高斯的参数。

我比较好奇的是,原始的gaussian splatting我每次迭代渲染只渲染一张image,然后render后会返回一个bool类型的数组(记录半径大于0的椭球),以及一个记录梯度的数组(在代码中是viewspace_point_tensor, visibility_filter以及radii),这几个数据是会在后面自适应的致密化操作中使用的。但是如果按照您这篇论文的思路的话,假设我插值生成了5个pose,每个pose我渲染出一张virtual-image,那么每次渲染都会返回我上面说的那三个数据,我是要将每次render得到的这三个数据也求平均后再进行致密化吗? image

我粗略的画了两张图,以便您能更好的理解我的问题: 下面这张图是您这篇论文中我假设的五个pose的情况下,一个大概的流程(主要是下面自适应操作的处理,是做五次自适应操作,还是说将对应的数据求平均后做一次自适应的操作,但是如果求平均的话感觉原理上不太合理,因为有些梯度高的和梯度低的部分的椭球可能会被平均掉,影响自适应的准确性) image 这张是原始的3DGaussian splatting的流程: image

LingzheZhao commented 1 month ago

您好! 感谢您对我们论文的关注。我们的代码是基于nerfstudio中的Splatfacto的,它与INRIA的原版3DGS在这里主要的不同就是它把render过程与densification做了解耦,viewspace_point_tensor(xys), visibility_filter(visible_mask)以及radii这三个变量不会在每次render时返回,而是在每次forward(在我们这里就是通过平均virtual sharp image 得到一张blur image,跟input image做loss)再backword之后,再调用一个after_traincallback,在这个callback里面再去根据此时的梯度去做adaptive density control。这里面它的梯度会从blur image传递到每个virtual sharp image再传到各个params(Gaussians & camera poses)。所以这里第一,每个virtual sharp image在梯度计算中都有贡献;第二,在要求Gaussians数量基本不变的前提下,我们也发现densify_grad_thresh这个超参数的设定会跟num_virtual_views有一个线性的关系(我们在补充材料C.2里也提到了,根据推导是求平均的这个操作带来的)。

520jz commented 1 month ago

您好! 感谢您对我们论文的关注。我们的代码是基于nerfstudio中的Splatfacto的,它与INRIA的原版3DGS在这里主要的不同就是它把render过程与densification做了解耦,viewspace_point_tensor(xys), visibility_filter(visible_mask)以及radii这三个变量不会在每次render时返回,而是在每次forward(在我们这里就是通过平均virtual sharp image 得到一张blur image,跟input image做loss)再backword之后,再调用一个after_traincallback,在这个callback里面再去根据此时的梯度去做adaptive density control。这里面它的梯度会从blur image传递到每个virtual sharp image再传到各个params(Gaussians & camera poses)。所以这里第一,每个virtual sharp image在梯度计算中都有贡献;第二,在要求Gaussians数量基本不变的前提下,我们也发现densify_grad_thresh这个超参数的设定会跟num_virtual_views有一个线性的关系(我们在补充材料C.2里也提到了,根据推导是求平均的这个操作带来的)。

哦哦哦,好的,非常感谢,我好像明白了,nerfstudio里面的代码是在backword之后再去获取相关信息进行adaptive density control的,不是原始的gs代码那样每次render后返回相关参数后进行adaptive density control。我去看看nerfttudio的源码,谢谢您。

520jz commented 1 month ago

您好! 感谢您对我们论文的关注。我们的代码是基于nerfstudio中的Splatfacto的,它与INRIA的原版3DGS在这里主要的不同就是它把render过程与densification做了解耦,viewspace_point_tensor(xys), visibility_filter(visible_mask)以及radii这三个变量不会在每次render时返回,而是在每次forward(在我们这里就是通过平均virtual sharp image 得到一张blur image,跟input image做loss)再backword之后,再调用一个after_traincallback,在这个callback里面再去根据此时的梯度去做adaptive density control。这里面它的梯度会从blur image传递到每个virtual sharp image再传到各个params(Gaussians & camera poses)。所以这里第一,每个virtual sharp image在梯度计算中都有贡献;第二,在要求Gaussians数量基本不变的前提下,我们也发现densify_grad_thresh这个超参数的设定会跟num_virtual_views有一个线性的关系(我们在补充材料C.2里也提到了,根据推导是求平均的这个操作带来的)。

您好,我想再问一个问题,您这篇论文是否考虑了同一运动轨迹下合成的blur-image不唯一的情况呢?比如在同一运动轨迹下,我估计的pose的采样间隔可以不是均匀的,也可以是均匀的等等,那么这样的话我average后得到的模糊图像可能得到不同的模糊图像,我记得您论文中好像用的是插值,也就是估计的timetamp的间隔都是均匀的哦?

LingzheZhao commented 1 month ago

这里我们把CMOS上的2D image记为一个与时间 $t$ 相关的函数: $\mathbf{I}(\mathbf{x};t)$ , 那么我们motion blurred image的物理模型就是:

$$\mathbf{B}(\mathbf{x}) = \lambda \int_0^\tau \mathbf{I}(\mathbf{x}; t) \mathrm{d}t$$

$\tau$ 是曝光时间, $\lambda$ 是一个归一化系数。

然后我们做离散化近似,从这里引入了时间上的等间隔插值:

$$\mathbf{B}(\mathbf{x}) = \lambda \int0^\tau \mathbf{I}(\mathbf{x}; t) \mathrm{d}t \approx \frac{1}{n}\sum{i=0}^{n-1}\mathbf{I}_i(\mathbf{x})$$

用 $n$ 张virtual sharp image去average得到blur image。这里,因为数字图像本身就是离散的,我们只要保证任意相邻两张virtual sharp image之间的optical flow 都小于1个 pixel,就认为这个近似效果是理想的(也是唯一的),反之,就会有artifact (在原版Deblur-NeRF的合成数据集里就有出现)。最暴力的方法就是只要增加 $n$,就可以消除不理想的近似。当然,在 $n$ 不变的情况下,是存在比这个等时间间隔插值更好的方式的,那就是运动快的时候多采样,运动慢的时候少采样,最理想的情况就是让相邻两张virtual sharp image间的optical flow都恰好略小于1个 pixel,不过个人认为这个就是属于优化计算代价的一些工程问题了,这里就没有再深入研究了。

520jz commented 1 month ago

这里我们把CMOS上的2D image记为一个与时间 t 相关的函数: I(x;t) , 那么我们motion blurred image的物理模型就是:

B(x)=λ∫0τI(x;t)dt

τ 是曝光时间, λ 是一个归一化系数。

然后我们做离散化近似,从这里引入了时间上的等间隔插值:

B(x)=λ∫0τI(x;t)dt≈1n∑i=0n−1Ii(x)

用 n 张virtual sharp image去average得到blur image。这里,因为数字图像本身就是离散的,我们只要保证任意相邻两张virtual sharp image之间的optical flow 都小于1个 pixel,就认为这个近似效果是理想的(也是唯一的),反之,就会有artifact (在原版Deblur-NeRF的合成数据集里就有出现)。最暴力的方法就是只要增加 n,就可以消除不理想的近似。当然,在 n 不变的情况下,是存在比这个等时间间隔插值更好的方式的,那就是运动快的时候多采样,运动慢的时候少采样,最理想的情况就是让相邻两张virtual sharp image间的optical flow都恰好略小于1个 pixel,不过个人认为这个就是属于优化计算代价的一些工程问题了,这里就没有再深入研究了。

噢噢噢噢,原来如此,谢谢大佬,那大佬,在代码层面如何保证任意相邻两张virtual sharp image之间的optical flow 都小于1个 pixel呢?我以前试过从图片的起始pose与截止pose之间等间隔的插值pose,然后将所有渲染的清晰图片求取average,但是我发现这样高斯学习不好模糊,虽然loss一直在降低,但是迭代到一定次数就不下降了,而且渲染的模糊肉眼可见跟GT-blur差别很大,这是什么原因呢,还有我想再问一下,就在那个公式推导的部分 image 这个数学推导我看明白了,就是高斯椭球的均值对pose的影响,但是可能是代码实现的问题,就这个梯度我需要在高斯的光栅化部分(cuda部分)进行修改来实现吗?不好意思主要是nerfstudio版本的gs源码不太熟悉,因为一直看的原版的gs源码

520jz commented 1 month ago

这里我们把CMOS上的2D image记为一个与时间 t 相关的函数: I(x;t) , 那么我们motion blurred image的物理模型就是: B(x)=λ∫0τI(x;t)dt τ 是曝光时间, λ 是一个归一化系数。 然后我们做离散化近似,从这里引入了时间上的等间隔插值: B(x)=λ∫0τI(x;t)dt≈1n∑i=0n−1Ii(x) 用 n 张virtual sharp image去average得到blur image。这里,因为数字图像本身就是离散的,我们只要保证任意相邻两张virtual sharp image之间的optical flow 都小于1个 pixel,就认为这个近似效果是理想的(也是唯一的),反之,就会有artifact (在原版Deblur-NeRF的合成数据集里就有出现)。最暴力的方法就是只要增加 n,就可以消除不理想的近似。当然,在 n 不变的情况下,是存在比这个等时间间隔插值更好的方式的,那就是运动快的时候多采样,运动慢的时候少采样,最理想的情况就是让相邻两张virtual sharp image间的optical flow都恰好略小于1个 pixel,不过个人认为这个就是属于优化计算代价的一些工程问题了,这里就没有再深入研究了。

噢噢噢噢,原来如此,谢谢大佬,那大佬,在代码层面如何保证任意相邻两张virtual sharp image之间的optical flow 都小于1个 pixel呢?我以前试过从图片的起始pose与截止pose之间等间隔的插值pose,然后将所有渲染的清晰图片求取average,但是我发现这样高斯学习不好模糊,虽然loss一直在降低,但是迭代到一定次数就不下降了,而且渲染的模糊肉眼可见跟GT-blur差别很大,这是什么原因呢,还有我想再问一下,就在那个公式推导的部分 image 这个数学推导我看明白了,就是高斯椭球的均值对pose的影响,但是可能是代码实现的问题,就这个梯度我需要在高斯的光栅化部分(cuda部分)进行修改来实现吗?不好意思主要是nerfstudio版本的gs源码不太熟悉,因为一直看的原版的gs源码

上面是我求平均渲染出来的模糊图片,下面是GT-blur image image

LingzheZhao commented 1 month ago

是的,camera pose的梯度中,主要就是高斯椭球的均值对pose的Jacobian需要计算,gsplat v0.1 里面这部分是python实现的,然后最近他们正在用CUDA重构整个代码也包括这个部分

从你提供的图片来看,可能就是pose的梯度中间是断的,所以pose没有被优化,你可以把pose print出来看看是不是没有在被优化

如果想继续在原版gs上开发pose优化的话,一个比较方便的选择就是把diff-gaussian-rasterization换成gsplat,他们最近开源了一个minimal example的仓库,只要改100行左右,这里提到替换之后速度和质量都能有所提升这个branch是对应gsplat 0.1的;main branch 是对应他们正在重构的gsplat 1.0的,感觉可以试下

520jz commented 1 month ago

是的,camera pose的梯度中,主要就是高斯椭球的均值对pose的Jacobian需要计算,gsplat v0.1 里面这部分是python实现的,然后最近他们正在用CUDA重构整个代码也包括这个部分

从你提供的图片来看,可能就是pose的梯度中间是断的,所以pose没有被优化,你可以把pose print出来看看是不是没有在被优化

如果想继续在原版gs上开发pose优化的话,一个比较方便的选择就是把diff-gaussian-rasterization换成gsplat,他们最近开源了一个minimal example的仓库,只要改100行左右,这里提到替换之后速度和质量都能有所提升这个branch是对应gsplat 0.1的;main branch 是对应他们正在重构的gsplat 1.0的,感觉可以试下

哦哦哦好的,谢谢大佬,话说,nerfstudio中的Splatfacto这个框架是不是本来就比原本的GS的质量更好速度更快?

520jz commented 1 month ago

是的,camera pose的梯度中,主要就是高斯椭球的均值对pose的Jacobian需要计算,gsplat v0.1 里面这部分是python实现的,然后最近他们正在用CUDA重构整个代码也包括这个部分 。 从你提供的图片来看,可能就是pose的梯度中间是断的,所以pose没有被优化,你可以把pose print出来看看是不是没有在被优化 如果想继续在原版gs上开发pose优化的话,一个比较方便的选择就是把diff-gaussian-rasterization换成gsplat,他们最近开源了一个minimal example的仓库,只要改100行左右,这里提到替换之后速度和质量都能有所提升这个branch是对应gsplat 0.1的;main branch 是对应他们正在重构的gsplat 1.0的,感觉可以试下

哦哦哦好的,谢谢大佬,话说,nerfstudio中的Splatfacto这个框架是不是本来就比原本的GS的质量更好速度更快?

哦对了,大佬,我看论文里面的那个Table3,是deblurring的PSNR,我记得deblur-nerf的数据集(blender数据集)模糊的图片没有对应的清晰GT,只有每间隔为8的一张清晰的GT作为测试集,您table3中的去模糊PSNR是根据什么计算的呢? image

LingzheZhao commented 1 month ago

哦哦哦好的,谢谢大佬,话说,nerfstudio中的Splatfacto这个框架是不是本来就比原本的GS的质量更好速度更快?

这里有他们的一些测试结果,总体上是能看到一些提升的,他们还在持续更新,后面提升应该会更大;

哦对了,大佬,我看论文里面的那个Table3,是deblurring的PSNR,我记得deblur-nerf的数据集(blender数据集)模糊的图片没有对应的清晰GT,只有每间隔为8的一张清晰的GT作为测试集,您table3中的去模糊PSNR是根据什么计算的呢?

其实是有的,Deblur-NeRF把合成数据集的deblurring的GT放在一个单独的文件夹synthetic_gt里了(合成数据集的主要意义就是能提供这个GT),然后我们从BAD-NeRF开始为了方便和一致性就把他们移动到每个sequence下的images_test文件夹里了。