Gaussian Splatting中球谐的使用


Gaussian Splatting中球谐的使用

定义

class GaussianModel中,球谐是通过下面的方式定义的:

self._features_dc = torch.empty(0)  # 第一个球谐系数,用于表示基础颜色
self._features_rest = torch.empty(0)  # 其余的球谐系数,用于表示颜色的细节和变化

_features_dc_features_rest 张量的形状为 (样本数, 3, 1)(样本数, 3, (max_sh_degree + 1) ** 2 - 1),分别表示基础颜色的球谐系数和其余球谐系数。

把相关的代码发给ChatGPT,能够帮助分析,参考链接为:https://chatgpt.com/share/35888723-ab2f-4384-ac63-0fcc87aaaa22

初始化

之后,通过如下的方式进行初始化:

fused_color = RGB2SH(torch.tensor(np.asarray(pcd.colors)).float().cuda())
# 初始化存储球谐系数的张量,每个颜色通道有(max_sh_degree + 1) ** 2个球谐系数
features = torch.zeros((fused_color.shape[0], 3, (self.max_sh_degree + 1) ** 2)).float().cuda()
# 将RGB转换后的球谐系数C0项的系数存入
features[:, :3, 0] = fused_color
# 其余球谐系数初始化为0
features[:, 3:, 1:] = 0.0
self._features_dc = nn.Parameter(features[:, :, 0:1].transpose(1, 2).contiguous().requires_grad_(True))
self._features_rest = nn.Parameter(features[:, :, 1:].transpose(1, 2).contiguous().requires_grad_(True))

相关的函数为:

def RGB2SH(rgb):
    """
    将RGB颜色值转换为球谐系数C0项的系数。
    :param rgb: RGB颜色值。
    :return: 转换后的球谐系数C0项的系数。
    """
    return (rgb - 0.5) / C0

保存

class GaussianModel中,球谐通过如下方式保存到ply文件中:

# 从 self._features_dc 张量中分离数据,转置第二维和第三维,扁平化,变为连续存储,并复制到 CPU,最后转换为 numpy 数组
f_dc = self._features_dc.detach().transpose(1, 2).flatten(start_dim=1).contiguous().cpu().numpy()
# 从 self._features_rest 张量中分离数据,转置第二维和第三维,扁平化,变为连续存储,并复制到 CPU,最后转换为 numpy 数组
f_rest = self._features_rest.detach().transpose(1, 2).flatten(start_dim=1).contiguous().cpu().numpy()

使用

通过如下的方式,将球谐转换为RGB:

if pipe.convert_SHs_python:
    # ************ 重点:球谐函数如何实现对颜色的掌控 **********
    # 将SH特征的形状调整为(batch_size * num_points,3,(max_sh_degree+1)**2)
    shs_view = pc.get_features.transpose(1, 2).view(-1, 3, (pc.max_sh_degree + 1) ** 2)
    # 计算相机中心到每个点的方向向量,并归一化
    dir_pp = (pc.get_xyz - viewpoint_camera.camera_center.repeat(pc.get_features.shape[0], 1))
    # 计算相机中心到每个点的方向向量,并归一化
    dir_pp_normalized = dir_pp / dir_pp.norm(dim=1, keepdim=True)
    # 使用SH特征将方向向量转换为RGB颜色
    sh2rgb = eval_sh(pc.active_sh_degree, shs_view, dir_pp_normalized)
    # 将RGB颜色的范围限制在0到1之间
    colors_precomp = torch.clamp_min(sh2rgb + 0.5, 0.0)

相关的几个函数为:

def get_features(self):
    features_dc = self._features_dc
    features_rest = self._features_rest
    return torch.cat((features_dc, features_rest), dim=1)
def eval_sh(deg, sh, dirs):
    """
    Evaluate spherical harmonics at unit directions
    using hardcoded SH polynomials.
    Works with torch/np/jnp.
    ... Can be 0 or more batch dimensions.
    Args:
        deg: int SH deg. Currently, 0-3 supported
        sh: jnp.ndarray SH coeffs [..., C, (deg + 1) ** 2]
        dirs: jnp.ndarray unit directions [..., 3]
    Returns:
        [..., C]
    """
    assert deg <= 4 and deg >= 0
    coeff = (deg + 1) ** 2
    assert sh.shape[-1] >= coeff

    result = C0 * sh[..., 0]
    if deg > 0:
        x, y, z = dirs[..., 0:1], dirs[..., 1:2], dirs[..., 2:3]
        result = (result -
                  C1 * y * sh[..., 1] +
                  C1 * z * sh[..., 2] -
                  C1 * x * sh[..., 3])

        if deg > 1:
            xx, yy, zz = x * x, y * y, z * z
            xy, yz, xz = x * y, y * z, x * z
            result = (result +
                      C2[0] * xy * sh[..., 4] +
                      C2[1] * yz * sh[..., 5] +
                      C2[2] * (2.0 * zz - xx - yy) * sh[..., 6] +
                      C2[3] * xz * sh[..., 7] +
                      C2[4] * (xx - yy) * sh[..., 8])

            if deg > 2:
                result = (result +
                          C3[0] * y * (3 * xx - yy) * sh[..., 9] +
                          C3[1] * xy * z * sh[..., 10] +
                          C3[2] * y * (4 * zz - xx - yy) * sh[..., 11] +
                          C3[3] * z * (2 * zz - 3 * xx - 3 * yy) * sh[..., 12] +
                          C3[4] * x * (4 * zz - xx - yy) * sh[..., 13] +
                          C3[5] * z * (xx - yy) * sh[..., 14] +
                          C3[6] * x * (xx - 3 * yy) * sh[..., 15])

                if deg > 3:
                    result = (result + C4[0] * xy * (xx - yy) * sh[..., 16] +
                              C4[1] * yz * (3 * xx - yy) * sh[..., 17] +
                              C4[2] * xy * (7 * zz - 1) * sh[..., 18] +
                              C4[3] * yz * (7 * zz - 3) * sh[..., 19] +
                              C4[4] * (zz * (35 * zz - 30) + 3) * sh[..., 20] +
                              C4[5] * xz * (7 * zz - 3) * sh[..., 21] +
                              C4[6] * (xx - yy) * (7 * zz - 1) * sh[..., 22] +
                              C4[7] * xz * (xx - 3 * yy) * sh[..., 23] +
                              C4[8] * (xx * (xx - 3 * yy) - yy * (3 * xx - yy)) * sh[..., 24])
    return result

文章作者: Immortalqx
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Immortalqx !
评论
  目录