Ours_S3GS_Waymo / scene /background_model.py
Dororo99's picture
Add files using upload-large-folder tool
b4e816e verified
Raw
History Blame Contribute Delete
25.3 kB
""" from EMER-NeRF """
from torch import Tensor
import torch.nn as nn
from typing import Optional, Tuple
import torch
import torch.nn.functional as F
from .encodings import SinusoidalEncoder
from typing import Dict, Optional, Union
import math
from scene.gaussian_model import GaussianModel
from utils.general_utils import sample_on_aabb_surface
from utils.sh_utils import SH2RGB
import numpy as np
from utils.general_utils import inverse_sigmoid, get_expon_lr_func, build_rotation
import os
from utils.system_utils import mkdir_p
from plyfile import PlyData, PlyElement
from utils.sh_utils import RGB2SH
from simple_knn._C import distCUDA2
from utils.graphics_utils import BasicPointCloud
from utils.general_utils import strip_symmetric, build_scaling_rotation
import matplotlib.pyplot as plt
def min_max(x, name='x', print_info=True):
print(f"{name} range : {x.min().item():.7f} ~ {x.max().item():.7f}")
# 如果x 是一维
if x.dim() == 1:
x = x.unsqueeze(1)
out = torch.cat((x.min(dim=0).values, x.max(dim=0).values), dim=0)
return out
def get_rays(
x: Tensor, y: Tensor, c2w: Tensor, intrinsic: Tensor
) -> Tuple[Tensor, Tensor, Tensor]:
"""
Args:
x: the horizontal coordinates of the pixels, shape: (num_rays,)
y: the vertical coordinates of the pixels, shape: (num_rays,)
c2w: the camera-to-world matrices, shape: (num_cams, 4, 4)
intrinsic: the camera intrinsic matrices, shape: (num_cams, 3, 3)
Returns:
origins: the ray origins, shape: (num_rays, 3)
viewdirs: the ray directions, shape: (num_rays, 3)
direction_norm: the norm of the ray directions, shape: (num_rays, 1)
"""
if len(intrinsic.shape) == 2:
intrinsic = intrinsic[None, :, :]
if len(c2w.shape) == 2:
c2w = c2w[None, :, :]
camera_dirs = torch.nn.functional.pad(
torch.stack(
[
(x - intrinsic[:, 0, 2] + 0.5) / intrinsic[:, 0, 0],
(y - intrinsic[:, 1, 2] + 0.5) / intrinsic[:, 1, 1],
],
dim=-1,
),
(0, 1),
value=1.0,
) # [num_rays, 3]
# rotate the camera rays w.r.t. the camera pose
directions = (camera_dirs[:, None, :] * c2w[:, :3, :3]).sum(dim=-1)
origins = torch.broadcast_to(c2w[:, :3, -1], directions.shape)
# TODO: not sure if we still need direction_norm
direction_norm = torch.linalg.norm(directions, dim=-1, keepdims=True)
# normalize the ray directions
viewdirs = directions / (direction_norm + 1e-8)
return origins, viewdirs, direction_norm
def get_directions(
x: Tensor, y: Tensor, c2w: Tensor, intrinsic: Tensor
) -> Tuple[Tensor, Tensor, Tensor]:
"""
Args:
x: the horizontal coordinates of the pixels, shape: (num_rays,)
y: the vertical coordinates of the pixels, shape: (num_rays,)
c2w: the camera-to-world matrices, shape: (num_cams, 4, 4)
intrinsic: the camera intrinsic matrices, shape: (num_cams, 3, 3)
Returns:
origins: the ray origins, shape: (num_rays, 3)
viewdirs: the ray directions, shape: (num_rays, 3)
direction_norm: the norm of the ray directions, shape: (num_rays, 1)
"""
if len(intrinsic.shape) == 2:
intrinsic = intrinsic[None, :, :]
if len(c2w.shape) == 2:
c2w = c2w[None, :, :]
camera_dirs = torch.nn.functional.pad(
torch.stack(
[
(x - intrinsic[:, 0, 2] + 0.5) / intrinsic[:, 0, 0],
(y - intrinsic[:, 1, 2] + 0.5) / intrinsic[:, 1, 1],
],
dim=-1,
),
(0, 1),
value=1.0,
) # [num_rays, 3]
# rotate the camera rays w.r.t. the camera pose
directions = (camera_dirs[:, None, :] * c2w[:, :3, :3]).sum(dim=-1)
# TODO: not sure if we still need direction_norm
direction_norm = torch.linalg.norm(directions, dim=-1, keepdims=True)
# normalize the ray directions
viewdirs = directions / (direction_norm + 1e-8)
return viewdirs
def ray_box_intersect(rays_o: torch.Tensor, rays_d: torch.Tensor, r: Union[float, torch.Tensor]) -> torch.Tensor:
""" Calculate intersections of each rays and each boxes
Args:
rays_o (torch.Tensor): [B,3]
rays_d (torch.Tensor): [B,3]
r (Union[float, torch.Tensor]): [(B,)P] half side-lengths of boxes
Returns:
torch.Tensor: [B,P] depths of intersections along rays
"""
# Expand to (B, 1, 3)
rays_o = rays_o.unsqueeze(1)
rays_d = rays_d.unsqueeze(1)
# Expand to ([B, ]P, 1)
r = r[..., None]
# t_min, t_max: (B, P, 3)
t_min = (-r - rays_o) / rays_d
t_max = (r - rays_o) / rays_d
# t_near, t_far: (B, P)
t_near = torch.minimum(t_min, t_max).max(dim=-1).values
t_far = torch.maximum(t_min, t_max).min(dim=-1).values
# Check if rays are inside boxes and boxes are in front of the ray origin
mask_intersect = (t_far > t_near) & (t_far > 0)
t_far[~mask_intersect] = math.nan
return t_far
class MLP(nn.Module):
"""A simple MLP with skip connections."""
def __init__(
self,
in_dims: int,
out_dims: int,
num_layers: int = 3,
hidden_dims: Optional[int] = 256,
skip_connections: Optional[Tuple[int]] = [0],
) -> None:
super().__init__()
self.in_dims = in_dims
self.hidden_dims = hidden_dims
self.n_output_dims = out_dims
self.num_layers = num_layers
self.skip_connections = skip_connections
layers = []
if self.num_layers == 1:
layers.append(nn.Linear(in_dims, out_dims))
else:
for i in range(self.num_layers - 1):
if i == 0:
layers.append(nn.Linear(in_dims, hidden_dims))
elif i in skip_connections:
layers.append(nn.Linear(in_dims + hidden_dims, hidden_dims))
else:
layers.append(nn.Linear(hidden_dims, hidden_dims))
layers.append(nn.Linear(hidden_dims, out_dims))
self.layers = nn.ModuleList(layers)
def forward(self, x: Tensor) -> Tensor:
input = x
for i, layer in enumerate(self.layers):
if i in self.skip_connections:
x = torch.cat([x, input], -1)
x = layer(x)
if i < len(self.layers) - 1:
x = nn.functional.relu(x)
return x
class ShellModel(nn.Module):
"""
find_intersection + grid_interpolation --> sky_feat
"""
def __init__(self, model_cfg: dict):
super().__init__()
pass
class SkyModel(nn.Module):
"""
dir_encoding + MLP --> sky_feat
"""
def __init__(self, model_cfg: dict = {"mlp_width": 256,}):
super().__init__()
self.model_cfg = model_cfg
self.direction_encoding = SinusoidalEncoder(
n_input_dims=3, min_deg=0, max_deg=4
)
self.sky_head = MLP(
in_dims=self.direction_encoding.n_output_dims,
out_dims=3,
num_layers=3,
hidden_dims=model_cfg['mlp_width'],
skip_connections=[1],
)
def forward(self, directions):
dd = self.direction_encoding(directions)
sky_feat = self.sky_head(dd)
#rgb_sky = F.sigmoid(sky_feat)
return sky_feat
class BackgroundGaussianModel(GaussianModel):
"""
Gaussian model for background
- 输入 aabb
- 增加了 xyz 的映射 , 将坐标 固定在指定范围内: aabb 之外
-
"""
def print_gs_info(self, hist_bins='auto', hist_density=True, hist_img_path=None):
pass
print('------------------ GS info ------------------')
print("Number of points : ", self.get_xyz.shape[0])
# -------------------------- get data -----------------------------------
# scale
scale_before_activation = self._scaling
scale = self.get_scaling
# rotation: 四元数 用 norm 来归一化
rotation_before_activation = self._rotation
rotation = self.get_rotation
# opacity
opacity_before_activation = self._opacity
opacity = self.get_opacity
# xyz
xyz = self.get_xyz
# -------------------------- static-info -----------------------------------
# ----------------- min-max range -----------------
#scale_before_activation_range = min_max(scale_before_activation, 'scale_before_activation')
scale_x_before_activation_range = min_max(scale_before_activation[:, 0:1], 'scale_x_before_activation')
scale_y_before_activation_range = min_max(scale_before_activation[:, 1:2], 'scale_y_before_activation')
scale_z_before_activation_range = min_max(scale_before_activation[:, 2:3], 'scale_z_before_activation')
#scale_range = min_max(scale, 'scale')
scale_x_range = min_max(scale[:, 0:1], 'scale_x')
scale_y_range = min_max(scale[:, 1:2], 'scale_y')
scale_z_range = min_max(scale[:, 2:3], 'scale_z')
# scale ratio # 长轴 与 短轴之比
min_axis = torch.min(scale, dim=1)[0]
max_axis = torch.max(scale, dim=1)[0]
scale_ratio = max_axis / min_axis
scale_ratio_range = min_max(scale_ratio, 'scale_ratio')
# rot 暂不考虑
#rotation_before_activation_range = min_max(rotation_before_activation, 'rotation_before_activation')
#rotation_range = min_max(rotation, 'rotation')
opacity_before_activation_range = min_max(opacity_before_activation, 'opacity_before_activation')
opacity_range = min_max(opacity, 'opacity')
#xyz_range = min_max(xyz, 'xyz')
x_range = min_max(xyz[:, 0:1], 'x')
y_range = min_max(xyz[:, 1:2], 'y')
z_range = min_max(xyz[:, 2:3], 'z')
# ----------------- hist -----------------
#scale_before_activation_hist = np.histogram(scale_before_activation.cpu().numpy(), bins=hist_bins, density=hist_density)
scale_x_before_activation_hist = np.histogram(scale_before_activation[:, 0:1].cpu().numpy(), bins=hist_bins, density=hist_density)
scale_y_before_activation_hist = np.histogram(scale_before_activation[:, 1:2].cpu().numpy(), bins=hist_bins, density=hist_density)
scale_z_before_activation_hist = np.histogram(scale_before_activation[:, 2:3].cpu().numpy(), bins=hist_bins, density=hist_density)
#scale_hist = np.histogram(scale.cpu().numpy(), bins=hist_bins, density=hist_density)
scale_x_hist = np.histogram(scale[:, 0:1].cpu().numpy(), bins=hist_bins, density=hist_density)
scale_y_hist = np.histogram(scale[:, 1:2].cpu().numpy(), bins=hist_bins, density=hist_density)
scale_z_hist = np.histogram(scale[:, 2:3].cpu().numpy(), bins=hist_bins, density=hist_density)
scale_ratio_hist = np.histogram(scale_ratio.cpu().numpy(), bins=hist_bins, density=hist_density)
#rotation_before_activation_hist = np.histogram(rotation_before_activation.cpu().numpy(), bins=hist_bins, density=hist_density)
#rotation_hist = np.histogram(rotation.cpu().numpy(), bins=hist_bins, density=hist_density)
opacity_before_activation_hist = np.histogram(opacity_before_activation.cpu().numpy(), bins=hist_bins, density=hist_density)
opacity_hist = np.histogram(opacity.cpu().numpy(), bins=hist_bins, density=hist_density)
#xyz_hist = np.histogram(xyz.cpu().numpy(), bins=hist_bins, density=hist_density)
x_hist = np.histogram(xyz[:, 0:1].cpu().numpy(), bins=hist_bins, density=hist_density)
y_hist = np.histogram(xyz[:, 1:2].cpu().numpy(), bins=hist_bins, density=hist_density)
z_hist = np.histogram(xyz[:, 2:3].cpu().numpy(), bins=hist_bins, density=hist_density)
# collect_info
info_dict = {
'scale_x_before_activation_range': scale_x_before_activation_range,
'scale_y_before_activation_range': scale_y_before_activation_range,
'scale_z_before_activation_range': scale_z_before_activation_range,
'scale_x_range': scale_x_range,
'scale_y_range': scale_y_range,
'scale_z_range': scale_z_range,
'scale_ratio_range': scale_ratio_range,
'opacity_before_activation_range': opacity_before_activation_range,
'opacity_range': opacity_range,
'x_range': x_range,
'y_range': y_range,
'z_range': z_range,
'scale_x_before_activation_hist': scale_x_before_activation_hist,
'scale_y_before_activation_hist': scale_y_before_activation_hist,
'scale_z_before_activation_hist': scale_z_before_activation_hist,
'scale_x_hist': scale_x_hist,
'scale_y_hist': scale_y_hist,
'scale_z_hist': scale_z_hist,
'scale_ratio_hist': scale_ratio_hist,
'opacity_before_activation_hist': opacity_before_activation_hist,
'opacity_hist': opacity_hist,
'x_hist': x_hist,
'y_hist': y_hist,
'z_hist': z_hist,
}
if hist_img_path is not None:
# plot hist
fig, axes = plt.subplots(4, 3, figsize=(15, 15))
axes[0, 0].hist(scale_x_before_activation_hist[1][:-1], scale_x_before_activation_hist[1], weights=scale_x_before_activation_hist[0])
axes[0, 0].set_title('scale_x_before_activation_hist')
axes[0, 1].hist(scale_y_before_activation_hist[1][:-1], scale_y_before_activation_hist[1], weights=scale_y_before_activation_hist[0])
axes[0, 1].set_title('scale_y_before_activation_hist')
axes[0, 2].hist(scale_z_before_activation_hist[1][:-1], scale_z_before_activation_hist[1], weights=scale_z_before_activation_hist[0])
axes[0, 2].set_title('scale_z_before_activation_hist')
axes[1, 0].hist(scale_x_hist[1][:-1], scale_x_hist[1], weights=scale_x_hist[0])
axes[1, 0].set_title('scale_x_hist')
axes[1, 1].hist(scale_y_hist[1][:-1], scale_y_hist[1], weights=scale_y_hist[0])
axes[1, 1].set_title('scale_y_hist')
axes[1, 2].hist(scale_z_hist[1][:-1], scale_z_hist[1], weights=scale_z_hist[0])
axes[1, 2].set_title('scale_z_hist')
axes[2, 0].hist(scale_ratio_hist[1][:-1], scale_ratio_hist[1], weights=scale_ratio_hist[0])
axes[2, 0].set_title('scale_ratio_hist')
axes[2, 1].hist(opacity_before_activation_hist[1][:-1], opacity_before_activation_hist[1], weights=opacity_before_activation_hist[0])
axes[2, 1].set_title('opacity_before_activation_hist')
axes[2, 2].hist(opacity_hist[1][:-1], opacity_hist[1], weights=opacity_hist[0])
axes[2, 2].set_title('opacity_hist')
axes[3, 0].hist(x_hist[1][:-1], x_hist[1], weights=x_hist[0])
axes[3, 0].set_title('x_hist')
axes[3, 1].hist(y_hist[1][:-1], y_hist[1], weights=y_hist[0])
axes[3, 1].set_title('y_hist')
axes[3, 2].hist(z_hist[1][:-1], z_hist[1], weights=z_hist[0])
axes[3, 2].set_title('z_hist')
plt.savefig(hist_img_path)
plt.close()
print(f'hist_img_path: {hist_img_path}')
return info_dict
def training_setup_no_feat(self, training_args):
self.percent_dense = training_args.bg_percent_dense
self.xyz_gradient_accum = torch.zeros((self.get_xyz.shape[0], 1), device="cuda")
self.denom = torch.zeros((self.get_xyz.shape[0], 1), device="cuda")
# 相当于是 基于相机运动轨迹 or 基于场景大小 的一定比例 来确定 xyz 的学习率
# 但如果 我对 xyz 进行了归一化, 那么就应该避免 场景尺度的学习
l = [
{'params': [self._xyz], 'lr': training_args.position_lr_init * self.spatial_lr_scale, "name": "xyz"},
{'params': [self._features_dc], 'lr': training_args.feature_lr, "name": "f_dc"},
{'params': [self._features_rest], 'lr': training_args.feature_lr / 20.0, "name": "f_rest"},
{'params': [self._opacity], 'lr': training_args.opacity_lr, "name": "opacity"},
{'params': [self._scaling], 'lr': training_args.scaling_lr, "name": "scaling"},
{'params': [self._rotation], 'lr': training_args.rotation_lr, "name": "rotation"}
]
self.optimizer = torch.optim.Adam(l, lr=0.0, eps=1e-15)
self.xyz_scheduler_args = get_expon_lr_func(lr_init=training_args.position_lr_init*self.spatial_lr_scale,
lr_final=training_args.position_lr_final*self.spatial_lr_scale,
lr_delay_mult=training_args.position_lr_delay_mult,
max_steps=training_args.position_lr_max_steps)
def training_setup(self, training_args):
self.percent_dense = training_args.bg_percent_dense
self.xyz_gradient_accum = torch.zeros((self.get_xyz.shape[0], 1), device="cuda")
self.denom = torch.zeros((self.get_xyz.shape[0], 1), device="cuda")
if self._language_feature is None or self._language_feature.shape[0] != self._xyz.shape[0]:
# 开始feature训练的时候,往模型中加入language feature参数
language_feature = torch.zeros((self._xyz.shape[0], training_args.feat_dim), device="cuda")
self._language_feature = nn.Parameter(language_feature.requires_grad_(True))
# 初始化 language feature
self._language_feature.data.uniform_(-1.0 / self.num_panoptic_objects,
1.0 / self.num_panoptic_objects)
# 与 codebook-embedding 的 dim-norm 的情况 保持一致
self.with_dim_norm = training_args.gs_dim_norm
self.with_render_dim_norm = training_args.render_gs_dim_norm
l = [
{'params': [self._xyz], 'lr': training_args.position_lr_init * self.spatial_lr_scale, "name": "xyz"},
{'params': [self._features_dc], 'lr': training_args.feature_lr, "name": "f_dc"},
{'params': [self._features_rest], 'lr': training_args.feature_lr / 20.0, "name": "f_rest"},
{'params': [self._opacity], 'lr': training_args.opacity_lr, "name": "opacity"},
{'params': [self._scaling], 'lr': training_args.scaling_lr, "name": "scaling"},
{'params': [self._rotation], 'lr': training_args.rotation_lr, "name": "rotation"},
{'params': [self._language_feature], 'lr': training_args.language_feature_lr, "name": "language_feature"}, # TODO: training_args.language_feature_lr
# bg-gs 不需要 codebook
#{'params': self.feat_conv.parameters(), 'lr': training_args.feat_conv_lr, "name": "feat_conv"},
]
# bg-gs 不需要 背景模型
self.bg_model = self.bg_gs = None
self.optimizer = torch.optim.Adam(l, lr=0.0, eps=1e-15)
# bg-gs 不需要 multi-scheduler
self.xyz_scheduler_args = get_expon_lr_func(lr_init=training_args.position_lr_init*self.spatial_lr_scale,
lr_final=training_args.position_lr_final*self.spatial_lr_scale,
lr_delay_mult=training_args.position_lr_delay_mult,
max_steps=training_args.position_lr_max_steps)
def create_from_pcd(self, pcd : BasicPointCloud, spatial_lr_scale : float, init_opacity = 0.1):
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
print("Number of BackGround points at initialisation : ", fused_point_cloud.shape[0])
dist2 = torch.clamp_min(distCUDA2(torch.from_numpy(np.asarray(pcd.points)).float().cuda()), 0.0000001)
scales = torch.log(torch.sqrt(dist2))[...,None].repeat(1, 3)
rots = torch.zeros((fused_point_cloud.shape[0], 4), device="cuda")
rots[:, 0] = 1
opacities = inverse_sigmoid(init_opacity * torch.ones((fused_point_cloud.shape[0], 1), dtype=torch.float, device="cuda"))
self._xyz = nn.Parameter(fused_point_cloud.requires_grad_(True))
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))
self._scaling = nn.Parameter(scales.requires_grad_(True))
self._rotation = nn.Parameter(rots.requires_grad_(True))
self._opacity = nn.Parameter(opacities.requires_grad_(True))
self.max_radii2D = torch.zeros((self.get_xyz.shape[0]), device="cuda")
def min_max(x, name='x', print_info=True):
print(f"{name} range : {x.min().item():.7f} ~ {x.max().item():.7f}")
# 如果x 是一维
if x.dim() == 1:
x = x.unsqueeze(1)
out = torch.cat((x.min(dim=0).values, x.max(dim=0).values), dim=0)
return out
class Background_Model(nn.Module):
""" NeRF-like bg-model """
def __init__(self,
img_width = 960, img_height = 640,
aabb = None, cam_norm = None,
model_type = "mlp", # mlp, rec_shell, sphere_shell
model_cfg: dict = {"head_mlp_layer_width": 256,},
model_args: dict = {},
) -> None:
super().__init__()
self.aabb = aabb
self.img_width, self.img_height = img_width, img_height
self.model_type = model_type
self.model_cfg = model_cfg
if model_type == 'mlp':
self.sky_model = SkyModel(model_cfg)
elif model_type == 'rec_shell' or model_type == 'sphere_shell':
# TODO: has not been implemented
self.sky_model = ShellModel(model_cfg)
elif model_type == 'gs':
self.sky_model = BackgroundGaussianModel(**model_cfg)
else:
raise NotImplementedError("model_type {} not implemented".format(model_type))
if model_type == 'gs':
# 先构造高斯点
fg_aabb_center, fg_aabb_size = (aabb[0] + aabb[1]) / 2, aabb[1] - aabb[0] # cam-frustum aabb
# use bg_scale to scale the aabb
bg_gs_aabb = np.stack([fg_aabb_center - fg_aabb_size * model_args.bg_aabb_scale / 2,
fg_aabb_center + fg_aabb_size * model_args.bg_aabb_scale / 2], axis=0)
bg_aabb_center, bg_aabb_size = (bg_gs_aabb[0] + bg_gs_aabb[1]) / 2, bg_gs_aabb[1] - bg_gs_aabb[0]
# add bg_gs_aabb SURFACE points
bg_points = sample_on_aabb_surface(bg_aabb_center, bg_aabb_size, model_args.bg_gs_num, above_half=True)
print("bg_gs_points min:",np.min(bg_points,axis=0))
print("bg_gs_points max:",np.max(bg_points,axis=0))
# DO NOT add bg_gs_points to points
#points = np.concatenate([points, bg_points], axis=0)
#shs = np.concatenate([shs, np.random.random((len(bg_points), 3)) / 255.0], axis=0)
bg_shs = np.random.random((len(bg_points), 3)) / 255.0
# visualize
#from utils.general_utils import visualize_points
#visualize_points(points, fg_aabb_center, fg_aabb_size)
#bg_ply_path = os.path.join(data_root, "ds-bg-points3d.ply")
#storePly(bg_ply_path, bg_points, SH2RGB(bg_shs) * 255)
bg_pcd = BasicPointCloud(points=bg_points, colors=SH2RGB(bg_shs), normals=np.zeros((len(bg_points), 3)))
self.sky_model.create_from_pcd(bg_pcd,
spatial_lr_scale = cam_norm['radius'] * model_args.bg_aabb_scale,
#spatial_lr_scale = 1.0 # TODO 设置 xyz 在 0-1之内变化 , 后续映射到 inf 空间里
) # cam 距离 轨迹中心的 最大距离
# prepare bg-hyper-params: 暂且与 fg-gs 保持一致
#bg_model_args = model_args['']
# gs-training-setup
self.sky_model.training_setup(model_args)
else:
# pixel coords
x = torch.arange(0, img_width, dtype=torch.float32, device="cuda")
y = torch.arange(0, img_height, dtype=torch.float32, device="cuda")
x, y = x.long(), y.long()
self.pixel_coord = torch.stack(torch.meshgrid(x, y, indexing='xy')).float().cuda()
def get_ray_directions(self, c2w, intrinsic):
x, y= self.pixel_coord[0].flatten(), self.pixel_coord[1].flatten()
viewdirs = get_directions(x, y, c2w, intrinsic)
viewdirs = viewdirs.reshape(self.img_height, self.img_width, 3)
return viewdirs
def forward(self, c2w, intrinsic, img_idx=None) -> Tensor:
directions = self.get_ray_directions(c2w, intrinsic)
sky_feat = self.sky_model(directions)
rgb_sky = torch.sigmoid(sky_feat)
return rgb_sky.permute(2, 0, 1)