From 004d8ca9d24b294b1877d4d5599879c4ce812bc7 Mon Sep 17 00:00:00 2001 From: Matthew Tancik Date: Fri, 23 Jun 2023 17:56:11 -0700 Subject: [PATCH 01/44] Update nerfacto.md (#2126) --- docs/nerfology/methods/nerfacto.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/nerfology/methods/nerfacto.md b/docs/nerfology/methods/nerfacto.md index 63684848f..e835176dd 100644 --- a/docs/nerfology/methods/nerfacto.md +++ b/docs/nerfology/methods/nerfacto.md @@ -10,11 +10,12 @@ ns-train nerfacto --help We provide a few additional variants: -| Method | Description | Memory | Speed | -| ---------------- | --------------------- | ------ | ------ | -| `nerfacto` | Default Model | ~6GB | Fast | -| `nerfacto-big` | Larger higher quality | ~12GB | Slower | -| `depth-nerfacto` | Supervise on depth | ~6GB | Fast | +| Method | Description | Memory | Speed | +| ---------------- | ------------------------------ | ------ | ------- | +| `nerfacto` | Default Model | ~6GB | Fast | +| `nerfacto-big` | Larger higher quality | ~12GB | Slower | +| `nerfacto-huge` | Even larger and higher quality | ~24GB | Slowest | +| `depth-nerfacto` | Supervise on depth | ~6GB | Fast | ## Method From ac6a83cc53b19408afc51f12c9e2e2b64134893f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Kulh=C3=A1nek?= Date: Sun, 25 Jun 2023 18:46:07 +0200 Subject: [PATCH 02/44] Fix pickling of VanillaDataManager (#2129) * Fix pickling of VanillaDataManager * Fix vanilladatamanager init --- .../data/datamanagers/base_datamanager.py | 38 +++++++++++++----- nerfstudio/utils/misc.py | 31 ++++++++++++++ tests/data/test_datamanager.py | 40 +++++++++++++++++-- 3 files changed, 96 insertions(+), 13 deletions(-) diff --git a/nerfstudio/data/datamanagers/base_datamanager.py b/nerfstudio/data/datamanagers/base_datamanager.py index b2f23feb7..e48e3621a 100644 --- a/nerfstudio/data/datamanagers/base_datamanager.py +++ b/nerfstudio/data/datamanagers/base_datamanager.py @@ -18,11 +18,11 @@ Datamanager. from __future__ import annotations -import functools from abc import abstractmethod from collections import defaultdict from dataclasses import dataclass, field from pathlib import Path +from functools import cached_property from typing import ( Any, Callable, @@ -35,6 +35,9 @@ from typing import ( Type, Union, cast, + ForwardRef, + get_origin, + get_args, ) import torch @@ -66,6 +69,7 @@ from nerfstudio.engine.callbacks import TrainingCallback, TrainingCallbackAttrib from nerfstudio.model_components.ray_generators import RayGenerator from nerfstudio.utils.misc import IterableWrapper from nerfstudio.utils.rich_utils import CONSOLE +from nerfstudio.utils.misc import get_orig_class def variable_res_collate(batch: List[Dict]) -> Dict: @@ -376,7 +380,6 @@ class VanillaDataManager(DataManager, Generic[TDataset]): local_rank: int = 0, **kwargs, ): - self.dataset_type: Type[TDataset] = kwargs.get("_dataset_type", getattr(TDataset, "__default__")) self.config = config self.device = device self.world_size = world_size @@ -409,15 +412,32 @@ class VanillaDataManager(DataManager, Generic[TDataset]): CONSOLE.print("Variable resolution, using variable_res_collate") self.config.collate_fn = variable_res_collate break - super().__init__() - def __class_getitem__(cls, item): - return type( - cls.__name__, - (cls,), - {"__module__": cls.__module__, "__init__": functools.partialmethod(cls.__init__, _dataset_type=item)}, - ) + @cached_property + def dataset_type(self) -> Type[TDataset]: + """Returns the dataset type passed as the generic argument""" + default: Type[TDataset] = cast(TDataset, TDataset.__default__) # type: ignore + orig_class: Type[VanillaDataManager] = get_orig_class(self, default=None) # type: ignore + if type(self) is VanillaDataManager and orig_class is None: + return default + if orig_class is not None and get_origin(orig_class) is VanillaDataManager: + return get_args(orig_class)[0] + + # For inherited classes, we need to find the correct type to instantiate + for base in getattr(self, "__orig_bases__", []): + if get_origin(base) is VanillaDataManager: + for value in get_args(base): + if isinstance(value, ForwardRef): + if value.__forward_evaluated__: + value = value.__forward_value__ + elif value.__forward_module__ is None: + value.__forward_module__ = type(self).__module__ + value = getattr(value, "_evaluate")(None, None, set()) + assert isinstance(value, type) + if issubclass(value, InputDataset): + return cast(Type[TDataset], value) + return default def create_train_dataset(self) -> TDataset: """Sets up the data loaders for training""" diff --git a/nerfstudio/utils/misc.py b/nerfstudio/utils/misc.py index e95bd4188..3e4c4df86 100644 --- a/nerfstudio/utils/misc.py +++ b/nerfstudio/utils/misc.py @@ -17,6 +17,8 @@ Miscellaneous helper code. """ +from inspect import currentframe +import typing import platform from typing import Any, Callable, Dict, List, Optional, TypeVar, Union import warnings @@ -182,3 +184,32 @@ def torch_compile(*args, **kwargs): return lambda x: x else: return torch.compile(*args, **kwargs) + + +def get_orig_class(obj, default=None): + """Returns the __orig_class__ class of `obj` even when it is not initialized in __init__ (Python>=3.8). + + Workaround for https://github.com/python/typing/issues/658. + Inspired by https://github.com/Stewori/pytypes/pull/53. + """ + try: + return object.__getattribute__(obj, "__orig_class__") + except AttributeError: + cls = object.__getattribute__(obj, "__class__") + try: + is_type_generic = isinstance(cls, typing.GenericMeta) # type: ignore + except AttributeError: # Python 3.8 + is_type_generic = issubclass(cls, typing.Generic) + if is_type_generic: + frame = currentframe().f_back.f_back # type: ignore + try: + while frame: + try: + res = frame.f_locals["self"] + if res.__origin__ is cls: + return res + except (KeyError, AttributeError): + frame = frame.f_back + finally: + del frame + return default diff --git a/tests/data/test_datamanager.py b/tests/data/test_datamanager.py index e1a2a24c0..0547b736e 100644 --- a/tests/data/test_datamanager.py +++ b/tests/data/test_datamanager.py @@ -1,3 +1,4 @@ +import pickle from pathlib import Path from typing import Any @@ -52,18 +53,49 @@ def config(): def test_data_manager_type_inference(config): # Mock for a faster test - assert VanillaDataManager(config).dataset_type is InputDataset assert VanillaDataManager[DepthDataset](config).dataset_type is DepthDataset + assert VanillaDataManager(config).dataset_type is InputDataset + + class tmp2(VanillaDataManager[DepthDataset]): + pass + + assert tmp2(config).dataset_type is DepthDataset class tmp(VanillaDataManager): pass assert tmp(config).dataset_type is InputDataset - class tmp2(VanillaDataManager[DepthDataset]): - pass - assert tmp2(config).dataset_type is DepthDataset +class _pickle_enabled_tmp(VanillaDataManager): + pass + + +class _pickle_enabled_tmp2(VanillaDataManager[DepthDataset]): + pass + + +def test_data_manager_type_can_be_pickled(config): + # Mock for a faster test + assert VanillaDataManager[DepthDataset](config).dataset_type is DepthDataset + obj = pickle.loads(pickle.dumps(VanillaDataManager[DepthDataset](config))) + assert obj.dataset_type is DepthDataset + assert isinstance(obj, VanillaDataManager) + + assert VanillaDataManager(config).dataset_type is InputDataset + obj = pickle.loads(pickle.dumps(VanillaDataManager(config))) + assert obj.dataset_type is InputDataset + assert isinstance(obj, VanillaDataManager) + + assert _pickle_enabled_tmp(config).dataset_type is InputDataset + obj = pickle.loads(pickle.dumps(_pickle_enabled_tmp(config))) + assert obj.dataset_type is InputDataset + assert isinstance(obj, _pickle_enabled_tmp) + + assert _pickle_enabled_tmp2(config).dataset_type is DepthDataset + obj = pickle.loads(pickle.dumps(_pickle_enabled_tmp2(config))) + assert obj.dataset_type is DepthDataset + assert isinstance(obj, _pickle_enabled_tmp2) def test_data_manager_type_can_be_serialized(config): From aa6c0526602f6ca4c5f41449163ee5d6ca37f4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Kulh=C3=A1nek?= Date: Sun, 25 Jun 2023 19:08:17 +0200 Subject: [PATCH 03/44] Jkulhanek/colmap dataparser (#2115) * Add COLMAP dataparser implementation * Fix downscale_images input * Add help to COLMAP dataparser --- nerfstudio/configs/dataparser_configs.py | 14 +- .../data/dataparsers/colmap_dataparser.py | 403 ++++++++++++++++++ 2 files changed, 408 insertions(+), 9 deletions(-) create mode 100644 nerfstudio/data/dataparsers/colmap_dataparser.py diff --git a/nerfstudio/configs/dataparser_configs.py b/nerfstudio/configs/dataparser_configs.py index 80b915151..46a728141 100644 --- a/nerfstudio/configs/dataparser_configs.py +++ b/nerfstudio/configs/dataparser_configs.py @@ -20,23 +20,18 @@ from typing import TYPE_CHECKING import tyro -from nerfstudio.data.dataparsers.arkitscenes_dataparser import ( - ARKitScenesDataParserConfig, -) +from nerfstudio.data.dataparsers.arkitscenes_dataparser import ARKitScenesDataParserConfig from nerfstudio.data.dataparsers.base_dataparser import DataParserConfig from nerfstudio.data.dataparsers.blender_dataparser import BlenderDataParserConfig +from nerfstudio.data.dataparsers.colmap_dataparser import ColmapDataParserConfig from nerfstudio.data.dataparsers.dnerf_dataparser import DNeRFDataParserConfig from nerfstudio.data.dataparsers.dycheck_dataparser import DycheckDataParserConfig -from nerfstudio.data.dataparsers.instant_ngp_dataparser import ( - InstantNGPDataParserConfig, -) +from nerfstudio.data.dataparsers.instant_ngp_dataparser import InstantNGPDataParserConfig from nerfstudio.data.dataparsers.minimal_dataparser import MinimalDataParserConfig from nerfstudio.data.dataparsers.nerfosr_dataparser import NeRFOSRDataParserConfig from nerfstudio.data.dataparsers.nerfstudio_dataparser import NerfstudioDataParserConfig from nerfstudio.data.dataparsers.nuscenes_dataparser import NuScenesDataParserConfig -from nerfstudio.data.dataparsers.phototourism_dataparser import ( - PhototourismDataParserConfig, -) +from nerfstudio.data.dataparsers.phototourism_dataparser import PhototourismDataParserConfig from nerfstudio.data.dataparsers.scannet_dataparser import ScanNetDataParserConfig from nerfstudio.data.dataparsers.sdfstudio_dataparser import SDFStudioDataParserConfig from nerfstudio.data.dataparsers.sitcoms3d_dataparser import Sitcoms3DDataParserConfig @@ -56,6 +51,7 @@ dataparsers = { "sdfstudio-data": SDFStudioDataParserConfig(), "nerfosr-data": NeRFOSRDataParserConfig(), "sitcoms3d-data": Sitcoms3DDataParserConfig(), + "colmap": ColmapDataParserConfig(), } external_dataparsers = discover_dataparsers() diff --git a/nerfstudio/data/dataparsers/colmap_dataparser.py b/nerfstudio/data/dataparsers/colmap_dataparser.py new file mode 100644 index 000000000..bb3284f33 --- /dev/null +++ b/nerfstudio/data/dataparsers/colmap_dataparser.py @@ -0,0 +1,403 @@ +# Copyright 2022 the Regents of the University of California, Nerfstudio Team and contributors. 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. +""" Data parser for nerfstudio datasets. """ + +from __future__ import annotations + +import math +import sys +from dataclasses import dataclass, field +from pathlib import Path +from typing import List, Literal, Optional, Type + +import numpy as np +import torch +from PIL import Image +from rich.prompt import Confirm + +from nerfstudio.cameras import camera_utils +from nerfstudio.cameras.cameras import CAMERA_MODEL_TO_TYPE, Cameras +from nerfstudio.data.dataparsers.base_dataparser import DataParser, DataParserConfig, DataparserOutputs +from nerfstudio.data.scene_box import SceneBox +from nerfstudio.data.utils import colmap_parsing_utils as colmap_utils +from nerfstudio.process_data.colmap_utils import parse_colmap_camera_params +from nerfstudio.process_data.process_data_utils import downscale_images +from nerfstudio.utils.rich_utils import CONSOLE + +MAX_AUTO_RESOLUTION = 1600 + + +@dataclass +class ColmapDataParserConfig(DataParserConfig): + """Nerfstudio dataset config""" + + _target: Type = field(default_factory=lambda: ColmapDataParser) + """target class to instantiate""" + data: Path = Path() + """Directory or explicit json file path specifying location of data.""" + scale_factor: float = 1.0 + """How much to scale the camera origins by.""" + downscale_factor: Optional[int] = None + """How much to downscale images. If not set, images are chosen such that the max dimension is <1600px.""" + scene_scale: float = 1.0 + """How much to scale the region of interest by.""" + orientation_method: Literal["pca", "up", "vertical", "none"] = "up" + """The method to use for orientation.""" + center_method: Literal["poses", "focus", "none"] = "poses" + """The method to use to center the poses.""" + auto_scale_poses: bool = True + """Whether to automatically scale the poses to fit in +/- 1 bounding box.""" + train_split_fraction: float = 0.9 + """The fraction of images to use for training. The remaining images are for eval.""" + depth_unit_scale_factor: float = 1e-3 + """Scales the depth values to meters. Default value is 0.001 for a millimeter to meter conversion.""" + + images_path: Path = Path("images") + """Path to images directory relative to the data path.""" + masks_path: Optional[Path] = None + """Path to masks directory. If not set, masks are not loaded.""" + depths_path: Optional[Path] = None + """Path to depth maps directory. If not set, depths are not loaded.""" + colmap_path: Path = Path("sparse/0") + """Path to the colmap reconstruction directory relative to the data path.""" + + +class ColmapDataParser(DataParser): + """COLMAP DatasetParser. + Expects a folder with the following structure: + images/ # folder containing images used to create the COLMAP model + sparse/0 # folder containing the COLMAP reconstruction (either TEXT or BINARY format) + masks/ # (OPTIONAL) folder containing masks for each image + depths/ # (OPTIONAL) folder containing depth maps for each image + The paths can be different and can be specified in the config. (e.g., sparse/0 -> sparse) + Currently, most COLMAP camera models are supported except for the FULL_OPENCV and THIN_PRISM_FISHEYE models. + + The dataparser loads the downscaled images from folders with `_{downscale_factor}` suffix. + If these folders do not exist, the user can choose to automatically downscale the images and + create these folders. + + The loader is compatible with the datasets processed using the ns-process-data script and + can be used as a drop-in replacement. It further supports datasets like Mip-NeRF 360 (although + in the case of Mip-NeRF 360 the downsampled images may have a different resolution because they + use different rounding when computing the image resolution). + """ + + config: ColmapDataParserConfig + + def __init__(self, config: ColmapDataParserConfig): + super().__init__(config) + self.config = config + self._downscale_factor = None + + def _get_all_images_and_cameras(self, recon_dir: Path): + if (recon_dir / "cameras.txt").exists(): + cam_id_to_camera = colmap_utils.read_cameras_text(recon_dir / "cameras.txt") + im_id_to_image = colmap_utils.read_images_text(recon_dir / "images.txt") + elif (recon_dir / "cameras.bin").exists(): + cam_id_to_camera = colmap_utils.read_cameras_binary(recon_dir / "cameras.bin") + im_id_to_image = colmap_utils.read_images_binary(recon_dir / "images.bin") + else: + raise ValueError(f"Could not find cameras.txt or cameras.bin in {recon_dir}") + + cameras = {} + frames = [] + camera_model = None + + # Parse cameras + for cam_id, cam_data in cam_id_to_camera.items(): + cameras[cam_id] = parse_colmap_camera_params(cam_data) + + # Parse frames + for im_id, im_data in im_id_to_image.items(): + # NB: COLMAP uses Eigen / scalar-first quaternions + # * https://colmap.github.io/format.html + # * https://github.com/colmap/colmap/blob/bf3e19140f491c3042bfd85b7192ef7d249808ec/src/base/pose.cc#L75 + # the `rotation_matrix()` handles that format for us. + rotation = colmap_utils.qvec2rotmat(im_data.qvec) + translation = im_data.tvec.reshape(3, 1) + w2c = np.concatenate([rotation, translation], 1) + w2c = np.concatenate([w2c, np.array([[0, 0, 0, 1]])], 0) + c2w = np.linalg.inv(w2c) + # Convert from COLMAP's camera coordinate system (OpenCV) to ours (OpenGL) + c2w[0:3, 1:3] *= -1 + c2w = c2w[np.array([1, 0, 2, 3]), :] + c2w[2, :] *= -1 + + frame = { + "file_path": (self.config.images_path / im_data.name).as_posix(), + "transform_matrix": c2w, + "colmap_im_id": im_id, + } + frame.update(cameras[im_data.camera_id]) + if self.config.masks_path is not None: + frame["mask_path"] = ((self.config.masks_path / im_data.name).with_suffix(".png").as_posix(),) + if self.config.depths_path is not None: + frame["depth_path"] = ((self.config.depths_path / im_data.name).with_suffix(".png").as_posix(),) + frames.append(frame) + if camera_model is not None: + assert camera_model == frame["camera_model"], "Multiple camera models are not supported" + else: + camera_model = frame["camera_model"] + + out = {} + out["frames"] = frames + applied_transform = np.eye(4)[:3, :] + applied_transform = applied_transform[np.array([1, 0, 2]), :] + applied_transform[2, :] *= -1 + out["applied_transform"] = applied_transform.tolist() + out["camera_model"] = camera_model + assert len(frames) > 0, "No images found in the colmap model" + return out + + def _get_image_indices(self, image_filenames, split): + has_split_files_spec = ( + (self.config.data / "train_list.txt").exists() + or (self.config.data / "test_list.txt").exists() + or (self.config.data / "validation_list.txt").exists() + ) + if (self.config.data / f"{split}_list.txt").exists(): + CONSOLE.log(f"Using {split}_list.txt to get indices for split {split}.") + with (self.config.data / f"{split}_list.txt").open("r", encoding="utf8") as f: + filenames = f.read().splitlines() + # Validate split first + split_filenames = set(self.config.images_path / x for x in filenames) + unmatched_filenames = split_filenames.difference(image_filenames) + if unmatched_filenames: + breakpoint() + raise RuntimeError( + f"Some filenames for split {split} were not found: {set(map(str, unmatched_filenames))}." + ) + + indices = [i for i, path in enumerate(image_filenames) if path in split_filenames] + CONSOLE.log(f"[yellow] Dataset is overriding {split}_indices to {indices}") + indices = np.array(indices, dtype=np.int32) + elif has_split_files_spec: + raise RuntimeError(f"The dataset's list of filenames for split {split} is missing.") + else: + # filter image_filenames and poses based on train/eval split percentage + num_images = len(image_filenames) + num_train_images = math.ceil(num_images * self.config.train_split_fraction) + num_eval_images = num_images - num_train_images + i_all = np.arange(num_images) + i_train = np.linspace( + 0, num_images - 1, num_train_images, dtype=int + ) # equally spaced training images starting and ending at 0 and num_images-1 + i_eval = np.setdiff1d(i_all, i_train) # eval images are the remaining images + assert len(i_eval) == num_eval_images + if split == "train": + indices = i_train + elif split in ["val", "test"]: + indices = i_eval + else: + raise ValueError(f"Unknown dataparser split {split}") + return indices + + def _generate_dataparser_outputs(self, split: str = "train"): + assert self.config.data.exists(), f"Data directory {self.config.data} does not exist." + colmap_path = self.config.data / self.config.colmap_path + assert colmap_path.exists(), f"Colmap path {colmap_path} does not exist." + + meta = self._get_all_images_and_cameras(colmap_path) + camera_type = CAMERA_MODEL_TO_TYPE[meta["camera_model"]] + + image_filenames = [] + mask_filenames = [] + depth_filenames = [] + poses = [] + + fx = [] + fy = [] + cx = [] + cy = [] + height = [] + width = [] + distort = [] + + for frame in meta["frames"]: + fx.append(float(frame["fl_x"])) + fy.append(float(frame["fl_y"])) + cx.append(float(frame["cx"])) + cy.append(float(frame["cy"])) + height.append(int(frame["h"])) + width.append(int(frame["w"])) + distort.append( + camera_utils.get_distortion_params( + k1=float(frame["k1"]) if "k1" in frame else 0.0, + k2=float(frame["k2"]) if "k2" in frame else 0.0, + k3=float(frame["k3"]) if "k3" in frame else 0.0, + k4=float(frame["k4"]) if "k4" in frame else 0.0, + p1=float(frame["p1"]) if "p1" in frame else 0.0, + p2=float(frame["p2"]) if "p2" in frame else 0.0, + ) + ) + + image_filenames.append(Path(frame["file_path"])) + poses.append(frame["transform_matrix"]) + if "mask_path" in frame: + mask_filenames.append(Path(frame["mask_path"])) + if "depth_path" in frame: + depth_filenames.append(Path(frame["depth_path"])) + + assert len(mask_filenames) == 0 or ( + len(mask_filenames) == len(image_filenames) + ), """ + Different number of image and mask filenames. + You should check that mask_path is specified for every frame (or zero frames) in transforms.json. + """ + assert len(depth_filenames) == 0 or ( + len(depth_filenames) == len(image_filenames) + ), """ + Different number of image and depth filenames. + You should check that depth_file_path is specified for every frame (or zero frames) in transforms.json. + """ + poses = torch.from_numpy(np.array(poses).astype(np.float32)) + poses, transform_matrix = camera_utils.auto_orient_and_center_poses( + poses, + method=self.config.orientation_method, + center_method=self.config.center_method, + ) + + # Scale poses + scale_factor = 1.0 + if self.config.auto_scale_poses: + scale_factor /= float(torch.max(torch.abs(poses[:, :3, 3]))) + scale_factor *= self.config.scale_factor + + poses[:, :3, 3] *= scale_factor + + # Choose image_filenames and poses based on split, but after auto orient and scaling the poses. + indices = self._get_image_indices(image_filenames, split) + image_filenames, mask_filenames, depth_filenames, downscale_factor = self._setup_downscale_factor( + image_filenames, mask_filenames, depth_filenames + ) + + image_filenames = [image_filenames[i] for i in indices] + mask_filenames = [mask_filenames[i] for i in indices] if len(mask_filenames) > 0 else [] + depth_filenames = [depth_filenames[i] for i in indices] if len(depth_filenames) > 0 else [] + + idx_tensor = torch.tensor(indices, dtype=torch.long) + poses = poses[idx_tensor] + + # in x,y,z order + # assumes that the scene is centered at the origin + aabb_scale = self.config.scene_scale + scene_box = SceneBox( + aabb=torch.tensor( + [[-aabb_scale, -aabb_scale, -aabb_scale], [aabb_scale, aabb_scale, aabb_scale]], dtype=torch.float32 + ) + ) + + fx = torch.tensor(fx, dtype=torch.float32)[idx_tensor] + fy = torch.tensor(fy, dtype=torch.float32)[idx_tensor] + cx = torch.tensor(cx, dtype=torch.float32)[idx_tensor] + cy = torch.tensor(cy, dtype=torch.float32)[idx_tensor] + height = torch.tensor(height, dtype=torch.int32)[idx_tensor] + width = torch.tensor(width, dtype=torch.int32)[idx_tensor] + distortion_params = torch.stack(distort, dim=0)[idx_tensor] + + cameras = Cameras( + fx=fx, + fy=fy, + cx=cx, + cy=cy, + distortion_params=distortion_params, + height=height, + width=width, + camera_to_worlds=poses[:, :3, :4], + camera_type=camera_type, + ) + + cameras.rescale_output_resolution(scaling_factor=1.0 / downscale_factor) + + if "applied_transform" in meta: + applied_transform = torch.tensor(meta["applied_transform"], dtype=transform_matrix.dtype) + transform_matrix = transform_matrix @ torch.cat( + [applied_transform, torch.tensor([[0, 0, 0, 1]], dtype=transform_matrix.dtype)], 0 + ) + if "applied_scale" in meta: + applied_scale = float(meta["applied_scale"]) + scale_factor *= applied_scale + + dataparser_outputs = DataparserOutputs( + image_filenames=image_filenames, + cameras=cameras, + scene_box=scene_box, + mask_filenames=mask_filenames if len(mask_filenames) > 0 else None, + dataparser_scale=scale_factor, + dataparser_transform=transform_matrix, + metadata={ + "depth_filenames": depth_filenames if len(depth_filenames) > 0 else None, + "depth_unit_scale_factor": self.config.depth_unit_scale_factor, + }, + ) + return dataparser_outputs + + def _setup_downscale_factor( + self, image_filenames: List[Path], mask_filenames: List[Path], depth_filenames: List[Path] + ): + """ + Setup the downscale factor for the dataset. This is used to downscale the images and cameras. + """ + + def get_fname(filepath: Path) -> Path: + """Returns transformed file name when downscale factor is applied""" + parts = list(filepath.parts) + parts[-2] += f"_{self._downscale_factor}" + filepath = Path(*parts) + return self.config.data / filepath + + filepath = next(iter(image_filenames)) + if self._downscale_factor is None: + if self.config.downscale_factor is None: + test_img = Image.open(self.config.data / filepath) + h, w = test_img.size + max_res = max(h, w) + df = 0 + while True: + if (max_res / 2 ** (df)) < MAX_AUTO_RESOLUTION: + break + df += 1 + + self._downscale_factor = 2**df + CONSOLE.log(f"Using image downscale factor of {self._downscale_factor}") + else: + self._downscale_factor = self.config.downscale_factor + if self._downscale_factor > 1 and not all(get_fname(fp).parent.exists() for fp in image_filenames): + # Downscaled images not found + # Ask if user wants to downscale the images automatically here + CONSOLE.print( + f"[bold red]Downscaled images do not exist for factor of {self._downscale_factor}.[/bold red]" + ) + if Confirm.ask("\nWould you like to downscale the images now?", default=False, console=CONSOLE): + # Install the method + image_dir = image_filenames[0].parent + num_downscales = int(math.log2(self._downscale_factor)) + assert 2**num_downscales == self._downscale_factor, "Downscale factor must be a power of 2" + downscale_images(image_dir, num_downscales, folder_name=image_dir.name, nearest_neighbor=False) + if len(mask_filenames) > 0: + mask_dir = mask_filenames[0].parent + downscale_images(mask_dir, num_downscales, folder_name=mask_dir.name, nearest_neighbor=True) + if len(depth_filenames) > 0: + depth_dir = depth_filenames[0].parent + downscale_images(depth_dir, num_downscales, folder_name=depth_dir.name, nearest_neighbor=False) + else: + sys.exit(1) + + # Return transformed filenames + if self._downscale_factor > 1: + image_filenames = [get_fname(fp) for fp in image_filenames] + mask_filenames = [get_fname(fp) for fp in mask_filenames] + depth_filenames = [get_fname(fp) for fp in depth_filenames] + assert isinstance(self._downscale_factor, int) + return image_filenames, mask_filenames, depth_filenames, self._downscale_factor From 6486c00d4d717ee437198d08930a86f5cced5359 Mon Sep 17 00:00:00 2001 From: Matthew Tancik Date: Sun, 25 Jun 2023 23:48:33 -0700 Subject: [PATCH 04/44] Update generfacto.md (#2132) --- docs/nerfology/methods/generfacto.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/nerfology/methods/generfacto.md b/docs/nerfology/methods/generfacto.md index 2c037f08b..4adba5837 100644 --- a/docs/nerfology/methods/generfacto.md +++ b/docs/nerfology/methods/generfacto.md @@ -38,7 +38,7 @@ Specify which diffusion model to use with the diffusion_model flag: ns-train generfacto --pipeline.model.diffusion_model ["stablediffusion", "deepfloyd"] ``` -# Example Results +## Example Results The following videos are renders of NeRFs generated from Generfacto. Each model was trained 30k steps, which took around 1 hour with DeepFloyd and around 4 hours with Stable Diffusion. From d9ee5752d20212a867da0c6cf67ba6558206b132 Mon Sep 17 00:00:00 2001 From: Ethan Weber Date: Tue, 27 Jun 2023 09:33:28 -0700 Subject: [PATCH 05/44] Fix to fall back to torch if tcnn not found (#2124) fix for falling back to torch if tcnn not found Co-authored-by: Matias Turkulainen <30566358+maturk@users.noreply.github.com> --- nerfstudio/field_components/mlp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nerfstudio/field_components/mlp.py b/nerfstudio/field_components/mlp.py index 2d766e233..52eabd29d 100644 --- a/nerfstudio/field_components/mlp.py +++ b/nerfstudio/field_components/mlp.py @@ -102,6 +102,7 @@ class MLP(FieldComponent): self.build_nn_modules() elif implementation == "tcnn" and not TCNN_EXISTS: print_tcnn_speed_warning("MLP") + self.build_nn_modules() elif implementation == "tcnn": activation_str = activation_to_tcnn_string(activation) output_activation_str = activation_to_tcnn_string(out_activation) From d78af5b5699cb343cb46b5163d464e455524040f Mon Sep 17 00:00:00 2001 From: machenmusik Date: Tue, 27 Jun 2023 16:02:58 -0400 Subject: [PATCH 06/44] allow ns-process-data to use CR2 raw images (#2119) * allow ns-process-data to use CR2 raw images * Update process_data_utils.py * black wants an extra newline --------- Co-authored-by: Matias Turkulainen <30566358+maturk@users.noreply.github.com> --- nerfstudio/process_data/process_data_utils.py | 20 +++++++++++++++++-- pyproject.toml | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/nerfstudio/process_data/process_data_utils.py b/nerfstudio/process_data/process_data_utils.py index 5b7ae9686..9bd835c3a 100644 --- a/nerfstudio/process_data/process_data_utils.py +++ b/nerfstudio/process_data/process_data_utils.py @@ -25,11 +25,19 @@ from typing import List, Literal, Optional, OrderedDict, Tuple, Union import cv2 import numpy as np +import imageio +import rawpy + from nerfstudio.utils.rich_utils import CONSOLE, status from nerfstudio.utils.scripts import run_command POLYCAM_UPSCALING_TIMES = 2 +"""Lowercase suffixes to treat as raw image.""" +ALLOWED_RAW_EXTS = [".cr2"] +"""Suffix to use for converted images from raw.""" +RAW_CONVERTED_SUFFIX = ".tiff" + class CameraModel(Enum): """Enum for camera types.""" @@ -54,7 +62,7 @@ def list_images(data: Path) -> List[Path]: Returns: Paths to images contained in the directory """ - allowed_exts = [".jpg", ".jpeg", ".png", ".tif", ".tiff"] + allowed_exts = [".jpg", ".jpeg", ".png", ".tif", ".tiff"] + ALLOWED_RAW_EXTS image_paths = sorted([p for p in data.glob("[!.]*") if p.suffix.lower() in allowed_exts]) return image_paths @@ -209,7 +217,15 @@ def copy_images_list( CONSOLE.log(f"Copying image {idx + 1} of {len(image_paths)}...") copied_image_path = image_dir / f"frame_{idx + 1:05d}{image_path.suffix}" try: - shutil.copy(image_path, copied_image_path) + # if CR2 raw, we want to read raw and write TIFF, and change the file suffix for downstream processing + if image_path.suffix.lower() in ALLOWED_RAW_EXTS: + copied_image_path = image_dir / f"frame_{idx + 1:05d}{RAW_CONVERTED_SUFFIX}" + with rawpy.imread(str(image_path)) as raw: + rgb = raw.postprocess() + imageio.imsave(copied_image_path, rgb) + image_paths[idx] = copied_image_path + else: + shutil.copy(image_path, copied_image_path) except shutil.SameFileError: pass copied_image_paths.append(copied_image_path) diff --git a/pyproject.toml b/pyproject.toml index b8f742954..e4de87116 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ dependencies = [ "pyngrok>=5.1.0", "python-socketio>=5.7.1", "pyquaternion>=0.9.9", + "rawpy>=0.18.1", "requests", "rich>=12.5.1", "scikit-image>=0.19.3", From 000272f895268a1e85c3b6373cb3514168f8110d Mon Sep 17 00:00:00 2001 From: Konstantin Kozlovtsev Date: Thu, 29 Jun 2023 18:54:59 +0200 Subject: [PATCH 07/44] Fix unnecessary encodings calculation in tensorf field (#2145) Fixed unnecessary encodings calculation in tensorf field --- nerfstudio/fields/tensorf_field.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nerfstudio/fields/tensorf_field.py b/nerfstudio/fields/tensorf_field.py index 27d5f3cd4..b3aba4e54 100644 --- a/nerfstudio/fields/tensorf_field.py +++ b/nerfstudio/fields/tensorf_field.py @@ -98,14 +98,14 @@ class TensoRFField(Field): rgb_features = self.color_encoding(positions) rgb_features = self.B(rgb_features) - d_encoded = self.direction_encoding(d) - rgb_features_encoded = self.feature_encoding(rgb_features) - if self.use_sh: sh_mult = self.sh(d)[:, :, None] rgb_sh = rgb_features.view(sh_mult.shape[0], sh_mult.shape[1], 3, sh_mult.shape[-1]) rgb = torch.relu(torch.sum(sh_mult * rgb_sh, dim=-1) + 0.5) else: + d_encoded = self.direction_encoding(d) + rgb_features_encoded = self.feature_encoding(rgb_features) + out = self.mlp_head(torch.cat([rgb_features, d, rgb_features_encoded, d_encoded], dim=-1)) # type: ignore rgb = self.field_output_rgb(out) From b3df46314ce685325939945c71245619daeaf754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Kulh=C3=A1nek?= Date: Thu, 29 Jun 2023 19:03:29 +0200 Subject: [PATCH 08/44] Remove breakpoint from colmap_dataparser (#2141) Co-authored-by: Matthew Tancik --- nerfstudio/data/dataparsers/colmap_dataparser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nerfstudio/data/dataparsers/colmap_dataparser.py b/nerfstudio/data/dataparsers/colmap_dataparser.py index bb3284f33..dac6aebeb 100644 --- a/nerfstudio/data/dataparsers/colmap_dataparser.py +++ b/nerfstudio/data/dataparsers/colmap_dataparser.py @@ -174,7 +174,6 @@ class ColmapDataParser(DataParser): split_filenames = set(self.config.images_path / x for x in filenames) unmatched_filenames = split_filenames.difference(image_filenames) if unmatched_filenames: - breakpoint() raise RuntimeError( f"Some filenames for split {split} were not found: {set(map(str, unmatched_filenames))}." ) From 0719026c158e45836f9a077ffe8984e32d791ca1 Mon Sep 17 00:00:00 2001 From: hturki Date: Thu, 29 Jun 2023 19:26:56 -0400 Subject: [PATCH 09/44] Add option to render evaluated images (#2135) * Add option to render evaluated images * add args * fix formatting * attempt to fix pyright * attempt 2 --------- Co-authored-by: Matthew Tancik --- nerfstudio/pipelines/base_pipeline.py | 27 +++++++++++++++++++++++---- nerfstudio/scripts/eval.py | 7 ++++++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/nerfstudio/pipelines/base_pipeline.py b/nerfstudio/pipelines/base_pipeline.py index e9d30a9ab..363c8c9ea 100644 --- a/nerfstudio/pipelines/base_pipeline.py +++ b/nerfstudio/pipelines/base_pipeline.py @@ -20,11 +20,13 @@ from __future__ import annotations import typing from abc import abstractmethod from dataclasses import dataclass, field +from pathlib import Path from time import time from typing import Any, Dict, List, Literal, Mapping, Optional, Tuple, Type, Union, cast import torch import torch.distributed as dist +from PIL import Image from rich.progress import ( BarColumn, MofNCompleteColumn, @@ -173,8 +175,13 @@ class Pipeline(nn.Module): @abstractmethod @profiler.time_function - def get_average_eval_image_metrics(self, step: Optional[int] = None): - """Iterate over all the images in the eval dataset and get the average.""" + def get_average_eval_image_metrics(self, step: Optional[int] = None, output_path: Optional[Path] = None): + """Iterate over all the images in the eval dataset and get the average. + + Args: + step: current training step + output_path: optional path to save rendered images to + """ def load_pipeline(self, loaded_state: Dict[str, Any], step: int) -> None: """Load the checkpoint from the given path @@ -339,9 +346,13 @@ class VanillaPipeline(Pipeline): return metrics_dict, images_dict @profiler.time_function - def get_average_eval_image_metrics(self, step: Optional[int] = None): + def get_average_eval_image_metrics(self, step: Optional[int] = None, output_path: Optional[Path] = None): """Iterate over all the images in the eval dataset and get the average. + Args: + step: current training step + output_path: optional path to save rendered images to + Returns: metrics_dict: dictionary of metrics """ @@ -363,7 +374,15 @@ class VanillaPipeline(Pipeline): height, width = camera_ray_bundle.shape num_rays = height * width outputs = self.model.get_outputs_for_camera_ray_bundle(camera_ray_bundle) - metrics_dict, _ = self.model.get_image_metrics_and_images(outputs, batch) + metrics_dict, images_dict = self.model.get_image_metrics_and_images(outputs, batch) + + if output_path is not None: + camera_indices = camera_ray_bundle.camera_indices + assert camera_indices is not None + for key, val in images_dict.items(): + Image.fromarray((val * 255).byte().cpu().numpy()).save( + output_path / "{0:06d}-{1}.jpg".format(int(camera_indices[0, 0, 0]), key) + ) assert "num_rays_per_sec" not in metrics_dict metrics_dict["num_rays_per_sec"] = num_rays / (time() - inner_start) fps_str = "fps" diff --git a/nerfstudio/scripts/eval.py b/nerfstudio/scripts/eval.py index ef47d7a24..8f5995117 100644 --- a/nerfstudio/scripts/eval.py +++ b/nerfstudio/scripts/eval.py @@ -21,6 +21,7 @@ from __future__ import annotations import json from dataclasses import dataclass from pathlib import Path +from typing import Optional import tyro @@ -36,12 +37,16 @@ class ComputePSNR: load_config: Path # Name of the output file. output_path: Path = Path("output.json") + # Optional path to save rendered outputs to. + render_output_path: Optional[Path] = None def main(self) -> None: """Main function.""" config, pipeline, checkpoint_path, _ = eval_setup(self.load_config) assert self.output_path.suffix == ".json" - metrics_dict = pipeline.get_average_eval_image_metrics() + if self.render_output_path is not None: + self.render_output_path.mkdir(parents=True) + metrics_dict = pipeline.get_average_eval_image_metrics(output_path=self.render_output_path) self.output_path.parent.mkdir(parents=True, exist_ok=True) # Get the output and define the names to save to benchmark_info = { From f4be2b729a1ce881afa26fbc2455502b3767f411 Mon Sep 17 00:00:00 2001 From: Milan van Wouden <56082205+MvWouden@users.noreply.github.com> Date: Fri, 30 Jun 2023 10:44:53 +0200 Subject: [PATCH 10/44] Fix typo in nerfacto.py (#2149) --- nerfstudio/models/nerfacto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nerfstudio/models/nerfacto.py b/nerfstudio/models/nerfacto.py index 75e798873..2dbbee939 100644 --- a/nerfstudio/models/nerfacto.py +++ b/nerfstudio/models/nerfacto.py @@ -70,7 +70,7 @@ class NerfactoModelConfig(ModelConfig): num_levels: int = 16 """Number of levels of the hashmap for the base mlp.""" base_res: int = 16 - """Resolution of the base grid for the hasgrid.""" + """Resolution of the base grid for the hashgrid.""" max_res: int = 2048 """Maximum resolution of the hashmap for the base mlp.""" log2_hashmap_size: int = 19 From 5c4b0a1dd6be1941fbf4d175ef0a4f4f94f96d4c Mon Sep 17 00:00:00 2001 From: Ilya Basharov Date: Sat, 1 Jul 2023 20:06:05 +0300 Subject: [PATCH 11/44] add `build-arg` cuda version to dockerfile (#2139) --- Dockerfile | 15 ++++++++++----- docs/quickstart/installation.md | 7 ++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 755b67c52..f40c2404b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,13 @@ +ARG CUDA_VERSION=11.8.0 +ARG OS_VERSION=22.04 # Define base image. -FROM nvidia/cuda:11.8.0-devel-ubuntu22.04 +FROM nvidia/cuda:${CUDA_VERSION}-devel-ubuntu${OS_VERSION} # metainformation LABEL org.opencontainers.image.version = "0.1.18" LABEL org.opencontainers.image.source = "https://github.com/nerfstudio-project/nerfstudio" LABEL org.opencontainers.image.licenses = "Apache License 2.0" -LABEL org.opencontainers.image.base.name="docker.io/library/nvidia/cuda:11.8.0-devel-ubuntu22.04" +LABEL org.opencontainers.image.base.name="docker.io/library/nvidia/cuda:${CUDA_VERSION}-devel-ubuntu${OS_VERSION}" # Variables used at build time. ## CUDA architectures, required by Colmap and tiny-cuda-nn. @@ -115,7 +117,10 @@ SHELL ["/bin/bash", "-c"] # Upgrade pip and install packages. RUN python3.10 -m pip install --upgrade pip setuptools pathtools promise pybind11 # Install pytorch and submodules -RUN python3.10 -m pip install torch==2.0.1+cu118 torchvision==0.15.2+cu118 --extra-index-url https://download.pytorch.org/whl/cu118 +RUN CUDA_VER=${CUDA_VERSION%.*} && CUDA_VER=${CUDA_VER//./} && python3.10 -m pip install \ + torch==2.0.1+cu${CUDA_VER} \ + torchvision==0.15.2+cu${CUDA_VER} \ + --extra-index-url https://download.pytorch.org/whl/cu${CUDA_VER} # Install tynyCUDNN (we need to set the target architectures as environment variable first). ENV TCNN_CUDA_ARCHITECTURES=${CUDA_ARCHITECTURES} RUN python3.10 -m pip install git+https://github.com/NVlabs/tiny-cuda-nn.git@v1.6#subdirectory=bindings/torch @@ -128,7 +133,7 @@ RUN git clone --branch v0.3.0 --recursive https://github.com/colmap/pycolmap.git cd .. # Install hloc master (last release (1.3) is too old) as alternative feature detector and matcher option for nerfstudio. -RUN git clone --branch master --recursive https://github.com/cvg/Hierarchical-Localization && \ +RUN git clone --branch master --recursive https://github.com/cvg/Hierarchical-Localization.git && \ cd Hierarchical-Localization && \ python3.10 -m pip install -e . && \ cd .. @@ -140,7 +145,7 @@ RUN git clone --branch main --recursive https://github.com/cvg/pyceres.git && \ cd .. # Install pixel perfect sfm. -RUN git clone --branch main --recursive https://github.com/cvg/pixel-perfect-sfm && \ +RUN git clone --branch main --recursive https://github.com/cvg/pixel-perfect-sfm.git && \ cd pixel-perfect-sfm && \ python3.10 -m pip install -e . && \ cd .. diff --git a/docs/quickstart/installation.md b/docs/quickstart/installation.md index 68341a591..203d6e35d 100644 --- a/docs/quickstart/installation.md +++ b/docs/quickstart/installation.md @@ -144,7 +144,12 @@ build arg and look up [the compute capability for your GPU](https://developer.nv For example, here's how to build with support for GeForce 30xx series GPUs: ```bash -docker build --build-arg CUDA_ARCHITECTURES=86 --tag nerfstudio-86 -f Dockerfile . +docker build \ + --build-arg CUDA_VERSION=11.8.0 \ + --build-arg CUDA_ARCHITECTURES=86 \ + --build-arg OS_VERSION=22.04 \ + --tag nerfstudio-86 \ + --file Dockerfile . ``` ### Using an interactive container From d6bb8bd8e5d278634b5fa73c18ae8bcc0daad986 Mon Sep 17 00:00:00 2001 From: Brent Yi Date: Sat, 1 Jul 2023 11:54:27 -0700 Subject: [PATCH 12/44] Fix `use_appearance_embedding` for instant-ngp (#1999) * Attempt fixes for instant-ngp * Run `ns-dev-test` * Remove NGP field implementation * Test nerfacto field instead of ngp field * Black --- docs/reference/api/fields.rst | 39 ++++- nerfstudio/fields/instant_ngp_field.py | 198 ------------------------- nerfstudio/fields/nerfw_field.py | 6 +- nerfstudio/models/instant_ngp.py | 1 + tests/field_components/test_fields.py | 25 +++- 5 files changed, 58 insertions(+), 211 deletions(-) delete mode 100644 nerfstudio/fields/instant_ngp_field.py diff --git a/docs/reference/api/fields.rst b/docs/reference/api/fields.rst index 4dea6f8e9..33bde75c1 100644 --- a/docs/reference/api/fields.rst +++ b/docs/reference/api/fields.rst @@ -10,10 +10,45 @@ Base :members: :show-inheritance: -Instant NGP +Density ---------------- -.. automodule:: nerfstudio.fields.instant_ngp_field +.. automodule:: nerfstudio.fields.density_fields + :members: + :show-inheritance: + +Nerfacto +---------------- + +.. automodule:: nerfstudio.fields.nerfacto_field + :members: + :show-inheritance: + +Nerf-W +---------------- + +.. automodule:: nerfstudio.fields.nerfw_field + :members: + :show-inheritance: + +SDF +---------------- + +.. automodule:: nerfstudio.fields.sdf_field + :members: + :show-inheritance: + +Semantic NeRF +---------------- + +.. automodule:: nerfstudio.fields.semantic_nerf_field + :members: + :show-inheritance: + +TensoRF +---------------- + +.. automodule:: nerfstudio.fields.tensorf_field :members: :show-inheritance: diff --git a/nerfstudio/fields/instant_ngp_field.py b/nerfstudio/fields/instant_ngp_field.py deleted file mode 100644 index 63b01eff6..000000000 --- a/nerfstudio/fields/instant_ngp_field.py +++ /dev/null @@ -1,198 +0,0 @@ -# Copyright 2022 the Regents of the University of California, Nerfstudio Team and contributors. 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. - -""" -Instant-NGP field implementations using tiny-cuda-nn, torch, .... -""" - - -from typing import Dict, Optional, Tuple - -import numpy as np -import torch -from jaxtyping import Float -from torch import Tensor -from torch.nn.parameter import Parameter - -from nerfstudio.cameras.rays import 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.spatial_distortions import ( - SceneContraction, - SpatialDistortion, -) -from nerfstudio.fields.base_field import Field, shift_directions_for_tcnn - -try: - import tinycudann as tcnn -except ModuleNotFoundError: - # tinycudann module doesn't exist - pass - - -class TCNNInstantNGPField(Field): - """TCNN implementation of the Instant-NGP field. - - Args: - aabb: parameters of scene aabb bounds - num_layers: number of hidden layers - hidden_dim: dimension of hidden layers - 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 - num_images: number of images, required if use_appearance_embedding is True - appearance_embedding_dim: dimension of appearance embedding - contraction_type: type of contraction - num_levels: number of levels of the hashmap for the base mlp - log2_hashmap_size: size of the hashmap for the base mlp - max_res: maximum resolution of the hashmap for the base mlp - mode: which implementation to use for the field - """ - - def __init__( - self, - aabb: Tensor, - 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: Optional[bool] = False, - num_images: Optional[int] = None, - appearance_embedding_dim: int = 32, - num_levels: int = 16, - log2_hashmap_size: int = 19, - max_res: int = 2048, - spatial_distortion: Optional[SpatialDistortion] = SceneContraction(), - ) -> None: - super().__init__() - - self.aabb = Parameter(aabb, requires_grad=False) - self.geo_feat_dim = geo_feat_dim - self.spatial_distortion = spatial_distortion - - 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) - - # TODO: set this properly based on the aabb - base_res: int = 16 - features_per_level: int = 2 - growth_factor = np.exp((np.log(max_res) - np.log(base_res)) / (num_levels - 1)) - - self.direction_encoding = tcnn.Encoding( - n_input_dims=3, - encoding_config={ - "otype": "SphericalHarmonics", - "degree": 4, - }, - ) - - self.mlp_base = tcnn.NetworkWithInputEncoding( - n_input_dims=3, - n_output_dims=1 + self.geo_feat_dim, - encoding_config={ - "otype": "HashGrid", - "n_levels": num_levels, - "n_features_per_level": features_per_level, - "log2_hashmap_size": log2_hashmap_size, - "base_resolution": base_res, - "per_level_scale": growth_factor, - }, - 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 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) -> Tuple[Tensor, Tensor]: - 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) - # Make sure the tcnn gets inputs between 0 and 1. - selector = ((positions > 0.0) & (positions < 1.0)).all(dim=-1) - positions = positions * selector[..., None] - positions_flat = positions.view(-1, 3) - - h = self.mlp_base(positions_flat).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)) - density = density * selector[..., None] - return density, base_mlp_out - - def get_outputs( - self, ray_samples: RaySamples, density_embedding: Optional[Tensor] = None - ) -> Dict[FieldHeadNames, Tensor]: - assert density_embedding is not None - directions = shift_directions_for_tcnn(ray_samples.frustums.directions) - directions_flat = directions.view(-1, 3) - d = self.direction_encoding(directions_flat) - - h = torch.cat([d, density_embedding.view(-1, self.geo_feat_dim)], dim=-1) - - 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} - - def get_opacity(self, positions: Float[Tensor, "*bs 3"], step_size) -> Float[Tensor, "*bs 1"]: - """Returns the opacity for a position. Used primarily by the occupancy grid. - - Args: - positions: the positions to evaluate the opacity at. - step_size: the step size to use for the opacity evaluation. - """ - density = self.density_fn(positions) - opacity = density * step_size - return opacity diff --git a/nerfstudio/fields/nerfw_field.py b/nerfstudio/fields/nerfw_field.py index be71f15bb..4f385a371 100644 --- a/nerfstudio/fields/nerfw_field.py +++ b/nerfstudio/fields/nerfw_field.py @@ -123,11 +123,11 @@ class VanillaNerfWField(Field): """Returns the outputs of the NeRF-W field. Args: - ray_samples (RaySamples): Ray samples. - density_embedding (Float[Tensor, "*batch embedding_size"], optional): Density embedding. + ray_samples: Ray samples. + density_embedding: Density embedding. Returns: - Dict[FieldHeadNames, Tensor]: Outputs of the NeRF-W field. + Outputs of the NeRF-W field. """ outputs = {} encoded_dir = self.direction_encoding(ray_samples.frustums.directions) diff --git a/nerfstudio/models/instant_ngp.py b/nerfstudio/models/instant_ngp.py index e8ac52b96..95f059b60 100644 --- a/nerfstudio/models/instant_ngp.py +++ b/nerfstudio/models/instant_ngp.py @@ -110,6 +110,7 @@ class NGPModel(Model): self.field = NerfactoField( aabb=self.scene_box.aabb, + appearance_embedding_dim=0 if self.config.use_appearance_embedding else 32, num_images=self.num_train_data, log2_hashmap_size=self.config.log2_hashmap_size, max_res=self.config.max_res, diff --git a/tests/field_components/test_fields.py b/tests/field_components/test_fields.py index 9e5611fcc..74d7d6c3a 100644 --- a/tests/field_components/test_fields.py +++ b/tests/field_components/test_fields.py @@ -4,11 +4,11 @@ Test the fields import torch from nerfstudio.cameras.rays import Frustums, RaySamples -from nerfstudio.fields.instant_ngp_field import TCNNInstantNGPField +from nerfstudio.fields.nerfacto_field import NerfactoField -def test_tcnn_instant_ngp_field(): - """Test the tiny-cuda-nn field""" +def test_nerfacto_field(): + """Test the Nerfacto field""" try: import tinycudann as tcnn # noqa: F401 except ModuleNotFoundError as e: @@ -18,9 +18,11 @@ def test_tcnn_instant_ngp_field(): device = torch.device("cuda" if torch.cuda.is_available() else "cpu") aabb_scale = 1.0 aabb = torch.tensor( - [[-aabb_scale, -aabb_scale, -aabb_scale], [aabb_scale, aabb_scale, aabb_scale]], dtype=torch.float32 - ).to(device) - field = TCNNInstantNGPField(aabb) + [[-aabb_scale, -aabb_scale, -aabb_scale], [aabb_scale, aabb_scale, aabb_scale]], + dtype=torch.float32, + device=device, + ) + field = NerfactoField(aabb, num_images=1).to(device) num_rays = 1024 num_samples = 256 positions = torch.rand((num_rays, num_samples, 3), dtype=torch.float32, device=device) @@ -32,9 +34,16 @@ def test_tcnn_instant_ngp_field(): ends=torch.zeros((*directions.shape[:-1], 1), device=device), pixel_area=torch.ones((*directions.shape[:-1], 1), device=device), ) - ray_samples = RaySamples(frustums=frustums) + ray_samples = RaySamples( + frustums=frustums, + camera_indices=torch.zeros( + (num_rays, 1, 1), + device=device, + dtype=torch.int32, + ), + ) field.forward(ray_samples) if __name__ == "__main__": - test_tcnn_instant_ngp_field() + test_nerfacto_field() From 061d691d4335d46d1149cce0fd9813ad094eb6cc Mon Sep 17 00:00:00 2001 From: Matthew Tancik Date: Sun, 2 Jul 2023 21:36:28 -0700 Subject: [PATCH 13/44] Default to tcnn (#2159) --- nerfstudio/field_components/encodings.py | 2 +- nerfstudio/fields/density_fields.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nerfstudio/field_components/encodings.py b/nerfstudio/field_components/encodings.py index 48ec17556..652d387f5 100644 --- a/nerfstudio/field_components/encodings.py +++ b/nerfstudio/field_components/encodings.py @@ -264,7 +264,7 @@ class HashEncoding(Encoding): log2_hashmap_size: int = 19, features_per_level: int = 2, hash_init_scale: float = 0.001, - implementation: Literal["tcnn", "torch"] = "torch", + implementation: Literal["tcnn", "torch"] = "tcnn", interpolation: Optional[Literal["Nearest", "Linear", "Smoothstep"]] = None, ) -> None: super().__init__(in_dim=3) diff --git a/nerfstudio/fields/density_fields.py b/nerfstudio/fields/density_fields.py index 75fc5dc78..0837e6382 100644 --- a/nerfstudio/fields/density_fields.py +++ b/nerfstudio/fields/density_fields.py @@ -56,7 +56,7 @@ class HashMLPDensityField(Field): base_res: int = 16, log2_hashmap_size: int = 18, features_per_level: int = 2, - implementation: Literal["tcnn", "torch"] = "torch", + implementation: Literal["tcnn", "torch"] = "tcnn", ) -> None: super().__init__() self.register_buffer("aabb", aabb) From 8ed2a2d6675a28795485e04b92f576a5c5850650 Mon Sep 17 00:00:00 2001 From: Daniel Crispell Date: Mon, 3 Jul 2023 17:00:38 -0400 Subject: [PATCH 14/44] Make all SDFFieldConfig params configurable via constructor (#2167) Co-authored-by: Brent Yi --- nerfstudio/fields/sdf_field.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nerfstudio/fields/sdf_field.py b/nerfstudio/fields/sdf_field.py index a6d222933..2f730d2c5 100644 --- a/nerfstudio/fields/sdf_field.py +++ b/nerfstudio/fields/sdf_field.py @@ -97,19 +97,19 @@ class SDFFieldConfig(FieldConfig): beta_init: float = 0.1 """Init learnable beta value for transformation of sdf to density""" encoding_type: Literal["hash", "periodic", "tensorf_vm"] = "hash" - num_levels = 16 + num_levels: int = 16 """Number of encoding levels""" - max_res = 2048 + max_res: int = 2048 """Maximum resolution of the encoding""" - base_res = 16 + base_res: int = 16 """Base resolution of the encoding""" - log2_hashmap_size = 19 + log2_hashmap_size: int = 19 """Size of the hash map""" - features_per_level = 2 + features_per_level: int = 2 """Number of features per encoding level""" - use_hash = True + use_hash: bool = True """Whether to use hash encoding""" - smoothstep = True + smoothstep: bool = True """Whether to use the smoothstep function""" From 18ab50dc6b5ea7f6882a018876e64f2d5c2522e0 Mon Sep 17 00:00:00 2001 From: KaerMorh <30862059+KaerMorh@users.noreply.github.com> Date: Tue, 4 Jul 2023 23:14:15 +0800 Subject: [PATCH 15/44] Correct path notation for Windows in documentation (#2171) Fixed an error in the documentation where the incorrect path notation was used for Windows systems. The documentation had paths denoted with "./", which is incorrect for Windows. This has been corrected to ".\", the appropriate notation for Windows. This change ensures that Windows users correctly follow the instructions in the documentation without encountering any path-related issues. --- docs/quickstart/custom_dataset.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/quickstart/custom_dataset.md b/docs/quickstart/custom_dataset.md index decc968cf..f3c825cc1 100644 --- a/docs/quickstart/custom_dataset.md +++ b/docs/quickstart/custom_dataset.md @@ -106,8 +106,8 @@ cd vcpkg ```bash git clone https://github.com/microsoft/vcpkg cd vcpkg -./bootstrap-vcpkg.bat -./vcpkg install colmap[cuda]:x64-windows +.\bootstrap-vcpkg.bat +.\vcpkg install colmap[cuda]:x64-windows ``` ::: @@ -116,8 +116,8 @@ cd vcpkg ```bash git clone https://github.com/microsoft/vcpkg cd vcpkg -./bootstrap-vcpkg.sh -./vcpkg install colmap:x64-windows +.\bootstrap-vcpkg.sh +.\vcpkg install colmap:x64-windows ``` ::: From c7def8ce8cb42fc2869a31ec886e5797e076489a Mon Sep 17 00:00:00 2001 From: Weihang Li Date: Tue, 4 Jul 2023 23:07:04 +0200 Subject: [PATCH 16/44] Fix image saving format issue in render (#2172) * fix scannet dataparser pose issue * camera pose corrected * fix image format saving issue --- nerfstudio/scripts/render.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nerfstudio/scripts/render.py b/nerfstudio/scripts/render.py index aef5d4909..60378616d 100644 --- a/nerfstudio/scripts/render.py +++ b/nerfstudio/scripts/render.py @@ -469,6 +469,7 @@ class RenderInterpolated(BaseRender): rendered_resolution_scaling_factor=1.0 / self.downscale_factor, seconds=seconds, output_format=self.output_format, + image_format=self.image_format, colormap_options=self.colormap_options, ) @@ -511,6 +512,7 @@ class SpiralRender(BaseRender): rendered_resolution_scaling_factor=1.0 / self.downscale_factor, seconds=self.seconds, output_format=self.output_format, + image_format=self.image_format, colormap_options=self.colormap_options, ) From 77cf4c88edc15b82ff0b5aa2b36b839b51766279 Mon Sep 17 00:00:00 2001 From: Weibo Gao <929817104@qq.com> Date: Fri, 7 Jul 2023 01:38:10 +0800 Subject: [PATCH 17/44] fix the ZeroDivisionError occured in int(image_width / aspect_ratio) when aspect_ratio equals zero (#2177) fix the ZeroDivisionError occured in int(image_width / aspect_ratio) Co-authored-by: Web --- nerfstudio/viewer/server/render_state_machine.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nerfstudio/viewer/server/render_state_machine.py b/nerfstudio/viewer/server/render_state_machine.py index 540a8378b..390390d48 100644 --- a/nerfstudio/viewer/server/render_state_machine.py +++ b/nerfstudio/viewer/server/render_state_machine.py @@ -243,6 +243,8 @@ class RenderStateMachine(threading.Thread): image_height: the maximum image height that can be rendered in the time budget image_width: the maximum image width that can be rendered in the time budget """ + if aspect_ratio == 0: + aspect_ratio = 0.001 max_res = self.viewer.control_panel.max_res if self.state == "high": # high res is always static From 492fa0e67b238a1ca3158a37845aec313e5deb23 Mon Sep 17 00:00:00 2001 From: Georg Hess Date: Thu, 6 Jul 2023 19:55:22 +0200 Subject: [PATCH 18/44] Remove pytorch hash table if tcnn implementation is used (#2175) * fixes div by 1 error * removes pytorch hash_table if tcnn is used * formatting * test fixes --------- Co-authored-by: Matthew Tancik --- nerfstudio/field_components/encodings.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/nerfstudio/field_components/encodings.py b/nerfstudio/field_components/encodings.py index 652d387f5..6e0158654 100644 --- a/nerfstudio/field_components/encodings.py +++ b/nerfstudio/field_components/encodings.py @@ -274,18 +274,18 @@ class HashEncoding(Encoding): self.hash_table_size = 2**log2_hashmap_size levels = torch.arange(num_levels) - growth_factor = np.exp((np.log(max_res) - np.log(min_res)) / (num_levels - 1)) + growth_factor = np.exp((np.log(max_res) - np.log(min_res)) / (num_levels - 1)) if num_levels > 1 else 1 self.scalings = torch.floor(min_res * growth_factor**levels) self.hash_offset = levels * self.hash_table_size - self.hash_table = torch.rand(size=(self.hash_table_size * num_levels, features_per_level)) * 2 - 1 - self.hash_table *= hash_init_scale - self.hash_table = nn.Parameter(self.hash_table) self.tcnn_encoding = None + self.hash_table = torch.empty(0) if implementation == "tcnn" and not TCNN_EXISTS: print_tcnn_speed_warning("HashEncoding") - elif implementation == "tcnn": + implementation = "torch" + + if implementation == "tcnn": encoding_config = { "otype": "HashGrid", "n_levels": self.num_levels, @@ -301,6 +301,10 @@ class HashEncoding(Encoding): n_input_dims=3, encoding_config=encoding_config, ) + elif implementation == "torch": + self.hash_table = torch.rand(size=(self.hash_table_size * num_levels, features_per_level)) * 2 - 1 + self.hash_table *= hash_init_scale + self.hash_table = nn.Parameter(self.hash_table) if self.tcnn_encoding is None: assert ( From b86a4251a95f220a2e3988d67a6c4c92983ba9a3 Mon Sep 17 00:00:00 2001 From: Ilya Basharov Date: Sat, 8 Jul 2023 01:01:14 +0300 Subject: [PATCH 19/44] Feature/run_beta_viewer (#2157) * add `build-arg` cuda version to dockerfile * [Feature] add 'vis' option to 'ns-viewer' script * fix pylint * [Fix] fix data format --------- Co-authored-by: Brent Yi Co-authored-by: Matthew Tancik --- nerfstudio/scripts/viewer/run_viewer.py | 35 ++++++++++++++++++------- nerfstudio/viewer_beta/viewer.py | 4 +-- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/nerfstudio/scripts/viewer/run_viewer.py b/nerfstudio/scripts/viewer/run_viewer.py index 0600a9107..c4ebefacb 100644 --- a/nerfstudio/scripts/viewer/run_viewer.py +++ b/nerfstudio/scripts/viewer/run_viewer.py @@ -22,6 +22,8 @@ import time from dataclasses import dataclass, field, fields from pathlib import Path +from typing import Literal + import tyro from nerfstudio.configs.base_config import ViewerConfig @@ -30,6 +32,7 @@ from nerfstudio.pipelines.base_pipeline import Pipeline from nerfstudio.utils import writer from nerfstudio.utils.eval_utils import eval_setup from nerfstudio.viewer.server.viewer_state import ViewerState +from nerfstudio.viewer_beta.viewer import Viewer as ViewerBetaState @dataclass @@ -51,6 +54,8 @@ class RunViewer: """Path to config YAML file.""" viewer: ViewerConfigWithoutNumRays = field(default_factory=ViewerConfigWithoutNumRays) """Viewer configuration""" + vis: Literal["viewer", "viewer_beta"] = "viewer" + """Type of viewer""" def main(self) -> None: """Main function.""" @@ -61,7 +66,7 @@ class RunViewer: ) num_rays_per_chunk = config.viewer.num_rays_per_chunk assert self.viewer.num_rays_per_chunk == -1 - config.vis = "viewer" + config.vis = self.vis config.viewer = self.viewer.as_viewer_config() config.viewer.num_rays_per_chunk = num_rays_per_chunk @@ -83,13 +88,24 @@ def _start_viewer(config: TrainerConfig, pipeline: Pipeline, step: int): """ base_dir = config.get_base_dir() viewer_log_path = base_dir / config.viewer.relative_log_filename - viewer_state = ViewerState( - config.viewer, - log_filename=viewer_log_path, - datapath=pipeline.datamanager.get_datapath(), - pipeline=pipeline, - ) - banner_messages = [f"Viewer at: {viewer_state.viewer_url}"] + banner_messages = None + viewer_state = None + if config.vis == "viewer": + viewer_state = ViewerState( + config.viewer, + log_filename=viewer_log_path, + datapath=pipeline.datamanager.get_datapath(), + pipeline=pipeline, + ) + banner_messages = [f"Viewer at: {viewer_state.viewer_url}"] + if config.vis == "viewer_beta": + viewer_state = ViewerBetaState( + config.viewer, + log_filename=viewer_log_path, + datapath=base_dir, + pipeline=pipeline, + ) + banner_messages = [f"Viewer Beta at: {viewer_state.viewer_url}"] # We don't need logging, but writer.GLOBAL_BUFFER needs to be populated config.logging.local_writer.enable = False @@ -100,7 +116,8 @@ def _start_viewer(config: TrainerConfig, pipeline: Pipeline, step: int): dataset=pipeline.datamanager.train_dataset, train_state="completed", ) - viewer_state.viser_server.set_training_state("completed") + if isinstance(viewer_state, ViewerState): + viewer_state.viser_server.set_training_state("completed") viewer_state.update_scene(step=step) while True: time.sleep(0.01) diff --git a/nerfstudio/viewer_beta/viewer.py b/nerfstudio/viewer_beta/viewer.py index 51ca6ab14..4c007f082 100644 --- a/nerfstudio/viewer_beta/viewer.py +++ b/nerfstudio/viewer_beta/viewer.py @@ -118,8 +118,8 @@ class Viewer: def _(_: viser.CameraHandle) -> None: R = vtf.SO3(wxyz=client.camera.wxyz) R = R @ vtf.SO3.from_x_radians(np.pi) - R = torch.tensor(R.as_matrix()) - pos = torch.tensor(client.camera.position, dtype=torch.float64) / VISER_NERFSTUDIO_SCALE_RATIO + R = torch.tensor(R.as_matrix(), dtype=torch.float32) + pos = torch.tensor(client.camera.position, dtype=torch.float32) / VISER_NERFSTUDIO_SCALE_RATIO c2w = torch.concatenate([R, pos[:, None]], dim=1) self.camera_state = CameraState(fov=client.camera.fov, aspect=client.camera.aspect, c2w=c2w) self.render_statemachine.action(RenderAction("move", self.camera_state)) From 82b3f70f87833f5a3365049956e0f4105d586fe9 Mon Sep 17 00:00:00 2001 From: machenmusik Date: Mon, 10 Jul 2023 18:09:44 -0400 Subject: [PATCH 20/44] Avoid zero duration crash (#2193) Avoid zero duration divide-by-zero crash when processing too quickly. --- nerfstudio/engine/trainer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nerfstudio/engine/trainer.py b/nerfstudio/engine/trainer.py index 0ce05db0b..0e194b631 100644 --- a/nerfstudio/engine/trainer.py +++ b/nerfstudio/engine/trainer.py @@ -266,7 +266,7 @@ class Trainer: name=EventName.TRAIN_RAYS_PER_SEC, duration=self.world_size * self.pipeline.datamanager.get_train_rays_per_batch() - / train_t.duration, + / max(0.001, train_t.duration), step=step, avg_over_steps=True, ) From 904e1352d94eca787a64ad6bef23d612f5ef2cde Mon Sep 17 00:00:00 2001 From: Jan-Niklas Dihlmann Date: Tue, 11 Jul 2023 16:51:03 +0200 Subject: [PATCH 21/44] Fixing Colmap VocabTreeMatching command to support spaces in Filename (#2195) fix: colmap vocab tree file path error --- nerfstudio/process_data/colmap_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nerfstudio/process_data/colmap_utils.py b/nerfstudio/process_data/colmap_utils.py index bccdaa2f1..cae229922 100644 --- a/nerfstudio/process_data/colmap_utils.py +++ b/nerfstudio/process_data/colmap_utils.py @@ -138,7 +138,7 @@ def run_colmap( ] if matching_method == "vocab_tree": vocab_tree_filename = get_vocab_tree() - feature_matcher_cmd.append(f"--VocabTreeMatching.vocab_tree_path {vocab_tree_filename}") + feature_matcher_cmd.append(f"--VocabTreeMatching.vocab_tree_path '{vocab_tree_filename}'") feature_matcher_cmd = " ".join(feature_matcher_cmd) with status(msg="[bold yellow]Running COLMAP feature matcher...", spinner="runner", verbose=verbose): run_command(feature_matcher_cmd, verbose=verbose) From e9a5a66a5487067115983a3320ef945c65f09b09 Mon Sep 17 00:00:00 2001 From: hyperzy <50564821+hyperzy@users.noreply.github.com> Date: Wed, 12 Jul 2023 00:02:48 +0800 Subject: [PATCH 22/44] Default fied_heads with instances will cause shared parameters (#2197) * - Bug report. Modified the default parameters of NeRFField to avoid shared RGBFieldHead * - Modified to ensure minimal changes * - Modified type checking --- nerfstudio/fields/vanilla_nerf_field.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nerfstudio/fields/vanilla_nerf_field.py b/nerfstudio/fields/vanilla_nerf_field.py index 96ef0dc56..c9d2bbb78 100644 --- a/nerfstudio/fields/vanilla_nerf_field.py +++ b/nerfstudio/fields/vanilla_nerf_field.py @@ -15,7 +15,7 @@ """Classic NeRF field""" -from typing import Dict, Optional, Tuple +from typing import Dict, Optional, Tuple, Type import torch from torch import Tensor, nn @@ -57,7 +57,7 @@ class NeRFField(Field): head_mlp_num_layers: int = 2, head_mlp_layer_width: int = 128, skip_connections: Tuple[int] = (4,), - field_heads: Optional[Tuple[FieldHead]] = (RGBFieldHead(),), + field_heads: Optional[Tuple[Type[FieldHead]]] = (RGBFieldHead,), use_integrated_encoding: bool = False, spatial_distortion: Optional[SpatialDistortion] = None, ) -> None: @@ -83,7 +83,7 @@ class NeRFField(Field): ) self.field_output_density = DensityFieldHead(in_dim=self.mlp_base.get_out_dim()) - self.field_heads = nn.ModuleList(field_heads) + self.field_heads = nn.ModuleList([field_head() for field_head in field_heads] if field_heads else []) # type: ignore for field_head in self.field_heads: field_head.set_in_dim(self.mlp_head.get_out_dim()) # type: ignore From 70adb01b4cd0efc2a5be5657e54bf012021a9150 Mon Sep 17 00:00:00 2001 From: Paul-Edouard Sarlin Date: Tue, 11 Jul 2023 19:25:07 +0200 Subject: [PATCH 23/44] Pin versions of pyceres+pixsfm, up pycolmap (#2199) * Pin versions of pyceres+pixsfm, up pycolmap * Update comment --- Dockerfile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index f40c2404b..966db1cbc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -125,9 +125,8 @@ RUN CUDA_VER=${CUDA_VERSION%.*} && CUDA_VER=${CUDA_VER//./} && python3.10 -m pip ENV TCNN_CUDA_ARCHITECTURES=${CUDA_ARCHITECTURES} RUN python3.10 -m pip install git+https://github.com/NVlabs/tiny-cuda-nn.git@v1.6#subdirectory=bindings/torch -# Install pycolmap 0.3.0, required by hloc. -# TODO(https://github.com/colmap/pycolmap/issues/111) use wheel when available for Python 3.10 -RUN git clone --branch v0.3.0 --recursive https://github.com/colmap/pycolmap.git && \ +# Install pycolmap, required by hloc. +RUN git clone --branch v0.4.0 --recursive https://github.com/colmap/pycolmap.git && \ cd pycolmap && \ python3.10 -m pip install . && \ cd .. @@ -139,13 +138,13 @@ RUN git clone --branch master --recursive https://github.com/cvg/Hierarchical-Lo cd .. # Install pyceres from source -RUN git clone --branch main --recursive https://github.com/cvg/pyceres.git && \ +RUN git clone --branch v1.0 --recursive https://github.com/cvg/pyceres.git && \ cd pyceres && \ python3.10 -m pip install -e . && \ cd .. # Install pixel perfect sfm. -RUN git clone --branch main --recursive https://github.com/cvg/pixel-perfect-sfm.git && \ +RUN git clone --branch v1.0 --recursive https://github.com/cvg/pixel-perfect-sfm.git && \ cd pixel-perfect-sfm && \ python3.10 -m pip install -e . && \ cd .. From 6cb10ae682030b2d66bb45049b3d765002e24948 Mon Sep 17 00:00:00 2001 From: Riley Date: Tue, 11 Jul 2023 15:56:12 -0700 Subject: [PATCH 24/44] Adding input handling for RFFEncoding's get_out_dim(self) (#2201) fixed RFFEncoding's get_out_dim(self) to incude dims of input if include_input --- nerfstudio/field_components/encodings.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nerfstudio/field_components/encodings.py b/nerfstudio/field_components/encodings.py index 6e0158654..7145ae599 100644 --- a/nerfstudio/field_components/encodings.py +++ b/nerfstudio/field_components/encodings.py @@ -208,7 +208,12 @@ class RFFEncoding(Encoding): self.include_input = include_input def get_out_dim(self) -> int: - return self.num_frequencies * 2 + out_dim = self.num_frequencies * 2 + if self.include_input: + if self.in_dim is None: + raise ValueError("Input dimension has not been set") + out_dim += self.in_dim + return out_dim def forward( self, From 49a4be635dc7b0f459729919fcbc06faba83fec9 Mon Sep 17 00:00:00 2001 From: machenmusik Date: Wed, 12 Jul 2023 10:03:23 -0400 Subject: [PATCH 25/44] Fix colmap vocab_tree_path (#2195) to work with Windows (#2204) * Fix colmap vocab_tree_path to work with Windows * black --- nerfstudio/process_data/colmap_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nerfstudio/process_data/colmap_utils.py b/nerfstudio/process_data/colmap_utils.py index cae229922..ac15d0937 100644 --- a/nerfstudio/process_data/colmap_utils.py +++ b/nerfstudio/process_data/colmap_utils.py @@ -138,7 +138,7 @@ def run_colmap( ] if matching_method == "vocab_tree": vocab_tree_filename = get_vocab_tree() - feature_matcher_cmd.append(f"--VocabTreeMatching.vocab_tree_path '{vocab_tree_filename}'") + feature_matcher_cmd.append(f'--VocabTreeMatching.vocab_tree_path "{vocab_tree_filename}"') feature_matcher_cmd = " ".join(feature_matcher_cmd) with status(msg="[bold yellow]Running COLMAP feature matcher...", spinner="runner", verbose=verbose): run_command(feature_matcher_cmd, verbose=verbose) From cebe787bedffd4966fdcb067682da64e168b5565 Mon Sep 17 00:00:00 2001 From: hyperzy <50564821+hyperzy@users.noreply.github.com> Date: Wed, 12 Jul 2023 22:14:59 +0800 Subject: [PATCH 26/44] Add saving/loading scheduler state_dict (#2203) - Add saving/loading scheduler stat_dict --- nerfstudio/engine/optimizers.py | 9 +++++++++ nerfstudio/engine/trainer.py | 2 ++ 2 files changed, 11 insertions(+) diff --git a/nerfstudio/engine/optimizers.py b/nerfstudio/engine/optimizers.py index 3ff693685..3e147dcdc 100644 --- a/nerfstudio/engine/optimizers.py +++ b/nerfstudio/engine/optimizers.py @@ -159,3 +159,12 @@ class Optimizers: """ for k, v in loaded_state.items(): self.optimizers[k].load_state_dict(v) + + def load_schedulers(self, loaded_state: Dict[str, Any]) -> None: + """Helper to load the scheduler state from previous checkpoint + + Args: + loaded_state: the state from the previous checkpoint + """ + for k, v in loaded_state.items(): + self.schedulers[k].load_state_dict(v) diff --git a/nerfstudio/engine/trainer.py b/nerfstudio/engine/trainer.py index 0e194b631..a89e4801c 100644 --- a/nerfstudio/engine/trainer.py +++ b/nerfstudio/engine/trainer.py @@ -406,6 +406,7 @@ class Trainer: # load the checkpoints for pipeline, optimizers, and gradient scalar self.pipeline.load_pipeline(loaded_state["pipeline"], loaded_state["step"]) self.optimizers.load_optimizers(loaded_state["optimizers"]) + self.optimizers.load_schedulers(loaded_state["schedulers"]) self.grad_scaler.load_state_dict(loaded_state["scalers"]) CONSOLE.print(f"Done loading Nerfstudio checkpoint from {load_path}") elif load_checkpoint is not None: @@ -439,6 +440,7 @@ class Trainer: if hasattr(self.pipeline, "module") else self.pipeline.state_dict(), "optimizers": {k: v.state_dict() for (k, v) in self.optimizers.optimizers.items()}, + "schedulers": {k: v.state_dict() for (k, v) in self.optimizers.schedulers.items()}, "scalers": self.grad_scaler.state_dict(), }, ckpt_path, From 554dec77f20fdc88b3465e86c172a562a296d4fa Mon Sep 17 00:00:00 2001 From: Cyrus Vachha Date: Thu, 13 Jul 2023 07:59:10 -0700 Subject: [PATCH 27/44] Omnidirectional Stereo Update - Alignment and Compositing (#2217) Update to omnidirectional stereo rendering (VR 360) to fix slight positional alignment to accurately composite with Blender objects. Modified documentation for ODS and the Blender add-on to include details on Blender camera settings to composite with the Nerfstudio ODS camera model. --- docs/extensions/blender_addon.md | 8 ++++++-- docs/quickstart/custom_dataset.md | 10 ++++++---- nerfstudio/cameras/cameras.py | 3 +-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/extensions/blender_addon.md b/docs/extensions/blender_addon.md index 8e2fa4bd2..b433fd2cf 100644 --- a/docs/extensions/blender_addon.md +++ b/docs/extensions/blender_addon.md @@ -6,7 +6,7 @@ ## Overview -This Blender add-on allows for compositing with a Nerfstudio render as a background layer by generating a camera path JSON file from the Blender camera path, as well as a way to import Nerfstudio JSON files as a Blender camera baked with the Nerfstudio camera path. This add-on also allows compositing multiple NeRF objects into a NeRF scene. This is achieved by importing a mesh or point-cloud representation of the NeRF scene from Nerfstudio into Blender and getting the camera coordinates relative to the transformations of the NeRF representation. Dynamic FOV from the Blender camera is supported and will match the Nerfstudio render. Perspective and equirectangular cameras are supported. +This Blender add-on allows for compositing with a Nerfstudio render as a background layer by generating a camera path JSON file from the Blender camera path, as well as a way to import Nerfstudio JSON files as a Blender camera baked with the Nerfstudio camera path. This add-on also allows compositing multiple NeRF objects into a NeRF scene. This is achieved by importing a mesh or point-cloud representation of the NeRF scene from Nerfstudio into Blender and getting the camera coordinates relative to the transformations of the NeRF representation. Dynamic FOV from the Blender camera is supported and will match the Nerfstudio render. Perspective, equirectangular, and omnidirectional stereo (VR 360) cameras are supported.
image @@ -89,7 +89,7 @@ This Blender add-on allows for compositing with a Nerfstudio render as a backgro - The generated JSON camera path follows the user specified frame start, end, and step fields in the Output Properties in Blender. The JSON file specifies the user specified x and y render resolutions at the given % in the Output Properties. - The add-on computes the camera coordinates based on the active camera in the scene. - FOV animated changes of the camera will be matched with the NeRF render. -- Perspective and equirectangular cameras are supported and can be selected within Blender. +- Perspective, equirectangular, and omnidirectional stereo cameras are supported and can be selected within Blender. - The generated JSON file can be imported into Nerfstudio. Each keyframe of the camera transform in the frame sequence in Blender will be a keyframe in Nerfstudio. The exported JSON camera path is baked, where each frame in the sequence is a keyframe. This is to ensure that frame interpolation across Nerfstudio and Blender do not differ.
image @@ -101,6 +101,10 @@ This Blender add-on allows for compositing with a Nerfstudio render as a backgro - It is recommended to export a high fidelity mesh as the NeRF representation from Nerfstudio. However if that is not possible or the scene is too large, a point cloud representation also works. - For compositing, it is recommended to convert the video render into image frames to ensure the Blender and NeRF renders are in sync. - Currently, dynamic camera focus is not supported. +- For compositing Blender objects or NeRFs with Nerfstudio omnidirectional stereo renders (VR 360), configure the Blender camera to be equirectangular and enable stereoscopy in the Output Properties. Under the Stereoscopy panel the Blender camera settings, change the mode to "Parallel", set the Interocular Distance to 0.064 m, and checkmark "Spherical Stereo". +
+ image +
- Fisheye and orthographic cameras are not supported. - A walkthrough of this section is included in the tutorial video. diff --git a/docs/quickstart/custom_dataset.md b/docs/quickstart/custom_dataset.md index f3c825cc1..4792aeb72 100644 --- a/docs/quickstart/custom_dataset.md +++ b/docs/quickstart/custom_dataset.md @@ -355,7 +355,7 @@ Stereo equirectangular rendering for VR video is supported as omni-directional s
-To render in ODS it is essential to adjust the NeRF to have an approximately true-to-life real world scale (adjustable in the camera path) to ensure that the scene depth and IPD (distance between the eyes) is appropriate for the render to be viewable in VR. You can adjust the scene scale with the [Blender Add-on](https://docs.nerf.studio/en/latest/extensions/blender_addon.html) by appropriately scaling a point cloud representation of the NeRF. +To render in ODS it is essential to adjust the NeRF to have an approximately true-to-life real world scale (adjustable in the camera path) to ensure that the scene depth and IPD (distance between the eyes) is appropriate for the render to be viewable in VR. You can adjust the scene scale with the [Nerfstudio Blender Add-on](https://docs.nerf.studio/en/latest/extensions/blender_addon.html) by appropriately scaling a point cloud representation of the NeRF. Results may be unviewable if the scale is not set appropriately. The IPD is set at 64mm by default but only is accurate when the NeRF scene is true to scale. For good quality renders, it is recommended to render at 4096x2048 per eye, or 2048x1024 per eye. Render resolutions must be specified in the camera path in a 2:1 aspect ratio. The final stacked render output will automatically be constructed as 1:1. @@ -366,12 +366,12 @@ If you are rendering an image sequence, it is recommended to render as png inste ::: To render with the ODS camera: -1. Use the Nerfstudio Blender Add-on to set the scale of the NeRF scene and create the camera path +1. Use the [Nerfstudio Blender Add-on](https://docs.nerf.studio/en/latest/extensions/blender_addon.html) to set the scale of the NeRF scene and create the camera path - Export a point cloud representation of the NeRF - - Import the point cloud representation in Blender and enable the Blender Add-on + - Import the point cloud representation in Blender and enable the Nerfstudio Blender Add-on - Create a reference object such as a cube which may be 1x1x1 meter. You could also create a cylinder and scale it to an appropriate height of a viewer. - Now scale the point cloud representation accordingly to match the reference object. This is to ensure that the NeRF scene is scaled as close to real life. - - To place the camera at the correct height from the ground in the scene, you create a cylinder representing the viewer vertically scaled to the viewer’s height, and place the camera at eye level. + - To place the camera at the correct height from the ground in the scene, you can create a cylinder representing the viewer vertically scaled to the viewer’s height, and place the camera at eye level. - Animate the camera movement as needed - Create the camera path JSON file with the Nerfstudio Blender Add-on 2. Edit the JSON camera path file @@ -392,3 +392,5 @@ If the depth of the scene is unviewable and looks too close or expanded when vie - When rendering ODS content, Nerfstudio will first render the left eye, then the right eye, and finally vertically stack the renders. During this process, Nerfstudio will create a temporary folder to store the left and right eye renders and delete this folder once the final renders are stacked. - If rendering content where the camera is stationary for many frames, it is recommended to only render once at that position and extend the time in a video editor since ODS renders can take a lot of time to render. - It is recommended to render a preliminary render at a much lower resolution or frame rate to test and ensure that the depth and camera position look accurate in VR. + - The IPD can be modified in the `cameras.py` script as the variable `vr_ipd` (default is 64 mm). + - If using the Nerfstudio Blender Add-on to composite Blender objects or NeRFs with the omnidirectional render, change the Stereoscopy mode to "Parallel" set the Interocular Distance to 0.064 m. diff --git a/nerfstudio/cameras/cameras.py b/nerfstudio/cameras/cameras.py index 47b7c419c..c9886d768 100644 --- a/nerfstudio/cameras/cameras.py +++ b/nerfstudio/cameras/cameras.py @@ -694,8 +694,7 @@ class Cameras(TensorDataclass): # circle of ODS ray origins ods_origins_circle = ( - ods_cam_position - + isRightEye * (vr_ipd / 2.0) * (ods_x_axis.repeat(c2w.shape[1], 1)) * (torch.cos(ods_theta))[:, None] + isRightEye * (vr_ipd / 2.0) * (ods_x_axis.repeat(c2w.shape[1], 1)) * (torch.cos(ods_theta))[:, None] + isRightEye * (vr_ipd / 2.0) * (ods_z_axis.repeat(c2w.shape[1], 1)) * (torch.sin(ods_theta))[:, None] ) From 7acaed11a0e740994aab73be4e83f7f4c3fd7f64 Mon Sep 17 00:00:00 2001 From: Victor Rong <47129420+notagoose@users.noreply.github.com> Date: Thu, 13 Jul 2023 15:01:53 -0700 Subject: [PATCH 28/44] Reorient point cloud normals for better surface reconstruction (#2220) Implemented normal reorientation --- nerfstudio/exporter/exporter_utils.py | 17 +++++++++++++++++ nerfstudio/scripts/exporter.py | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/nerfstudio/exporter/exporter_utils.py b/nerfstudio/exporter/exporter_utils.py index 68211b5c3..21ea545b9 100644 --- a/nerfstudio/exporter/exporter_utils.py +++ b/nerfstudio/exporter/exporter_utils.py @@ -85,6 +85,7 @@ def generate_point_cloud( num_points: int = 1000000, remove_outliers: bool = True, estimate_normals: bool = False, + reorient_normals: bool = False, rgb_output_name: str = "rgb", depth_output_name: str = "depth", normal_output_name: Optional[str] = None, @@ -99,6 +100,7 @@ def generate_point_cloud( pipeline: Pipeline to evaluate with. num_points: Number of points to generate. May result in less if outlier removal is used. remove_outliers: Whether to remove outliers. + reorient_normals: Whether to re-orient the normals based on the view direction. estimate_normals: Whether to estimate normals. rgb_output_name: Name of the RGB output. depth_output_name: Name of the depth output. @@ -122,6 +124,8 @@ def generate_point_cloud( points = [] rgbs = [] normals = [] + view_directions = [] + with progress as progress_bar: task = progress_bar.add_task("Generating Point Cloud", total=num_points) while not progress_bar.finished: @@ -154,6 +158,7 @@ def generate_point_cloud( ), "Normal values from method output must be in [0, 1]" normal = (normal * 2.0) - 1.0 point = ray_bundle.origins + ray_bundle.directions * depth + view_direction = ray_bundle.directions if use_bounding_box: comp_l = torch.tensor(bounding_box_min, device=point.device) @@ -164,16 +169,19 @@ def generate_point_cloud( mask = torch.all(torch.concat([point > comp_l, point < comp_m], dim=-1), dim=-1) point = point[mask] rgb = rgb[mask] + view_direction = view_direction[mask] if normal is not None: normal = normal[mask] points.append(point) rgbs.append(rgb) + view_directions.append(view_direction) if normal is not None: normals.append(normal) progress.advance(task, point.shape[0]) points = torch.cat(points, dim=0) rgbs = torch.cat(rgbs, dim=0) + view_directions = torch.cat(view_directions, dim=0).cpu() pcd = o3d.geometry.PointCloud() pcd.points = o3d.utility.Vector3dVector(points.double().cpu().numpy()) @@ -185,6 +193,8 @@ def generate_point_cloud( pcd, ind = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=std_ratio) print("\033[A\033[A") CONSOLE.print("[bold green]:white_check_mark: Cleaning Point Cloud") + if ind is not None: + view_directions = view_directions[ind] # either estimate_normals or normal_output_name, not both if estimate_normals: @@ -203,6 +213,13 @@ def generate_point_cloud( normals = normals[ind] pcd.normals = o3d.utility.Vector3dVector(normals.double().cpu().numpy()) + # re-orient the normals + if reorient_normals: + normals = torch.from_numpy(np.array(pcd.normals)).float() + mask = torch.sum(view_directions * normals, dim=-1) > 0 + normals[mask] *= -1 + pcd.normals = o3d.utility.Vector3dVector(normals.double().cpu().numpy()) + return pcd diff --git a/nerfstudio/scripts/exporter.py b/nerfstudio/scripts/exporter.py index 153ab4e4b..d5ccd82bb 100644 --- a/nerfstudio/scripts/exporter.py +++ b/nerfstudio/scripts/exporter.py @@ -97,6 +97,8 @@ class ExportPointCloud(Exporter): """Number of points to generate. May result in less if outlier removal is used.""" remove_outliers: bool = True """Remove outliers from the point cloud.""" + reorient_normals: bool = True + """Reorient point cloud normals based on view direction.""" normal_method: Literal["open3d", "model_output"] = "model_output" """Method to estimate normals with.""" normal_output_name: str = "normals" @@ -138,6 +140,7 @@ class ExportPointCloud(Exporter): pipeline=pipeline, num_points=self.num_points, remove_outliers=self.remove_outliers, + reorient_normals=self.reorient_normals, estimate_normals=estimate_normals, rgb_output_name=self.rgb_output_name, depth_output_name=self.depth_output_name, @@ -243,6 +246,8 @@ class ExportPoissonMesh(Exporter): """Number of points to generate. May result in less if outlier removal is used.""" remove_outliers: bool = True """Remove outliers from the point cloud.""" + reorient_normals: bool = True + """Reorient point cloud normals based on view direction.""" depth_output_name: str = "depth" """Name of the depth output.""" rgb_output_name: str = "rgb" @@ -296,6 +301,7 @@ class ExportPoissonMesh(Exporter): pipeline=pipeline, num_points=self.num_points, remove_outliers=self.remove_outliers, + reorient_normals=self.reorient_normals, estimate_normals=estimate_normals, rgb_output_name=self.rgb_output_name, depth_output_name=self.depth_output_name, From 365e4c6663dac0c9c1dc19b1e54a386c0eafa8d0 Mon Sep 17 00:00:00 2001 From: Jeff Cook Date: Thu, 13 Jul 2023 16:22:36 -0600 Subject: [PATCH 29/44] Try non-strict model loading on RuntimeError. ... (#2214) * Try non-strict model loading on RuntimeError. ... This allows tcnn models that contained a torch hash_table to load. In particular, it allows us to load tcnn models prior to PR #2175. cf. https://github.com/nerfstudio-project/nerfstudio/pull/2175 * Only allow non-strict loading when called with defaults. * Reflow to avoid code duplication; blacken. --------- Co-authored-by: Matthew Tancik --- nerfstudio/pipelines/base_pipeline.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/nerfstudio/pipelines/base_pipeline.py b/nerfstudio/pipelines/base_pipeline.py index 363c8c9ea..11ba841e0 100644 --- a/nerfstudio/pipelines/base_pipeline.py +++ b/nerfstudio/pipelines/base_pipeline.py @@ -106,7 +106,7 @@ class Pipeline(nn.Module): """Returns the device that the model is on.""" return self.model.device - def load_state_dict(self, state_dict: Mapping[str, Any], strict: bool = True): + def load_state_dict(self, state_dict: Mapping[str, Any], strict: Optional[bool] = None): is_ddp_model_state = True model_state = {} for key, value in state_dict.items(): @@ -122,7 +122,15 @@ class Pipeline(nn.Module): model_state = {key[len("module.") :]: value for key, value in model_state.items()} pipeline_state = {key: value for key, value in state_dict.items() if not key.startswith("_model.")} - self.model.load_state_dict(model_state, strict=strict) + + try: + self.model.load_state_dict(model_state, strict=True) + except RuntimeError: + if not strict: + self.model.load_state_dict(model_state, strict=False) + else: + raise + super().load_state_dict(pipeline_state, strict=False) @profiler.time_function @@ -410,7 +418,7 @@ class VanillaPipeline(Pipeline): (key[len("module.") :] if key.startswith("module.") else key): value for key, value in loaded_state.items() } self.model.update_to_step(step) - self.load_state_dict(state, strict=True) + self.load_state_dict(state) def get_training_callbacks( self, training_callback_attributes: TrainingCallbackAttributes From edbe319924349b907dc5fc4cfc189c61e57c38c5 Mon Sep 17 00:00:00 2001 From: Zhizheng Xiang <50263991+Xzzit@users.noreply.github.com> Date: Fri, 14 Jul 2023 07:43:11 +0900 Subject: [PATCH 30/44] Add documatations for TensoRF (#2209) * add tensorf intro video * add tensorf images * update images to adapt dark mode * add documentation for TensoRF * grammer check * add some anotations --------- Co-authored-by: Matthew Tancik --- .../tensorf/models_tensorf_factorization.png | Bin 0 -> 43785 bytes .../imgs/tensorf/models_tensorf_pipeline.png | Bin 0 -> 279312 bytes docs/nerfology/methods/tensorf.md | 16 +++++++++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 docs/nerfology/methods/imgs/tensorf/models_tensorf_factorization.png create mode 100644 docs/nerfology/methods/imgs/tensorf/models_tensorf_pipeline.png diff --git a/docs/nerfology/methods/imgs/tensorf/models_tensorf_factorization.png b/docs/nerfology/methods/imgs/tensorf/models_tensorf_factorization.png new file mode 100644 index 0000000000000000000000000000000000000000..8557a6c37ac0a005f3c331ab07ae72974476393a GIT binary patch literal 43785 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 (fX=CvK41$ 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

x?YrUFdXkqIDVCgWr|q|vQ716#O>Ubl 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 literal 0 HcmV?d00001 diff --git a/docs/nerfology/methods/tensorf.md b/docs/nerfology/methods/tensorf.md index d3b90786b..e418f5241 100644 --- a/docs/nerfology/methods/tensorf.md +++ b/docs/nerfology/methods/tensorf.md @@ -8,6 +8,10 @@ Paper Website ``` + + ### Running Model ```bash @@ -16,4 +20,14 @@ ns-train tensorf ## Overview -🚧 Guide under construction 🚧 +```{image} imgs/tensorf/models_tensorf_pipeline.png +:align: center +``` + +TensoRF models the radiance field of a scene as a 4D tensor, which represents a 3D voxel grid with per-voxel multi-channel features. TensoRF factorizes the 4D scene tensor into multiple compact low-rank tensor components using CP or VM modes. CP decomposition factorizes tensors into rank-one components with compact vectors. Vector-Matrix (VM) decomposition factorizes tensors into compact vector and matrix factors. + +```{image} imgs/tensorf/models_tensorf_factorization.png +:align: center +``` + +TensoRF with CP(left) and VM(right) decompositions results in a significantly reduced memory footprint compared to previous and concurrent works that directly optimize per-voxel features, such as [Plenoxels](https://alexyu.net/plenoxels/) and [PlenOctrees](https://alexyu.net/plenoctrees/). In experiments, TensoRF with CP decomposition achieves fast reconstruction with improved rendering quality and a smaller model size compared to NeRF. Furthermore, TensoRF with VM decomposition enhances rendering quality even further, while reducing reconstruction time and maintaining a compact model size. From 91291bc52c2501c8c93378382416edc2693647a8 Mon Sep 17 00:00:00 2001 From: machenmusik Date: Thu, 13 Jul 2023 18:54:19 -0400 Subject: [PATCH 31/44] Fast downscaling for video and image processing (PR2168 plus images) (#2213) * Merge video image extraction and downscaling * Linting * Fix extracting all frames, only apply vfr when thumnailing * integrate downscale chains into image processing * fast polycam upscale/downscale depth * black * black * Update process_data_utils.py Remove code change from other branch. * use same suffix for downscale, don't force png * black --------- Co-authored-by: Sebastiaan Meijer --- .../images_to_nerfstudio_dataset.py | 16 +- nerfstudio/process_data/polycam_utils.py | 17 +-- nerfstudio/process_data/process_data_utils.py | 141 ++++++++++++------ .../video_to_nerfstudio_dataset.py | 18 ++- nerfstudio/scripts/process_data.py | 16 +- 5 files changed, 129 insertions(+), 79 deletions(-) diff --git a/nerfstudio/process_data/images_to_nerfstudio_dataset.py b/nerfstudio/process_data/images_to_nerfstudio_dataset.py index 4c0a5bf03..dc613412a 100644 --- a/nerfstudio/process_data/images_to_nerfstudio_dataset.py +++ b/nerfstudio/process_data/images_to_nerfstudio_dataset.py @@ -60,16 +60,24 @@ class ImagesToNerfstudioDataset(ColmapConverterToNerfstudioDataset): if not self.skip_image_processing: # Copy images to output directory image_rename_map_paths = process_data_utils.copy_images( - self.data, image_dir=self.image_dir, crop_factor=self.crop_factor, verbose=self.verbose + self.data, + image_dir=self.image_dir, + crop_factor=self.crop_factor, + verbose=self.verbose, + num_downscales=self.num_downscales, ) image_rename_map = dict((a.name, b.name) for a, b in image_rename_map_paths.items()) num_frames = len(image_rename_map) summary_log.append(f"Starting with {num_frames} images") - # Downscale images - summary_log.append( - process_data_utils.downscale_images(self.image_dir, self.num_downscales, verbose=self.verbose) + # # Create mask + mask_path = process_data_utils.save_mask( + image_dir=self.image_dir, + num_downscales=self.num_downscales, + crop_factor=(0.0, 0.0, 0.0, 0.0), ) + if mask_path is not None: + summary_log.append("Saved mask(s)") else: num_frames = len(process_data_utils.list_images(self.data)) if num_frames == 0: diff --git a/nerfstudio/process_data/polycam_utils.py b/nerfstudio/process_data/polycam_utils.py index 416da15ad..0b1ba7a62 100644 --- a/nerfstudio/process_data/polycam_utils.py +++ b/nerfstudio/process_data/polycam_utils.py @@ -131,6 +131,7 @@ def process_images( image_dir=image_dir, crop_border_pixels=crop_border_pixels, verbose=verbose, + num_downscales=num_downscales, ) num_frames = len(copied_image_paths) @@ -145,9 +146,6 @@ def process_images( else: summary_log.append(f"Started with {num_frames} images") - # Downscale images - summary_log.append(process_data_utils.downscale_images(image_dir, num_downscales, verbose=verbose)) - # Save json if num_frames == 0: CONSOLE.print("[bold red]No images found, exiting") @@ -190,7 +188,11 @@ def process_depth_maps( # Copy depth images to output directory copied_depth_maps_paths = process_data_utils.copy_and_upscale_polycam_depth_maps_list( - polycam_depth_maps_filenames, depth_dir=depth_dir, crop_border_pixels=crop_border_pixels, verbose=verbose + polycam_depth_maps_filenames, + depth_dir=depth_dir, + num_downscales=num_downscales, + crop_border_pixels=crop_border_pixels, + verbose=verbose, ) num_processed_depth_maps = len(copied_depth_maps_paths) @@ -211,11 +213,4 @@ def process_depth_maps( else: summary_log.append(f"Started with {num_processed_depth_maps} images") - # Downscale depth maps - summary_log.append( - process_data_utils.downscale_images( - depth_dir, num_downscales, folder_name="depths", nearest_neighbor=True, verbose=verbose - ) - ) - return summary_log, polycam_depth_maps_filenames diff --git a/nerfstudio/process_data/process_data_utils.py b/nerfstudio/process_data/process_data_utils.py index 9bd835c3a..6d4cee094 100644 --- a/nerfstudio/process_data/process_data_utils.py +++ b/nerfstudio/process_data/process_data_utils.py @@ -111,6 +111,7 @@ def convert_video_to_images( video_path: Path, image_dir: Path, num_frames_target: int, + num_downscales: int, crop_factor: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 0.0), verbose: bool = False, ) -> Tuple[List[str], int]: @@ -120,6 +121,7 @@ def convert_video_to_images( video_path: Path to the video. output_dir: Path to the output directory. num_frames_target: Number of frames to extract. + num_downscales: Number of times to downscale the images. Downscales by 2 each time. crop_factor: Portion of the image to crop. Should be in [0,1] (top, bottom, left, right) verbose: If True, logs the output of the command. Returns: @@ -151,7 +153,6 @@ def convert_video_to_images( sys.exit(1) CONSOLE.print("Number of frames in video:", num_frames) - out_filename = image_dir / "frame_%05d.png" ffmpeg_cmd = f'ffmpeg -i "{video_path}"' crop_cmd = "" @@ -160,41 +161,66 @@ def convert_video_to_images( width = 1 - crop_factor[2] - crop_factor[3] start_x = crop_factor[2] start_y = crop_factor[0] - crop_cmd = f',"crop=w=iw*{width}:h=ih*{height}:x=iw*{start_x}:y=ih*{start_y}"' + crop_cmd = f"crop=w=iw*{width}:h=ih*{height}:x=iw*{start_x}:y=ih*{start_y}," + num_frames = get_num_frames_in_video(video_path) spacing = num_frames // num_frames_target + + downscale_chains = [f"[t{i}]scale=iw/{2**i}:ih/{2**i}[out{i}]" for i in range(num_downscales + 1)] + downscale_dirs = [Path(str(image_dir) + (f"_{2**i}" if i > 0 else "")) for i in range(num_downscales + 1)] + downscale_paths = [downscale_dirs[i] / "frame_%05d.png" for i in range(num_downscales + 1)] + + for dir in downscale_dirs: + dir.mkdir(parents=True, exist_ok=True) + + downscale_chain = ( + f"split={num_downscales + 1}" + + "".join([f"[t{i}]" for i in range(num_downscales + 1)]) + + ";" + + ";".join(downscale_chains) + ) + if spacing > 1: - ffmpeg_cmd += f" -vf thumbnail={spacing},setpts=N/TB{crop_cmd} -r 1" CONSOLE.print("Number of frames to extract:", math.ceil(num_frames / spacing)) + ffmpeg_cmd += " -vsync vfr -r1" + select_cmd = f"thumbnail={spacing},setpts=N/TB," else: CONSOLE.print("[bold red]Can't satisfy requested number of frames. Extracting all frames.") ffmpeg_cmd += " -pix_fmt bgr8" - if crop_cmd != "": - ffmpeg_cmd += f" -vf {crop_cmd[1:]}" + select_cmd = "" + + downscale_cmd = f' -filter_complex "{select_cmd}{crop_cmd}{downscale_chain}"' + "".join( + [f' -map "[out{i}]" "{downscale_paths[i]}"' for i in range(num_downscales + 1)] + ) + + ffmpeg_cmd += downscale_cmd - ffmpeg_cmd += f" {out_filename}" run_command(ffmpeg_cmd, verbose=verbose) - num_final_frames = len(list(image_dir.glob("*.png"))) - summary_log = [] - summary_log.append(f"Starting with {num_frames} video frames") - summary_log.append(f"We extracted {num_final_frames} images") - CONSOLE.log("[bold green]:tada: Done converting video to images.") + num_final_frames = len(list(image_dir.glob("*.png"))) + summary_log = [] + summary_log.append(f"Starting with {num_frames} video frames") + summary_log.append(f"We extracted {num_final_frames} images") + CONSOLE.log("[bold green]:tada: Done converting video to images.") - return summary_log, num_final_frames + return summary_log, num_final_frames def copy_images_list( image_paths: List[Path], image_dir: Path, + num_downscales: int, crop_border_pixels: Optional[int] = None, crop_factor: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 0.0), verbose: bool = False, + upscale_factor: Optional[int] = None, + nearest_neighbor: bool = False, ) -> List[Path]: """Copy all images in a list of Paths. Useful for filtering from a directory. Args: image_paths: List of Paths of images to copy to a new directory. image_dir: Path to the output directory. + num_downscales: Number of times to downscale the images. Downscales by 2 each time. crop_border_pixels: If not None, crops each edge by the specified number of pixels. crop_factor: Portion of the image to crop. Should be in [0,1] (top, bottom, left, right) verbose: If True, print extra logging. @@ -230,26 +256,48 @@ def copy_images_list( pass copied_image_paths.append(copied_image_path) + nn_flag = "" if not nearest_neighbor else ":flags=neighbor" + downscale_chains = [f"[t{i}]scale=iw/{2**i}:ih/{2**i}{nn_flag}[out{i}]" for i in range(num_downscales + 1)] + downscale_dirs = [Path(str(image_dir) + (f"_{2**i}" if i > 0 else "")) for i in range(num_downscales + 1)] + downscale_paths = [ + downscale_dirs[i] / ("frame_%05d" + copied_image_paths[0].suffix) for i in range(num_downscales + 1) + ] + + for dir in downscale_dirs: + dir.mkdir(parents=True, exist_ok=True) + + downscale_chain = ( + f"split={num_downscales + 1}" + + "".join([f"[t{i}]" for i in range(num_downscales + 1)]) + + ";" + + ";".join(downscale_chains) + ) + + num_frames = len(image_paths) + ffmpeg_cmd = f'ffmpeg -y -noautorotate -i "{image_dir / f"frame_%05d{copied_image_paths[0].suffix}"}" -q:v 2 ' + + crop_cmd = "" if crop_border_pixels is not None: - file_type = image_paths[0].suffix - filename = f"frame_%05d{file_type}" - crop = f"crop=iw-{crop_border_pixels*2}:ih-{crop_border_pixels*2}" - ffmpeg_cmd = f'ffmpeg -y -noautorotate -i "{image_dir / filename}" -q:v 2 -vf {crop} "{image_dir / filename}"' - run_command(ffmpeg_cmd, verbose=verbose) + crop_cmd = f"crop=iw-{crop_border_pixels*2}:ih-{crop_border_pixels*2}[cropped];[cropped]" elif crop_factor != (0.0, 0.0, 0.0, 0.0): - file_type = image_paths[0].suffix - filename = f"frame_%05d{file_type}" height = 1 - crop_factor[0] - crop_factor[1] width = 1 - crop_factor[2] - crop_factor[3] start_x = crop_factor[2] start_y = crop_factor[0] - crop_cmd = f',"crop=w=iw*{width}:h=ih*{height}:x=iw*{start_x}:y=ih*{start_y}"' - ffmpeg_cmd = ( - f'ffmpeg -y -noautorotate -i "{image_dir / filename}" -q:v 2 -vf {crop_cmd[1:]} "{image_dir / filename}"' - ) - run_command(ffmpeg_cmd, verbose=verbose) + crop_cmd = f"crop=w=iw*{width}:h=ih*{height}:x=iw*{start_x}:y=ih*{start_y}[cropped];[cropped]" - num_frames = len(image_paths) + select_cmd = "[0:v]" + if upscale_factor is not None: + select_cmd = f"[0:v]scale=iw*{upscale_factor}:ih*{upscale_factor}:flags=neighbor[upscaled];[upscaled]" + + downscale_cmd = f' -filter_complex "{select_cmd}{crop_cmd}{downscale_chain}"' + "".join( + [f' -map "[out{i}]" "{downscale_paths[i]}"' for i in range(num_downscales + 1)] + ) + + ffmpeg_cmd += downscale_cmd + if verbose: + CONSOLE.log(f"... {ffmpeg_cmd}") + run_command(ffmpeg_cmd, verbose=verbose) if num_frames == 0: CONSOLE.log("[bold red]:skull: No usable images in the data folder.") @@ -262,6 +310,7 @@ def copy_images_list( def copy_and_upscale_polycam_depth_maps_list( polycam_depth_image_filenames: List[Path], depth_dir: Path, + num_downscales: int, crop_border_pixels: Optional[int] = None, verbose: bool = False, ) -> List[Path]: @@ -284,31 +333,26 @@ def copy_and_upscale_polycam_depth_maps_list( assert upscale_factor > 1 assert isinstance(upscale_factor, int) - copied_depth_map_paths = [] - for idx, depth_map in enumerate(polycam_depth_image_filenames): - destination = depth_dir / f"frame_{idx + 1:05d}{depth_map.suffix}" - ffmpeg_cmd = [ - f'ffmpeg -y -i "{depth_map}" ', - f"-q:v 2 -vf scale=iw*{upscale_factor}:ih*{upscale_factor}:flags=neighbor ", - f'"{destination}"', - ] - ffmpeg_cmd = " ".join(ffmpeg_cmd) - run_command(ffmpeg_cmd, verbose=verbose) - copied_depth_map_paths.append(destination) - - if crop_border_pixels is not None: - file_type = depth_dir.glob("frame_*").__next__().suffix - filename = f"frame_%05d{file_type}" - crop = f"crop=iw-{crop_border_pixels * 2}:ih-{crop_border_pixels * 2}" - ffmpeg_cmd = f'ffmpeg -y -i "{depth_dir / filename}" -q:v 2 -vf {crop} "{depth_dir / filename}"' - run_command(ffmpeg_cmd, verbose=verbose) + copied_depth_map_paths = copy_images_list( + image_paths=polycam_depth_image_filenames, + image_dir=depth_dir, + num_downscales=num_downscales, + crop_border_pixels=crop_border_pixels, + verbose=verbose, + upscale_factor=upscale_factor, + nearest_neighbor=True, + ) CONSOLE.log("[bold green]:tada: Done upscaling depth maps.") return copied_depth_map_paths def copy_images( - data: Path, image_dir: Path, verbose, crop_factor: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 0.0) + data: Path, + image_dir: Path, + verbose: bool = False, + crop_factor: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 0.0), + num_downscales: int = 0, ) -> OrderedDict[Path, Path]: """Copy images from a directory to a new directory. @@ -328,7 +372,11 @@ def copy_images( sys.exit(1) copied_images = copy_images_list( - image_paths=image_paths, image_dir=image_dir, crop_factor=crop_factor, verbose=verbose + image_paths=image_paths, + image_dir=image_dir, + crop_factor=crop_factor, + verbose=verbose, + num_downscales=num_downscales, ) return OrderedDict((original_path, new_path) for original_path, new_path in zip(image_paths, copied_images)) @@ -340,7 +388,8 @@ def downscale_images( nearest_neighbor: bool = False, verbose: bool = False, ) -> str: - """Downscales the images in the directory. Uses FFMPEG. + """(Now deprecated; much faster integrated into copy_images.) + Downscales the images in the directory. Uses FFMPEG. Assumes images are named frame_00001.png, frame_00002.png, etc. diff --git a/nerfstudio/process_data/video_to_nerfstudio_dataset.py b/nerfstudio/process_data/video_to_nerfstudio_dataset.py index 13e4b6bc4..b1eec97ea 100644 --- a/nerfstudio/process_data/video_to_nerfstudio_dataset.py +++ b/nerfstudio/process_data/video_to_nerfstudio_dataset.py @@ -30,9 +30,8 @@ class VideoToNerfstudioDataset(ColmapConverterToNerfstudioDataset): This script does the following: - 1. Converts the video into images. - 2. Scales images to a specified size. - 3. Calculates the camera poses for each image using `COLMAP `_. + 1. Converts the video into images and downscales them. + 2. Calculates the camera poses for each image using `COLMAP `_. """ num_frames_target: int = 300 @@ -53,14 +52,17 @@ class VideoToNerfstudioDataset(ColmapConverterToNerfstudioDataset): self.data, image_dir=temp_image_dir, num_frames_target=self.num_frames_target, + num_downscales=0, crop_factor=(0.0, 0.0, 0.0, 0.0), verbose=self.verbose, ) else: + # If we're not dealing with equirects we can downscale in one step. summary_log, num_extracted_frames = process_data_utils.convert_video_to_images( self.data, image_dir=self.image_dir, num_frames_target=self.num_frames_target, + num_downscales=self.num_downscales, crop_factor=self.crop_factor, verbose=self.verbose, ) @@ -90,6 +92,11 @@ class VideoToNerfstudioDataset(ColmapConverterToNerfstudioDataset): self.camera_type = "perspective" + # # Downscale images + summary_log.append( + process_data_utils.downscale_images(self.image_dir, self.num_downscales, verbose=self.verbose) + ) + # # Create mask mask_path = process_data_utils.save_mask( image_dir=self.image_dir, @@ -100,11 +107,6 @@ class VideoToNerfstudioDataset(ColmapConverterToNerfstudioDataset): if mask_path is not None: summary_log.append(f"Saved mask to {mask_path}") - # # Downscale images - summary_log.append( - process_data_utils.downscale_images(self.image_dir, self.num_downscales, verbose=self.verbose) - ) - # Run Colmap if not self.skip_colmap: self._run_colmap(mask_path) diff --git a/nerfstudio/scripts/process_data.py b/nerfstudio/scripts/process_data.py index 50a10bb5a..201eb94fc 100644 --- a/nerfstudio/scripts/process_data.py +++ b/nerfstudio/scripts/process_data.py @@ -89,7 +89,10 @@ class ProcessRecord3D(BaseConverterToNerfstudioDataset): record3d_image_filenames = list(np.array(record3d_image_filenames)[idx]) # Copy images to output directory copied_image_paths = process_data_utils.copy_images_list( - record3d_image_filenames, image_dir=image_dir, verbose=self.verbose + record3d_image_filenames, + image_dir=image_dir, + verbose=self.verbose, + num_downscales=self.num_downscales, ) num_frames = len(copied_image_paths) @@ -101,9 +104,6 @@ class ProcessRecord3D(BaseConverterToNerfstudioDataset): f"larger than the current value ({self.max_dataset_size}), or -1 to use all images." ) - # Downscale images - summary_log.append(process_data_utils.downscale_images(image_dir, self.num_downscales, verbose=self.verbose)) - metadata_path = self.data / "metadata.json" record3d_utils.record3d_to_json(copied_image_paths, metadata_path, self.output_dir, indices=idx) CONSOLE.rule("[bold green]:tada: :tada: :tada: All DONE :tada: :tada: :tada:") @@ -266,6 +266,7 @@ class ProcessMetashape(BaseConverterToNerfstudioDataset, _NoDefaultProcessMetash image_filenames, image_dir=image_dir, verbose=self.verbose, + num_downscales=self.num_downscales, ) num_frames = len(copied_image_paths) @@ -282,9 +283,6 @@ class ProcessMetashape(BaseConverterToNerfstudioDataset, _NoDefaultProcessMetash else: summary_log.append(f"Started with {num_frames} images") - # Downscale images - summary_log.append(process_data_utils.downscale_images(image_dir, self.num_downscales, verbose=self.verbose)) - # Save json if num_frames == 0: CONSOLE.print("[bold red]No images found, exiting") @@ -353,6 +351,7 @@ class ProcessRealityCapture(BaseConverterToNerfstudioDataset, _NoDefaultProcessR image_filenames, image_dir=image_dir, verbose=self.verbose, + num_downscales=self.num_downscales, ) num_frames = len(copied_image_paths) @@ -369,9 +368,6 @@ class ProcessRealityCapture(BaseConverterToNerfstudioDataset, _NoDefaultProcessR else: summary_log.append(f"Started with {num_frames} images") - # Downscale images - summary_log.append(process_data_utils.downscale_images(image_dir, self.num_downscales, verbose=self.verbose)) - # Save json if num_frames == 0: CONSOLE.print("[bold red]No images found, exiting") From 0cb98fbdd884b1d465e59355e54d429c573d777c Mon Sep 17 00:00:00 2001 From: Saurav Maheshkar Date: Fri, 14 Jul 2023 05:44:07 +0530 Subject: [PATCH 32/44] refactor: `get_normalized_directions` (#2216) * refactor: normalized_directions * fix: directions shape --- nerfstudio/fields/base_field.py | 4 ++-- nerfstudio/fields/generfacto_field.py | 20 +++++--------------- nerfstudio/fields/nerfacto_field.py | 4 ++-- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/nerfstudio/fields/base_field.py b/nerfstudio/fields/base_field.py index 068d4e7f1..b0215aae6 100644 --- a/nerfstudio/fields/base_field.py +++ b/nerfstudio/fields/base_field.py @@ -133,8 +133,8 @@ class Field(nn.Module): return field_outputs -def shift_directions_for_tcnn(directions: Float[Tensor, "*bs 3"]) -> Float[Tensor, "*bs 3"]: - """Shift directions from [-1, 1] to [0, 1] +def get_normalized_directions(directions: Float[Tensor, "*bs 3"]) -> Float[Tensor, "*bs 3"]: + """SH encoding must be in the range [0, 1] Args: directions: batch of directions diff --git a/nerfstudio/fields/generfacto_field.py b/nerfstudio/fields/generfacto_field.py index d72f5c263..ee31bcb0b 100644 --- a/nerfstudio/fields/generfacto_field.py +++ b/nerfstudio/fields/generfacto_field.py @@ -17,30 +17,20 @@ Field for Generfacto model """ -from typing import Optional, Tuple, Dict, Literal +from typing import Dict, Literal, Optional, Tuple import numpy as np import torch -from torch.nn.parameter import Parameter -from jaxtyping import Float from torch import Tensor, nn +from torch.nn.parameter import Parameter -from nerfstudio.field_components.mlp import MLP from nerfstudio.cameras.rays import RayBundle, RaySamples from nerfstudio.data.scene_box import SceneBox from nerfstudio.field_components.activations import trunc_exp -from nerfstudio.field_components.field_heads import FieldHeadNames from nerfstudio.field_components.encodings import HashEncoding, SHEncoding -from nerfstudio.fields.base_field import Field - - -def get_normalized_directions(directions: Float[Tensor, "bs 3"]): - """SH encoding must be in the range [0, 1] - - Args: - directions: batch of directions - """ - return (directions + 1.0) / 2.0 +from nerfstudio.field_components.field_heads import FieldHeadNames +from nerfstudio.field_components.mlp import MLP +from nerfstudio.fields.base_field import Field, get_normalized_directions class GenerfactoField(Field): diff --git a/nerfstudio/fields/nerfacto_field.py b/nerfstudio/fields/nerfacto_field.py index 1c35b7121..3ba9efb82 100644 --- a/nerfstudio/fields/nerfacto_field.py +++ b/nerfstudio/fields/nerfacto_field.py @@ -37,7 +37,7 @@ from nerfstudio.field_components.field_heads import ( ) from nerfstudio.field_components.mlp import MLP from nerfstudio.field_components.spatial_distortions import SpatialDistortion -from nerfstudio.fields.base_field import Field, shift_directions_for_tcnn +from nerfstudio.fields.base_field import Field, get_normalized_directions class NerfactoField(Field): @@ -232,7 +232,7 @@ class NerfactoField(Field): if ray_samples.camera_indices is None: raise AttributeError("Camera indices are not provided.") camera_indices = ray_samples.camera_indices.squeeze() - directions = shift_directions_for_tcnn(ray_samples.frustums.directions) + directions = get_normalized_directions(ray_samples.frustums.directions) directions_flat = directions.view(-1, 3) d = self.direction_encoding(directions_flat) From 54d26814d94a48a5711168ee34651a41a1eff2ae Mon Sep 17 00:00:00 2001 From: Matthew Tancik Date: Thu, 13 Jul 2023 18:02:19 -0700 Subject: [PATCH 33/44] Update torchmetrics (#2223) --- nerfstudio/configs/base_config.py | 3 --- nerfstudio/models/base_surface_model.py | 2 +- nerfstudio/models/instant_ngp.py | 2 +- nerfstudio/models/mipnerf.py | 2 +- nerfstudio/models/nerfacto.py | 2 +- nerfstudio/models/semantic_nerfw.py | 2 +- nerfstudio/models/tensorf.py | 2 +- nerfstudio/models/vanilla_nerf.py | 2 +- pyproject.toml | 2 +- 9 files changed, 8 insertions(+), 11 deletions(-) diff --git a/nerfstudio/configs/base_config.py b/nerfstudio/configs/base_config.py index 4f1d82238..315659acb 100644 --- a/nerfstudio/configs/base_config.py +++ b/nerfstudio/configs/base_config.py @@ -17,7 +17,6 @@ from __future__ import annotations -import warnings from dataclasses import dataclass from pathlib import Path from typing import Any, List, Literal, Optional, Tuple, Type @@ -25,8 +24,6 @@ from typing import Any, List, Literal, Optional, Tuple, Type # model instances from nerfstudio.utils import writer -warnings.filterwarnings("ignore", module="torchvision") - # Pretty printing class class PrintableConfig: diff --git a/nerfstudio/models/base_surface_model.py b/nerfstudio/models/base_surface_model.py index f7af6e2c1..f8d3bca10 100644 --- a/nerfstudio/models/base_surface_model.py +++ b/nerfstudio/models/base_surface_model.py @@ -25,8 +25,8 @@ from typing import Any, Dict, List, Literal, Tuple, Type, cast import torch import torch.nn.functional as F from torch.nn import Parameter -from torchmetrics import PeakSignalNoiseRatio from torchmetrics.functional import structural_similarity_index_measure +from torchmetrics.image import PeakSignalNoiseRatio from torchmetrics.image.lpip import LearnedPerceptualImagePatchSimilarity from nerfstudio.cameras.rays import RayBundle diff --git a/nerfstudio/models/instant_ngp.py b/nerfstudio/models/instant_ngp.py index 95f059b60..abdc892dd 100644 --- a/nerfstudio/models/instant_ngp.py +++ b/nerfstudio/models/instant_ngp.py @@ -24,8 +24,8 @@ from typing import Dict, List, Literal, Optional, Tuple, Type import nerfacc import torch from torch.nn import Parameter -from torchmetrics import PeakSignalNoiseRatio from torchmetrics.functional import structural_similarity_index_measure +from torchmetrics.image import PeakSignalNoiseRatio from torchmetrics.image.lpip import LearnedPerceptualImagePatchSimilarity from nerfstudio.cameras.rays import RayBundle diff --git a/nerfstudio/models/mipnerf.py b/nerfstudio/models/mipnerf.py index f0deb9bc4..9a3fdabe2 100644 --- a/nerfstudio/models/mipnerf.py +++ b/nerfstudio/models/mipnerf.py @@ -21,8 +21,8 @@ from typing import Dict, List, Tuple import torch from torch.nn import Parameter -from torchmetrics import PeakSignalNoiseRatio from torchmetrics.functional import structural_similarity_index_measure +from torchmetrics.image import PeakSignalNoiseRatio from torchmetrics.image.lpip import LearnedPerceptualImagePatchSimilarity from nerfstudio.cameras.rays import RayBundle diff --git a/nerfstudio/models/nerfacto.py b/nerfstudio/models/nerfacto.py index 2dbbee939..79e5b180e 100644 --- a/nerfstudio/models/nerfacto.py +++ b/nerfstudio/models/nerfacto.py @@ -24,8 +24,8 @@ from typing import Dict, List, Literal, Tuple, Type import numpy as np import torch from torch.nn import Parameter -from torchmetrics import PeakSignalNoiseRatio from torchmetrics.functional import structural_similarity_index_measure +from torchmetrics.image import PeakSignalNoiseRatio from torchmetrics.image.lpip import LearnedPerceptualImagePatchSimilarity from nerfstudio.cameras.rays import RayBundle, RaySamples diff --git a/nerfstudio/models/semantic_nerfw.py b/nerfstudio/models/semantic_nerfw.py index bf7914b1c..106f1794a 100644 --- a/nerfstudio/models/semantic_nerfw.py +++ b/nerfstudio/models/semantic_nerfw.py @@ -24,8 +24,8 @@ from typing import Dict, List, Tuple, Type import numpy as np import torch from torch.nn import Parameter -from torchmetrics import PeakSignalNoiseRatio from torchmetrics.functional import structural_similarity_index_measure +from torchmetrics.image import PeakSignalNoiseRatio from torchmetrics.image.lpip import LearnedPerceptualImagePatchSimilarity from nerfstudio.cameras.rays import RayBundle diff --git a/nerfstudio/models/tensorf.py b/nerfstudio/models/tensorf.py index 742818b64..160ef1b86 100644 --- a/nerfstudio/models/tensorf.py +++ b/nerfstudio/models/tensorf.py @@ -24,8 +24,8 @@ from typing import Dict, List, Literal, Tuple, Type, cast import numpy as np import torch from torch.nn import Parameter -from torchmetrics import PeakSignalNoiseRatio from torchmetrics.functional import structural_similarity_index_measure +from torchmetrics.image import PeakSignalNoiseRatio from torchmetrics.image.lpip import LearnedPerceptualImagePatchSimilarity from nerfstudio.cameras.rays import RayBundle diff --git a/nerfstudio/models/vanilla_nerf.py b/nerfstudio/models/vanilla_nerf.py index 4a23bf0e5..62d6b96b7 100644 --- a/nerfstudio/models/vanilla_nerf.py +++ b/nerfstudio/models/vanilla_nerf.py @@ -23,8 +23,8 @@ from typing import Any, Dict, List, Tuple, Type import torch from torch.nn import Parameter -from torchmetrics import PeakSignalNoiseRatio from torchmetrics.functional import structural_similarity_index_measure +from torchmetrics.image import PeakSignalNoiseRatio from torchmetrics.image.lpip import LearnedPerceptualImagePatchSimilarity from nerfstudio.cameras.rays import RayBundle diff --git a/pyproject.toml b/pyproject.toml index e4de87116..804dab015 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ dependencies = [ "tensorboard>=2.13.0", "torch>=1.13.1", "torchvision>=0.14.1", - "torchmetrics[image]>=0.9.3", + "torchmetrics[image]>=1.0.1", "typing_extensions>=4.4.0", "viser>=0.0.12", "nuscenes-devkit>=1.1.1", From 6329f53a5caa2d71a449f2c8ca9ef46b9ac5b7c2 Mon Sep 17 00:00:00 2001 From: Sebastiaan <751205+SharkWipf@users.noreply.github.com> Date: Fri, 14 Jul 2023 19:32:58 +0200 Subject: [PATCH 34/44] Remove superfluous -r1 (fix ffmpeg/colmap video) (#2231) --- nerfstudio/process_data/process_data_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nerfstudio/process_data/process_data_utils.py b/nerfstudio/process_data/process_data_utils.py index 6d4cee094..355e5bc1c 100644 --- a/nerfstudio/process_data/process_data_utils.py +++ b/nerfstudio/process_data/process_data_utils.py @@ -182,7 +182,7 @@ def convert_video_to_images( if spacing > 1: CONSOLE.print("Number of frames to extract:", math.ceil(num_frames / spacing)) - ffmpeg_cmd += " -vsync vfr -r1" + ffmpeg_cmd += " -vsync vfr" select_cmd = f"thumbnail={spacing},setpts=N/TB," else: CONSOLE.print("[bold red]Can't satisfy requested number of frames. Extracting all frames.") From 8cd9beccd58a271bc7b071335590aa6eb9ff98df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yueh-Cheng=20Liu=20=E5=8A=89=E5=B2=B3=E6=89=BF?= Date: Sat, 15 Jul 2023 16:24:37 +0200 Subject: [PATCH 35/44] Fix arguments that are not passed from config to NerfactoField (#2186) * Fix arguments that are not passed from config to NerfactoField * Add comments * Reset base_res and features_per_level config of nerfacto-big and nerfacto-huge to default to match the original behavior --------- Co-authored-by: Matthew Tancik --- nerfstudio/configs/method_configs.py | 3 --- nerfstudio/fields/nerfacto_field.py | 2 ++ nerfstudio/models/nerfacto.py | 2 ++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/nerfstudio/configs/method_configs.py b/nerfstudio/configs/method_configs.py index b8ec7fede..428f91d01 100644 --- a/nerfstudio/configs/method_configs.py +++ b/nerfstudio/configs/method_configs.py @@ -136,7 +136,6 @@ method_configs["nerfacto-big"] = TrainerConfig( hidden_dim=128, hidden_dim_color=128, appearance_embed_dim=128, - base_res=32, max_res=4096, proposal_weights_anneal_max_num_iters=5000, log2_hashmap_size=21, @@ -183,8 +182,6 @@ method_configs["nerfacto-huge"] = TrainerConfig( hidden_dim=256, hidden_dim_color=256, appearance_embed_dim=32, - features_per_level=4, - base_res=32, max_res=8192, proposal_weights_anneal_max_num_iters=5000, log2_hashmap_size=21, diff --git a/nerfstudio/fields/nerfacto_field.py b/nerfstudio/fields/nerfacto_field.py index 3ba9efb82..34c6346fc 100644 --- a/nerfstudio/fields/nerfacto_field.py +++ b/nerfstudio/fields/nerfacto_field.py @@ -50,10 +50,12 @@ class NerfactoField(Field): 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 + base_res: base resolution 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 + features_per_level: number of features per level for the hashgrid 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 diff --git a/nerfstudio/models/nerfacto.py b/nerfstudio/models/nerfacto.py index 79e5b180e..3024c77cd 100644 --- a/nerfstudio/models/nerfacto.py +++ b/nerfstudio/models/nerfacto.py @@ -152,6 +152,8 @@ class NerfactoModel(Model): hidden_dim=self.config.hidden_dim, num_levels=self.config.num_levels, max_res=self.config.max_res, + base_res=self.config.base_res, + features_per_level=self.config.features_per_level, log2_hashmap_size=self.config.log2_hashmap_size, hidden_dim_color=self.config.hidden_dim_color, hidden_dim_transient=self.config.hidden_dim_transient, From e417ee07e7e00c6430942ece8e1e46286abd395c Mon Sep 17 00:00:00 2001 From: Justin Kerr Date: Sun, 16 Jul 2023 18:27:36 -0700 Subject: [PATCH 36/44] Minor kwarg fix to be future-compatible with viser version (#2235) * add kwarg to step to avoid breaking in newer version of viser --- nerfstudio/viewer_beta/viewer_elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nerfstudio/viewer_beta/viewer_elements.py b/nerfstudio/viewer_beta/viewer_elements.py index 5fbfc8598..5c49ec8ae 100644 --- a/nerfstudio/viewer_beta/viewer_elements.py +++ b/nerfstudio/viewer_beta/viewer_elements.py @@ -436,5 +436,5 @@ class ViewerVec3(ViewerParameter[Tuple[float, float, float]]): def _create_gui_handle(self, viser_server: ViserServer) -> None: self.gui_handle = viser_server.add_gui_vector3( - self.name, self.default_value, self.step, disabled=self.disabled, hint=self.hint + self.name, self.default_value, step=self.step, disabled=self.disabled, hint=self.hint ) From dbd4092dbb68a7ff9cb9667cdbcc56dec53bd2e8 Mon Sep 17 00:00:00 2001 From: Khoa Nguyen-Tuan <12138158+Khoa-NT@users.noreply.github.com> Date: Tue, 18 Jul 2023 07:02:32 +0900 Subject: [PATCH 37/44] Add std for eval (#2239) * Add std for eval * change default value of get_std --------- Co-authored-by: khoa --- nerfstudio/pipelines/base_pipeline.py | 23 ++++++++++++++++++----- nerfstudio/scripts/eval.py | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/nerfstudio/pipelines/base_pipeline.py b/nerfstudio/pipelines/base_pipeline.py index 11ba841e0..5e202b06c 100644 --- a/nerfstudio/pipelines/base_pipeline.py +++ b/nerfstudio/pipelines/base_pipeline.py @@ -183,12 +183,15 @@ class Pipeline(nn.Module): @abstractmethod @profiler.time_function - def get_average_eval_image_metrics(self, step: Optional[int] = None, output_path: Optional[Path] = None): + def get_average_eval_image_metrics( + self, step: Optional[int] = None, output_path: Optional[Path] = None, get_std: bool = False + ): """Iterate over all the images in the eval dataset and get the average. Args: step: current training step output_path: optional path to save rendered images to + get_std: Set True if you want to return std with the mean metric. """ def load_pipeline(self, loaded_state: Dict[str, Any], step: int) -> None: @@ -354,12 +357,15 @@ class VanillaPipeline(Pipeline): return metrics_dict, images_dict @profiler.time_function - def get_average_eval_image_metrics(self, step: Optional[int] = None, output_path: Optional[Path] = None): + def get_average_eval_image_metrics( + self, step: Optional[int] = None, output_path: Optional[Path] = None, get_std: bool = False + ): """Iterate over all the images in the eval dataset and get the average. Args: step: current training step output_path: optional path to save rendered images to + get_std: Set True if you want to return std with the mean metric. Returns: metrics_dict: dictionary of metrics @@ -401,9 +407,16 @@ class VanillaPipeline(Pipeline): # average the metrics list metrics_dict = {} for key in metrics_dict_list[0].keys(): - metrics_dict[key] = float( - torch.mean(torch.tensor([metrics_dict[key] for metrics_dict in metrics_dict_list])) - ) + if get_std: + key_std, key_mean = torch.std_mean( + torch.tensor([metrics_dict[key] for metrics_dict in metrics_dict_list]) + ) + metrics_dict[key] = float(key_mean) + metrics_dict[f"{key}_std"] = float(key_std) + else: + metrics_dict[key] = float( + torch.mean(torch.tensor([metrics_dict[key] for metrics_dict in metrics_dict_list])) + ) self.train() return metrics_dict diff --git a/nerfstudio/scripts/eval.py b/nerfstudio/scripts/eval.py index 8f5995117..751b318a2 100644 --- a/nerfstudio/scripts/eval.py +++ b/nerfstudio/scripts/eval.py @@ -46,7 +46,7 @@ class ComputePSNR: assert self.output_path.suffix == ".json" if self.render_output_path is not None: self.render_output_path.mkdir(parents=True) - metrics_dict = pipeline.get_average_eval_image_metrics(output_path=self.render_output_path) + metrics_dict = pipeline.get_average_eval_image_metrics(output_path=self.render_output_path, get_std=True) self.output_path.parent.mkdir(parents=True, exist_ok=True) # Get the output and define the names to save to benchmark_info = { From 9f487a464fe31bb9f873832596c3213fc45a1d31 Mon Sep 17 00:00:00 2001 From: Nicholas Pfaff <53228351+nepfaff@users.noreply.github.com> Date: Tue, 18 Jul 2023 12:54:52 +0200 Subject: [PATCH 38/44] Implement nerfacto alpha transparency training (#2165) * Implement nerfacto alpha transparency training * Stop adding all ones alpha channel and clean up blend_background * Make background_color a required argument in blend_background * Enable alpha transparent training by default * Improve background_color typing * Blend uniform background color for metric computation * Fix typing error * Clean up typing * Fix background_color to outputs assignment * Model outputs returns unblended RGB (black bg) for random bg * Update renderers * Fix comments * Add support for transparent BG for other methods * Minor refac * Fix get_background_color * Fix typing error for blend_background * Fix comments * Export utils support transparency * Pylint fix * Fix pcd export masking * Filter pcd view directions based on opacity * Clean up get_rgba_image --------- Co-authored-by: Jonas Kulhanek --- nerfstudio/data/datasets/base_dataset.py | 3 - nerfstudio/exporter/exporter_utils.py | 26 ++++-- nerfstudio/exporter/texture_utils.py | 4 +- nerfstudio/exporter/tsdf_utils.py | 4 + nerfstudio/model_components/renderers.py | 107 ++++++++++++++++++++--- nerfstudio/models/base_model.py | 24 +++++ nerfstudio/models/base_surface_model.py | 9 +- nerfstudio/models/instant_ngp.py | 11 ++- nerfstudio/models/mipnerf.py | 15 +++- nerfstudio/models/nerfacto.py | 32 ++++--- nerfstudio/models/semantic_nerfw.py | 8 ++ nerfstudio/models/tensorf.py | 10 ++- nerfstudio/models/vanilla_nerf.py | 15 +++- tests/field_components/test_mlp.py | 2 +- 14 files changed, 225 insertions(+), 45 deletions(-) diff --git a/nerfstudio/data/datasets/base_dataset.py b/nerfstudio/data/datasets/base_dataset.py index 95269a162..6532d5d50 100644 --- a/nerfstudio/data/datasets/base_dataset.py +++ b/nerfstudio/data/datasets/base_dataset.py @@ -85,10 +85,7 @@ class InputDataset(Dataset): """ image = torch.from_numpy(self.get_numpy_image(image_idx).astype("float32") / 255.0) if self._dataparser_outputs.alpha_color is not None and image.shape[-1] == 4: - assert image.shape[-1] == 4 image = image[:, :, :3] * image[:, :, -1:] + self._dataparser_outputs.alpha_color * (1.0 - image[:, :, -1:]) - else: - image = image[:, :, :3] return image def get_data(self, image_idx: int) -> Dict: diff --git a/nerfstudio/exporter/exporter_utils.py b/nerfstudio/exporter/exporter_utils.py index 21ea545b9..1baeeb2b5 100644 --- a/nerfstudio/exporter/exporter_utils.py +++ b/nerfstudio/exporter/exporter_utils.py @@ -28,13 +28,7 @@ import open3d as o3d import pymeshlab import torch from jaxtyping import Float -from rich.progress import ( - BarColumn, - Progress, - TaskProgressColumn, - TextColumn, - TimeRemainingColumn, -) +from rich.progress import BarColumn, Progress, TaskProgressColumn, TextColumn, TimeRemainingColumn from torch import Tensor from nerfstudio.cameras.cameras import Cameras @@ -144,7 +138,7 @@ def generate_point_cloud( CONSOLE.print(f"Could not find {depth_output_name} in the model outputs", justify="center") CONSOLE.print(f"Please set --depth_output_name to one of: {outputs.keys()}", justify="center") sys.exit(1) - rgb = outputs[rgb_output_name] + rgba = pipeline.model.get_rgba_image(outputs, rgb_output_name) depth = outputs[depth_output_name] if normal_output_name is not None: if normal_output_name not in outputs: @@ -160,6 +154,14 @@ def generate_point_cloud( point = ray_bundle.origins + ray_bundle.directions * depth view_direction = ray_bundle.directions + # Filter points with opacity lower than 0.5 + mask = rgba[..., -1] > 0.5 + point = point[mask] + view_direction = view_direction[mask] + rgb = rgba[mask][..., :3] + if normal is not None: + normal = normal[mask] + if use_bounding_box: comp_l = torch.tensor(bounding_box_min, device=point.device) comp_m = torch.tensor(bounding_box_max, device=point.device) @@ -230,6 +232,7 @@ def render_trajectory( depth_output_name: str, rendered_resolution_scaling_factor: float = 1.0, disable_distortion: bool = False, + return_rgba_images: bool = False, ) -> Tuple[List[np.ndarray], List[np.ndarray]]: """Helper function to create a video of a trajectory. @@ -240,6 +243,7 @@ def render_trajectory( depth_output_name: Name of the depth output. rendered_resolution_scaling_factor: Scaling factor to apply to the camera image resolution. disable_distortion: Whether to disable distortion. + return_rgba_images: Whether to return RGBA images (default RGB). Returns: List of rgb images, list of depth images. @@ -272,7 +276,11 @@ def render_trajectory( CONSOLE.print(f"Could not find {depth_output_name} in the model outputs", justify="center") CONSOLE.print(f"Please set --depth_output_name to one of: {outputs.keys()}", justify="center") sys.exit(1) - images.append(outputs[rgb_output_name].cpu().numpy()) + if return_rgba_images: + image = pipeline.model.get_rgba_image(outputs, rgb_output_name) + else: + image = outputs[rgb_output_name] + images.append(image.cpu().numpy()) depths.append(outputs[depth_output_name].cpu().numpy()) return images, depths diff --git a/nerfstudio/exporter/texture_utils.py b/nerfstudio/exporter/texture_utils.py index 8a304a7b8..120696752 100644 --- a/nerfstudio/exporter/texture_utils.py +++ b/nerfstudio/exporter/texture_utils.py @@ -403,9 +403,11 @@ def export_textured_mesh( CONSOLE.print("Creating texture image by rendering with NeRF...") with torch.no_grad(): outputs = pipeline.model.get_outputs_for_camera_ray_bundle(camera_ray_bundle) + # TODO: this can be done better by using the alpha channel + rgb = pipeline.model.get_rgba_image(outputs, "rgb")[..., :3] # save the texture image - texture_image = outputs["rgb"].cpu().numpy() + texture_image = rgb.cpu().numpy() media.write_image(str(output_dir / "material_0.png"), texture_image) CONSOLE.print("Writing relevant OBJ information to files...") diff --git a/nerfstudio/exporter/tsdf_utils.py b/nerfstudio/exporter/tsdf_utils.py index d144686c9..301591316 100644 --- a/nerfstudio/exporter/tsdf_utils.py +++ b/nerfstudio/exporter/tsdf_utils.py @@ -331,8 +331,12 @@ def export_tsdf_mesh( depth_output_name=depth_output_name, rendered_resolution_scaling_factor=1.0 / downscale_factor, disable_distortion=True, + return_rgba_images=True, ) + # TODO: this can be done better by removing transparent points from the TSDF + color_images = [x[..., :3] for x in color_images] + # camera extrinsics and intrinsics c2w: Float[Tensor, "N 3 4"] = cameras.camera_to_worlds.to(device) # make c2w homogeneous diff --git a/nerfstudio/model_components/renderers.py b/nerfstudio/model_components/renderers.py index da3277908..ae50b0e12 100644 --- a/nerfstudio/model_components/renderers.py +++ b/nerfstudio/model_components/renderers.py @@ -28,7 +28,7 @@ Example: """ import contextlib import math -from typing import Generator, Literal, Optional, Union +from typing import Generator, Literal, Optional, Tuple, Union import nerfacc import torch @@ -39,7 +39,7 @@ from nerfstudio.cameras.rays import RaySamples from nerfstudio.utils import colors from nerfstudio.utils.math import components_from_spherical_harmonics, safe_normalize -BackgroundColor = Union[Literal["random", "last_sample", "black", "white"], Float[Tensor, "3"]] +BackgroundColor = Union[Literal["random", "last_sample", "black", "white"], Float[Tensor, "3"], Float[Tensor, "*bs 3"]] BACKGROUND_COLOR_OVERRIDE: Optional[Float[Tensor, "3"]] = None @@ -75,7 +75,8 @@ class RGBRenderer(nn.Module): ray_indices: Optional[Int[Tensor, "num_samples"]] = None, num_rays: Optional[int] = None, ) -> Float[Tensor, "*bs 3"]: - """Composite samples along ray and render color image + """Composite samples along ray and render color image. + If background color is random, no BG color is added - as if the background was black! Args: rgb: RGB for each sample @@ -101,19 +102,99 @@ class RGBRenderer(nn.Module): comp_rgb = torch.sum(weights * rgb, dim=-2) accumulated_weight = torch.sum(weights, dim=-2) + if background_color == "random": + # If background color is random, the predicted color is returned without blending, + # as if the background color was black. + return comp_rgb + + elif background_color == "last_sample": + # Note, this is only supported for non-packed samples. + background_color = rgb[..., -1, :] + else: + background_color = cls.get_background_color(background_color, shape=comp_rgb.shape, device=comp_rgb.device) + assert isinstance(background_color, torch.Tensor) + comp_rgb = comp_rgb + background_color * (1.0 - accumulated_weight) + return comp_rgb + + @classmethod + def get_background_color( + cls, background_color: BackgroundColor, shape: Tuple[int, ...], device: torch.device + ) -> Union[Float[Tensor, "3"], Float[Tensor, "*bs 3"]]: + """Returns the RGB background color for a specified background color. + + Note: + This function CANNOT be called for background_color being either "last_sample" or "random". + + Args: + rgb: RGB for each sample. + background_color: The background color specification. + + Returns: + Background color as RGB. + """ + assert background_color not in {"last_sample", "random"} + assert shape[-1] == 3, "Background color must be RGB." if BACKGROUND_COLOR_OVERRIDE is not None: background_color = BACKGROUND_COLOR_OVERRIDE - if background_color == "last_sample": - background_color = rgb[..., -1, :] - if background_color == "random": - background_color = torch.rand_like(comp_rgb).to(rgb.device) if isinstance(background_color, str) and background_color in colors.COLORS_DICT: - background_color = colors.COLORS_DICT[background_color].to(rgb.device) + background_color = colors.COLORS_DICT[background_color] + assert isinstance(background_color, Tensor) + # Ensure correct shape + return background_color.expand(shape).to(device) + + def blend_background( + self, + image: Tensor, + background_color: Optional[BackgroundColor] = None, + ) -> Float[Tensor, "*bs 3"]: + """Blends the background color into the image if image is RGBA. + Otherwise no blending is performed (we assume opacity of 1). + + Args: + image: RGB/RGBA per pixel. + opacity: Alpha opacity per pixel. + background_color: Background color. + + Returns: + Blended RGB. + """ + if image.size(-1) < 4: + return image + + rgb, opacity = image[..., :3], image[..., 3:] + if background_color is None: + background_color = self.background_color + if background_color in {"last_sample", "random"}: + background_color = "black" + background_color = self.get_background_color(background_color, shape=rgb.shape, device=rgb.device) assert isinstance(background_color, torch.Tensor) - comp_rgb = comp_rgb + background_color.to(weights.device) * (1.0 - accumulated_weight) + return rgb * opacity + background_color.to(rgb.device) * (1 - opacity) - return comp_rgb + def blend_background_for_loss_computation( + self, + pred_image: Tensor, + pred_accumulation: Tensor, + gt_image: Tensor, + ) -> Tuple[Tensor, Tensor]: + """Blends a background color into the ground truth and predicted image for + loss computation. + + Args: + gt_image: The ground truth image. + pred_image: The predicted RGB values (without background blending). + pred_accumulation: The predicted opacity/ accumulation. + Returns: + A tuple of the predicted and ground truth RGB values. + """ + background_color = self.background_color + if background_color == "last_sample": + background_color = "black" # No background blending for GT + elif background_color == "random": + background_color = torch.rand_like(pred_image) + pred_image = pred_image + background_color * (1.0 - pred_accumulation) + gt_image = self.blend_background(gt_image, background_color=background_color) + return pred_image, gt_image def forward( self, @@ -121,6 +202,7 @@ class RGBRenderer(nn.Module): weights: Float[Tensor, "*bs num_samples 1"], ray_indices: Optional[Int[Tensor, "num_samples"]] = None, num_rays: Optional[int] = None, + background_color: Optional[BackgroundColor] = None, ) -> Float[Tensor, "*bs 3"]: """Composite samples along ray and render color image @@ -129,15 +211,18 @@ class RGBRenderer(nn.Module): weights: Weights for each sample ray_indices: Ray index for each sample, used when samples are packed. num_rays: Number of rays, used when samples are packed. + background_color: The background color to use for rendering. Returns: Outputs of rgb values. """ + if background_color is None: + background_color = self.background_color if not self.training: rgb = torch.nan_to_num(rgb) rgb = self.combine_rgb( - rgb, weights, background_color=self.background_color, ray_indices=ray_indices, num_rays=num_rays + rgb, weights, background_color=background_color, ray_indices=ray_indices, num_rays=num_rays ) if not self.training: torch.clamp_(rgb, min=0.0, max=1.0) diff --git a/nerfstudio/models/base_model.py b/nerfstudio/models/base_model.py index 6f31c3b30..ebda710b3 100644 --- a/nerfstudio/models/base_model.py +++ b/nerfstudio/models/base_model.py @@ -187,6 +187,30 @@ class Model(nn.Module): outputs[output_name] = torch.cat(outputs_list).view(image_height, image_width, -1) # type: ignore return outputs + def get_rgba_image(self, outputs: Dict[str, torch.Tensor], output_name: str = "rgb") -> torch.Tensor: + """Returns the RGBA image from the outputs of the model. + + Args: + outputs: Outputs of the model. + + Returns: + RGBA image. + """ + accumulation_name = output_name.replace("rgb", "accumulation") + if ( + not hasattr(self, "renderer_rgb") + or not hasattr(self.renderer_rgb, "background_color") + or accumulation_name not in outputs + ): + raise NotImplementedError(f"get_rgba_image is not implemented for model {self.__class__.__name__}") + rgb = outputs[output_name] + if self.renderer_rgb.background_color == "random": # type: ignore + acc = outputs[accumulation_name] + if acc.dim() < rgb.dim(): + acc = acc.unsqueeze(-1) + return torch.cat((rgb / acc.clamp(min=1e-10), acc), dim=-1) + return torch.cat((rgb, torch.ones_like(rgb[..., :1])), dim=-1) + @abstractmethod def get_image_metrics_and_images( self, outputs: Dict[str, torch.Tensor], batch: Dict[str, torch.Tensor] diff --git a/nerfstudio/models/base_surface_model.py b/nerfstudio/models/base_surface_model.py index f8d3bca10..638aa2713 100644 --- a/nerfstudio/models/base_surface_model.py +++ b/nerfstudio/models/base_surface_model.py @@ -293,7 +293,12 @@ class SurfaceModel(Model): """ loss_dict = {} image = batch["image"].to(self.device) - loss_dict["rgb_loss"] = self.rgb_loss(image, outputs["rgb"]) + pred_image, image = self.renderer_rgb.blend_background_for_loss_computation( + pred_image=outputs["rgb"], + pred_accumulation=outputs["accumulation"], + gt_image=image, + ) + loss_dict["rgb_loss"] = self.rgb_loss(image, pred_image) if self.training: # eikonal loss grad_theta = outputs["eik_grad"] @@ -337,6 +342,7 @@ class SurfaceModel(Model): """ metrics_dict = {} image = batch["image"].to(self.device) + image = self.renderer_rgb.blend_background(image) metrics_dict["psnr"] = self.psnr(outputs["rgb"], image) return metrics_dict @@ -352,6 +358,7 @@ class SurfaceModel(Model): A dictionary of metrics. """ image = batch["image"].to(self.device) + image = self.renderer_rgb.blend_background(image) rgb = outputs["rgb"] acc = colormaps.apply_colormap(outputs["accumulation"]) diff --git a/nerfstudio/models/instant_ngp.py b/nerfstudio/models/instant_ngp.py index abdc892dd..c67d45db5 100644 --- a/nerfstudio/models/instant_ngp.py +++ b/nerfstudio/models/instant_ngp.py @@ -219,14 +219,20 @@ class NGPModel(Model): def get_metrics_dict(self, outputs, batch): image = batch["image"].to(self.device) + image = self.renderer_rgb.blend_background(image) metrics_dict = {} metrics_dict["psnr"] = self.psnr(outputs["rgb"], image) metrics_dict["num_samples_per_batch"] = outputs["num_samples_per_ray"].sum() return metrics_dict def get_loss_dict(self, outputs, batch, metrics_dict=None): - image = batch["image"].to(self.device) - rgb_loss = self.rgb_loss(image, outputs["rgb"]) + image = batch["image"][..., :3].to(self.device) + pred_rgb, image = self.renderer_rgb.blend_background_for_loss_computation( + pred_image=outputs["rgb"], + pred_accumulation=outputs["accumulation"], + gt_image=image, + ) + rgb_loss = self.rgb_loss(image, pred_rgb) loss_dict = {"rgb_loss": rgb_loss} return loss_dict @@ -234,6 +240,7 @@ class NGPModel(Model): self, outputs: Dict[str, torch.Tensor], batch: Dict[str, torch.Tensor] ) -> Tuple[Dict[str, float], Dict[str, torch.Tensor]]: image = batch["image"].to(self.device) + image = self.renderer_rgb.blend_background(image) rgb = outputs["rgb"] acc = colormaps.apply_colormap(outputs["accumulation"]) depth = colormaps.apply_depth_colormap( diff --git a/nerfstudio/models/mipnerf.py b/nerfstudio/models/mipnerf.py index 9a3fdabe2..a3a783461 100644 --- a/nerfstudio/models/mipnerf.py +++ b/nerfstudio/models/mipnerf.py @@ -142,8 +142,18 @@ class MipNerfModel(Model): def get_loss_dict(self, outputs, batch, metrics_dict=None): image = batch["image"].to(self.device) - rgb_loss_coarse = self.rgb_loss(image, outputs["rgb_coarse"]) - rgb_loss_fine = self.rgb_loss(image, outputs["rgb_fine"]) + pred_coarse, image_coarse = self.renderer_rgb.blend_background_for_loss_computation( + pred_image=outputs["rgb_coarse"], + pred_accumulation=outputs["accumulation_coarse"], + gt_image=image, + ) + pred_fine, image_fine = self.renderer_rgb.blend_background_for_loss_computation( + pred_image=outputs["rgb_fine"], + pred_accumulation=outputs["accumulation_fine"], + gt_image=image, + ) + rgb_loss_coarse = self.rgb_loss(image_coarse, pred_coarse) + rgb_loss_fine = self.rgb_loss(image_fine, pred_fine) loss_dict = {"rgb_loss_coarse": rgb_loss_coarse, "rgb_loss_fine": rgb_loss_fine} loss_dict = misc.scale_dict(loss_dict, self.config.loss_coefficients) return loss_dict @@ -153,6 +163,7 @@ class MipNerfModel(Model): ) -> Tuple[Dict[str, float], Dict[str, torch.Tensor]]: assert self.config.collider_params is not None, "mip-NeRF requires collider parameters to be set." image = batch["image"].to(outputs["rgb_coarse"].device) + image = self.renderer_rgb.blend_background(image) rgb_coarse = outputs["rgb_coarse"] rgb_fine = outputs["rgb_fine"] acc_coarse = colormaps.apply_colormap(outputs["accumulation_coarse"]) diff --git a/nerfstudio/models/nerfacto.py b/nerfstudio/models/nerfacto.py index 3024c77cd..065fb571e 100644 --- a/nerfstudio/models/nerfacto.py +++ b/nerfstudio/models/nerfacto.py @@ -323,8 +323,11 @@ class NerfactoModel(Model): def get_metrics_dict(self, outputs, batch): metrics_dict = {} - image = batch["image"].to(self.device) - metrics_dict["psnr"] = self.psnr(outputs["rgb"], image) + gt_rgb = batch["image"].to(self.device) # RGB or RGBA image + gt_rgb = self.renderer_rgb.blend_background(gt_rgb) # Blend if RGBA + predicted_rgb = outputs["rgb"] + metrics_dict["psnr"] = self.psnr(predicted_rgb, gt_rgb) + if self.training: metrics_dict["distortion"] = distortion_loss(outputs["weights_list"], outputs["ray_samples_list"]) return metrics_dict @@ -332,7 +335,13 @@ class NerfactoModel(Model): 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"]) + pred_rgb, gt_rgb = self.renderer_rgb.blend_background_for_loss_computation( + pred_image=outputs["rgb"], + pred_accumulation=outputs["accumulation"], + gt_image=image, + ) + + loss_dict["rgb_loss"] = self.rgb_loss(gt_rgb, pred_rgb) if self.training: loss_dict["interlevel_loss"] = self.config.interlevel_loss_mult * interlevel_loss( outputs["weights_list"], outputs["ray_samples_list"] @@ -354,25 +363,26 @@ class NerfactoModel(Model): def get_image_metrics_and_images( self, outputs: Dict[str, torch.Tensor], batch: Dict[str, torch.Tensor] ) -> Tuple[Dict[str, float], Dict[str, torch.Tensor]]: - image = batch["image"].to(self.device) - rgb = outputs["rgb"] + gt_rgb = batch["image"].to(self.device) + predicted_rgb = outputs["rgb"] # Blended with background (black if random background) + gt_rgb = self.renderer_rgb.blend_background(gt_rgb) acc = colormaps.apply_colormap(outputs["accumulation"]) depth = colormaps.apply_depth_colormap( outputs["depth"], accumulation=outputs["accumulation"], ) - combined_rgb = torch.cat([image, rgb], dim=1) + combined_rgb = torch.cat([gt_rgb, predicted_rgb], dim=1) combined_acc = torch.cat([acc], dim=1) combined_depth = torch.cat([depth], dim=1) # Switch images from [H, W, C] to [1, C, H, W] for metrics computations - image = torch.moveaxis(image, -1, 0)[None, ...] - rgb = torch.moveaxis(rgb, -1, 0)[None, ...] + gt_rgb = torch.moveaxis(gt_rgb, -1, 0)[None, ...] + predicted_rgb = torch.moveaxis(predicted_rgb, -1, 0)[None, ...] - psnr = self.psnr(image, rgb) - ssim = self.ssim(image, rgb) - lpips = self.lpips(image, rgb) + psnr = self.psnr(gt_rgb, predicted_rgb) + ssim = self.ssim(gt_rgb, predicted_rgb) + lpips = self.lpips(gt_rgb, predicted_rgb) # all of these metrics will be logged as scalars metrics_dict = {"psnr": float(psnr.item()), "ssim": float(ssim)} # type: ignore diff --git a/nerfstudio/models/semantic_nerfw.py b/nerfstudio/models/semantic_nerfw.py index 106f1794a..87388d0e9 100644 --- a/nerfstudio/models/semantic_nerfw.py +++ b/nerfstudio/models/semantic_nerfw.py @@ -230,6 +230,7 @@ class SemanticNerfWModel(Model): def get_metrics_dict(self, outputs, batch): metrics_dict = {} image = batch["image"].to(self.device) + image = self.renderer_rgb.blend_background(image) metrics_dict["psnr"] = self.psnr(outputs["rgb"], image) metrics_dict["distortion"] = distortion_loss(outputs["weights_list"], outputs["ray_samples_list"]) return metrics_dict @@ -237,6 +238,7 @@ class SemanticNerfWModel(Model): def get_loss_dict(self, outputs, batch, metrics_dict=None): loss_dict = {} image = batch["image"].to(self.device) + image = self.renderer_rgb.blend_background(image) loss_dict["interlevel_loss"] = self.config.interlevel_loss_mult * interlevel_loss( outputs["weights_list"], outputs["ray_samples_list"] ) @@ -263,6 +265,12 @@ class SemanticNerfWModel(Model): ) -> Tuple[Dict[str, float], Dict[str, torch.Tensor]]: image = batch["image"].to(self.device) rgb = outputs["rgb"] + rgb, image = self.renderer_rgb.blend_background_for_loss_computation( + pred_image=rgb, + pred_accumulation=outputs["accumulation"], + gt_image=image, + ) + rgb = torch.clamp(rgb, min=0, max=1) acc = colormaps.apply_colormap(outputs["accumulation"]) depth = colormaps.apply_depth_colormap( diff --git a/nerfstudio/models/tensorf.py b/nerfstudio/models/tensorf.py index 160ef1b86..a2cd9901d 100644 --- a/nerfstudio/models/tensorf.py +++ b/nerfstudio/models/tensorf.py @@ -303,9 +303,14 @@ class TensoRFModel(Model): def get_loss_dict(self, outputs, batch, metrics_dict=None) -> Dict[str, torch.Tensor]: # Scaling metrics by coefficients to create the losses. device = outputs["rgb"].device - image = batch["image"].to(device) + image = batch["image"][..., :3].to(device) + pred_image, image = self.renderer_rgb.blend_background_for_loss_computation( + pred_image=outputs["rgb"], + pred_accumulation=outputs["accumulation"], + gt_image=image, + ) - rgb_loss = self.rgb_loss(image, outputs["rgb"]) + rgb_loss = self.rgb_loss(image, pred_image) loss_dict = {"rgb_loss": rgb_loss} @@ -334,6 +339,7 @@ class TensoRFModel(Model): self, outputs: Dict[str, torch.Tensor], batch: Dict[str, torch.Tensor] ) -> Tuple[Dict[str, float], Dict[str, torch.Tensor]]: image = batch["image"].to(outputs["rgb"].device) + image = self.renderer_rgb.blend_background(image) rgb = outputs["rgb"] acc = colormaps.apply_colormap(outputs["accumulation"]) assert self.config.collider_params is not None diff --git a/nerfstudio/models/vanilla_nerf.py b/nerfstudio/models/vanilla_nerf.py index 62d6b96b7..ea5344fb8 100644 --- a/nerfstudio/models/vanilla_nerf.py +++ b/nerfstudio/models/vanilla_nerf.py @@ -192,9 +192,19 @@ class NeRFModel(Model): # Scaling metrics by coefficients to create the losses. device = outputs["rgb_coarse"].device image = batch["image"].to(device) + coarse_pred, coarse_image = self.renderer_rgb.blend_background_for_loss_computation( + pred_image=outputs["rgb_coarse"], + pred_accumulation=outputs["accumulation_coarse"], + gt_image=image, + ) + fine_pred, fine_image = self.renderer_rgb.blend_background_for_loss_computation( + pred_image=outputs["rgb_fine"], + pred_accumulation=outputs["accumulation_fine"], + gt_image=image, + ) - rgb_loss_coarse = self.rgb_loss(image, outputs["rgb_coarse"]) - rgb_loss_fine = self.rgb_loss(image, outputs["rgb_fine"]) + rgb_loss_coarse = self.rgb_loss(coarse_image, coarse_pred) + rgb_loss_fine = self.rgb_loss(fine_image, fine_pred) loss_dict = {"rgb_loss_coarse": rgb_loss_coarse, "rgb_loss_fine": rgb_loss_fine} loss_dict = misc.scale_dict(loss_dict, self.config.loss_coefficients) @@ -204,6 +214,7 @@ class NeRFModel(Model): self, outputs: Dict[str, torch.Tensor], batch: Dict[str, torch.Tensor] ) -> Tuple[Dict[str, float], Dict[str, torch.Tensor]]: image = batch["image"].to(outputs["rgb_coarse"].device) + image = self.renderer_rgb.blend_background(image) rgb_coarse = outputs["rgb_coarse"] rgb_fine = outputs["rgb_fine"] acc_coarse = colormaps.apply_colormap(outputs["accumulation_coarse"]) diff --git a/tests/field_components/test_mlp.py b/tests/field_components/test_mlp.py index e54c38a54..b52734654 100644 --- a/tests/field_components/test_mlp.py +++ b/tests/field_components/test_mlp.py @@ -4,7 +4,7 @@ MLP Test import torch from torch import nn -from nerfstudio.field_components import MLP +from nerfstudio.field_components.mlp import MLP def test_mlp(): From e7fd1aefa29abf3c66c503934af49cd0aaa91c90 Mon Sep 17 00:00:00 2001 From: hyperzy <50564821+hyperzy@users.noreply.github.com> Date: Wed, 19 Jul 2023 02:02:15 +0800 Subject: [PATCH 39/44] Load schedulers from ckpt in a backward-compatible manner (#2236) --- nerfstudio/configs/experiment_config.py | 2 ++ nerfstudio/engine/trainer.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/nerfstudio/configs/experiment_config.py b/nerfstudio/configs/experiment_config.py index c3a400dd1..1fa3598f6 100644 --- a/nerfstudio/configs/experiment_config.py +++ b/nerfstudio/configs/experiment_config.py @@ -76,6 +76,8 @@ class ExperimentConfig(InstantiateConfig): """Alias for --pipeline.model.prompt""" relative_model_dir: Path = Path("nerfstudio_models/") """Relative path to save all checkpoints.""" + load_scheduler: bool = True + """Whether to load the scheduler state_dict to resume training, if exists""" def is_viewer_enabled(self) -> bool: """Checks if a viewer is enabled.""" diff --git a/nerfstudio/engine/trainer.py b/nerfstudio/engine/trainer.py index a89e4801c..489543f0a 100644 --- a/nerfstudio/engine/trainer.py +++ b/nerfstudio/engine/trainer.py @@ -406,7 +406,8 @@ class Trainer: # load the checkpoints for pipeline, optimizers, and gradient scalar self.pipeline.load_pipeline(loaded_state["pipeline"], loaded_state["step"]) self.optimizers.load_optimizers(loaded_state["optimizers"]) - self.optimizers.load_schedulers(loaded_state["schedulers"]) + if "schedulers" in loaded_state and self.config.load_scheduler: + self.optimizers.load_schedulers(loaded_state["schedulers"]) self.grad_scaler.load_state_dict(loaded_state["scalers"]) CONSOLE.print(f"Done loading Nerfstudio checkpoint from {load_path}") elif load_checkpoint is not None: @@ -416,6 +417,8 @@ class Trainer: # load the checkpoints for pipeline, optimizers, and gradient scalar self.pipeline.load_pipeline(loaded_state["pipeline"], loaded_state["step"]) self.optimizers.load_optimizers(loaded_state["optimizers"]) + if "schedulers" in loaded_state and self.config.load_scheduler: + self.optimizers.load_schedulers(loaded_state["schedulers"]) self.grad_scaler.load_state_dict(loaded_state["scalers"]) CONSOLE.print(f"Done loading Nerfstudio checkpoint from {load_checkpoint}") else: From 4f2157cd5849d2565b1773df6f19de6fa4f848ab Mon Sep 17 00:00:00 2001 From: hturki Date: Thu, 20 Jul 2023 12:48:25 -0400 Subject: [PATCH 40/44] support packed samples in semantic renderer (#2247) --- nerfstudio/model_components/renderers.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/nerfstudio/model_components/renderers.py b/nerfstudio/model_components/renderers.py index ae50b0e12..24090872e 100644 --- a/nerfstudio/model_components/renderers.py +++ b/nerfstudio/model_components/renderers.py @@ -410,10 +410,17 @@ class SemanticRenderer(nn.Module): cls, semantics: Float[Tensor, "*bs num_samples num_classes"], weights: Float[Tensor, "*bs num_samples 1"], + ray_indices: Optional[Int[Tensor, "num_samples"]] = None, + num_rays: Optional[int] = None, ) -> Float[Tensor, "*bs num_classes"]: """Calculate semantics along the ray.""" - sem = torch.sum(weights * semantics, dim=-2) - return sem + if ray_indices is not None and num_rays is not None: + # Necessary for packed samples from volumetric ray sampler + return nerfacc.accumulate_along_rays( + weights[..., 0], values=semantics, ray_indices=ray_indices, n_rays=num_rays + ) + else: + return torch.sum(weights * semantics, dim=-2) class NormalsRenderer(nn.Module): From 8682e481b16e4725681b95d9a449ee3cfe4a9e7f Mon Sep 17 00:00:00 2001 From: Cyrus Vachha Date: Mon, 24 Jul 2023 07:26:20 -0700 Subject: [PATCH 41/44] Render VR180 video (#2261) * Render VR180 video Adding the VR180 camera model to render VR180 video and images. I condensed the omnidirectional stereo and VR180 render post processing in the render script. I have updated the documentation for VR180 support. * Condensing formatting of camera types Condensing the if statement for camera type intrinsics. --- docs/extensions/blender_addon.md | 9 ++-- docs/quickstart/custom_dataset.md | 57 ++++++++++++++------ nerfstudio/cameras/camera_paths.py | 14 +++-- nerfstudio/cameras/cameras.py | 69 ++++++++++++++++++++++++ nerfstudio/scripts/render.py | 85 +++++++++++++++++++++--------- 5 files changed, 187 insertions(+), 47 deletions(-) diff --git a/docs/extensions/blender_addon.md b/docs/extensions/blender_addon.md index b433fd2cf..cc56ff346 100644 --- a/docs/extensions/blender_addon.md +++ b/docs/extensions/blender_addon.md @@ -6,7 +6,7 @@ ## Overview -This Blender add-on allows for compositing with a Nerfstudio render as a background layer by generating a camera path JSON file from the Blender camera path, as well as a way to import Nerfstudio JSON files as a Blender camera baked with the Nerfstudio camera path. This add-on also allows compositing multiple NeRF objects into a NeRF scene. This is achieved by importing a mesh or point-cloud representation of the NeRF scene from Nerfstudio into Blender and getting the camera coordinates relative to the transformations of the NeRF representation. Dynamic FOV from the Blender camera is supported and will match the Nerfstudio render. Perspective, equirectangular, and omnidirectional stereo (VR 360) cameras are supported. +This Blender add-on allows for compositing with a Nerfstudio render as a background layer by generating a camera path JSON file from the Blender camera path, as well as a way to import Nerfstudio JSON files as a Blender camera baked with the Nerfstudio camera path. This add-on also allows compositing multiple NeRF objects into a NeRF scene. This is achieved by importing a mesh or point-cloud representation of the NeRF scene from Nerfstudio into Blender and getting the camera coordinates relative to the transformations of the NeRF representation. Dynamic FOV from the Blender camera is supported and will match the Nerfstudio render. Perspective, equirectangular, VR180, and omnidirectional stereo (VR 360) cameras are supported.

image @@ -89,7 +89,7 @@ This Blender add-on allows for compositing with a Nerfstudio render as a backgro - The generated JSON camera path follows the user specified frame start, end, and step fields in the Output Properties in Blender. The JSON file specifies the user specified x and y render resolutions at the given % in the Output Properties. - The add-on computes the camera coordinates based on the active camera in the scene. - FOV animated changes of the camera will be matched with the NeRF render. -- Perspective, equirectangular, and omnidirectional stereo cameras are supported and can be selected within Blender. +- Perspective, equirectangular, VR180, and omnidirectional stereo cameras are supported and can be configured within Blender. - The generated JSON file can be imported into Nerfstudio. Each keyframe of the camera transform in the frame sequence in Blender will be a keyframe in Nerfstudio. The exported JSON camera path is baked, where each frame in the sequence is a keyframe. This is to ensure that frame interpolation across Nerfstudio and Blender do not differ.
image @@ -101,7 +101,10 @@ This Blender add-on allows for compositing with a Nerfstudio render as a backgro - It is recommended to export a high fidelity mesh as the NeRF representation from Nerfstudio. However if that is not possible or the scene is too large, a point cloud representation also works. - For compositing, it is recommended to convert the video render into image frames to ensure the Blender and NeRF renders are in sync. - Currently, dynamic camera focus is not supported. -- For compositing Blender objects or NeRFs with Nerfstudio omnidirectional stereo renders (VR 360), configure the Blender camera to be equirectangular and enable stereoscopy in the Output Properties. Under the Stereoscopy panel the Blender camera settings, change the mode to "Parallel", set the Interocular Distance to 0.064 m, and checkmark "Spherical Stereo". +- Compositing with Blender Objects and VR180 or ODS Renders + - Configure the Blender camera to be panoramic equirectangular and enable stereoscopy in the Output Properties. For the VR180 Blender camera, set the panoramic longitude min and max to -90 and 90. + - Under the Stereoscopy panel the Blender camera settings, change the mode to "Parallel", set the Interocular Distance to 0.064 m, and checkmark "Spherical Stereo". +
image
diff --git a/docs/quickstart/custom_dataset.md b/docs/quickstart/custom_dataset.md index 4792aeb72..1b6c24071 100644 --- a/docs/quickstart/custom_dataset.md +++ b/docs/quickstart/custom_dataset.md @@ -347,25 +347,37 @@ The pixels representing the distorted hand and head are obviously not useful in If cropping only needs to be done from the bottom, you can use the `--crop-bottom [num]` argument which would be the same as doing `--crop-factor 0.0 [num] 0.0 0.0` -## 🥽 Render Omni-directional Stereo (360 VR) +## 🥽 Render VR Video -Stereo equirectangular rendering for VR video is supported as omni-directional stereo video and image rendering. This outputs two equirectangular renders vertically stacked, one for each eye. Omni-directional stereo (ODS) is a method to render VR 3D 360 videos, and may introduce slight depth distortions for close objects. For additional information on how ODS works, refer to this [writeup](https://developers.google.com/vr/jump/rendering-ods-content.pdf). +Stereo equirectangular rendering for VR video is supported as VR180 and omni-directional stereo (360 VR) Nerfstudio camera types for video and image rendering. + +### Omni-directional Stereo (360 VR) +This outputs two equirectangular renders vertically stacked, one for each eye. Omni-directional stereo (ODS) is a method to render VR 3D 360 videos, and may introduce slight depth distortions for close objects. For additional information on how ODS works, refer to this [writeup](https://developers.google.com/vr/jump/rendering-ods-content.pdf).
- +
-To render in ODS it is essential to adjust the NeRF to have an approximately true-to-life real world scale (adjustable in the camera path) to ensure that the scene depth and IPD (distance between the eyes) is appropriate for the render to be viewable in VR. You can adjust the scene scale with the [Nerfstudio Blender Add-on](https://docs.nerf.studio/en/latest/extensions/blender_addon.html) by appropriately scaling a point cloud representation of the NeRF. + +### VR180 +This outputs two 180 deg equirectangular renders horizontally stacked, one for each eye. VR180 is a video format for VR 3D 180 videos. Unlike in omnidirectional stereo, VR180 content only displays front facing content. + +
+ +
+ +### Setup instructions +To render for VR video it is essential to adjust the NeRF to have an approximately true-to-life real world scale (adjustable in the camera path) to ensure that the scene depth and IPD (distance between the eyes) is appropriate for the render to be viewable in VR. You can adjust the scene scale with the [Nerfstudio Blender Add-on](https://docs.nerf.studio/en/latest/extensions/blender_addon.html) by appropriately scaling a point cloud representation of the NeRF. Results may be unviewable if the scale is not set appropriately. The IPD is set at 64mm by default but only is accurate when the NeRF scene is true to scale. -For good quality renders, it is recommended to render at 4096x2048 per eye, or 2048x1024 per eye. Render resolutions must be specified in the camera path in a 2:1 aspect ratio. The final stacked render output will automatically be constructed as 1:1. +For good quality renders, it is recommended to render at high resolutions (For ODS: 4096x2048 per eye, or 2048x1024 per eye. For VR180: 4096x4096 per eye or 2048x2048 per eye). Render resolutions for a single eye are specified in the camera path. For VR180, resolutions must be in a 1:1 aspect ratio. For ODS, resolutions must be in a 2:1 aspect ratio. The final stacked render output will automatically be constructed (with aspect ratios for VR180 as 2:1 and ODS as 1:1). :::{admonition} Note :class: info If you are rendering an image sequence, it is recommended to render as png instead of jpeg, since the png will appear clearer. However, file sizes can be significantly larger with png. ::: -To render with the ODS camera: +To render with the VR videos camera: 1. Use the [Nerfstudio Blender Add-on](https://docs.nerf.studio/en/latest/extensions/blender_addon.html) to set the scale of the NeRF scene and create the camera path - Export a point cloud representation of the NeRF - Import the point cloud representation in Blender and enable the Nerfstudio Blender Add-on @@ -374,23 +386,38 @@ To render with the ODS camera: - To place the camera at the correct height from the ground in the scene, you can create a cylinder representing the viewer vertically scaled to the viewer’s height, and place the camera at eye level. - Animate the camera movement as needed - Create the camera path JSON file with the Nerfstudio Blender Add-on + 2. Edit the JSON camera path file - - Open the camera path JSON file and specify the `camera_type` as `omnidirectional` - - Specify the `render_height` and `render_width` to the resolution of a single eye. The width:height aspect ratio must be 2:1. Recommended resolutions are 4096x2048 or 2048x1024. + + **Omni-directional Stereo (360 VR)** + - Open the camera path JSON file and specify the `camera_type` as `omnidirectional` + - Specify the `render_height` and `render_width` to the resolution of a single eye. The width:height aspect ratio must be 2:1. Recommended resolutions are 4096x2048 or 2048x1024. +
+ +
+ + + **VR180** + - Open the camera path JSON file and specify the `camera_type` as `vr180` + - Specify the `render_height` and `render_width` to the resolution of a single eye. The width:height aspect ratio must be 1:1. Recommended resolutions are 4096x4096 or 2048x2048. +
+ +
+ - Save the camera path and render the NeRF -
- -
+ :::{admonition} Note :class: info If the depth of the scene is unviewable and looks too close or expanded when viewing the render in VR, the scale of the NeRF may be set too small. If there is almost no discernible depth, the scale of the NeRF may be too large. Getting the right scale may take some experimentation, so it is recommended to either render at a much lower resolution or just one frame to ensure the depth and render is viewable in the VR headset. ::: -### Additional Notes -- Rendering with ODS can take significantly longer than traditional renders due to higher resolutions and needing to render a left and right eye view for each frame. Render times may grow exponentially with larger resolutions. -- When rendering ODS content, Nerfstudio will first render the left eye, then the right eye, and finally vertically stack the renders. During this process, Nerfstudio will create a temporary folder to store the left and right eye renders and delete this folder once the final renders are stacked. +#### Additional Notes +- Rendering with VR180 or ODS can take significantly longer than traditional renders due to higher resolutions and needing to render a left and right eye view for each frame. Render times may grow exponentially with larger resolutions. +- When rendering VR180 or ODS content, Nerfstudio will first render the left eye, then the right eye, and finally vertically stack the renders. During this process, Nerfstudio will create a temporary folder to store the left and right eye renders and delete this folder once the final renders are stacked. - If rendering content where the camera is stationary for many frames, it is recommended to only render once at that position and extend the time in a video editor since ODS renders can take a lot of time to render. - It is recommended to render a preliminary render at a much lower resolution or frame rate to test and ensure that the depth and camera position look accurate in VR. - The IPD can be modified in the `cameras.py` script as the variable `vr_ipd` (default is 64 mm). - - If using the Nerfstudio Blender Add-on to composite Blender objects or NeRFs with the omnidirectional render, change the Stereoscopy mode to "Parallel" set the Interocular Distance to 0.064 m. + - Compositing with Blender Objects and VR180 or ODS Renders + - Configure the Blender camera as panoramic and equirectangular. For the VR180 Blender camera, set the panoramic longitude min and max to -90 and 90. + - Change the Stereoscopy mode to "Parallel" set the Interocular Distance to 0.064 m. \ No newline at end of file diff --git a/nerfstudio/cameras/camera_paths.py b/nerfstudio/cameras/camera_paths.py index 50d417f92..fef982ae1 100644 --- a/nerfstudio/cameras/camera_paths.py +++ b/nerfstudio/cameras/camera_paths.py @@ -141,6 +141,8 @@ def get_path_from_json(camera_path: Dict[str, Any]) -> Cameras: camera_type = CameraType.EQUIRECTANGULAR elif camera_path["camera_type"].lower() == "omnidirectional": camera_type = CameraType.OMNIDIRECTIONALSTEREO_L + elif camera_path["camera_type"].lower() == "vr180": + camera_type = CameraType.VR180_L else: camera_type = CameraType.PERSPECTIVE @@ -151,11 +153,13 @@ def get_path_from_json(camera_path: Dict[str, Any]) -> Cameras: # pose c2w = torch.tensor(camera["camera_to_world"]).view(4, 4)[:3] c2ws.append(c2w) - if ( - camera_type == CameraType.EQUIRECTANGULAR - or camera_type == CameraType.OMNIDIRECTIONALSTEREO_L - or camera_type == CameraType.OMNIDIRECTIONALSTEREO_R - ): + if camera_type in [ + CameraType.EQUIRECTANGULAR, + CameraType.OMNIDIRECTIONALSTEREO_L, + CameraType.OMNIDIRECTIONALSTEREO_R, + CameraType.VR180_L, + CameraType.VR180_R, + ]: fxs.append(image_width / 2) fys.append(image_height) else: diff --git a/nerfstudio/cameras/cameras.py b/nerfstudio/cameras/cameras.py index c9886d768..8ad46e223 100644 --- a/nerfstudio/cameras/cameras.py +++ b/nerfstudio/cameras/cameras.py @@ -46,6 +46,8 @@ class CameraType(Enum): EQUIRECTANGULAR = auto() OMNIDIRECTIONALSTEREO_L = auto() OMNIDIRECTIONALSTEREO_R = auto() + VR180_L = auto() + VR180_R = auto() CAMERA_MODEL_TO_TYPE = { @@ -58,6 +60,8 @@ CAMERA_MODEL_TO_TYPE = { "EQUIRECTANGULAR": CameraType.EQUIRECTANGULAR, "OMNIDIRECTIONALSTEREO_L": CameraType.OMNIDIRECTIONALSTEREO_L, "OMNIDIRECTIONALSTEREO_R": CameraType.OMNIDIRECTIONALSTEREO_R, + "VR180_L": CameraType.VR180_L, + "VR180_R": CameraType.VR180_R, } @@ -708,6 +712,60 @@ class Cameras(TensorDataclass): return ods_origins_circle, directions_stack + def _compute_rays_for_vr180( + eye: Literal["left", "right"] + ) -> Tuple[Float[Tensor, "num_rays_shape 3"], Float[Tensor, "3 num_rays_shape 3"]]: + """Compute the rays for a VR180 camera + + Args: + eye: Which eye to compute rays for. + + Returns: + A tuple containing the origins and the directions of the rays. + """ + # Directions calculated similarly to equirectangular + vr180_cam_type = CameraType.VR180_R.value if eye == "right" else CameraType.VR180_L.value + mask = (self.camera_type[true_indices] == vr180_cam_type).squeeze(-1) + mask = torch.stack([mask, mask, mask], dim=0) + + # adjusting theta range to +/-90 deg + theta = -torch.pi * ((x - cx) / (fx * 2))[0] + phi = torch.pi * (0.5 - coord_stack[..., 1]) + + directions_stack[..., 0][mask] = torch.masked_select(-torch.sin(theta) * torch.sin(phi), mask).float() + directions_stack[..., 1][mask] = torch.masked_select(torch.cos(phi), mask).float() + directions_stack[..., 2][mask] = torch.masked_select(-torch.cos(theta) * torch.sin(phi), mask).float() + + vr_ipd = 0.064 # IPD in meters (note: scale of NeRF must be true to life and can be adjusted with the Blender add-on) + isRightEye = 1 if eye == "right" else -1 + + # find VR180 camera position + c2w = self.camera_to_worlds[true_indices] + assert c2w.shape == num_rays_shape + (3, 4) + transposedC2W = c2w[0][0].t() + vr180_cam_position = transposedC2W[3].repeat(c2w.shape[1], 1) + + rotation = c2w[..., :3, :3] + + -torch.pi * ((x - cx) / fx)[0] + + # interocular axis of the VR180 camera + vr180_x_axis = torch.tensor([1, 0, 0], device=c2w.device) + + # VR180 ray origins of horizontal offset + vr180_origins = isRightEye * (vr_ipd / 2.0) * (vr180_x_axis.repeat(c2w.shape[1], 1)) + + # rotate origins to match the camera rotation + for i in range(vr180_origins.shape[0]): + vr180_origins[i] = rotation[0][0] @ vr180_origins[i] + vr180_cam_position[0] + + vr180_origins = vr180_origins.unsqueeze(0).repeat(c2w.shape[0], 1, 1) + + # assign final camera origins + c2w[..., :3, 3] = vr180_origins + + return vr180_origins, directions_stack + for cam in cam_types: if CameraType.PERSPECTIVE.value in cam_types: mask = (self.camera_type[true_indices] == CameraType.PERSPECTIVE.value).squeeze(-1) # (num_rays) @@ -755,6 +813,17 @@ class Cameras(TensorDataclass): ods_origins_circle, directions_stack = _compute_rays_for_omnidirectional_stereo("right") # assign final camera origins c2w[..., :3, 3] = ods_origins_circle + + elif CameraType.VR180_L.value in cam_types: + vr180_origins, directions_stack = _compute_rays_for_vr180("left") + # assign final camera origins + c2w[..., :3, 3] = vr180_origins + + elif CameraType.VR180_R.value in cam_types: + vr180_origins, directions_stack = _compute_rays_for_vr180("right") + # assign final camera origins + c2w[..., :3, 3] = vr180_origins + else: raise ValueError(f"Camera type {cam} not supported.") diff --git a/nerfstudio/scripts/render.py b/nerfstudio/scripts/render.py index 60378616d..84c8950b7 100644 --- a/nerfstudio/scripts/render.py +++ b/nerfstudio/scripts/render.py @@ -344,16 +344,23 @@ class RenderCameraPath(BaseRender): crop_data = get_crop_from_json(camera_path) camera_path = get_path_from_json(camera_path) - if camera_path.camera_type[0] == CameraType.OMNIDIRECTIONALSTEREO_L.value: + if ( + camera_path.camera_type[0] == CameraType.OMNIDIRECTIONALSTEREO_L.value + or camera_path.camera_type[0] == CameraType.VR180_L.value + ): # temp folder for writing left and right view renders temp_folder_path = self.output_path.parent / (self.output_path.stem + "_temp") Path(temp_folder_path).mkdir(parents=True, exist_ok=True) - left_eye_path = temp_folder_path / "ods_render_Left.mp4" + left_eye_path = temp_folder_path / "render_left.mp4" self.output_path = left_eye_path - CONSOLE.print("[bold green]:goggles: Omni-directional Stereo VR :goggles:") + if camera_path.camera_type[0] == CameraType.OMNIDIRECTIONALSTEREO_L.value: + CONSOLE.print("[bold green]:goggles: Omni-directional Stereo VR :goggles:") + else: + CONSOLE.print("[bold green]:goggles: VR180 :goggles:") + CONSOLE.print("Rendering left eye view") # add mp4 suffix to video output if none is specified @@ -374,14 +381,21 @@ class RenderCameraPath(BaseRender): colormap_options=self.colormap_options, ) - if camera_path.camera_type[0] == CameraType.OMNIDIRECTIONALSTEREO_L.value: + if ( + camera_path.camera_type[0] == CameraType.OMNIDIRECTIONALSTEREO_L.value + or camera_path.camera_type[0] == CameraType.VR180_L.value + ): # declare paths for left and right renders left_eye_path = self.output_path - right_eye_path = left_eye_path.parent / "ods_render_Right.mp4" + right_eye_path = left_eye_path.parent / "render_right.mp4" self.output_path = right_eye_path - camera_path.camera_type[0] = CameraType.OMNIDIRECTIONALSTEREO_R.value + + if camera_path.camera_type[0] == CameraType.OMNIDIRECTIONALSTEREO_L.value: + camera_path.camera_type[0] = CameraType.OMNIDIRECTIONALSTEREO_R.value + else: + camera_path.camera_type[0] = CameraType.VR180_R.value CONSOLE.print("Rendering right eye view") _render_trajectory_video( @@ -398,26 +412,49 @@ class RenderCameraPath(BaseRender): colormap_options=self.colormap_options, ) - # stack the left and right eye renders for final output self.output_path = Path(str(left_eye_path.parent)[:-5] + ".mp4") - ffmpeg_ods_command = "" - if self.output_format == "video": - ffmpeg_ods_command = f'ffmpeg -y -i "{left_eye_path}" -i "{right_eye_path}" -filter_complex "[0:v]pad=iw:2*ih[int];[int][1:v]overlay=0:h" -c:v libx264 -crf 23 -preset veryfast "{self.output_path}"' - run_command(ffmpeg_ods_command, verbose=False) - if self.output_format == "images": - # create a folder for the stacked renders - self.output_path = Path(str(left_eye_path.parent)[:-5]) - self.output_path.mkdir(parents=True, exist_ok=True) - if self.image_format == "png": - ffmpeg_ods_command = f'ffmpeg -y -pattern_type glob -i "{str(left_eye_path.with_suffix("") / "*.png")}" -pattern_type glob -i "{str(right_eye_path.with_suffix("") / "*.png")}" -filter_complex vstack -start_number 0 "{str(self.output_path)+"//%05d.png"}"' - elif self.image_format == "jpeg": - ffmpeg_ods_command = f'ffmpeg -y -pattern_type glob -i "{str(left_eye_path.with_suffix("") / "*.jpg")}" -pattern_type glob -i "{str(right_eye_path.with_suffix("") / "*.jpg")}" -filter_complex vstack -start_number 0 "{str(self.output_path)+"//%05d.jpg"}"' - run_command(ffmpeg_ods_command, verbose=False) - # remove the temp files directory - if str(left_eye_path.parent)[-5:] == "_temp": - shutil.rmtree(left_eye_path.parent, ignore_errors=True) - CONSOLE.print("[bold green]Final ODS Render Complete") + if camera_path.camera_type[0] == CameraType.OMNIDIRECTIONALSTEREO_R.value: + # stack the left and right eye renders vertically for ODS final output + ffmpeg_ods_command = "" + if self.output_format == "video": + ffmpeg_ods_command = f'ffmpeg -y -i "{left_eye_path}" -i "{right_eye_path}" -filter_complex "[0:v]pad=iw:2*ih[int];[int][1:v]overlay=0:h" -c:v libx264 -crf 23 -preset veryfast "{self.output_path}"' + run_command(ffmpeg_ods_command, verbose=False) + if self.output_format == "images": + # create a folder for the stacked renders + self.output_path = Path(str(left_eye_path.parent)[:-5]) + self.output_path.mkdir(parents=True, exist_ok=True) + if self.image_format == "png": + ffmpeg_ods_command = f'ffmpeg -y -pattern_type glob -i "{str(left_eye_path.with_suffix("") / "*.png")}" -pattern_type glob -i "{str(right_eye_path.with_suffix("") / "*.png")}" -filter_complex vstack -start_number 0 "{str(self.output_path)+"//%05d.png"}"' + elif self.image_format == "jpeg": + ffmpeg_ods_command = f'ffmpeg -y -pattern_type glob -i "{str(left_eye_path.with_suffix("") / "*.jpg")}" -pattern_type glob -i "{str(right_eye_path.with_suffix("") / "*.jpg")}" -filter_complex vstack -start_number 0 "{str(self.output_path)+"//%05d.jpg"}"' + run_command(ffmpeg_ods_command, verbose=False) + + # remove the temp files directory + if str(left_eye_path.parent)[-5:] == "_temp": + shutil.rmtree(left_eye_path.parent, ignore_errors=True) + CONSOLE.print("[bold green]Final ODS Render Complete") + else: + # stack the left and right eye renders horizontally for VR180 final output + self.output_path = Path(str(left_eye_path.parent)[:-5] + ".mp4") + ffmpeg_vr180_command = "" + if self.output_format == "video": + ffmpeg_vr180_command = f'ffmpeg -y -i "{right_eye_path}" -i "{left_eye_path}" -filter_complex "[1:v]hstack=inputs=2" -c:a copy "{self.output_path}"' + run_command(ffmpeg_vr180_command, verbose=False) + if self.output_format == "images": + # create a folder for the stacked renders + self.output_path = Path(str(left_eye_path.parent)[:-5]) + self.output_path.mkdir(parents=True, exist_ok=True) + if self.image_format == "png": + ffmpeg_vr180_command = f'ffmpeg -y -pattern_type glob -i "{str(left_eye_path.with_suffix("") / "*.png")}" -pattern_type glob -i "{str(right_eye_path.with_suffix("") / "*.png")}" -filter_complex hstack -start_number 0 "{str(self.output_path)+"//%05d.png"}"' + elif self.image_format == "jpeg": + ffmpeg_vr180_command = f'ffmpeg -y -pattern_type glob -i "{str(left_eye_path.with_suffix("") / "*.jpg")}" -pattern_type glob -i "{str(right_eye_path.with_suffix("") / "*.jpg")}" -filter_complex hstack -start_number 0 "{str(self.output_path)+"//%05d.jpg"}"' + run_command(ffmpeg_vr180_command, verbose=False) + + # remove the temp files directory + if str(left_eye_path.parent)[-5:] == "_temp": + shutil.rmtree(left_eye_path.parent, ignore_errors=True) + CONSOLE.print("[bold green]Final VR180 Render Complete") @dataclass From ad73c5142e9ba5cf35d2e76e7772e0c47a5d629b Mon Sep 17 00:00:00 2001 From: Frederik Warburg <32867328+FrederikWarburg@users.noreply.github.com> Date: Tue, 25 Jul 2023 22:52:59 +0200 Subject: [PATCH 42/44] nerfbusters doc (#2208) * nerfbusters doc * ordered alphabetically and added nerfbusters to list of 3rd party methods --- docs/index.md | 1 + docs/nerfology/methods/index.md | 1 + docs/nerfology/methods/nerfbusters.md | 66 +++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 docs/nerfology/methods/nerfbusters.md diff --git a/docs/index.md b/docs/index.md index b189e03c3..6e78a1668 100644 --- a/docs/index.md +++ b/docs/index.md @@ -114,6 +114,7 @@ This documentation is organized into 3 parts: - [Instruct-NeRF2NeRF](nerfology/methods/in2n.md): Editing 3D Scenes with Instructions - [K-Planes](nerfology/methods/kplanes.md): Unified 3D and 4D Radiance Fields - [LERF](nerfology/methods/lerf.md): Language Embedded Radiance Fields +- [Nerfbusters](nerfology/methods/nerfbusters.md): Removing Ghostly Artifacts from Casually Captured NeRFs - [NeRFPlayer](nerfology/methods/nerfplayer.md): 4D Radiance Fields by Streaming Feature Channels - [Tetra-NeRF](nerfology/methods/tetranerf.md): Representing Neural Radiance Fields Using Tetrahedra diff --git a/docs/nerfology/methods/index.md b/docs/nerfology/methods/index.md index 8eac41800..f1d4bd5e8 100644 --- a/docs/nerfology/methods/index.md +++ b/docs/nerfology/methods/index.md @@ -33,6 +33,7 @@ The following methods are supported in nerfstudio: Mip-NeRF NeRF Nerfacto + Nerfbusters NeRFPlayer Tetra-NeRF TensoRF diff --git a/docs/nerfology/methods/nerfbusters.md b/docs/nerfology/methods/nerfbusters.md new file mode 100644 index 000000000..c75baab96 --- /dev/null +++ b/docs/nerfology/methods/nerfbusters.md @@ -0,0 +1,66 @@ +# Nerfbusters + +

Removing Ghostly Artifacts from Casually Captured NeRFs 👻

+ +```{button-link} https://ethanweber.me/nerfbusters +:color: primary +:outline: +Paper Website +``` + +```{button-link} https://github.com/ethanweber/nerfbusters +:color: primary +:outline: +Code and Data +``` + +**TLDR: We present a method that uses a 3D diffusion prior to clean NeRFs and an evaluation procedure for in-the-wild NeRFs** + + + +## Installation + +First install nerfstudio dependencies. Then run: + +```bash +pip install git+https://github.com/ethanweber/nerfbusters +nerfbusters-setup +ns-train nerfbusters --help +``` + +For more details, see the [installation instructions](https://github.com/ethanweber/nerfbusters). + +### Running the Method + +Please checkout the readme for the [Nerfbusters repository](https://github.com/ethanweber/nerfbusters) + +## Abstract + +Casually captured Neural Radiance Fields (NeRFs) suffer from artifacts such as floaters or flawed geometry when rendered outside the path of the training views. However, common practice is still to evaluate on every 8th frame, which does not measure rendering quality away from training views, hindering progress in volume rendering. We propose a new dataset and evaluation procedure, where two camera trajectories are recorded of the scene, one used for training, and the other for evaluation. We find that existing hand-crafted regularizers do not remove floaters nor improve scene geometry in this more challenging in-the-wild setting. To this end, we propose a learned, local 3D diffusion prior and a novel density score distillation sampling loss. We show that this learned prior removes floaters and improves scene geometry for casual captures. + +## NeRF evaluation for casually captured videos + +We train on a single camera trajectory and evaluate on a second camera trajectory. Both methods succeed on the training trajectory, but only our method succeeds on the evaluation trajectory. + + + +### Model Overview + +We learn a local 3D prior with a diffusion model that regularizes the 3D geometry of NeRFs. We use importance sampling to query a cube with NeRF densities. We binarize these densities and perform one single denoising step using a pre-trained 3D diffusion model. With these denoised densities, we compute a density score distillation sampling (DSDS) that penalizes NeRF densities where the diffusion model predicts empty voxels and pushes the NeRF densities above the target w where the diffusion model predicts occupied voxels. + +![Overview](https://ethanweber.me/nerfbusters/media/method.png) + + +### Visibiltiy Loss + +Our visibility loss enables stepping behind or outside the training camera frustums. We accomplish this by supervising densities to be low when not seen by at least one training view. Other solutions would be to store an occupancy grid or compute ray-frustum intersection tests during rendering. Our solution is easy to implement and applicable to any NeRF. + +![Visibility](https://ethanweber.me/nerfbusters/media/visibility_loss.png) + +### Results and dataset preview + +For results and a dataset preview, view the [project page](https://ethanweber.me/nerfbusters)! From 4d80f0ceeefa13e91e14e9cdebec39232b5cb72e Mon Sep 17 00:00:00 2001 From: hturki Date: Tue, 25 Jul 2023 21:05:40 -0400 Subject: [PATCH 43/44] Use correct pixel area in volumetric sampler (#2268) --- nerfstudio/model_components/ray_samplers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nerfstudio/model_components/ray_samplers.py b/nerfstudio/model_components/ray_samplers.py index c0b4b883d..e18c680ae 100644 --- a/nerfstudio/model_components/ray_samplers.py +++ b/nerfstudio/model_components/ray_samplers.py @@ -505,14 +505,13 @@ class VolumetricSampler(Sampler): if camera_indices is not None: camera_indices = camera_indices[ray_indices] - zeros = torch.zeros_like(origins[:, :1]) ray_samples = RaySamples( frustums=Frustums( origins=origins, directions=dirs, starts=starts[..., None], ends=ends[..., None], - pixel_area=zeros, + pixel_area=ray_bundle[ray_indices].pixel_area, ), camera_indices=camera_indices, ) From da813549e81c181012270e7d5ea165725b9aa48b Mon Sep 17 00:00:00 2001 From: Ethan Weber Date: Thu, 27 Jul 2023 14:06:54 -0400 Subject: [PATCH 44/44] ns-render with depth colormap support (#2125) * can render depth with a colormap * include colormap_options as argument --- nerfstudio/scripts/render.py | 52 +++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/nerfstudio/scripts/render.py b/nerfstudio/scripts/render.py index 84c8950b7..5a3ca78bd 100644 --- a/nerfstudio/scripts/render.py +++ b/nerfstudio/scripts/render.py @@ -74,6 +74,8 @@ def _render_trajectory_video( output_format: Literal["images", "video"] = "video", image_format: Literal["jpeg", "png"] = "jpeg", jpeg_quality: int = 100, + depth_near_plane: Optional[float] = None, + depth_far_plane: Optional[float] = None, colormap_options: colormaps.ColormapOptions = colormaps.ColormapOptions(), ) -> None: """Helper function to create a video of the spiral trajectory. @@ -87,6 +89,8 @@ def _render_trajectory_video( rendered_resolution_scaling_factor: Scaling factor to apply to the camera image resolution. seconds: Length of output video. output_format: How to save output data. + depth_near_plane: Closest depth to consider when using the colormap for depth. If None, use min value. + depth_far_plane: Furthest depth to consider when using the colormap for depth. If None, use max value. colormap_options: Options for colormap. """ CONSOLE.print("[bold green]Creating trajectory " + output_format) @@ -149,14 +153,28 @@ def _render_trajectory_video( ) sys.exit(1) output_image = outputs[rendered_output_name] - output_image = ( - colormaps.apply_colormap( - image=output_image, - colormap_options=colormap_options, + is_depth = rendered_output_name.find("depth") != -1 + if is_depth: + output_image = ( + colormaps.apply_depth_colormap( + output_image, + accumulation=outputs["accumulation"], + near_plane=depth_near_plane, + far_plane=depth_far_plane, + colormap_options=colormap_options, + ) + .cpu() + .numpy() + ) + else: + output_image = ( + colormaps.apply_colormap( + image=output_image, + colormap_options=colormap_options, + ) + .cpu() + .numpy() ) - .cpu() - .numpy() - ) render_image.append(output_image) render_image = np.concatenate(render_image, axis=1) if output_format == "images": @@ -313,6 +331,12 @@ class BaseRender: """Scaling factor to apply to the camera image resolution.""" eval_num_rays_per_chunk: Optional[int] = None """Specifies number of rays per chunk during eval. If None, use the value in the config file.""" + rendered_output_names: List[str] = field(default_factory=lambda: ["rgb"]) + """Name of the renderer outputs to use. rgb, depth, etc. concatenates them along y axis""" + depth_near_plane: Optional[float] = None + """Closest depth to consider when using the colormap for depth. If None, use min value.""" + depth_far_plane: Optional[float] = None + """Furthest depth to consider when using the colormap for depth. If None, use max value.""" colormap_options: colormaps.ColormapOptions = colormaps.ColormapOptions() """Colormap options.""" @@ -321,8 +345,6 @@ class BaseRender: class RenderCameraPath(BaseRender): """Render a camera path generated by the viewer or blender add-on.""" - rendered_output_names: List[str] = field(default_factory=lambda: ["rgb"]) - """Name of the renderer outputs to use. rgb, depth, etc. concatenates them along y axis""" camera_path_filename: Path = Path("camera_path.json") """Filename of the camera path to render.""" output_format: Literal["images", "video"] = "video" @@ -378,6 +400,8 @@ class RenderCameraPath(BaseRender): output_format=self.output_format, image_format=self.image_format, jpeg_quality=self.jpeg_quality, + depth_near_plane=self.depth_near_plane, + depth_far_plane=self.depth_far_plane, colormap_options=self.colormap_options, ) @@ -409,6 +433,8 @@ class RenderCameraPath(BaseRender): output_format=self.output_format, image_format=self.image_format, jpeg_quality=self.jpeg_quality, + depth_near_plane=self.depth_near_plane, + depth_far_plane=self.depth_far_plane, colormap_options=self.colormap_options, ) @@ -461,8 +487,6 @@ class RenderCameraPath(BaseRender): class RenderInterpolated(BaseRender): """Render a trajectory that interpolates between training or eval dataset images.""" - rendered_output_names: List[str] = field(default_factory=lambda: ["rgb"]) - """Name of the renderer outputs to use. rgb, depth, etc. concatenates them along y axis""" pose_source: Literal["eval", "train"] = "eval" """Pose source to render.""" interpolation_steps: int = 10 @@ -507,6 +531,8 @@ class RenderInterpolated(BaseRender): seconds=seconds, output_format=self.output_format, image_format=self.image_format, + depth_near_plane=self.depth_near_plane, + depth_far_plane=self.depth_far_plane, colormap_options=self.colormap_options, ) @@ -515,8 +541,6 @@ class RenderInterpolated(BaseRender): class SpiralRender(BaseRender): """Render a spiral trajectory (often not great).""" - rendered_output_names: List[str] = field(default_factory=lambda: ["rgb"]) - """Name of the renderer outputs to use. rgb, depth, etc. concatenates them along y axis""" seconds: float = 3.0 """How long the video should be.""" output_format: Literal["images", "video"] = "video" @@ -550,6 +574,8 @@ class SpiralRender(BaseRender): seconds=self.seconds, output_format=self.output_format, image_format=self.image_format, + depth_near_plane=self.depth_near_plane, + depth_far_plane=self.depth_far_plane, colormap_options=self.colormap_options, )