mirror of
https://github.com/nerfstudio-project/nerfstudio.git
synced 2026-11-11 18:44:21 +08:00
405 lines
12 KiB
Python
405 lines
12 KiB
Python
# Copyright 2022 The Plenoptix 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.
|
|
|
|
"""Base Configs"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List, Literal, Optional, Tuple, Type
|
|
|
|
import torch
|
|
|
|
from nerfactory.configs.utils import to_immutable_dict
|
|
from nerfactory.dataloaders.base import VanillaDataManager
|
|
from nerfactory.dataloaders.data_parsers import (
|
|
Blender,
|
|
DataParser,
|
|
Friends,
|
|
InstantNGP,
|
|
Mipnerf360,
|
|
Nerfactory,
|
|
Record3D,
|
|
)
|
|
from nerfactory.models.base import Model
|
|
from nerfactory.models.instant_ngp import NGPModel
|
|
from nerfactory.models.nerfw import NerfWModel
|
|
from nerfactory.models.tensorf import TensoRFModel
|
|
from nerfactory.optimizers.schedulers import ExponentialDecaySchedule
|
|
from nerfactory.pipelines.base import Pipeline
|
|
from nerfactory.utils import writer
|
|
|
|
|
|
# Pretty printing class
|
|
class PrintableConfig: # pylint: disable=too-few-public-methods
|
|
"""Printable Config defining str function"""
|
|
|
|
def __str__(self):
|
|
lines = [self.__class__.__name__ + ":"]
|
|
for key, val in vars(self).items():
|
|
if isinstance(val, Tuple):
|
|
flattened_val = "["
|
|
for item in val:
|
|
flattened_val += str(item) + "\n"
|
|
flattened_val = flattened_val.rstrip("\n")
|
|
val = flattened_val + "]"
|
|
lines += f"{key}: {str(val)}".split("\n")
|
|
return "\n ".join(lines)
|
|
|
|
|
|
# Base instantiate configs
|
|
@dataclass
|
|
class InstantiateConfig(PrintableConfig): # pylint: disable=too-few-public-methods
|
|
"""Config class for instantiating an the class specified in the _target attribute."""
|
|
|
|
_target: Type
|
|
|
|
def setup(self, **kwargs) -> Any:
|
|
"""Returns the instantiated object using the config."""
|
|
return self._target(self, **kwargs)
|
|
|
|
|
|
# Machine related configs
|
|
@dataclass
|
|
class MachineConfig(PrintableConfig):
|
|
"""Configuration of machine setup"""
|
|
|
|
seed: int = 42
|
|
num_gpus: int = 1
|
|
num_machines: int = 1
|
|
machine_rank: int = 0
|
|
dist_url: str = "auto"
|
|
|
|
|
|
# Logging related configs
|
|
@dataclass
|
|
class TensorboardWriterConfig(InstantiateConfig):
|
|
"""Tensorboard Writer config"""
|
|
|
|
_target: Type = writer.TensorboardWriter
|
|
enable: bool = False
|
|
relative_log_dir: Path = Path("./")
|
|
log_dir: Optional[Path] = None # full log dir path to be dynamically set
|
|
|
|
|
|
@dataclass
|
|
class WandbWriterConfig(InstantiateConfig):
|
|
"""WandDB Writer config"""
|
|
|
|
_target: Type = writer.WandbWriter
|
|
enable: bool = False
|
|
relative_log_dir: Path = Path("./")
|
|
log_dir: Optional[Path] = None # full log dir path to be dynamically set
|
|
|
|
|
|
@dataclass
|
|
class LocalWriterConfig(InstantiateConfig):
|
|
"""Local Writer config"""
|
|
|
|
_target: Type = writer.LocalWriter
|
|
enable: bool = False
|
|
stats_to_track: Tuple[writer.EventName, ...] = (
|
|
writer.EventName.ITER_LOAD_TIME,
|
|
writer.EventName.ITER_TRAIN_TIME,
|
|
writer.EventName.RAYS_PER_SEC,
|
|
writer.EventName.CURR_TEST_PSNR,
|
|
)
|
|
max_log_size: int = 10
|
|
relative_log_dir: Path = Path("./")
|
|
log_dir: Optional[Path] = None # full log dir path to be dynamically set
|
|
|
|
def setup(self, banner_messages: Optional[List[str]] = None, **kwargs) -> Any:
|
|
"""Instantiate local writer
|
|
|
|
Args:
|
|
banner_messages: List of strings that always print at the bottom of screen. Defaults to None.
|
|
"""
|
|
return self._target(self, banner_messages=banner_messages, **kwargs)
|
|
|
|
|
|
@dataclass
|
|
class LoggingConfig(PrintableConfig):
|
|
"""Configuration of loggers and profilers"""
|
|
|
|
steps_per_log: int = 10
|
|
max_buffer_size: int = 20
|
|
writer: Tuple[Any, ...] = (
|
|
TensorboardWriterConfig(enable=True),
|
|
WandbWriterConfig(enable=False),
|
|
LocalWriterConfig(enable=True),
|
|
)
|
|
# profiler logs run times of functions and prints at end of training
|
|
enable_profiler: bool = True
|
|
|
|
|
|
# Trainer related configs
|
|
@dataclass
|
|
class TrainerConfig(PrintableConfig):
|
|
"""Configuration for training regimen"""
|
|
|
|
steps_per_save: int = 1000
|
|
steps_per_test: int = 500
|
|
max_num_iterations: int = 1000000
|
|
mixed_precision: bool = False
|
|
relative_model_dir: Path = Path("nerfactory_models/")
|
|
model_dir: Optional[Path] = None # full model dir path to be dynamically set
|
|
# optional parameters if we want to resume training
|
|
load_dir: Optional[Path] = None
|
|
load_step: Optional[int] = None
|
|
load_config: Optional[Path] = None
|
|
|
|
|
|
# Dataset related configs
|
|
@dataclass
|
|
class DataParserConfig(InstantiateConfig):
|
|
"""Basic dataset config"""
|
|
|
|
_target: Type = DataParser
|
|
|
|
|
|
@dataclass
|
|
class NerfactoryDataParserConfig(DataParserConfig):
|
|
"""Nerfactory dataset config"""
|
|
|
|
_target: Type = Nerfactory
|
|
data_directory: Path = Path("data/ours/posterv2")
|
|
scale_factor: float = 1.0
|
|
downscale_factor: int = 1
|
|
scene_scale: float = 0.33
|
|
|
|
|
|
@dataclass
|
|
class BlenderDataParserConfig(DataParserConfig):
|
|
"""Blender dataset config"""
|
|
|
|
_target: Type = Blender
|
|
data_directory: Path = Path("data/blender/lego")
|
|
scale_factor: float = 1.0
|
|
alpha_color: str = "white"
|
|
downscale_factor: int = 1
|
|
|
|
|
|
@dataclass
|
|
class FriendsDataParserConfig(DataParserConfig):
|
|
"""Friends dataset config"""
|
|
|
|
_target: Type = Friends
|
|
data_directory: Path = Path("data/friends/TBBT-big_living_room")
|
|
|
|
|
|
@dataclass
|
|
class MipNerf360DataParserConfig(DataParserConfig):
|
|
"""Mipnerf 360 dataset config"""
|
|
|
|
_target: Type = Mipnerf360
|
|
data_directory: Path = Path("data/mipnerf_360/garden")
|
|
downscale_factor: int = 1
|
|
val_skip: int = 8
|
|
auto_scale: bool = True
|
|
aabb_scale = 4
|
|
|
|
|
|
@dataclass
|
|
class InstantNGPDataParserConfig(DataParserConfig):
|
|
"""Instant-NGP dataset config"""
|
|
|
|
_target: Type = InstantNGP
|
|
data_directory: Path = Path("data/ours/posterv2")
|
|
scale_factor: float = 1.0
|
|
downscale_factor: int = 1
|
|
scene_scale: float = 0.33
|
|
|
|
|
|
@dataclass
|
|
class Record3DDataParserConfig(DataParserConfig):
|
|
"""Mipnerf 360 dataset config"""
|
|
|
|
_target: Type = Record3D
|
|
data_directory: Path = Path("data/record3d/garden")
|
|
downscale_factor: int = 1
|
|
val_skip: int = 8
|
|
aabb_scale = 4.0
|
|
max_dataset_size: int = 150
|
|
|
|
|
|
@dataclass
|
|
class VanillaDataManagerConfig(InstantiateConfig):
|
|
"""Configuration for data manager instantiation"""
|
|
|
|
_target: Type = VanillaDataManager
|
|
train_dataset: DataParserConfig = BlenderDataParserConfig()
|
|
image_dataset_type: str = "rgb"
|
|
train_num_rays_per_batch: int = 1024
|
|
train_num_images_to_sample_from: int = -1
|
|
eval_dataset: Optional[InstantiateConfig] = None
|
|
eval_image_indices: Optional[Tuple[int, ...]] = (0,)
|
|
eval_num_rays_per_chunk: int = 4096
|
|
|
|
|
|
@dataclass
|
|
class FriendsDataManagerConfig(VanillaDataManagerConfig):
|
|
"""Friends data manager config"""
|
|
|
|
_target: Type = VanillaDataManager
|
|
train_dataset: DataParserConfig = FriendsDataParserConfig()
|
|
image_dataset_type: str = "panoptic"
|
|
|
|
|
|
# Model related configs
|
|
@dataclass
|
|
class ModelConfig(InstantiateConfig):
|
|
"""Configuration for model instantiation"""
|
|
|
|
_target: Type = Model
|
|
enable_collider: bool = True
|
|
collider_params: Dict[str, float] = to_immutable_dict({"near_plane": 2.0, "far_plane": 6.0})
|
|
loss_coefficients: Dict[str, float] = to_immutable_dict({"rgb_loss_coarse": 1.0, "rgb_loss_fine": 1.0})
|
|
num_coarse_samples: int = 64
|
|
num_importance_samples: int = 128
|
|
field_implementation: Literal["torch", "tcnn"] = "torch"
|
|
enable_density_field: bool = False
|
|
density_field_params: Dict[str, Any] = to_immutable_dict(
|
|
{
|
|
"center": 0.0, # simply set it as the center of the scene bbox
|
|
"base_scale": 3.0, # simply set it as the scale of the scene bbox
|
|
"num_cascades": 1, # if using more than 1 cascade, the `base_scale` can be smaller than scene scale.
|
|
"resolution": 128,
|
|
"update_every_num_iters": 16,
|
|
}
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class InstantNGPModelConfig(ModelConfig):
|
|
"""Instant NGP Model Config"""
|
|
|
|
_target: Type = NGPModel
|
|
enable_density_field: bool = True
|
|
enable_collider: bool = False
|
|
field_implementation: Literal["torch", "tcnn"] = "tcnn" # torch, tcnn, ...
|
|
loss_coefficients: Dict[str, float] = to_immutable_dict({"rgb_loss": 1.0})
|
|
num_samples: int = 1024 # instead of course/fine samples
|
|
|
|
|
|
@dataclass
|
|
class NerfWModelConfig(ModelConfig):
|
|
"""NerfW model config"""
|
|
|
|
_target: Type = NerfWModel
|
|
loss_coefficients: Dict[str, float] = to_immutable_dict(
|
|
{"rgb_loss_coarse": 1.0, "rgb_loss_fine": 1.0, "uncertainty_loss": 1.0, "density_loss": 0.01}
|
|
)
|
|
num_coarse_samples: int = 64
|
|
num_importance_samples: int = 64
|
|
uncertainty_min: float = 0.03
|
|
|
|
|
|
@dataclass
|
|
class TensoRFModelConfig(ModelConfig):
|
|
"""TensoRF model config"""
|
|
|
|
_target: Type = TensoRFModel
|
|
init_resolution: int = 128
|
|
final_resolution: int = 200
|
|
upsampling_iters: Tuple[int, ...] = (5000, 5500, 7000)
|
|
loss_coefficients: Dict[str, float] = to_immutable_dict({"rgb_loss_coarse": 1.0, "feature_loss": 8e-5})
|
|
|
|
|
|
# Pipeline related configs
|
|
@dataclass
|
|
class PipelineConfig(InstantiateConfig):
|
|
"""Configuration for pipeline instantiation"""
|
|
|
|
_target: Type = Pipeline
|
|
data_manager: VanillaDataManagerConfig = VanillaDataManagerConfig()
|
|
model: ModelConfig = ModelConfig()
|
|
|
|
|
|
# Viewer related configs
|
|
@dataclass
|
|
class ViewerConfig(PrintableConfig):
|
|
"""Configuration for viewer instantiation"""
|
|
|
|
enable: bool = False
|
|
zmq_url: str = "tcp://127.0.0.1:6000"
|
|
launch_bridge_server: bool = True
|
|
websocket_port: int = 7007
|
|
min_render_image_height: int = 64
|
|
max_render_image_height: int = 1024
|
|
num_rays_per_chunk: int = 4096
|
|
|
|
|
|
# Optimizer related configs
|
|
@dataclass
|
|
class OptimizerConfig(InstantiateConfig):
|
|
"""Basic optimizer config with RAdam"""
|
|
|
|
_target: Type = torch.optim.RAdam
|
|
lr: float = 0.0005
|
|
eps: float = 1e-08
|
|
|
|
# TODO: somehow make this more generic. i dont like the idea of overriding the setup function
|
|
# but also not sure how to go about passing things into predefined torch objects.
|
|
def setup(self, params=None, **kwargs) -> Any:
|
|
"""Returns the instantiated object using the config."""
|
|
return self._target(params, lr=self.lr, eps=self.eps)
|
|
|
|
|
|
@dataclass
|
|
class SchedulerConfig(InstantiateConfig):
|
|
"""Basic scheduler config with self-defined exponential decay schedule"""
|
|
|
|
_target: Type = ExponentialDecaySchedule
|
|
lr_final: float = 0.000005
|
|
max_steps: int = 1000000
|
|
|
|
# TODO: somehow make this more generic. i dont like the idea of overriding the setup function
|
|
# but also not sure how to go about passing things into predefined torch objects.
|
|
def setup(self, optimizer=None, lr_init=None, **kwargs) -> Any:
|
|
"""Returns the instantiated object using the config."""
|
|
return self._target(optimizer, lr_init, self.lr_final, self.max_steps)
|
|
|
|
|
|
@dataclass
|
|
class Config(PrintableConfig):
|
|
"""Full config contents"""
|
|
|
|
experiment_name: str = "blender_lego"
|
|
method_name: str = "base_method"
|
|
base_dir: Optional[Path] = None # base dir path to be dynamically set
|
|
machine: MachineConfig = MachineConfig()
|
|
logging: LoggingConfig = LoggingConfig()
|
|
trainer: TrainerConfig = TrainerConfig()
|
|
pipeline: PipelineConfig = PipelineConfig()
|
|
optimizers: Dict[str, Any] = to_immutable_dict(
|
|
{
|
|
"fields": {
|
|
"optimizer": OptimizerConfig(),
|
|
"scheduler": SchedulerConfig(),
|
|
}
|
|
}
|
|
)
|
|
viewer: ViewerConfig = ViewerConfig()
|
|
|
|
def __post_init__(self):
|
|
"""Convert logging directories to more specific filepaths"""
|
|
dt_str = datetime.now().strftime("%Y-%m-%d_%H%M%S")
|
|
self.base_dir = Path(f"outputs/{self.experiment_name}/{self.method_name}/{dt_str}")
|
|
if self.trainer.model_dir is None:
|
|
self.trainer.model_dir = self.base_dir / self.trainer.relative_model_dir
|
|
for curr_writer in self.logging.writer:
|
|
curr_writer.log_dir = self.base_dir / curr_writer.relative_log_dir
|