Scaffold-GS 代码阅读笔记

1. 系统启动部分

使用 python 中的 parser 库 为配置系统的参数设定, 和3DGS 类似,并且使用safe_state(args.quiet) 函数 为每一次的 log 输出加上对应的 时间戳

## 配置参数的设定lp = ModelParams(parser)op = OptimizationParams(parser)pp = PipelineParams(parser)## 指定随机数的种子,并且将 log 的 std::out 输出 加上对应的 时间戳
safe_state(args.quiet)

–lod 用于对 Train/Test 的图像进行划分,表示Test 的Image 的数量。 比如, lod=40, 那么表示 选择40张图像作为 Test,剩下的图像作为 Train

2. Training 函数

2.1 初始化 Gaussian 的设定:

gaussians = GaussianModel(dataset.feat_dim, dataset.n_offsets, dataset.voxel_size, dataset.update_depth, dataset.update_init_factor, dataset.update_hierachy_factor, dataset.use_feat_bank, dataset.appearance_dim, dataset.ratio, dataset.add_opacity_dist, dataset.add_cov_dist, dataset.add_color_dist)

定义了网络的结构:
MLP_opacity:
在这里插入图片描述
MLP_cov:
在这里插入图片描述

MLP_color
在这里插入图片描述
2.2 读取位姿和图像

读取 colmap 生成的图像和位姿:
首先对于colmap 图像的 所有图像和 Pose 进行 Train/Test 的划分, 其中默认是 每隔8张选择一张作为 Test Image( LLFF 的划分依据).

代码里也设置了 – lod 参数来指定 Test Image 的个数, 比如 设置 –lod = 20 , 那么就代表由20张图像作为 TestImage; 剩下的 图像作为 Train Image.

Pose 的 Normalize. [ 所有的相机相对于中心点来说 ]

之后,需要在 Mipnerf360 这种环绕拍摄的setting 对 Train Camera 的位姿进行处理,其目的是根据所有 camera 的 位置,确定 整个场景的 几何中心(center) 和整个场景的 半径 radius. 然后所有的相机的位置都要 减去这个 center 的坐标, 相对于对于Pose 进行了 【-1,1】 之间的映射。

直接读取了 PLY 格式的 3D Colmap 重建的点云

点云和相机的参数 都保存在 scene_info 这个对象里面,对应着点云的3个属性:
在这里插入图片描述

对于图像进行 降采样

降采样的参数是通过命令行的 参数 -r 去实现的. 将 -r 设置成4, 我们对于图像进行降采样 4倍;

需要注意的是,根据 视场角 fov 的定义,对于图像进行长和宽等比例的降采样之后,视场角FOV 是保持不变的 (如果考虑相机的主点不存在偏移的话)。因此 3DGS 使用的 projection Matrix 是没有变换的

根据相机的 Fov_x, Fov_y, z_near, z_far 构建相机透视投影矩阵:

透视投影矩阵 往往假设 视锥,假设一个对称的视锥,不直接考虑主点偏移, (cx,cy 位于图像的中心的地方)。

def getProjectionMatrix(znear, zfar, fovX, fovY):tanHalfFovY = math.tan((fovY / 2))tanHalfFovX = math.tan((fovX / 2))top = tanHalfFovY * znearbottom = -topright = tanHalfFovX * znearleft = -rightP = torch.zeros(4, 4)z_sign = 1.0P[0, 0] = 2.0 * znear / (right - left)P[1, 1] = 2.0 * znear / (top - bottom)P[0, 2] = (right + left) / (right - left)P[1, 2] = (top + bottom) / (top - bottom)P[3, 2] = z_signP[2, 2] = z_sign * zfar / (zfar - znear)P[2, 3] = -(zfar * znear) / (zfar - znear)return P

为每个相机计算 内参 ProjectionMatrix外参 world_view_transform,两个4*4的矩阵。

2.3 从点云去创建场景的 3D Gaussian

首先对于 redundancy 点云 进行删除:
  points = self.voxelize_sample(points, voxel_size=self.voxel_size)

在这里插入图片描述
然后去初始化 高斯球的一些属性, scale 的初始化 用到了 KNN 算法 去寻找最近的点。 旋转 rot 从 0 开始初始化, opacity 随机给定一个固定数值从 0.1 初始化. 初始化的 代码和原始的 3DGS 相似, 但是 却多了3个物理量
anchor: 实际上就是3D 点云的坐标 xyz

offset:: 一个 anchor point 会生成 k个 Gaussian, 其位置是 anchor point 的位置加上 offset 偏移量

**scaling ** : 每一个 anchor_point 额外有一个 scaling 的属性, 其初始化时每个3D点到最近3D点的距离, 但是却 repeat 了6次,因此 shape 时 (N,6). 这个 scaleing factor 不是高斯的属性,不能理解为 协方差中的 scale,而是一个缩放因子。

