【三维编辑】SPIn-NeRF:多视图分割与感知修复(CVPR 2023)

news2024/11/25 20:14:44

文章目录

  • 摘要
  • 一、简介
  • 二、相关工作
    • 1.Image Inpainting
    • 2.NeRF 操作
    • 3. 背景: NeRF 知识
  • 三、方法
    • 3.1.多视图分割
      • 3.1.1掩码初始化
      • 3.1.2基于nerf的分割
    • 3.2.多视图 Inpainting
      • 3.2.1 RGB先验
      • 3.2.2深度先验
      • 3.2.3基于patch 的优化
      • 3.2.4掩码精炼
  • 四、实验
  • 五、安装与代码讲解
    • 1.项目安装
    • 2.跑原始NeRF,得到深度信息:
    • 3.运行 LaMa 来产生几何和表观引导
    • 4.运行多视角 inpainter NeRF
    • 5.关键代码


项目主页:spinnerf3d.github.io (包含论文与代码)

摘要

编辑任务:从3D场景中移除不需要的对象

NeRF已经成为一种流行的新视角合成方法。然而,编辑和操作NeRF场景仍然是一个挑战。其中一个重要的编辑任务是从3D场景中移除不需要的对象,并对其进行修复,使得替换区域在视觉上是合理的并与其上下文一致。

本文方法:给定一组带姿态图像 单个输入图像的稀疏注释,首先获取目标对象的3D分割掩码。然后,我们引入了一种基于感知优化的方法,利用学习的2D图像inpainting 模型 将其信息转化到3D空间中,并确保视角一致性。

文章引入一个真实场景数据集,来评估3D场景inpainting 方法。


提示:以下是本篇文章正文内容,下面案例可供参考

一、简介

随着nerf 的广泛使用,编辑和操纵 nerf 场景的需求将继续增长,包括删除对象和图像inpaiting 3D场景,类似于已被充分研究的2D图像inpaiting 任务[23]。然而,障碍不仅是在三维inpaiting 过程本身,还存在于获得输入的分割mask。首先,NeRF场景被隐式地编码在神经映射权值中,导致一个纠缠和不可解释的表示,这不是易于操作的。此外,3D场景的inpaiting 必须在一个给定的视图中生成感知现实的外观,还必须保持基本的3D属性,如跨视图的外观一致性和几何合理性。该方法需要对多个图像进行注释(并保持多视角一致)对用户来说是一种负担

一个吸引人的替代方案是对单个视图只期望最小的一组注释。这激发了一种能够从单视图稀疏注释中获得对象的视图一致的3D分割 mask 的方法。

本文方法:首先从一个目标对象上的少量用户定义的图像点(以及外部的一些负样本),算法使用基于视频的模型[4]初始化掩模,并通过拟合 semantic NeRF [36,76,77]将其提升为一个连贯的3D分割。然后,在应用预先训练的二维画家[48]多视图图像集,一个定制NeRF拟合过程用于重建三维画场景,利用感知损失[72]负责 2维inpainting 的不一致。

在这里插入图片描述

二、相关工作

1.Image Inpainting

早期的技术依赖于patch[9,16],最近的神经方法优化感知实现和重建(例如,[22,27,48])。为了提高视觉保真度,各种研究方向仍在继续被探索,包括对抗性训练([40,73])、框架的改进([27,30,62,63])、多元输出([73,75])、多尺度处理([21,58,69])和感知指标(如[22,48,72])。

我们的工作利用了预训练的LaMa [48],它应用了受Transfirmer [11,12]启发的频域变换[7]。然而,这些都不能将问题提升到3D图像中;因此,以一致的方式修复一个场景的多个图像仍然是一项未被充分探索的任务。我们的方法通过基于多视图的NeRF模型直接在三维中操作。

2.NeRF 操作

基于可微体积渲染[18,52]和位置编码[13,49,53],NeRFs [35]在新视图合成中表现出了显著的性能。控制NeRF场景仍然是一个挑战。Clip-NeRF [55]、Object-NeRF [64]、LaTeRF [36]和其他[25,32,70]引入了改变和完成由NeRFs表示的对象的方法;然而,它们的性能仅限于简单的对象,而不是具有显著杂乱和纹理的场景,或者它们专注于其他任务,而不是一般的 inpainting(例如,重新上色或变形)。

