源代码分析
1、高斯模型
D:\desktop\3DGS\gaussian-splatting\scene\gaussian_model.py
- gaussian_model.py:高斯分布的参数是如何构建的,协方差矩阵,球谐函数,点云数据初始化,用于深度学习的激活函数学习率的设置,如何增加和删除高斯采用的方法。
- 第24行
1 2 3 4 5 6 7 8
| class GaussianModel:# 负责高斯模型的初始化和操作
def setup_functions(self):#设置一些激活函数,用于处理缩放、旋转、透明度等参数。 def build_covariance_from_scaling_rotation(scaling, scaling_modifier, rotation): L = build_scaling_rotation(scaling_modifier * scaling, rotation)# actual_covariance = L @ L.transpose(1, 2) symm = strip_symmetric(actual_covariance) return symm
|
调转到:D:\desktop\3DGS\gaussian-splatting\utils\general_utils.py
1 2 3 4 5 6 7 8 9 10 11
| def build_scaling_rotation(s, r): # 这个函数结合缩放和旋转的参数,构建一个相应的矩阵。 L = torch.zeros((s.shape[0], 3, 3), dtype=torch.float, device="cuda") R = build_rotation(r)
L[:,0,0] = s[:,0] L[:,1,1] = s[:,1] L[:,2,2] = s[:,2]
L = R @ L return L
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| def build_rotation(r):# 该函数根据输入的四元数构建旋转矩阵。
# 求这个r的模值,计算一个包含多个四维向量的张量中每个向量的长度 norm = torch.sqrt(r[:,0]*r[:,0] + r[:,1]*r[:,1] + r[:,2]*r[:,2] + r[:,3]*r[:,3]) q = r / norm[:, None]# 将r除以它的模值,得到单位四元数,即归一化值,这样去旋转的时候不会改变旋转的幅度,只改变方向,只改变高斯分布的形状
R = torch.zeros((q.size(0), 3, 3), device='cuda')
r = q[:, 0]#分别获取四元数的r,x,y,z x = q[:, 1] y = q[:, 2] z = q[:, 3]
|
1 2 3 4 5 6 7 8 9 10
| R[:, 0, 0] = 1 - 2 * (y*y + z*z) R[:, 0, 1] = 2 * (x*y - r*z) R[:, 0, 2] = 2 * (x*z + r*y) R[:, 1, 0] = 2 * (x*y + r*z) R[:, 1, 1] = 1 - 2 * (x*x + z*z) R[:, 1, 2] = 2 * (y*z - r*x) R[:, 2, 0] = 2 * (x*z - r*y) R[:, 2, 1] = 2 * (y*z + r*x) R[:, 2, 2] = 1 - 2 * (x*x + y*y) return R
|
1 2 3 4 5 6 7 8 9 10 11 12
| def build_scaling_rotation(s, r): # 这个函数结合缩放和旋转的参数,构建一个相应的矩阵。
L = torch.zeros((s.shape[0], 3, 3), dtype=torch.float, device="cuda") R = build_rotation(r)
L[:,0,0] = s[:,0] L[:,1,1] = s[:,1] L[:,2,2] = s[:,2]
L = R @ L return L
|
N就是高斯分布的总数
旋转矩阵乘以缩放矩阵得到新矩阵L,高斯椭球的变化就是通过这个旋转和缩放来完成的
1 2 3 4 5 6 7 8 9 10 11
| class GaussianModel:# 负责高斯模型的初始化和操作 def setup_functions(self):#设置一些激活函数,用于处理缩放、旋转、透明度等参数。 def build_covariance_from_scaling_rotation(scaling, scaling_modifier, rotation): L = build_scaling_rotation(scaling_modifier * scaling, rotation) actual_covariance = L @ L.transpose(1, 2) #L乘以它的转置,第一维和第二维转置,跳过第0维度,因为第0维度是高斯分布的总数 #这样就构建出了协方差矩阵 symm = strip_symmetric(actual_covariance) return symm
|
1
| symm = strip_symmetric(actual_covariance)
|
只保留上右半部分,保证完全对称or节省内存
第136行
1 2 3 4 5 6 7 8
| def create_from_pcd(self, pcd : BasicPointCloud, spatial_lr_scale : float): # 从点云创建模型,初始化点云坐标、颜色特征、缩放和旋转等参数。 self.spatial_lr_scale = spatial_lr_scale fused_point_cloud = torch.tensor(np.asarray(pcd.points)).float().cuda()# 点云的坐标 fused_color = RGB2SH(torch.tensor(np.asarray(pcd.colors)).float().cuda())# 点云的颜色特征 features = torch.zeros((fused_color.shape[0], 3, (self.max_sh_degree + 1) ** 2)).float().cuda() features[:, :3, 0 ] = fused_color features[:, 3:, 1:] = 0.0
|
查看RGB2SH
函数定义则跳转到D:\desktop\3DGS\gaussian-splatting\utils\sh_utils.py
其中包含了球谐函数的相关定义
1 2
| def RGB2SH(rgb): return (rgb - 0.5) / C0
|
1 2 3 4
| # 这个函数根据输入的球谐函数和单位方向,使用硬编码的球谐函数多项式来评估球谐函数。 assert deg <= 4 and deg >= 0 # 确保球谐函数的阶数在0到4之间,不能超过四阶,系数会非常多 coeff = (deg + 1) ** 2 # 计算球谐函数的系数数量 assert sh.shape[-1] >= coeff # 确保球谐函数的系数数量大于等于计算出的系数数量
|
1 2 3 4 5 6 7
| result = C0 * sh[..., 0] # 计算0阶球谐函数的第一项 if deg > 0: x, y, z = dirs[..., 0:1], dirs[..., 1:2], dirs[..., 2:3] # 提取单位方向中的x, y, z分量 result = (result - C1 * y * sh[..., 1] + # 计算1阶球谐函数的第一项 C1 * z * sh[..., 2] - # 计算1阶球谐函数的第二项 C1 * x * sh[..., 3]) # 计算1阶球谐函数的第三项
|
第148行
1
| dist2 = torch.clamp_min(distCUDA2(torch.from_numpy(np.asarray(pcd.points)).float().cuda()), 0.0000001)
|
函数distCUDA2()定义在D:\desktop\3DGS\gaussian-splatting\submodules\simple-knn\spatial.cu
1 2 3 4 5 6 7 8 9 10 11 12
| torch::Tensor distCUDA2(const torch::Tensor& points) { const int P = points.size(0);
auto float_opts = points.options().dtype(torch::kFloat32); torch::Tensor means = torch::full({P}, 0.0, float_opts); SimpleKNN::knn(P, (float3*)points.contiguous().data<float>(), means.contiguous().data<float>());
return means; }
|
假设空间中有一个高斯点,把这个高斯点最近的三个高斯点,把他们的距离取一个平均值,用这个平均值去构建高斯椭球,这就是通过点云构建的初始高斯球,高斯球的半径就是这个三个点平均值。
2、相机模型:
D:\desktop\3DGS\gaussian-splatting\scene\cameras.py
其中cameras.py的相机模型代码表达了高斯分布在三维空间,然后投影到二维空间的一个转换过程
第54行中
1 2 3 4 5
| self.world_view_transform = torch.tensor(getWorld2View2(R, T, trans, scale)).transpose(0, 1).cuda() #世界坐标系到相机坐标系的变换 self.projection_matrix = getProjectionMatrix(znear=self.znear, zfar=self.zfar, fovX=self.FoVx, fovY=self.FoVy).transpose(0,1).cuda() self.full_proj_transform = (self.world_view_transform.unsqueeze(0).bmm(self.projection_matrix.unsqueeze(0))).squeeze(0) self.camera_center = self.world_view_transform.inverse()[3, :3]
|
其中
1
| getWorld2View2()#世界坐标系到相机坐标系的变换
|
定义于D:\desktop\3DGS\gaussian-splatting\utils\graphics_utils.py
1 2 3 4 5 6 7 8 9 10 11 12
| def getWorld2View2(R, t, translate=np.array([.0, .0, .0]), scale=1.0): Rt = np.zeros((4, 4)) Rt[:3, :3] = R.transpose() Rt[:3, 3] = t Rt[3, 3] = 1.0 C2W = np.linalg.inv(Rt) cam_center = C2W[:3, 3] cam_center = (cam_center + translate) * scale C2W[:3, 3] = cam_center Rt = np.linalg.inv(C2W) return np.float32(Rt)
|
传入参数为R旋转矩阵,t平移矩阵,第三个参数是转换的数组,缩放因数
而查看函数定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| def getProjectionMatrix(znear, zfar, fovX, fovY):
tanHalfFovY = math.tan((fovY / 2)) tanHalfFovX = math.tan((fovX / 2)) top = tanHalfFovY * znear bottom = -top right = tanHalfFovX * znear left = -right
P = torch.zeros(4, 4)
z_sign = 1.0
P[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_sign P[2, 2] = z_sign * zfar / (zfar - znear) P[2, 3] = -(zfar * znear) / (zfar - znear) return P
|