## 这里的 anchor 其实就是高斯球的中心点,由点云得到的
self._anchor = nn.Parameter(fused_point_cloud.requires_grad_(True))## 代码里 offset 初始为10.  offset shape 【N,10,3】
self._offset = nn.Parameter(offsets.requires_grad_(True))
self._anchor_feat = nn.Parameter(anchors_feat.requires_grad_(True))
self._scaling = nn.Parameter(scales.requires_grad_(True))  
self._rotation = nn.Parameter(rots.requires_grad_(False))
self._opacity = nn.Parameter(opacities.requires_grad_(False))
## max_radii2D shape [N] 
self.max_radii2D = torch.zeros((self.get_anchor.shape[0]), device="cuda")

2.3 每次随机选择一个相机 进行训练

# Pick a random Cameraif not viewpoint_stack:viewpoint_stack = scene.getTrainCameras().copy()viewpoint_cam = viewpoint_stack.pop(randint(0, len(viewpoint_stack)-1))
Mask 掉 frustum 之外的 Gaussian, 并且对于 alpha 的阈值具有一定的要求

为每一个 anchor point 生成 一个 Mask

voxel_visible_mask = prefilter_voxel(viewpoint_cam, gaussians, pipe,background)

根据 anchor_point 生成 新的 3D Gaussian