与我们的方法最密切相关的是NeRF-In [31],一个同期未发表的作品,它使用2D图像的几何和亮度先验,但没有解决不一致性;相反,它使用简单的像素损失,来减少用于拟合的视图数量,从而降低了最终视图合成质量。类似地使用像素损失,同期的 Remove-NeRF模型[60]通过排除基于不确定机制的视图来减少不一致性。相比之下,我们的方法能够通过一种感知的[72]训练机制,以一种视图一致的方式合并二维信息,从而绘制出具有挑战性的现实世界场景的NeRF表示。这避免了对 inpainting 的过度限制(通常会导致模糊)。

3. 背景: NeRF 知识

请见博客【三维重建】NeRF原理+代码讲解


三、方法

给定一组RGB图像,I = {Ii} ni=1,以及相应的3D姿态,g = {Gi} ni=1,和相机内在矩阵,K,模型期望一个具有稀疏用户注释的“源”视图。从这些输入中,我们生成了一个场景的NeRF模型,能够从任何新的观点合成一个 inpaint 的图像。

我们首先从单视图注释源(4.1.1节)中获得一个初始的3D掩码,然后拟合一个semantic NeRF,以提高掩码的一致性和质量(4.1.2节)。在4.2中,我们描述了视图一致的inpaint 方法,它将视图和恢复的mask 作为输入。我们的方法利用二维inpainter [48]的输出作为外观和几何先验,来监督一个新的NeRF的拟合。

3.1.多视图分割

3.1.1掩码初始化

将带注释的源代码视图表示为I1 。将对象和源视图的稀疏信息给给一个交互式分割模型Edgeflow15],以估计初始源对象掩码 M   1   ^ \hat{M~1~} M 1 ^。然后将训练视图作为一个视频序列,与 M   1   ^ \hat{M~1~} M 1 ^ 一起给出一个视频实例分割模型V [4,57],以计算在这里插入图片描述,得到每个视图的mask。初始的掩码,{cMi} n i=1
,通常在边界附近是不准的,因为训练视图并不是相邻的视频帧。因此,我们使用语义NeRF模型[36,76,77]来解决不一致性并改进 mask(4.1.2),从而获得每个输入视图的 mask 用于inpaint(4.2)。

3.1.2基于nerf的分割

多视图分割模块输入为:RGB图像、相应的相机内在和外部参数,以及初始掩码,训练一个semantic NeRF [76,网络见图2];对于点x和视图方向d,除了密度σ(x)和颜色c(x,d),它返回一个“objectness”的逻辑值 s(x)。然后得到目标的概率 p(x) = Sigmoid(s(x) )。 我们使用快速收敛的 Instant-NGP[3,37,38]作为NeRF架构。
在这里插入图片描述

与光线 r 相关的期望objectness逻辑值,是通过渲染 r 上点的密度,而不是颜色得到的:

在这里插入图片描述

为简单起见,s(r(ti))si 表示。然后使用分类损失来监督一个射线,objectness的概率Pb(r)=Sigmoid
在这里插入图片描述

训练 Lcls时,color 被分离以限制对 logits 值的监督更新。这将防止更改现有的几何结构,因为梯度更新会改变密度值 σ。

在这里插入图片描述
经过优化后,通过对目标概率进行阈值化,并对概率大于0.5的像素进行mask,得到了3d一致性掩模{Mi}n i=1。最后,我们采用两阶段优化,进一步改进mask: 在获得初始三维mask后,从训练视图呈现mask,并用于监督二次多视图分割模型作为初始猜想。

3.2.多视图 Inpainting

图3显示了视图一致的Inpainting NeRF的概述,使用以下损失训练:
在这里插入图片描述

其中,L’rec 为 未mask 像素的重建损失,LLPIPSLdepth 定义了感知和深度损失,权值为λ.

在这里插入图片描述

3.2.1 RGB先验

Inpaint NeRF 使用RGB输入,相机的内在和外在参数,以及相应的对象 mask,以拟合一个 删除了mask目标的NeRF到场景。首先,将每个图像和掩模对(Ii,Mi)给图像 inpainter INP:在这里插入图片描述

由于每个视图都是独立 inpaint 的,直接用于监督NeRF,由于三维不一致,会导致模糊的结果(见图7)。在本文中,我们没有使用均方误差(MSE)来优化掩蔽区域,而是使用感知损失LPIPS [72]来优化图像的掩蔽部分,同时仍然使用MSE来优化未掩蔽部分,损失的计算方法如下:
在这里插入图片描述

其中,B是介于1到n之间的batch 索引, I ^ \hat{I} I^ i 是使用NeRF渲染的第i个视图。多视图Ipaint NeRF 和分割模型 semantic NeRF 使用相同的架构(见图2)

3.2.2深度先验

