nerfplayer with ngp and nerfacto (#1277)

* nerfplayer with ngp and nerfacto

* update comments

* update cpp extension build

* avoid Windows repeated CUDA compile

---------

Co-authored-by: machenmusik <machenmusik@users.noreply.github.com>
This commit is contained in:
LSongandmachenmusik authored and GitHub committed 2023-01-31 11:56:28 -08:00
1 parent 0e9e26dc7c
commit e73692e599
10 files changed
+1238 -15

No files matched your search

+62
View File
@@ -33,6 +33,7 @@ from nerfstudio.data.datamanagers.variable_res_datamanager import (
)
from nerfstudio.data.dataparsers.blender_dataparser import BlenderDataParserConfig
from nerfstudio.data.dataparsers.dnerf_dataparser import DNeRFDataParserConfig
from nerfstudio.data.dataparsers.dycheck_dataparser import DycheckDataParserConfig
from nerfstudio.data.dataparsers.friends_dataparser import FriendsDataParserConfig
from nerfstudio.data.dataparsers.instant_ngp_dataparser import (
InstantNGPDataParserConfig,
@@ -49,6 +50,8 @@ from nerfstudio.models.depth_nerfacto import DepthNerfactoModelConfig
from nerfstudio.models.instant_ngp import InstantNGPModelConfig
from nerfstudio.models.mipnerf import MipNerfModel
from nerfstudio.models.nerfacto import NerfactoModelConfig
from nerfstudio.models.nerfplayer_nerfacto import NerfplayerNerfactoModelConfig
from nerfstudio.models.nerfplayer_ngp import NerfplayerNGPModelConfig
from nerfstudio.models.semantic_nerfw import SemanticNerfWModelConfig
from nerfstudio.models.tensorf import TensoRFModelConfig
from nerfstudio.models.vanilla_nerf import NeRFModel, VanillaModelConfig
@@ -67,6 +70,8 @@ descriptions = {
"tensorf": "tensorf",
"dnerf": "Dynamic-NeRF model. (slow)",
"phototourism": "Uses the Phototourism data.",
"nerfplayer-nerfacto": "NeRFPlayer with nerfacto backbone.",
"nerfplayer-ngp": "NeRFPlayer with InstantNGP backbone.",
}
method_configs["nerfacto"] = TrainerConfig(
@@ -325,6 +330,63 @@ method_configs["phototourism"] = TrainerConfig(
vis="viewer",
)
method_configs["nerfplayer-nerfacto"] = TrainerConfig(
method_name="nerfplayer-nerfacto",
steps_per_eval_batch=500,
steps_per_save=2000,
max_num_iterations=30000,
mixed_precision=True,
pipeline=VanillaPipelineConfig(
datamanager=DepthDataManagerConfig(
dataparser=DycheckDataParserConfig(),
train_num_rays_per_batch=4096,
eval_num_rays_per_batch=4096,
camera_optimizer=CameraOptimizerConfig(
mode="SO3xR3", optimizer=AdamOptimizerConfig(lr=6e-4, eps=1e-8, weight_decay=1e-2)
),
),
model=NerfplayerNerfactoModelConfig(eval_num_rays_per_chunk=1 << 15),
),
optimizers={
"proposal_networks": {
"optimizer": AdamOptimizerConfig(lr=1e-2, eps=1e-15),
"scheduler": None,
},
"fields": {
"optimizer": AdamOptimizerConfig(lr=1e-2, eps=1e-15),
"scheduler": None,
},
},
viewer=ViewerConfig(num_rays_per_chunk=1 << 15),
vis="viewer",
)
method_configs["nerfplayer-ngp"] = TrainerConfig(
method_name="nerfplayer-ngp",
steps_per_eval_batch=500,
steps_per_save=2000,
max_num_iterations=30000,
mixed_precision=True,
pipeline=DynamicBatchPipelineConfig(
datamanager=DepthDataManagerConfig(dataparser=DycheckDataParserConfig(), train_num_rays_per_batch=8192),
model=NerfplayerNGPModelConfig(
eval_num_rays_per_chunk=8192,
contraction_type=ContractionType.AABB,
render_step_size=0.001,
max_num_samples_per_ray=48,
near_plane=0.01,
),
),
optimizers={
"fields": {
"optimizer": AdamOptimizerConfig(lr=1e-2, eps=1e-15),
"scheduler": None,
}
},
viewer=ViewerConfig(num_rays_per_chunk=64000),
vis="viewer",
)
AnnotatedBaseConfigUnion = tyro.conf.SuppressFixed[ # Don't show unparseable (fixed) arguments in helptext.
tyro.conf.FlagConversionOff[
tyro.extras.subcommand_type_from_defaults(defaults=method_configs, descriptions=descriptions)
+2 -1
View File
@@ -27,9 +27,10 @@ BUILD_DIR = _get_build_directory(NAME, verbose=False)
_C = None
if cuda_toolkit_available():
if os.path.exists(os.path.join(BUILD_DIR, f"{NAME}.so")):
if os.listdir(BUILD_DIR) != []:
# If the build exists, we assume the extension has been built
# and we can load it.
print("nerfstudio field components: CUDA set up, loading (should be quick)")
_C = load(
name=NAME,
sources=glob.glob(os.path.join(PATH, "csrc/*.cu")),
+1 -1
View File
@@ -182,7 +182,7 @@ class TemporalGridEncoder(nn.Module):
input_dim: int = 3,
num_levels: int = 16,
level_dim: int = 2,
per_level_scale: int = 2,
per_level_scale: float = 2.0,
base_resolution: int = 16,
log2_hashmap_size: int = 19,
desired_resolution: Optional[int] = None,
@@ -0,0 +1,415 @@
# Copyright 2022 The Nerfstudio Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Field implementations for NeRFPlayer (https://arxiv.org/abs/2210.15947) implementation with nerfacto backbone
"""
from typing import Optional
import numpy as np
import torch
from torch.nn.parameter import Parameter
from torchtyping import TensorType
from nerfstudio.cameras.rays import Frustums, RaySamples
from nerfstudio.data.scene_box import SceneBox
from nerfstudio.field_components.activations import trunc_exp
from nerfstudio.field_components.embedding import Embedding
from nerfstudio.field_components.field_heads import (
FieldHeadNames,
PredNormalsFieldHead,
SemanticFieldHead,
TransientDensityFieldHead,
TransientRGBFieldHead,
UncertaintyFieldHead,
)
from nerfstudio.field_components.spatial_distortions import SpatialDistortion
from nerfstudio.field_components.temporal_grid import TemporalGridEncoder
from nerfstudio.fields.base_field import Field
try:
import tinycudann as tcnn
except ImportError:
# tinycudann module doesn't exist
pass
def get_normalized_directions(directions: TensorType["bs":..., 3]):
"""SH encoding must be in the range [0, 1]
Args:
directions: batch of directions
"""
return (directions + 1.0) / 2.0
class TemporalHashMLPDensityField(Field):
"""A lightweight temporal density field module.
Args:
aabb: Parameters of scene aabb bounds
temporal_dim: Hashing grid parameter. A higher temporal dim means a higher temporal frequency.
num_layers: Number of hidden layers
hidden_dim: Dimension of hidden layers
spatial_distortion: Spatial distortion module
num_levels: Hashing grid parameter. Used for initialize TemporalGridEncoder class.
max_res: Hashing grid parameter. Used for initialize TemporalGridEncoder class.
base_res: Hashing grid parameter. Used for initialize TemporalGridEncoder class.
log2_hashmap_size: Hashing grid parameter. Used for initialize TemporalGridEncoder class.
features_per_level: Hashing grid parameter. Used for initialize TemporalGridEncoder class.
"""
def __init__(
self,
aabb,
temporal_dim: int = 64,
num_layers: int = 2,
hidden_dim: int = 64,
spatial_distortion: Optional[SpatialDistortion] = None,
num_levels=8,
max_res=1024,
base_res=16,
log2_hashmap_size=18,
features_per_level=2,
) -> None:
super().__init__()
# from .temporal_grid import test; test() # DEBUG
self.aabb = Parameter(aabb, requires_grad=False)
self.spatial_distortion = spatial_distortion
growth_factor = np.exp((np.log(max_res) - np.log(base_res)) / (num_levels - 1))
self.encoding = TemporalGridEncoder(
input_dim=3,
temporal_dim=temporal_dim,
num_levels=num_levels,
level_dim=features_per_level,
per_level_scale=growth_factor,
base_resolution=base_res,
log2_hashmap_size=log2_hashmap_size,
)
self.linear = tcnn.Network(
n_input_dims=num_levels * features_per_level,
n_output_dims=1,
network_config={
"otype": "FullyFusedMLP",
"activation": "ReLU",
"output_activation": "None",
"n_neurons": hidden_dim,
"n_hidden_layers": num_layers - 1,
},
)
# pylint: disable=arguments-differ
def density_fn(self, positions: TensorType["bs":..., 3], times: TensorType["bs", 1]) -> TensorType["bs":..., 1]:
"""Returns only the density. Used primarily with the density grid.
Args:
positions: the origin of the samples/frustums
times: the time of rays
"""
if len(positions.shape) == 3 and len(times.shape) == 2:
# position is [ray, sample, 3]; times is [ray, 1]
times = times[:, None] # RaySamples can handle the shape
# Need to figure out a better way to descibe positions with a ray.
ray_samples = RaySamples(
frustums=Frustums(
origins=positions,
directions=torch.ones_like(positions),
starts=torch.zeros_like(positions[..., :1]),
ends=torch.zeros_like(positions[..., :1]),
pixel_area=torch.ones_like(positions[..., :1]),
),
times=times,
)
density, _ = self.get_density(ray_samples)
return density
def get_density(self, ray_samples: RaySamples):
if self.spatial_distortion is not None:
positions = self.spatial_distortion(ray_samples.frustums.get_positions())
positions = (positions + 2.0) / 4.0
else:
positions = SceneBox.get_normalized_positions(ray_samples.frustums.get_positions(), self.aabb)
positions_flat = positions.view(-1, 3)
time_flat = ray_samples.times.reshape(-1, 1)
x = self.encoding(positions_flat, time_flat).to(positions)
density_before_activation = self.linear(x).view(*ray_samples.frustums.shape, -1)
# Rectifying the density with an exponential is much more stable than a ReLU or
# softplus, because it enables high post-activation (float32) density outputs
# from smaller internal (float16) parameters.
density = trunc_exp(density_before_activation)
return density, None
def get_outputs(self, ray_samples: RaySamples, density_embedding: Optional[TensorType] = None):
return {}
class NerfplayerNerfactoField(Field):
"""NeRFPlayer (https://arxiv.org/abs/2210.15947) field with nerfacto backbone.
Args:
aabb: parameters of scene aabb bounds
num_images: number of images in the dataset
num_layers: number of hidden layers
hidden_dim: dimension of hidden layers
geo_feat_dim: output geo feat dimensions
num_levels: number of levels of the hashmap for the base mlp
max_res: maximum resolution of the hashmap for the base mlp
log2_hashmap_size: size of the hashmap for the base mlp
num_layers_color: number of hidden layers for color network
num_layers_transient: number of hidden layers for transient network
hidden_dim_color: dimension of hidden layers for color network
hidden_dim_transient: dimension of hidden layers for transient network
appearance_embedding_dim: dimension of appearance embedding
transient_embedding_dim: dimension of transient embedding
use_transient_embedding: whether to use transient embedding
use_semantics: whether to use semantic segmentation
num_semantic_classes: number of semantic classes
use_pred_normals: whether to use predicted normals
use_average_appearance_embedding: whether to use average appearance embedding or zeros for inference
spatial_distortion: spatial distortion to apply to the scene
"""
def __init__(
self,
aabb,
num_images: int,
num_layers: int = 2,
hidden_dim: int = 64,
geo_feat_dim: int = 15,
temporal_dim: int = 64,
num_levels: int = 16,
features_per_level: int = 2,
log2_hashmap_size: int = 19,
num_layers_color: int = 3,
num_layers_transient: int = 2,
hidden_dim_color: int = 64,
hidden_dim_transient: int = 64,
appearance_embedding_dim: int = 32,
transient_embedding_dim: int = 16,
use_transient_embedding: bool = False,
use_semantics: bool = False,
num_semantic_classes: int = 100,
use_pred_normals: bool = False,
use_average_appearance_embedding: bool = False,
spatial_distortion: Optional[SpatialDistortion] = None,
) -> None:
super().__init__()
self.aabb = Parameter(aabb, requires_grad=False)
self.geo_feat_dim = geo_feat_dim
self.spatial_distortion = spatial_distortion
self.num_images = num_images
self.appearance_embedding_dim = appearance_embedding_dim
self.embedding_appearance = Embedding(self.num_images, self.appearance_embedding_dim)
self.use_average_appearance_embedding = use_average_appearance_embedding
self.use_transient_embedding = use_transient_embedding
self.use_semantics = use_semantics
self.use_pred_normals = use_pred_normals
self.direction_encoding = tcnn.Encoding(
n_input_dims=3,
encoding_config={
"otype": "SphericalHarmonics",
"degree": 4,
},
)
self.position_encoding = tcnn.Encoding(
n_input_dims=3,
encoding_config={"otype": "Frequency", "n_frequencies": 2},
)
self.mlp_base = TemporalGridEncoder(
input_dim=3,
temporal_dim=temporal_dim,
num_levels=num_levels,
level_dim=features_per_level,
log2_hashmap_size=log2_hashmap_size,
desired_resolution=1024 * (self.aabb.max() - self.aabb.min()),
)
self.mlp_base_decode = tcnn.Network(
n_input_dims=num_levels * features_per_level,
n_output_dims=1 + self.geo_feat_dim,
network_config={
"otype": "FullyFusedMLP",
"activation": "ReLU",
"output_activation": "None",
"n_neurons": hidden_dim,
"n_hidden_layers": num_layers - 1,
},
)
# transients
if self.use_transient_embedding:
self.transient_embedding_dim = transient_embedding_dim
self.embedding_transient = Embedding(self.num_images, self.transient_embedding_dim)
self.mlp_transient = tcnn.Network(
n_input_dims=self.geo_feat_dim + self.transient_embedding_dim,
n_output_dims=hidden_dim_transient,
network_config={
"otype": "FullyFusedMLP",
"activation": "ReLU",
"output_activation": "None",
"n_neurons": hidden_dim_transient,
"n_hidden_layers": num_layers_transient - 1,
},
)
self.field_head_transient_uncertainty = UncertaintyFieldHead(in_dim=self.mlp_transient.n_output_dims)
self.field_head_transient_rgb = TransientRGBFieldHead(in_dim=self.mlp_transient.n_output_dims)
self.field_head_transient_density = TransientDensityFieldHead(in_dim=self.mlp_transient.n_output_dims)
# semantics
if self.use_semantics:
self.mlp_semantics = tcnn.Network(
n_input_dims=self.geo_feat_dim,
n_output_dims=hidden_dim_transient,
network_config={
"otype": "FullyFusedMLP",
"activation": "ReLU",
"output_activation": "None",
"n_neurons": 64,
"n_hidden_layers": 1,
},
)
self.field_head_semantics = SemanticFieldHead(
in_dim=self.mlp_semantics.n_output_dims, num_classes=num_semantic_classes
)
# predicted normals
if self.use_pred_normals:
self.mlp_pred_normals = tcnn.Network(
n_input_dims=self.geo_feat_dim + self.position_encoding.n_output_dims,
n_output_dims=hidden_dim_transient,
network_config={
"otype": "FullyFusedMLP",
"activation": "ReLU",
"output_activation": "None",
"n_neurons": 64,
"n_hidden_layers": 2,
},
)
self.field_head_pred_normals = PredNormalsFieldHead(in_dim=self.mlp_pred_normals.n_output_dims)
self.mlp_head = tcnn.Network(
n_input_dims=self.direction_encoding.n_output_dims + self.geo_feat_dim + self.appearance_embedding_dim,
n_output_dims=3,
network_config={
"otype": "FullyFusedMLP",
"activation": "ReLU",
"output_activation": "Sigmoid",
"n_neurons": hidden_dim_color,
"n_hidden_layers": num_layers_color - 1,
},
)
def get_density(self, ray_samples: RaySamples):
"""Computes and returns the densities."""
if self.spatial_distortion is not None:
positions = ray_samples.frustums.get_positions()
positions = self.spatial_distortion(positions)
positions = (positions + 2.0) / 4.0
else:
positions = SceneBox.get_normalized_positions(ray_samples.frustums.get_positions(), self.aabb)
positions_flat = positions.view(-1, 3)
assert ray_samples.times is not None, "Time should be included in the input for NeRFPlayer"
time_flat = ray_samples.times.reshape(-1, 1)
h = self.mlp_base(positions_flat, time_flat)
h = self.mlp_base_decode(h).view(*ray_samples.frustums.shape, -1)
density_before_activation, base_mlp_out = torch.split(h, [1, self.geo_feat_dim], dim=-1)
# Rectifying the density with an exponential is much more stable than a ReLU or
# softplus, because it enables high post-activation (float32) density outputs
# from smaller internal (float16) parameters.
density = trunc_exp(density_before_activation.to(positions))
return density, base_mlp_out
def get_outputs(self, ray_samples: RaySamples, density_embedding: Optional[TensorType] = None):
assert density_embedding is not None
outputs = {}
if ray_samples.camera_indices is None:
raise AttributeError("Camera indices are not provided.")
camera_indices = ray_samples.camera_indices.squeeze()
directions = get_normalized_directions(ray_samples.frustums.directions)
directions_flat = directions.view(-1, 3)
d = self.direction_encoding(directions_flat)
outputs_shape = ray_samples.frustums.directions.shape[:-1]
# appearance
if self.training:
embedded_appearance = self.embedding_appearance(camera_indices)
else:
if self.use_average_appearance_embedding:
embedded_appearance = torch.ones(
(*directions.shape[:-1], self.appearance_embedding_dim), device=directions.device
) * self.embedding_appearance.mean(dim=0)
else:
embedded_appearance = torch.zeros(
(*directions.shape[:-1], self.appearance_embedding_dim), device=directions.device
)
# transients
if self.use_transient_embedding and self.training:
embedded_transient = self.embedding_transient(camera_indices)
transient_input = torch.cat(
[
density_embedding.view(-1, self.geo_feat_dim),
embedded_transient.view(-1, self.transient_embedding_dim),
],
dim=-1,
)
x = self.mlp_transient(transient_input).view(*outputs_shape, -1).to(directions)
outputs[FieldHeadNames.UNCERTAINTY] = self.field_head_transient_uncertainty(x)
outputs[FieldHeadNames.TRANSIENT_RGB] = self.field_head_transient_rgb(x)
outputs[FieldHeadNames.TRANSIENT_DENSITY] = self.field_head_transient_density(x)
# semantics
if self.use_semantics:
density_embedding_copy = density_embedding.clone().detach()
semantics_input = torch.cat(
[
density_embedding_copy.view(-1, self.geo_feat_dim),
],
dim=-1,
)
x = self.mlp_semantics(semantics_input).view(*outputs_shape, -1).to(directions)
outputs[FieldHeadNames.SEMANTICS] = self.field_head_semantics(x)
# predicted normals
if self.use_pred_normals:
positions = ray_samples.frustums.get_positions()
positions_flat = self.position_encoding(positions.view(-1, 3))
pred_normals_inp = torch.cat([positions_flat, density_embedding.view(-1, self.geo_feat_dim)], dim=-1)
x = self.mlp_pred_normals(pred_normals_inp).view(*outputs_shape, -1).to(directions)
outputs[FieldHeadNames.PRED_NORMALS] = self.field_head_pred_normals(x)
h = torch.cat(
[
d,
density_embedding.view(-1, self.geo_feat_dim),
embedded_appearance.view(-1, self.appearance_embedding_dim),
],
dim=-1,
)
rgb = self.mlp_head(h).view(*outputs_shape, -1).to(directions)
outputs.update({FieldHeadNames.RGB: rgb})
return outputs
+236
View File
@@ -0,0 +1,236 @@
# Copyright 2022 The Nerfstudio Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
NeRFPlayer (https://arxiv.org/abs/2210.15947) field implementations with InstantNGP backbone
"""
from typing import Optional
import torch
from nerfacc import ContractionType, contract
from torch.nn.parameter import Parameter
from torchtyping import TensorType
from nerfstudio.cameras.rays import Frustums, RaySamples
from nerfstudio.data.scene_box import SceneBox
from nerfstudio.field_components.activations import trunc_exp
from nerfstudio.field_components.embedding import Embedding
from nerfstudio.field_components.field_heads import FieldHeadNames
from nerfstudio.field_components.temporal_grid import TemporalGridEncoder
from nerfstudio.fields.base_field import Field
from nerfstudio.fields.instant_ngp_field import get_normalized_directions
try:
import tinycudann as tcnn
except ImportError:
# tinycudann module doesn't exist
pass
class NerfplayerNGPField(Field):
"""NeRFPlayer (https://arxiv.org/abs/2210.15947) field with InstantNGP backbone.
Args:
aabb: parameters of scene aabb bounds
temporal_dim: the dimension of temporal axis, a higher dimension indicates a higher temporal frequency
please refer to the implementation of TemporalGridEncoder for more details
num_levels: the number of multi-resolution levels; same as InstantNGP
features_per_level: the dim of output feature vector for each level; same as InstantNGP
log2_hashmap_size: the size of the table; same as InstantNGP
base_resolution: base resolution for the table; same as InstantNGP
num_layers: number of hidden layers (occupancy decoder network after sampling)
hidden_dim: dimension of hidden layers (occupancy decoder network after sampling)
geo_feat_dim: output geo feat dimensions
num_layers_color: number of hidden layers for color network
hidden_dim_color: dimension of hidden layers for color network
use_appearance_embedding: whether to use appearance embedding
disable_viewing_dependent: if true, disable the viewing dependent effect (no viewing direction as inputs)
Sometimes we need to disable viewing dependent effects in a dynamic scene, because there is
ambiguity between being dynamic and viewing dependent effects. For example, the shadow of the camera
should be a dynamic effect, but may be reconstructed as viewing dependent effects.
num_images: number of images, requried if use_appearance_embedding is True
appearance_embedding_dim: dimension of appearance embedding
contraction_type: type of contraction
"""
def __init__(
self,
aabb,
temporal_dim: int = 16,
num_levels: int = 16,
features_per_level: int = 2,
log2_hashmap_size: int = 19,
base_resolution: int = 16,
num_layers: int = 2,
hidden_dim: int = 64,
geo_feat_dim: int = 15,
num_layers_color: int = 3,
hidden_dim_color: int = 64,
use_appearance_embedding: bool = False,
disable_viewing_dependent: bool = False,
num_images: Optional[int] = None,
appearance_embedding_dim: int = 32,
contraction_type: ContractionType = ContractionType.UN_BOUNDED_SPHERE,
) -> None:
super().__init__()
self.aabb = Parameter(aabb, requires_grad=False)
self.geo_feat_dim = geo_feat_dim
self.contraction_type = contraction_type
self.use_appearance_embedding = use_appearance_embedding
if use_appearance_embedding:
assert num_images is not None
self.appearance_embedding_dim = appearance_embedding_dim
self.appearance_embedding = Embedding(num_images, appearance_embedding_dim)
self.direction_encoding = tcnn.Encoding(
n_input_dims=3,
encoding_config={
"otype": "SphericalHarmonics",
"degree": 4,
},
)
self.mlp_base = TemporalGridEncoder(
input_dim=3,
temporal_dim=temporal_dim,
num_levels=num_levels,
level_dim=features_per_level,
base_resolution=base_resolution,
log2_hashmap_size=log2_hashmap_size,
desired_resolution=1024 * (self.aabb.max() - self.aabb.min()),
)
self.mlp_base_decode = tcnn.Network(
n_input_dims=num_levels * features_per_level,
n_output_dims=1 + self.geo_feat_dim,
network_config={
"otype": "FullyFusedMLP",
"activation": "ReLU",
"output_activation": "None",
"n_neurons": hidden_dim,
"n_hidden_layers": num_layers - 1,
},
)
in_dim = self.direction_encoding.n_output_dims + self.geo_feat_dim
if disable_viewing_dependent:
in_dim = self.geo_feat_dim
self.direction_encoding = None
if self.use_appearance_embedding:
in_dim += self.appearance_embedding_dim
self.mlp_head = tcnn.Network(
n_input_dims=in_dim,
n_output_dims=3,
network_config={
"otype": "FullyFusedMLP",
"activation": "ReLU",
"output_activation": "Sigmoid",
"n_neurons": hidden_dim_color,
"n_hidden_layers": num_layers_color - 1,
},
)
def get_density(self, ray_samples: RaySamples):
positions = ray_samples.frustums.get_positions()
positions_flat = positions.view(-1, 3)
positions_flat = contract(x=positions_flat, roi=self.aabb, type=self.contraction_type)
assert ray_samples.times is not None, "Time should be included in the input for NeRFPlayer"
times_flat = ray_samples.times.view(-1, 1)
h = self.mlp_base(positions_flat, times_flat)
h = self.mlp_base_decode(h).view(*ray_samples.frustums.shape, -1)
density_before_activation, base_mlp_out = torch.split(h, [1, self.geo_feat_dim], dim=-1)
# Rectifying the density with an exponential is much more stable than a ReLU or
# softplus, because it enables high post-activation (float32) density outputs
# from smaller internal (float16) parameters.
density = trunc_exp(density_before_activation.to(positions))
return density, base_mlp_out
def get_outputs(self, ray_samples: RaySamples, density_embedding: Optional[TensorType] = None):
directions = get_normalized_directions(ray_samples.frustums.directions)
directions_flat = directions.view(-1, 3)
if self.direction_encoding is not None:
d = self.direction_encoding(directions_flat)
if density_embedding is None:
positions = SceneBox.get_normalized_positions(ray_samples.frustums.get_positions(), self.aabb)
h = torch.cat([d, positions.view(-1, 3)], dim=-1)
else:
h = torch.cat([d, density_embedding.view(-1, self.geo_feat_dim)], dim=-1)
else:
# viewing direction is disabled
if density_embedding is None:
positions = SceneBox.get_normalized_positions(ray_samples.frustums.get_positions(), self.aabb)
h = positions.view(-1, 3)
else:
h = density_embedding.view(-1, self.geo_feat_dim)
if self.use_appearance_embedding:
if ray_samples.camera_indices is None:
raise AttributeError("Camera indices are not provided.")
camera_indices = ray_samples.camera_indices.squeeze()
if self.training:
embedded_appearance = self.appearance_embedding(camera_indices)
else:
embedded_appearance = torch.zeros(
(*directions.shape[:-1], self.appearance_embedding_dim), device=directions.device
)
h = torch.cat([h, embedded_appearance.view(-1, self.appearance_embedding_dim)], dim=-1)
rgb = self.mlp_head(h).view(*ray_samples.frustums.directions.shape[:-1], -1).to(directions)
return {FieldHeadNames.RGB: rgb}
# pylint: disable=arguments-differ
def density_fn(self, positions: TensorType["bs":..., 3], times: TensorType["bs":..., 1]) -> TensorType["bs":..., 1]:
"""Returns only the density. Used primarily with the density grid.
Overwrite this function since density is time dependent now.
Args:
positions: the origin of the samples/frustums
times: the time of each position
"""
ray_samples = RaySamples(
frustums=Frustums(
origins=positions,
directions=torch.ones_like(positions),
starts=torch.zeros_like(positions[..., :1]),
ends=torch.zeros_like(positions[..., :1]),
pixel_area=torch.ones_like(positions[..., :1]),
),
times=times,
)
density, _ = self.get_density(ray_samples)
return density
def get_opacity(self, positions: TensorType["bs":..., 3], step_size, time_intervals=10) -> TensorType["bs":..., 1]:
"""Returns the opacity for a position and time. Used primarily by the occupancy grid.
This will return the maximum opacity of the points in the space in a dynamic sequence.
Args:
positions: the positions to evaluate the opacity at.
step_size: the step size to use for the opacity evaluation.
time_intervals: sample density on N time stamps
"""
# TODO: Converting opacity by time intervals is slow, and may lead to temporal artifacts.
# (Maybe random sample time and EMA?)
opacity = []
for t in range(0, time_intervals):
density = self.density_fn(positions, t / (time_intervals - 1) * torch.ones_like(positions)[..., [0]])
opacity.append(density * step_size)
opacity = torch.stack(opacity, dim=0).max(dim=0).values
return opacity
@@ -503,6 +503,8 @@ class VolumetricSampler(Sampler):
),
camera_indices=camera_indices,
)
if ray_bundle.times is not None:
ray_samples.times = ray_bundle.times[ray_indices]
return ray_samples, ray_indices
+251
View File
@@ -0,0 +1,251 @@
# Copyright 2022 The Nerfstudio Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
NeRFPlayer (https://arxiv.org/abs/2210.15947) implementation with nerfacto backbone.
"""
from __future__ import annotations
import functools
from dataclasses import dataclass, field
from typing import Dict, List, Type
import numpy as np
import torch
from torchmetrics import PeakSignalNoiseRatio
from torchmetrics.functional import structural_similarity_index_measure
from torchmetrics.image.lpip import LearnedPerceptualImagePatchSimilarity
from typing_extensions import Literal
from nerfstudio.cameras.rays import RayBundle
from nerfstudio.field_components.field_heads import FieldHeadNames
from nerfstudio.field_components.spatial_distortions import SceneContraction
from nerfstudio.fields.nerfplayer_nerfacto_field import (
NerfplayerNerfactoField,
TemporalHashMLPDensityField,
)
from nerfstudio.model_components.losses import (
MSELoss,
interlevel_loss,
orientation_loss,
pred_normal_loss,
)
from nerfstudio.model_components.ray_samplers import ProposalNetworkSampler
from nerfstudio.model_components.renderers import (
AccumulationRenderer,
DepthRenderer,
NormalsRenderer,
RGBRenderer,
)
from nerfstudio.model_components.scene_colliders import NearFarCollider
from nerfstudio.models.base_model import Model
from nerfstudio.models.nerfacto import NerfactoModel, NerfactoModelConfig
@dataclass
class NerfplayerNerfactoModelConfig(NerfactoModelConfig):
"""Nerfplayer Model Config with Nerfacto backbone"""
_target: Type = field(default_factory=lambda: NerfplayerNerfactoModel)
near_plane: float = 0.05
"""How far along the ray to start sampling."""
far_plane: float = 1000.0
"""How far along the ray to stop sampling."""
background_color: Literal["random", "last_sample"] = "random"
"""Whether to randomize the background color. (Random is reported to be better on DyCheck.)"""
num_levels: int = 16
"""Hashing grid parameter."""
features_per_level: int = 2
"""Hashing grid parameter."""
log2_hashmap_size: int = 18
"""Hashing grid parameter."""
temporal_dim: int = 32
"""Hashing grid parameter. A higher temporal dim means a higher temporal frequency."""
proposal_net_args_list: List[Dict] = field(
default_factory=lambda: [
{"hidden_dim": 16, "temporal_dim": 32, "log2_hashmap_size": 17, "num_levels": 5, "max_res": 64},
{"hidden_dim": 16, "temporal_dim": 32, "log2_hashmap_size": 17, "num_levels": 5, "max_res": 256},
]
)
"""Arguments for the proposal density fields."""
distortion_loss_mult: float = 1e-2
"""Distortion loss multiplier."""
temporal_tv_weight: float = 1
"""Temporal TV balancing weight for feature channels."""
depth_weight: float = 1e-1
"""depth loss balancing weight for feature channels."""
class NerfplayerNerfactoModel(NerfactoModel):
"""Nerfplayer model with Nerfacto backbone.
Args:
config: Nerfplayer configuration to instantiate model
"""
config: NerfplayerNerfactoModelConfig
def populate_modules(self):
"""Set the fields and modules."""
Model.populate_modules(self)
scene_contraction = SceneContraction(order=float("inf"))
# Fields
self.field = NerfplayerNerfactoField(
self.scene_box.aabb,
temporal_dim=self.config.temporal_dim,
num_levels=self.config.num_levels,
features_per_level=self.config.features_per_level,
log2_hashmap_size=self.config.log2_hashmap_size,
spatial_distortion=scene_contraction,
num_images=self.num_train_data,
use_pred_normals=self.config.predict_normals,
use_average_appearance_embedding=self.config.use_average_appearance_embedding,
)
self.density_fns = []
num_prop_nets = self.config.num_proposal_iterations
# Build the proposal network(s)
self.proposal_networks = torch.nn.ModuleList()
if self.config.use_same_proposal_network:
assert len(self.config.proposal_net_args_list) == 1, "Only one proposal network is allowed."
prop_net_args = self.config.proposal_net_args_list[0]
network = TemporalHashMLPDensityField(
self.scene_box.aabb, spatial_distortion=scene_contraction, **prop_net_args
)
self.proposal_networks.append(network)
self.density_fns.extend([network.density_fn for _ in range(num_prop_nets)])
else:
for i in range(num_prop_nets):
prop_net_args = self.config.proposal_net_args_list[min(i, len(self.config.proposal_net_args_list) - 1)]
network = TemporalHashMLPDensityField(
self.scene_box.aabb,
spatial_distortion=scene_contraction,
**prop_net_args,
)
self.proposal_networks.append(network)
self.density_fns.extend([network.density_fn for network in self.proposal_networks])
# Samplers
update_schedule = lambda step: np.clip(
np.interp(step, [0, self.config.proposal_warmup], [0, self.config.proposal_update_every]),
1,
self.config.proposal_update_every,
)
self.proposal_sampler = ProposalNetworkSampler(
num_nerf_samples_per_ray=self.config.num_nerf_samples_per_ray,
num_proposal_samples_per_ray=self.config.num_proposal_samples_per_ray,
num_proposal_network_iterations=self.config.num_proposal_iterations,
single_jitter=self.config.use_single_jitter,
update_sched=update_schedule,
)
# Collider
self.collider = NearFarCollider(near_plane=self.config.near_plane, far_plane=self.config.far_plane)
# renderers
self.renderer_rgb = RGBRenderer(background_color=self.config.background_color)
self.renderer_accumulation = AccumulationRenderer()
self.renderer_depth = DepthRenderer(method="expected") # for depth loss
self.renderer_normals = NormalsRenderer()
# losses
self.rgb_loss = MSELoss()
# metrics
self.psnr = PeakSignalNoiseRatio(data_range=1.0)
self.ssim = structural_similarity_index_measure
self.lpips = LearnedPerceptualImagePatchSimilarity()
self.temporal_distortion = True # for viewer
def get_outputs(self, ray_bundle: RayBundle):
assert ray_bundle.times is not None, "Time not provided."
ray_samples, weights_list, ray_samples_list = self.proposal_sampler(
ray_bundle, density_fns=[functools.partial(f, times=ray_bundle.times) for f in self.density_fns]
)
field_outputs = self.field(ray_samples, compute_normals=self.config.predict_normals)
weights = ray_samples.get_weights(field_outputs[FieldHeadNames.DENSITY])
weights_list.append(weights)
ray_samples_list.append(ray_samples)
rgb = self.renderer_rgb(rgb=field_outputs[FieldHeadNames.RGB], weights=weights)
depth = self.renderer_depth(weights=weights, ray_samples=ray_samples)
accumulation = self.renderer_accumulation(weights=weights)
outputs = {
"rgb": rgb,
"accumulation": accumulation,
"depth": depth,
}
if self.config.predict_normals:
outputs["normals"] = self.renderer_normals(normals=field_outputs[FieldHeadNames.NORMALS], weights=weights)
outputs["pred_normals"] = self.renderer_normals(field_outputs[FieldHeadNames.PRED_NORMALS], weights=weights)
# These use a lot of GPU memory, so we avoid storing them for eval.
if self.training:
outputs["weights_list"] = weights_list
outputs["ray_samples_list"] = ray_samples_list
if self.training and self.config.predict_normals:
outputs["rendered_orientation_loss"] = orientation_loss(
weights.detach(), field_outputs[FieldHeadNames.NORMALS], ray_bundle.directions
)
outputs["rendered_pred_normal_loss"] = pred_normal_loss(
weights.detach(),
field_outputs[FieldHeadNames.NORMALS].detach(),
field_outputs[FieldHeadNames.PRED_NORMALS],
)
for i in range(self.config.num_proposal_iterations):
outputs[f"prop_depth_{i}"] = self.renderer_depth(weights=weights_list[i], ray_samples=ray_samples_list[i])
return outputs
def get_loss_dict(self, outputs, batch, metrics_dict=None):
loss_dict = {}
image = batch["image"].to(self.device)
loss_dict["rgb_loss"] = self.rgb_loss(image, outputs["rgb"])
if self.training:
loss_dict["interlevel_loss"] = self.config.interlevel_loss_mult * interlevel_loss(
outputs["weights_list"], outputs["ray_samples_list"]
)
assert metrics_dict is not None and "distortion" in metrics_dict
loss_dict["distortion_loss"] = self.config.distortion_loss_mult * metrics_dict["distortion"]
if self.config.predict_normals:
# orientation loss for computed normals
loss_dict["orientation_loss"] = self.config.orientation_loss_mult * torch.mean(
outputs["rendered_orientation_loss"]
)
# ground truth supervision for normals
loss_dict["pred_normal_loss"] = self.config.pred_normal_loss_mult * torch.mean(
outputs["rendered_pred_normal_loss"]
)
if "depth_image" in batch.keys() and self.config.depth_weight > 0:
mask = (batch["depth_image"] != 0).view([-1])
loss_dict["depth_loss"] = 0
l = lambda x: self.config.depth_weight * (x - batch["depth_image"][mask]).pow(2).mean()
loss_dict["depth_loss"] = l(outputs["depth"][mask])
for i in range(self.config.num_proposal_iterations):
loss_dict["depth_loss"] += l(outputs[f"prop_depth_{i}"][mask])
if self.config.temporal_tv_weight > 0:
loss_dict["temporal_tv_loss"] = self.field.mlp_base.get_temporal_tv_loss()
for net in self.proposal_networks:
loss_dict["temporal_tv_loss"] += net.encoding.get_temporal_tv_loss()
loss_dict["temporal_tv_loss"] *= self.config.temporal_tv_weight
return loss_dict
+241
View File
@@ -0,0 +1,241 @@
# Copyright 2022 The Nerfstudio Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Implementation of NeRFPlayer (https://arxiv.org/abs/2210.15947) with InstantNGP backbone.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Type
import nerfacc
import torch
from nerfacc import ContractionType
from torch.nn import Parameter
from torchmetrics import PeakSignalNoiseRatio
from torchmetrics.functional import structural_similarity_index_measure
from torchmetrics.image.lpip import LearnedPerceptualImagePatchSimilarity
from typing_extensions import Literal
from nerfstudio.cameras.rays import RayBundle
from nerfstudio.field_components.field_heads import FieldHeadNames
from nerfstudio.fields.nerfplayer_ngp_field import NerfplayerNGPField
from nerfstudio.model_components.losses import MSELoss
from nerfstudio.model_components.ray_samplers import VolumetricSampler
from nerfstudio.model_components.renderers import (
AccumulationRenderer,
DepthRenderer,
RGBRenderer,
)
from nerfstudio.models.base_model import Model
from nerfstudio.models.instant_ngp import InstantNGPModelConfig, NGPModel
from nerfstudio.utils import colors
@dataclass
class NerfplayerNGPModelConfig(InstantNGPModelConfig):
"""NeRFPlayer Model Config with InstantNGP backbone.
Tips for tuning the performance:
1. If the scene is flickering, this is caused by unwanted high-freq on the temporal dimension.
Try reducing `temporal_dim` first, but don't be too small, otherwise the dynamic object is blurred.
Then try increasing the `temporal_tv_weight`. This is the loss for promoting smoothness among the
temporal channels.
2. If a faster rendering is preferred, then try reducing `log2_hashmap_size`. If more details are
wanted, try increasing `log2_hashmap_size`.
3. If the input cameras are of limited numbers, try reducing `num_levels`. `num_levels` is for
multi-resolution volume sampling, and has a similar behavior to the freq in NeRF. With a small
`num_levels`, a blurred rendering will be generated, but it is unlikely to overfit the training views.
"""
_target: Type = field(default_factory=lambda: NerfplayerNGPModel)
temporal_dim: int = 64
"""Hashing grid parameter. A higher temporal dim means a higher temporal frequency."""
num_levels: int = 16
"""Hashing grid parameter."""
features_per_level: int = 2
"""Hashing grid parameter."""
log2_hashmap_size: int = 17
"""Hashing grid parameter."""
base_resolution: int = 16
"""Hashing grid parameter."""
temporal_tv_weight: float = 1
"""Temporal TV loss balancing weight for feature channels."""
depth_weight: float = 1e-1
"""depth loss balancing weight for feature channels."""
train_background_color: Literal["random", "black", "white"] = "random"
"""The training background color that is given to untrained areas."""
eval_background_color: Literal["random", "black", "white"] = "white"
"""The training background color that is given to untrained areas."""
disable_viewing_dependent: bool = True
"""Disable viewing dependent effects."""
class NerfplayerNGPModel(NGPModel):
"""NeRFPlayer Model with InstantNGP backbone.
Args:
config: NeRFPlayer NGP configuration to instantiate model
"""
config: NerfplayerNGPModelConfig
field: NerfplayerNGPField
def populate_modules(self):
"""Set the fields and modules."""
Model.populate_modules(self)
self.field = NerfplayerNGPField(
aabb=self.scene_box.aabb,
contraction_type=self.config.contraction_type,
use_appearance_embedding=self.config.use_appearance_embedding,
num_images=self.num_train_data,
temporal_dim=self.config.temporal_dim,
num_levels=self.config.num_levels,
features_per_level=self.config.features_per_level,
log2_hashmap_size=self.config.log2_hashmap_size,
base_resolution=self.config.base_resolution,
disable_viewing_dependent=self.config.disable_viewing_dependent,
)
self.scene_aabb = Parameter(self.scene_box.aabb.flatten(), requires_grad=False)
# Occupancy Grid
self.occupancy_grid = nerfacc.OccupancyGrid(
roi_aabb=self.scene_aabb,
resolution=self.config.grid_resolution,
contraction_type=self.config.contraction_type,
)
# Sampler
vol_sampler_aabb = self.scene_box.aabb if self.config.contraction_type == ContractionType.AABB else None
self.sampler = VolumetricSampler(
scene_aabb=vol_sampler_aabb,
occupancy_grid=self.occupancy_grid,
density_fn=self.field.density_fn,
) # need to update the density_fn later during forward (for input time)
# renderers
self.train_background_color = self.config.train_background_color
self.eval_background_color = self.config.eval_background_color
if self.config.train_background_color in ["white", "black"]:
self.train_background_color = colors.COLORS_DICT[self.config.train_background_color]
if self.config.eval_background_color in ["white", "black"]:
self.eval_background_color = colors.COLORS_DICT[self.config.eval_background_color]
self.renderer_rgb = RGBRenderer() # will update bgcolor later during forward
self.renderer_accumulation = AccumulationRenderer()
self.renderer_depth = DepthRenderer(method="expected")
# losses
self.rgb_loss = MSELoss()
# metrics
self.psnr = PeakSignalNoiseRatio(data_range=1.0)
self.ssim = structural_similarity_index_measure
self.lpips = LearnedPerceptualImagePatchSimilarity()
self.temporal_distortion = True # for viewer
def get_outputs(self, ray_bundle: RayBundle):
num_rays = len(ray_bundle)
# update the density_fn of the sampler so that the density is time aware
self.sampler.density_fn = lambda x: self.field.density_fn(x, ray_bundle.times)
with torch.no_grad():
ray_samples, ray_indices = self.sampler(
ray_bundle=ray_bundle,
near_plane=self.config.near_plane,
far_plane=self.config.far_plane,
render_step_size=self.config.render_step_size,
cone_angle=self.config.cone_angle,
)
field_outputs = self.field(ray_samples)
# accumulation
packed_info = nerfacc.pack_info(ray_indices, num_rays)
weights = nerfacc.render_weight_from_density(
packed_info=packed_info,
sigmas=field_outputs[FieldHeadNames.DENSITY],
t_starts=ray_samples.frustums.starts,
t_ends=ray_samples.frustums.ends,
)
# update bgcolor in the renderer; usually random color for training and fixed color for inference
if self.training:
self.renderer_rgb.background_color = self.train_background_color
else:
self.renderer_rgb.background_color = self.eval_background_color
rgb = self.renderer_rgb(
rgb=field_outputs[FieldHeadNames.RGB],
weights=weights,
ray_indices=ray_indices,
num_rays=num_rays,
)
depth = self.renderer_depth(
weights=weights, ray_samples=ray_samples, ray_indices=ray_indices, num_rays=num_rays
)
accumulation = self.renderer_accumulation(weights=weights, ray_indices=ray_indices, num_rays=num_rays)
alive_ray_mask = accumulation.squeeze(-1) > 0
outputs = {
"rgb": rgb,
"accumulation": accumulation,
"depth": depth,
"alive_ray_mask": alive_ray_mask, # the rays we kept from sampler
"num_samples_per_ray": packed_info[:, 1],
}
# adding them to outputs for calculating losses
if self.training and self.config.depth_weight > 0:
outputs["ray_indices"] = ray_indices
outputs["ray_samples"] = ray_samples
outputs["weights"] = weights
outputs["sigmas"] = field_outputs[FieldHeadNames.DENSITY]
return outputs
def get_loss_dict(self, outputs, batch, metrics_dict=None):
image = batch["image"].to(self.device)
mask = outputs["alive_ray_mask"]
rgb_loss = self.rgb_loss(image[mask], outputs["rgb"][mask])
loss_dict = {"rgb_loss": rgb_loss}
if "depth_image" in batch.keys() and self.config.depth_weight > 0:
mask = batch["depth_image"] != 0
# First we calculate the depth value, just like most of the papers.
loss_dict["depth_loss"] = (outputs["depth"][mask] - batch["depth_image"][mask]).abs().mean()
# But this is not enough -- it will lead to fog like reconstructions, even with depth supervision.
# Because this loss only cares about the mean value.
# (Feel free to try it and see the fog-like effects on DyCheck by commenting out the following loss.)
# > The fog can be effectively penalized by multiview inputs (i.e., from another viewing point).
# > However, it is hard to get multiple views under the setting of dynamic scenes.
# > A new view always indicates another camera, which is expensive and brings sync problems.
# In DyCheck (https://arxiv.org/abs/2210.13445), surface sparsity regularizer
# (i.e., `distortion_loss` in nerfstudio) is used to make surface tight.
# The `distortion_loss` can be used here as well, but personally find it hard to implement...
# (Due to volume sampling, seems that cuda kernels are needed for efficiently computing the loss.)
# (Try nerfplayer with nerfacto backbone for distortion loss.)
# Instead, directly penalizing the empty space is found effective here. Perhaps due to a more
# "clear" loss (as it is directly applied to the network outputs, rather than post-processed values).
# But such a loss also has drawbacks: it tends to overfit wrong (or noise) presented in the depth map.
# Some structures are floating in the air with this loss...
gt_depth_packed = batch["depth_image"][outputs["ray_indices"]]
steps = (outputs["ray_samples"].frustums.starts + outputs["ray_samples"].frustums.ends) / 2
# empty area should not be too close to the given depth, so lets add a margin to the gt depth
margin = (self.scene_box.aabb.max() - self.scene_box.aabb.min()) / 128
density_min_mask = (gt_depth_packed - steps > margin) & (gt_depth_packed != 0)
loss_dict["depth_loss"] += (outputs["sigmas"][density_min_mask[..., 0]].pow(2)).mean() * 1e-2
loss_dict["depth_loss"] *= self.config.depth_weight
if self.config.temporal_tv_weight > 0:
loss_dict["temporal_tv_loss"] = self.config.temporal_tv_weight * self.field.mlp_base.get_temporal_tv_loss()
return loss_dict
+26 -13
View File
@@ -196,11 +196,16 @@ class CheckThread(threading.Thread):
while not self.state.check_done_render:
# check camera
data = self.state.vis["renderingState/camera"].read()
render_time = self.state.vis["renderingState/render_time"].read()
if data is not None:
camera_object = data["object"]
if self.state.prev_camera_matrix is None or (
not np.allclose(camera_object["matrix"], self.state.prev_camera_matrix)
and not self.state.prev_moving
if (
self.state.prev_camera_matrix is None
or (
not np.allclose(camera_object["matrix"], self.state.prev_camera_matrix)
and not self.state.prev_moving
)
or (render_time is not None and render_time != self.state.prev_render_time)
):
self.state.check_interrupt_vis = True
self.state.prev_moving = True
@@ -280,6 +285,7 @@ class ViewerState:
# viewer specific variables
self.prev_camera_matrix = None
self.prev_render_time = 0
self.prev_output_type = OutputTypes.INIT
self.prev_colormap_type = ColormapTypes.INIT
self.prev_moving = False
@@ -553,12 +559,23 @@ class ViewerState:
return None
camera_object = data["object"]
render_time = self.vis["renderingState/render_time"].read()
if self.prev_camera_matrix is not None and np.allclose(camera_object["matrix"], self.prev_camera_matrix):
self.camera_moving = False
if render_time is not None:
if (
self.prev_camera_matrix is not None and np.allclose(camera_object["matrix"], self.prev_camera_matrix)
) and (self.prev_render_time == render_time):
self.camera_moving = False
else:
self.prev_camera_matrix = camera_object["matrix"]
self.prev_render_time = render_time
self.camera_moving = True
else:
self.prev_camera_matrix = camera_object["matrix"]
self.camera_moving = True
if self.prev_camera_matrix is not None and np.allclose(camera_object["matrix"], self.prev_camera_matrix):
self.camera_moving = False
else:
self.prev_camera_matrix = camera_object["matrix"]
self.camera_moving = True
output_type = self.vis["renderingState/output_choice"].read()
if output_type is None:
@@ -901,10 +918,6 @@ class ViewerState:
dim=0,
)
times = self.vis["renderingState/render_time"].read()
if times is not None:
times = torch.tensor([float(times)])
camera_type_msg = camera_object["camera_type"]
if camera_type_msg == "perspective":
camera_type = CameraType.PERSPECTIVE
@@ -922,11 +935,11 @@ class ViewerState:
cy=intrinsics_matrix[1, 2],
camera_type=camera_type,
camera_to_worlds=camera_to_world[None, ...],
times=times,
times=torch.tensor([float(self.prev_render_time)]),
)
camera = camera.to(graph.device)
camera_ray_bundle = camera.generate_rays(camera_indices=0, aabb_box=graph.render_aabb)
camera_ray_bundle = camera.generate_rays(camera_indices=0)
graph.eval()
+2
View File
@@ -23,6 +23,8 @@ BLACKLIST = [
"nerfacto",
"phototourism",
"depth-nerfacto",
"nerfplayer-ngp",
"nerfplayer-nerfacto",
]