xyz, color, opacity, scaling, rot, neural_opacity, mask = generate_neural_gaussians(
viewpoint_camera,  ## 相机的属性
pc,                ## 3D高斯输入
visible_mask,      ## 可见的Maskis_training=is_training)

计算每个 3D gaussian 球到 相机的 距离 dist 和方向
在这里插入图片描述

    ob_view = anchor - viewpoint_camera.camera_center# distob_dist = ob_view.norm(dim=1, keepdim=True)# viewob_view = ob_view / ob_dist

每个 anchor point 有 dim=32 的feature , 将这个 feature 和 距离 dist, 方向dir concat 起来。 实际的实现里面
pc.add_opacity_dist = False , 因此 预测 opacity 的 输入 ,只有 方向 dir 和 feature concat 起来,组成了 35 维度的 vector,

预测 Neural Gaussian 的 opacity

cat_local_view = torch.cat([feat, ob_view, ob_dist], dim=1) # [N, c+3+1]
cat_local_view_wodist = torch.cat([feat, ob_view], dim=1) # [N, c+3]
## MLP 预测,将feature 转换成 opacity
neural_opacity = pc.get_opacity_mlp(cat_local_view_wodist)

预测 Neural Gaussian 的 color

和前面一张,使用的 feature 是35 维度的, 不包括 dist ;
同样是 输入 35维度的向量,我们可以直接 通过MLP 推理得到 color 的数值:

## color 的output dim = 30
color = pc.get_color_mlp(cat_local_view_wodist)
## 将30 dim 的feature , reshape 成 [N,10,3]. 因为每一个 anchor point 会生成10个 Gaussian, 每个 Gaussian 有3个维度的 color 
color = color.reshape([anchor.shape[0]*pc.n_offsets, 3])# [mask]

预测 Neural Gaussian 的 cov [ 旋转Rot 和缩放 scale ]

在这里插入图片描述
预测 协方差的 Cov_mlp 输入时35 维度(和前面保持一致),输出是70维度。

scale_rot = pc.get_cov_mlp(cat_local_view_wodist)
## reshape 成 [N,10,7]
scale_rot = scale_rot.reshape([anchor.shape[0]*pc.n_offsets, 7]) # [mask]

对于 Cov 进行后处理,得到每个高斯球的 scale 和 rot:

scaling repeat 是每一个 anchor point 的属性, shape 是 [N,6]. 6 个数字是一样的。 对应公式中的 l v l_v lv

scaling = scaling_repeat[:,3:] * torch.sigmoid(scale_rot[:,:3]) # 对于协方差的 scale 需要进行scaling
rot = pc.rotation_activation(scale_rot[:,3:7]) # 对于协方差的 rot 可以直接使用激活函数,进行激活

预测 Neural Gaussian 的 位置 xyz

从一个 anchor point 出发,加上 缩放之后的 offset, 可以确定 Neural Gaussian 的位置

# post-process offsets to get centers for gaussians
offsets = offsets * scaling_repeat[:,:3]  ## 对于offset 同样需要 乘以 scaling 
xyz = repeat_anchor + offsets

在这里插入图片描述

2. Render 函数. 根据生成的 3D Neural Gaussian 进行 Rasterize. 这一部分主要和3D GS 的实现保持一致。

这一部分是 CUDA 的代码,和3DGS 的渲染过程是一致的。

# Rasterize visible Gaussians to image, obtain their radii (on screen). 
rendered_image, radii = rasterizer(means3D = xyz,  ## 3D Gaussian 的中心点means2D = screenspace_points, ## output: 3DGS 投影到2D screen 上的点,用来记录梯度shs = None,colors_precomp = color,   ## 3DGS 的颜色opacities = opacity,      ## 3DGS 的不透明度 alphascales = scaling,         ## 3DGS 的 scaling rotations = rot,         ## 3DGS 的 旋转cov3D_precomp = None)

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试经典算法系列之二叉数6 -- 二叉树的右视图

面试经典算法21 - 二叉树的右视图 LeetCode.199 公众号:阿Q技术站 问题描述 给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,null,5,null,4]…

数字乡村创新实践探索农业现代化与乡村振兴新路径:科技赋能农村全面振兴与农民幸福新篇章

随着信息技术的飞速发展,数字乡村成为推动农业现代化与乡村振兴的重要战略举措。科技赋能下的数字乡村创新实践,不仅提升了农业生产的智能化水平,也为乡村治理和农民生活带来了翻天覆地的变化。本文旨在探讨数字乡村创新实践在农业现代化与乡…

80% 的人都不会的 15 个 Linux 实用技巧

熟悉 Linux 系统的同学都知道,它高效主要体现在命令行。通过命令行,可以将很多简单的命令,通过自由的组合,得到非常强大的功能。 命令行也就意味着可以自动化,自动化会使你的工作更高效,释放很多手工操作&…

【周总结】

周总结 完成工单脚本的编写以及解决操作问题 完成相关 jira 问题修改 学习了 sentinel 整合项目使用流控,熔断,热点等功能的使用 2024/4/14 晴 1. 这周只办两件事,吃!喝!!! 2.忙忙忙&am…

978: 输出利用先序遍历创建的二叉树的中序遍历序列

解法&#xff1a; #include<iostream> #include<queue> using namespace std; // 定义二叉树结点 struct TreeNode {char val;TreeNode* left;TreeNode* right;TreeNode(char x) :val(x), left(NULL), right(NULL) {}; }; // 先序递归遍历建立二叉树 TreeNode* bu…

房贷还款(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h> # include <math.h>int main() {//初始化变量值&#xff1b;double m, r 0.01;float d 300000;float p 6000;//运算还款所需月份&#xff1b;m log10…

WPS二次开发系列:WPS SDk功能就概览

作者持续关注WPS二次开发专题系列&#xff0c;持续为大家带来更多有价值的WPS开发技术细节&#xff0c;如果能够帮助到您&#xff0c;请帮忙来个一键三连&#xff0c;更多问题请联系我&#xff08;QQ:250325397&#xff09; 作者通过深度测试使用了WPS SDK提供的Demo&#xff0…

便携式污水采样器的工作环境要求

便携式污水采样器的工作环境要求极为严格&#xff0c;以确保其能够准确、稳定地采集和分析水样。首先&#xff0c;该采样器必须在干燥、通风良好的环境中工作&#xff0c;以避免潮湿和高温对其内部电子元件的损害。同时&#xff0c;为了保证采样器的稳定性和精度&#xff0c;工…

水尺读数监测识别摄像机

水尺读数监测识别摄像机是一种具有巨大潜力的新型技术&#xff0c;它能够通过摄像头和智能识别算法&#xff0c;实时监测水尺的读数&#xff0c;并对水位进行准确识别。这种技术在水文监测、防洪减灾等领域有着广泛的应用前景。 在过去&#xff0c;水尺读数监测通常需要人工观测…

行业大佬为什么要用海外仓系统?位像素海外仓系统有什么优势?

在全球化浪潮下&#xff0c;跨境电商蓬勃发展&#xff0c;各路大佬企业纷纷崭露头角。而在跨境电商这个竞争激烈的市场中&#xff0c;为何众多行业大佬会选择使用海外仓系统呢&#xff1f;这背后的原因并不复杂&#xff0c;但足以让每一个追求效率和效益的企业家心动。让我们看…

(四)qt中使用ffmpeg播放视频,可暂停恢复

一、在qt中添加ffmpeg库及头文件 INCLUDEPATH /usr/local/ffmpeg/include LIBS -L/usr/local/lib -lavutil -lavcodec -lavformat -lswscale 二、详细代码 FFempegVideoDecode 视频解码类&#xff08;放入线程中&#xff09; ffmpegvideodecode.h #ifndef FFMPEGVIDEODE…

攻防世界06-get_post

http的两种请求方式是get和post&#xff0c;比如我用通过通过这两种方式传参&#xff0c;分别传a1和b2。get的请求方式是通过在网址后面加上“&#xff1f;a1&b2”,例如&#xff1a;https://adworld.xctf.org.cn/task/answer?a1&b2 post传参的话通过hackbar&#xff0…