即使有感知损失,Inpaint 视图之间的差异也会错误地引导模型收敛到退化的几何图形(例如,在摄像机附近可能会形成“雾”几何图形,以解释每个视图的不同信息)。因此,我们使用 inpaint 的深度图作为NeRF模型的额外指导,并在计算感知损失时分离权重,并使用感知损失只拟合场景的颜色。为此,我们使用了一个对包含不需要的对象的图像进行了优化的NeRF,并渲染了与训练视图对应的深度地图。深度图的计算方法是用到相机的距离而不是点的颜色代替等式 1:
在这里插入图片描述
将渲染的深度给一个 inpainter,以获得已绘制的深度图:在这里插入图片描述

使用LaMa [48]进行深度 inpaint ,有足够高质量的结果,作为预处理步骤计算。然后,这些深度图被用来监督已绘制的NeRF的几何形状,通过其渲染深度的 L2距离:
在这里插入图片描述

3.2.3基于patch 的优化

计算感知损失(等式7)需要在优化期间呈现完整的输入视图。因为渲染每个像素需要多个向前通过MLP,对于高分辨率图像,这是一个昂贵的过程,导致问题如(i)批大小|B|必须小,减小相应的内存中的计算图;(ii)缓慢优化,即使批大小为|B| = 1。

一个简单的解决方案是渲染一个缩小后的图像,并将其与缩小后的 inpaint 的图像进行比较;然而,如果缩小系数很大,这就会导致信息的丢失。根据图像的工作(例如,SinGAN [46]和DPNN [14])和3D作品(例如,ARF [71]),我们在patch 的基础上执行计算;我们不是渲染完整的视图,而是渲染较小的补丁,并根据感知损失将它们与 inpaint 图像中的对应patch 进行比较。

为了拟合unmask 的区域,L’rec (Eq 6)只从 unmask 的像素中采样光线。通过分离感知损失和重建损失,我们防止了mask 内的不一致,同时避免了对场景的其余部分的不必要的变化。

3.2.4掩码精炼

在这里,我们考虑进一步利用多视图数据来指导图像 inpaint。特别是,目前二维inpainter 生成的部分训练图像可能在其他视图中可见;在这种情况下,没有必要对这些细节产生幻觉,因为它们可以从其他视图中检索到。为了防止这种不必要的inpaint,我们提出了一种mask refine 的方法:

对于每个源图像、深度和mask 元组,(Is、Ds、Ms),我们替换至少一个其他视图中可见的 Id 中的像素,以缩小源 mask,在这个细化步骤之后,只有在所有训练视图中被不希望的对象遮挡的部分 Id 将保持mask。因此,inpainter 必须填充更小的区域,从而改进 inpaint 。


四、实验

在这里插入图片描述

将我们的多视图分割模型与神经体积对象选择(NVOS)[44])、视频分割[4]和 人工注释mask(GT)进行了定性比较。我们的两阶段方法是指运行我们的多视图分割模型两次,第一次运行的输出作为第二次运行的初始化(见4.1.2)。我们的方法比NVOS的噪声小,NVOS也错过了目标的一些片段(例如,底部一行最低的花),但比单独的视频分割捕捉更多的细节(例如,花的模糊边缘)。我们的两阶段方法有助于填补单阶段输出的一些缺失的方面。

在这里插入图片描述

请注意,合成的视图彼此保持一致性;然而,依赖于视图的效果仍然存在(例如,钢琴裸露部分的照明)

在这里插入图片描述

由NeRF(在未掩蔽图像上),NeRF-In,NeRF-In(与单一掩蔽训练图像),和我们的方法。NeRF-In明显更加模糊,而NeRF-In(单一的)往往在细节更接近掩模边界的边缘时遇到困难

五、安装与代码讲解

1.项目安装

先克隆项目,然后安装依赖

git clone
pip install -r requirements.txt
pip install -r lama/requirements.txt

然后准备数据,以下是官方提供的数据链接:

https://drive.google.com/drive/folders/1N7D4-6IutYD40v9lfXGSVbWrd47UdJEC?usp=share_link

下载其中的statue压缩包,并用以下命令解压

unzip statue.zip -d data

其数据格式如下(images2是较大的分辨率,label中包含待去除目标的mask):

statue
├── images
│   ├── IMG_2707.jpg
│   ├── IMG_2708.jpg
│   ├── ...
│   └── IMG_2736.jpg
└── images_2
    ├── IMG_2707.png
    ├── IMG_2708.png
    ├── ...
    ├── IMG_2736.png
    └── label
        ├── IMG_2707.png
        ├── IMG_2708.png
        ├── ...
        └── IMG_2736.png

如果要训练自己的数据,可以安装colmap,用以下代码求出位姿信息:

python imgs2poses.py <your_datadir>

2.跑原始NeRF,得到深度信息:

rm -r LaMa_test_images/*  
rm -r output/label/*
python DS_NeRF/run_nerf.py --config DS_NeRF/configs/config.txt --render_factor 1 --prepare --i_weight 1000000000 --i_video 1000000000 --i_feat 4000 --N_iters 4001 --expname statue --datadir ./data/statue --factor 2 --N_gt 0

之后,渲染的disparities (反深度) 就会在 lama/LaMa_test_images文件夹中, 相关的前景mask标签在 lama/LaMa_test_images/label.

3.运行 LaMa 来产生几何和表观引导

LaMa主要结合mask,将深度图和RGB图中的目标去掉,并用背景填充,得到看着很真实的inpaint 图像。

cd lama

export TORCH_HOME=$(pwd) && export PYTHONPATH=$(pwd)
python bin/predict.py refine=True model.path=$(pwd)/big-lama indir=$(pwd)/LaMa_test_images outdir=$(pwd)/output

inpainted disparities(修改后的深度图) 在lama/output/label. 复制并将其放在 data/statue/images_2/depth.,代码如下:

dataset=statue
factor=2
rm -r ../data/$dataset/images_$factor/depth
mkdir ../data/$dataset/images_$factor/depth
cp ./output/label/*.png ../data/$dataset/images_$factor/depth

然后产生 inpainted RGB 图像:

dataset=statue
factor=2

rm -r LaMa_test_images/*
rm -r output/label/*
cp ../data/$dataset/images_$factor/*.png LaMa_test_images
mkdir LaMa_test_images/label
cp ../data/$dataset/images_$factor/label/*.png LaMa_test_images/label
python bin/predict.py refine=True model.path=$(pwd)/big-lama indir=$(pwd)/LaMa_test_images outdir=$(pwd)/output
rm -r ../data/$dataset/images_$factor/lama_images
mkdir ../data/$dataset/images_$factor/lama_images
cp ../data/$dataset/images_$factor/*.png ../data/$dataset/images_$factor/lama_images
cp ./output/label/*.png ../data/$dataset/images_$factor/lama_images

inpainted RGB 图像在 lama/output/label, 并复制到了 data/statue/images_2/lama_images.

4.运行多视角 inpainter NeRF

使用以下命令, 最后的 inpainted NeRF 优化将会展开. inpainted NeRF 的结果视频,将会按照每 i_video 保存一次.总训练次数 N_iters .

python DS_NeRF/run_nerf.py --config DS_NeRF/configs/config.txt --i_feat 200 --lpips --i_weight 1000000000000 --i_video 1000 --N_iters 10001 --expname statue --datadir ./data/statue --N_gt 0 --factor $factor

5.关键代码

  1. 读入数据,预处理,得到一些 GroundTruth

use_batching=True,表示将所有图像数据叠加在一起,并从中采样射线;

# 0.首先得到 colmap预处理得到的深度信息----------------------------------------------------------
if args.dataset_type == 'llff':
    if args.colmap_depth:
        depth_gts = load_colmap_depth( args.datadir, factor=args.factor, bd_factor=.75, prepare=args.prepare)  # n_img个list,每一个包含: depth(3879) coord(3879,2) weight(3879)归一化
    images, poses, bds, render_poses, i_test, masks, inpainted_depths, mask_indices = load_llff_data(args.datadir,  args.factor,  recenter=True,  bd_factor=.75,  spherify=args.spherify, prepare=args.prepare, args=args)     
    # mask代表前景掩码


H, W, focal = hwf 

# 1.创建 nerf model---------------------------------------------------------------------------
render_kwargs_train, render_kwargs_test, start, grad_vars, optimizer = create_nerf_tcnn(args)



# 2.得到 rays_rgb(射线o,d 加上img,mask)---------------------------------------------------------
labels = np.repeat(masks[:, None], 3, axis=1)  # [N, 3, H, W, 1]
rays = np.stack([get_rays_np(H, W, focal, p)
                   for p in poses[:, :3, :4]], 0)    # 维度[N, ro+rd, H, W, 3],比如 (29, 2, 378, 504, 3)
rays_rgb = np.concatenate([rays, images[:, None]], 1)    # (29, 2+1, 378, 504, 3)      ray融入了img,
rays_rgb = np.concatenate([rays_rgb, labels], -1)        # (29, 2+1, 378, 504, 3+1)    ray融入了img,mask

rays_rgb = np.reshape(rays_rgb, [-1, 3, 4])          # (29, 378, 504, 2+1, 3+1) --> (5524848, 3, 4)




# 3.得到 rays_inp (射线o,d 加上img,inpainted_depths)-----------------------------------------------
rays = np.stack([get_rays_np(H, W, focal, p)
                 for p in poses[:, :3, :4]], 0)  # [N, ro+rd, H, W, 3]  (29, 2, 378, 504, 3)
                 
labels = np.expand_dims(inpainted_depths, axis=-1)    # [N, H, W, 1]
labels = np.repeat(labels[:, None], 3, axis=1)        # [N, 3, H, W, 1]
      
rays_inp = np.concatenate([rays, images[:, None]], 1) # (29, 2+1, 378, 504, 3) ray融入了img
rays_inp = np.concatenate([rays_inp, labels], -1)     # (29, 2+1, 378, 504, 3+1) ray融入了img, inpainted_depths

rays_inp = np.reshape(rays_inp, [-1, 3, 4])           # (29, 378, 504, 2+1, 3+1) --> (5524848, 3, 4)




# 4.得到 rays_depth(colmap得到的4个深度值)-----------------------------------------------------------
if args.colmap_depth(True):
    for i in i_train(循环29张图):
        rays_depth = np.stack(get_rays_by_coord_np(H, W, focal, poses[i, :3, :4], depth_gts[i]['coord']),
                                      axis=0)  # 2 x N x 3
        # depth_gts[i]['coord']维度(27112),是图像上前景点的绝对坐标。这行代码,根据坐标得到对应的2711个射线
        # 具体函数定义如下:
        def get_rays_by_coord_np(H, W, focal, c2w, coords):
            i, j = (coords[:, 0] - W * 0.5) / focal, -(coords[:, 1] - H * 0.5) / focal  # (2711) (2711)
            dirs = np.stack([i, j, -np.ones_like(i)], -1)                               # (2711, 3) 
           rays_d = np.sum(dirs[..., np.newaxis, :] * c2w[:3, :3], -1)
           rays_o = np.broadcast_to(c2w[:3, -1], np.shape(rays_d))                      # c2w 最后一列是原点O
        return rays_o, rays_d                                                           # (2711, 3) (2711, 3) 


        depth_value = np.repeat(depth_gts[i]['depth'][:, None, None], 3, axis=2)   # (2711) 广播至(2711,1 ,3) 绝对深度
        weights = np.repeat( depth_gts[i]['weight'][:, None, None], 3, axis=2)     # (2711) 广播至(2711,1 ,3) 归一化权重
        rays_depth = np.concatenate([rays_depth, depth_value, weights], axis=1)    # (2711,4,3) 4分别代表 colmap估计的射线0+d,深度,权重
        rays_depth_list.append(rays_depth)
        
    rays_depth = np.concatenate(rays_depth_list, axis=0)      # (112377, 4 3) 29张图的前景射线,深度叠加在一起



# 5.得到 rays_rgb_clf 和 rays_inp--------------------------------------------------------------------
rays_rgb_clf = rays_rgb[rays_rgb[:, :, 3] == 0].reshape(-1, 3, 4)    # (5524848,3,4) -> (4827239,3,4) 只保留了mask==0的部分
rays_inp = rays_inp[rays_rgb[:, :, 3] != 0].reshape(-1, 3, 4)            # (5524848,3,4) -> (697609,3,4) 只保留了inpaint_mask!=0的部分

# 6.修改 rays_rgb
if not args.prepare:
   rays_rgb = rays_rgb[rays_rgb[:, :, 3] == 1].reshape(-1, 3, 4)        # (5524848,3,4) -> (23906,3,4)只保留了mask==0的部分



# 7.迭代产生 GroundTruth
raysRGB_iter = iter(DataLoader(RayDataset(rays_rgb), batch_size=N_rand, shuffle=True, num_workers=0,
                              generator=torch.Generator(device=device)))
raysINP_iter = iter(DataLoader(RayDataset(rays_inp), batch_size=N_rand, shuffle=True, num_workers=0,
                               generator=torch.Generator(device=device)))
raysRGBCLF_iter = iter(DataLoader(RayDataset(rays_rgb_clf), batch_size=N_rand, shuffle=True, num_workers=0,
                                  generator=torch.Generator(device=device)))

batch_inp = next(raysINP_iter).to(device)     # (1024,3,4)
batch = next(raysRGB_iter).to(device)         # (1024,3,4)
batch_clf = next(raysRGBCLF_iter).to(device)  # (1024,3,4)

# 7.1 target_s
batch_rays, target_s = batch[:2], batch[2]
target_s, label_s = target_s[:, :3], target_s[:, 3]           #(10243)(1024)射线的颜色和 mask(都是1)

# 7.2 target_inp
batch_inp, target_inp = batch_inp[:2], batch_inp[2]
target_inp, depth_inp = target_inp[:, :3], target_inp[:, 3]   #(10243)(1024)inpaint mask中的射线和深度

# 7.3 batch_clf
batch_rays_clf, target_clf = batch_clf[:2], batch_clf[2]      #(10243)(1024)只保留mask=0的前景 射线的颜色和 mask(都是0)

# 7.4 得到来自colmap的深度 gt
raysDepth_iter = iter(DataLoader(RayDataset(rays_depth), batch_size=N_rand, shuffle=True,  num_workers=0,   generator=torch.Generator(device=device))) if rays_depth is not None else None
batch_depth = next(raysDepth_iter).to(device)
batch_rays_depth = batch_depth[:2]        # 2 x B x 3
target_depth = batch_depth[2, :, 0]       # B=1024
ray_weights = batch_depth[3, :, 0]        # B=1024
  1. 读取待删除目标的mask,并用cv2.dilate做扩张(主要考虑到精准的mask,可能没涵盖目标的阴影等)
    for i, f in enumerate(mskfiles):    # mskfiles就是前景的mask,在label文件夹内
        try:
            msk = imread(f)
            msk = msk / msk.max()
            msk = cv2.resize( msk, (imgs.shape[1], imgs.shape[0]), interpolation=cv2.INTER_NEAREST)
                print(msk.shape)

            msk = cv2.dilate(msk, np.ones((5, 5), np.uint8), iterations=5)
            masks.append(msk)
            mask_indices.append(i)

2.DS_NeRF/run_nerf_helpers_tcnn.py中的 NeRF_TCNN函数,将xyz(位置)和d(射线方向),映射成颜色和密度值:

共渲染4次,每次输入都是不同的 ray:

rgb, disp, acc, depth, extras = render(H, W, focal, chunk=args.chunk, 
                                       rays=batch_rays_clf,   verbose=i < 10, retraw=True, **render_kwargs_train)  

rgb_complete, _, _, _, extras_complete = render(H, W, focal, chunk=args.chunk, 
                                               rays=batch_rays,  verbose=i < 10, retraw=True, detach_weights=True, **render_kwargs_train)

_, disp_inp, _, _, extras_inp = render(H, W, focal, chunk=args.chunk, 
                                      rays=batch_inp, verbose=i < 10, retraw=True,  **render_kwargs_train)                  # disp_inp (1024)预测每个点weight得到的深度图

_, _, _, depth_col, extras_col = render(H, W, focal, chunk=args.chunk, 
                                       rays=batch_rays_depth, verbose=i < 10, retraw=True, depths=target_depth,  **render_kwargs_train)             # depth_col (1024)      

每次渲染代码相同,具体 forward 过程如下

    def forward(self, input):
        x = input[:, :3]        # (655363)
        d = input[:, 3:]        # (655363)

        # sigma
        x = (x + self.bound) / (2 * self.bound)  # to [0, 1]  self.bound=100
        x = self.encoder(x)          # 16倍分辨率的hash表,得到(6553632)
        h = self.sigma_net(x)        # 得到(6553616)

        sigma = h[..., 0]
        geo_feat = h[..., 1:]

        # color
        d = (d + 1) / 2  # tcnn SH encoding requires inputs to be in [0, 1]
        d = self.encoder_dir(d)

        h = torch.cat([d, geo_feat], dim=-1)
        h = self.color_net(h)

        # sigmoid activation for rgb
        color = h                   # (65536,3)

        outputs = torch.cat([color, sigma[..., None]], -1)
        return outputs
  1. 计算损失
optimizer.zero_grad()
img_loss = img2mse(rgb, target_clf)    # 输入(1024,3) 输出:0.05 函数img2mse = lambda x, y: torch.mean((x - y) ** 2)
psnr = mse2psnr(img_loss)              # 函数 mse2psnr = lambda x: -10. * torch.log(x) / torch.log(torch.Tensor([10.]))


if not args.masked_NeRF and not args.object_removal:     # True
       img_loss += img2mse(rgb_complete, target_s)       # rgb_complete 是用batch_rays渲染出的颜色
       if 'rgb0' in extras_complete and not args.no_coarse:
                img_loss0 = img2mse(extras_complete['rgb0'], target_s)
                img_loss += img_loss0

# 3.计算深度损失
if args.depth_loss:
        if args.weighted_loss:
           depth_loss = img2mse(depth_col, target_depth)  # target_depth(1024),是colmap的深度

img_loss0 = img2mse(extras['rgb0'], target_clf)    # (10243)
loss = loss + img_loss0

每隔300个 iter,计算一次LPIP语义损失


np.random.shuffle(idx)      # 一个batch中,随机抽取几张图片
random_poses = poses[idx]

# 按比例裁剪出patch。实验中缩放因子分别是28
patch_len = (hwf[0] / / lpips_render_factor / / patch_len_factor,
             hwf[1] / / lpips_render_factor / / patch_len_factor)


rgbs, disps, (Xs, Ys) = render_path(random_poses, hwf, args.chunk, render_kwargs_test,
                                    render_factor=lpips_render_factor,rgb_require_grad=True,need_alpha=False,detach_weights=True,patch_len=patch_len,masks=masks[idx])                    
# 得到 (4,23,31,3) (4,23,31) 以及随机选的batch个中心点坐标


prediction = ((rgbs[_] - 0.5) * 2).permute(2, 0, 1)[None, ...]            # (23,31,3)

target = ((images[idx[_]] - 0.5) * 2).permute(2, 0, 1)[None, ...]
target = transform(target)[:, :, Xs[_]:Xs[_] + patch_len[0], Ys[_]:Ys[_] + patch_len[1]]         #  (23,31,3)

# 计算损失(LPIPS就是预训练的vgg):
lpips_loss += LPIPS(prediction, target).mean()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/849440.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一篇文章看懂Apipost Mock功能怎么用

在接口开发过程中&#xff0c;Mock功能可以帮助开发者快速测试和验证接口的正确性和稳定性&#xff0c;以便快速迭代和修复问题。Apipost推出智能Mock功能&#xff0c;可以在智能期望中填写一些触发条件&#xff0c;开启后&#xff0c;Apipost会根据已设置的触发条件&#xff0…

Linux-GPIO 配置pull up、pull down、no pull

author daisy.skye的博客_CSDN博客-Qt,嵌入式,Linux领域博主 https://blog.csdn.net/qq_40715266?typeblog 系列基于RK3568的Linux驱动开发——GPIO知识点&#xff08;一&#xff09;_daisy.skye的博客-CSDN博客基于RK3568的Linux驱动开发—— GPIO知识点&#xff08;二&#…

【H5】盘点HTML5新特性

html5总的来说比html4多了十个新特性&#xff0c;但其不支持ie8及ie8以下版本的浏览器 文章目录 一、语义标签二、增强型表单三、音频和视频四、Canvas绘图五、SVG绘图六、地理定位七、拖放API八、Web Worker九、Web Storage十、WebSocket 一、语义标签 html5语义标签&#x…

Maven的安装与配置(包含所有细节)

一、idea版本和maven配对 这里是很多新手都会遇到的大坑&#xff0c;一定要先将自己的idea版本和maven进行版本配配对。 Maven3.6.3版本兼容问题 注意&#xff1a;针对一些老项目 还是尽量采用 3.6.3版本&#xff0c;针对idea各个版本的兼容性就很兼容 IDEA 2022 兼容maven 3.8…

肠道重要菌属——埃希氏菌属 (Escherichia), 肠道炎症和生态失调相关

谷禾健康 —变形菌门 —γ变形菌纲 —肠杆菌目 —肠杆菌科 —埃希氏菌属 埃希氏菌属 (Escherichia)&#xff0c;是一种常见的细菌。其中最著名的种是大肠杆菌&#xff08;Escherichia coli&#xff09;&#xff0c;大肠杆菌是一种厌氧菌&#xff0c;通常生活在人和动物的肠道中…

echarts 日常设计感图表

饼图 pieChart(id) {const data {value:100,type:aaa}let angle 0; //角度&#xff0c;用来做简单的动画效果的let count echarts.init(document.getElementById(id));let option {title: [{text: "{a|" data.value "}{c|%}",x: "center"…

super父类 事物

一个没有事物的方法。 调用他的父类里有事物的方法。 无论this 和 super 都会让父类事物方法没有事物。 如果写了super.class 文件里面&#xff0c;就是super调用。 如果没写&#xff0c;就是this调用&#xff0c;坑爹 测试&#xff0c;把父类注入&#xff0c;事物才生效。

Redis——特性介绍与应用场景

Redis特性介绍 In-memory data structrues 众所周知&#xff0c;MySQL是一种关系型数据库&#xff0c;其通过表的结构存储数据&#xff0c;就类似于建立了一个excel表格来存储数据。但是像视频这类数据并不适合存储在关系型数据库中&#xff0c;因此存在非关系型数据库——通…

用例拆分情况考虑方案

文章目录 1、方案一方案概述方案分析(1) 把对应图商地图的逻辑给分离开(2) 要使用命令行的方式执行方法 2、方案二3、最终决定 1、方案一 方案概述 每个图商&#xff08;GD、BD、自建&#xff09;拆分成单独的类 把参数化的几个图商类别拆分成对应的图商类&#xff0c;在每个类…

服务器数据恢复-EXT3分区误删除邮件的数据恢复案例

服务器数据恢复环境&#xff1a; 一台服务器有一组由8块盘组建的RAID5阵列&#xff0c;EXT3文件系统。 服务器故障&#xff1a; 由于工作人员的误操作导致文件系统中的邮件丢失。用户需要恢复丢失的邮件数据。 服务器数据恢复过程&#xff1a; 1、将故障服务器中所有磁盘以只…

LabVIEW开发分段反射器测试台

LabVIEW开发分段反射器测试台 随着对太空的观察需求越来越远&#xff0c;而不是当前技术&#xff08;如哈勃望远镜&#xff09;所能达到的&#xff0c;有必要增加太空望远镜主镜的尺寸。但是&#xff0c;增加主镜像的大小时存在几个问题。随着反射镜尺寸的增加&#xff0c;制造…

车载开发能不能入?Android实属卷不动了

随着智能化的快速发展&#xff0c;车载开发成为了汽车行业中的一个重要领域。车载开发是指开发和设计车载系统中的软件和硬件&#xff0c;以实现车辆的智能化和互联化。在当前汽车行业竞争激烈的市场环境下&#xff0c;车载开发岗位具有广阔的发展前景。 随着人们对智能化的需…

VMware虚拟机开机状态动态增加内存和CPU

实验环境&#xff1a;一台虚拟机 1、右击虚拟机&#xff0c;点击“编辑设置”&#xff0c; 2、在“选项”中&#xff0c;找到“内存/cpu热插拔”这一项&#xff0c;把“为此虚拟机启动内存热添加”和“仅为此虚拟机启动CPU热添加”打钩&#xff0c;点击 “确定”。 注意&#x…

【Unity3D】Renderer Feature简介

1 3D 项目迁移到 URP 项目后出现的问题 3D 项目迁移至 URP 项目后&#xff0c;会出现很多渲染问题&#xff0c;如&#xff1a;材质显示异常、GL 渲染不显示、多 Pass 渲染异常、屏幕后处理异常等问题。下面将针对这些问题给出一些简单的解决方案。 URP 官方教程和 API 详见→Un…

Cloud Studio实战——热门视频Top100爬虫应用开发

最近Cloud Studio非常火&#xff0c;我也去试了一下&#xff0c;感觉真的非常方便&#xff01;我就以Python爬取B站各区排名前一百的视频&#xff0c;并作可视化来给大家分享一下Cloud Studio&#xff01;应用链接&#xff1a;Cloud Studio实战——B站热门视频Top100爬虫应用开…

MyCat分片规则——范围分片、取模分片、一致性hash、枚举分片

1.范围分片 2.取模分片 范围分片和取模分片针对数字类型的字段可以&#xff0c;但是针对于字符串类型的字段时。这两种就不适用了。 3.一致性hash 4.枚举分片 默认节点指的是&#xff0c;如果我们向数据库表插入数据的时候&#xff0c;超出了这个枚举值&#xff0c;那么默认向…

stm32 cubemx ps2无线(有线)手柄

文章目录 前言一、cubemx配置二、代码1.引入库bsp_hal_ps2.cbsp_hal_ps2.h 2.主函数 前言 本文讲解使用cubemx配置PS2手柄实现对手柄的按键和模拟值的读取。 很简单&#xff0c;库已经封装好了&#xff0c;直接就可以了。 文件 一、cubemx配置 这个很简单&#xff0c;不需要…

数据结构 二叉树(一篇基本掌握)

绪论 雄关漫道真如铁&#xff0c;而今迈步从头越。 本章将开始学习二叉树&#xff08;全文共一万两千字&#xff09;&#xff0c;二叉树相较于前面的数据结构来说难度会有许多的攀升&#xff0c;但只要跟着本篇博客深入的学习也可以基本的掌握基础二叉树。 话不多说安全带系好&…

怎么绘制汤姆索亚历险记思维导图?掌握这几个绘制步骤就可以

怎么绘制汤姆索亚历险记思维导图&#xff1f;如果你正在为学习汤姆索亚历险记而感到困惑&#xff0c;或者你想要更好地理解小说中的人物关系、情节和舞台背景&#xff0c;那么一个清晰的思维导图就可以帮助你梳理思路。那么下面就给大家介绍一下绘制步骤。 在进行思维导图绘制的…