You need to train a model with normals to use poisson meshing. Try tsdf
- or retrain the model with normals. You can use nerfacto with
+ or retrain the model with normals. You can use nerfacto with --pipeline.model.predict-normals True
);
diff --git a/nerfstudio/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx b/nerfstudio/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx
index ea6196a14..551aac3a0 100644
--- a/nerfstudio/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx
+++ b/nerfstudio/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx
@@ -162,24 +162,16 @@ export default function ViewerWindow(props) {
};
useEffect(() => {
- function update_dimensions() {
+ const handleNewDimensions = () => {
setDimensions({
height: get_window_height(),
width: get_window_width(),
});
- }
-
- window.addEventListener('resize', update_dimensions);
-
- // force a rerender
- setDimensions({
- height: get_window_height(),
- width: get_window_width(),
- });
- render();
+ };
+ window.addEventListener('resize', handleNewDimensions);
return () => {
- window.removeEventListener('resize', update_dimensions);
+ window.removeEventListener('resize', handleNewDimensions);
};
}, []);
diff --git a/nerfstudio/viewer/app/src/subscriber.js b/nerfstudio/viewer/app/src/subscriber.js
index 5af1a946c..dd95b196f 100644
--- a/nerfstudio/viewer/app/src/subscriber.js
+++ b/nerfstudio/viewer/app/src/subscriber.js
@@ -1,19 +1,17 @@
import { useContext } from 'react';
import { ReactReduxContext } from 'react-redux';
-export function subscribe_to_changes(selector_fn, fn) {
- // selector_fn: returns a value from the redux state
- // fn_valid: function to run on a valid input
- // fn_null: function to run on a null input
+export function subscribe_to_changes(selector_fn, callback_fn) {
const { store } = useContext(ReactReduxContext);
- let current;
+ let previousState;
const handleChange = () => {
- const previous = current;
- current = selector_fn(store.getState());
- if (previous !== current) {
- fn(previous, current);
+ const currentState = selector_fn(store.getState());
+ if (previousState !== currentState) {
+ callback_fn(previousState, currentState);
+ previousState = currentState;
}
};
+
store.subscribe(handleChange);
}
diff --git a/nerfstudio/viewer/server/viewer_utils.py b/nerfstudio/viewer/server/viewer_utils.py
index a6ff0a412..33fb9afe9 100644
--- a/nerfstudio/viewer/server/viewer_utils.py
+++ b/nerfstudio/viewer/server/viewer_utils.py
@@ -364,15 +364,6 @@ class ViewerState:
max_scene_box = torch.max(dataset.scene_box.aabb[1] - dataset.scene_box.aabb[0]).item()
self.vis["renderingState/max_box_size"].write(max_scene_box)
- # self.vis["renderingState/render_time"].write(str(0))
-
- # set the properties of the camera
- # self.vis["renderingState/camera"].write(json_)
-
- # set the main camera intrinsics to one from the dataset
- # K = camera.get_intrinsics_matrix()
- # set_persp_intrinsics_matrix(self.vis, K.double().numpy())
-
def _check_camera_path_payload(self, trainer, step: int):
"""Check to see if the camera path export button was pressed."""
# check if we should interrupt from a button press?
From 2b8140b583df96a52218f71170191e8de7ca4293 Mon Sep 17 00:00:00 2001
From: Matthew Tancik
Date: Thu, 23 Mar 2023 15:43:34 -0700
Subject: [PATCH 19/35] Add SDFStudio links (#1653)
Add sdf studio links
---
docs/extensions/imgs/sdfstudio_overview.svg | 33888 ++++++++++++++++++
docs/extensions/sdfstudio.md | 50 +
docs/index.md | 1 +
3 files changed, 33939 insertions(+)
create mode 100644 docs/extensions/imgs/sdfstudio_overview.svg
create mode 100644 docs/extensions/sdfstudio.md
diff --git a/docs/extensions/imgs/sdfstudio_overview.svg b/docs/extensions/imgs/sdfstudio_overview.svg
new file mode 100644
index 000000000..9183ef453
--- /dev/null
+++ b/docs/extensions/imgs/sdfstudio_overview.svg
@@ -0,0 +1,33888 @@
+
+
+
+
diff --git a/docs/extensions/sdfstudio.md b/docs/extensions/sdfstudio.md
new file mode 100644
index 000000000..8f8134662
--- /dev/null
+++ b/docs/extensions/sdfstudio.md
@@ -0,0 +1,50 @@
+# SDFStudio
+
+[project website](https://autonomousvision.github.io/sdfstudio/)
+
+```{image} imgs/sdfstudio_overview.svg
+:width: 800
+:align: center
+:alt: sdfstudio overview figure
+```
+
+## Overview
+
+SDFStudio is built on top of nerfstudio. It implements multiple implicit surface reconstruction methods including:
+
+- UniSurf
+- VolSDF
+- NeuS
+- MonoSDF
+- Mono-UniSurf
+- Mono-NeuS
+- Geo-NeuS
+- Geo-UniSurf
+- Geo-VolSDF
+- NeuS-acc
+- NeuS-facto
+- NeuralReconW
+
+You can learn more about these methods [here](https://github.com/autonomousvision/sdfstudio/blob/master/docs/sdfstudio-methods.md#Methods)
+
+## Surface models in nerfstudio
+
+We intend to integrate many of the SDFStudio improvments back into the nerstudio core repository.
+
+Supported methods:
+
+- NeuS
+
+## Citation
+
+If you use these surface based models in your research, you should consider citing the authors of SDFStudio,
+
+```none
+@misc{Yu2022SDFStudio,
+ author = {Yu, Zehao and Chen, Anpei and Antic, Bozidar and Peng, Songyou Peng and Bhattacharyya, Apratim
+ and Niemeyer, Michael and Tang, Siyu and Sattler, Torsten and Geiger, Andreas},
+ title = {SDFStudio: A Unified Framework for Surface Reconstruction},
+ year = {2022},
+ url = {https://github.com/autonomousvision/sdfstudio},
+}
+```
diff --git a/docs/index.md b/docs/index.md
index b03e8712b..b373097e5 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -60,6 +60,7 @@ Contributing
:caption: Extensions
extensions/blender_addon
extensions/unreal_engine
+extensions/sdfstudio
```
```{toctree}
From 83d0d7563518e5dbb1fedea5802b85cdf8f0e1ab Mon Sep 17 00:00:00 2001
From: machenmusik
Date: Thu, 23 Mar 2023 21:45:40 -0400
Subject: [PATCH 20/35] realitycapture nerfacto variable resolution (#1651)
* allow periods within filename, not only for file extension
* don't assume all images are the same resolution
* black reformatting
* Move variable resolution collate logic into vanilla datamanager
* good catch pylint
* now that vanilla supports variable resolution, remove VariableResDataManager
* remove comment
* CONSOLE.print
* avoid `IndexError: tensors used as indices must be long, byte or bool tensors`
* use large dataset parameters for phototourism
---
nerfstudio/configs/method_configs.py | 10 +--
.../data/datamanagers/base_datamanager.py | 35 ++++++++++
.../datamanagers/variable_res_datamanager.py | 66 -------------------
.../dataparsers/phototourism_dataparser.py | 4 +-
.../process_data/realitycapture_utils.py | 16 ++---
5 files changed, 48 insertions(+), 83 deletions(-)
delete mode 100644 nerfstudio/data/datamanagers/variable_res_datamanager.py
diff --git a/nerfstudio/configs/method_configs.py b/nerfstudio/configs/method_configs.py
index 22635a911..1e5a24ec0 100644
--- a/nerfstudio/configs/method_configs.py
+++ b/nerfstudio/configs/method_configs.py
@@ -29,9 +29,6 @@ from nerfstudio.data.datamanagers.base_datamanager import VanillaDataManagerConf
from nerfstudio.data.datamanagers.depth_datamanager import DepthDataManagerConfig
from nerfstudio.data.datamanagers.sdf_datamanager import SDFDataManagerConfig
from nerfstudio.data.datamanagers.semantic_datamanager import SemanticDataManagerConfig
-from nerfstudio.data.datamanagers.variable_res_datamanager import (
- VariableResDataManagerConfig,
-)
from nerfstudio.data.dataparsers.blender_dataparser import BlenderDataParserConfig
from nerfstudio.data.dataparsers.dnerf_dataparser import DNeRFDataParserConfig
from nerfstudio.data.dataparsers.dycheck_dataparser import DycheckDataParserConfig
@@ -358,13 +355,18 @@ method_configs["phototourism"] = TrainerConfig(
max_num_iterations=30000,
mixed_precision=True,
pipeline=VanillaPipelineConfig(
- datamanager=VariableResDataManagerConfig( # NOTE: one of the only differences with nerfacto
+ datamanager=VanillaDataManagerConfig(
dataparser=PhototourismDataParserConfig(), # NOTE: one of the only differences with nerfacto
train_num_rays_per_batch=4096,
eval_num_rays_per_batch=4096,
camera_optimizer=CameraOptimizerConfig(
mode="SO3xR3", optimizer=AdamOptimizerConfig(lr=6e-4, eps=1e-8, weight_decay=1e-2)
),
+ # Large dataset, so using prior values from VariableResDataManager.
+ train_num_images_to_sample_from=40,
+ train_num_times_to_repeat_images=100,
+ eval_num_images_to_sample_from=40,
+ eval_num_times_to_repeat_images=100,
),
model=NerfactoModelConfig(eval_num_rays_per_chunk=1 << 15),
),
diff --git a/nerfstudio/data/datamanagers/base_datamanager.py b/nerfstudio/data/datamanagers/base_datamanager.py
index dc96e22dc..8bcd21bbe 100644
--- a/nerfstudio/data/datamanagers/base_datamanager.py
+++ b/nerfstudio/data/datamanagers/base_datamanager.py
@@ -74,6 +74,31 @@ from nerfstudio.utils.misc import IterableWrapper
CONSOLE = Console(width=120)
+
+def variable_res_collate(batch: List[Dict]) -> Dict:
+ """Default collate function for the cached dataloader.
+ Args:
+ batch: Batch of samples from the dataset.
+ Returns:
+ Collated batch.
+ """
+ images = []
+ masks = []
+ for data in batch:
+ image = data.pop("image")
+ mask = data.pop("mask", None)
+ images.append(image)
+ if mask:
+ masks.append(mask)
+
+ new_batch: dict = nerfstudio_collate(batch)
+ new_batch["image"] = images
+ if masks:
+ new_batch["mask"] = masks
+
+ return new_batch
+
+
AnnotatedDataParserUnion = tyro.conf.OmitSubcommandPrefixes[ # Omit prefixes of flags in subcommands.
tyro.extras.subcommand_type_from_defaults(
{
@@ -385,6 +410,16 @@ class VanillaDataManager(DataManager): # pylint: disable=abstract-method
self.train_dataset = self.create_train_dataset()
self.eval_dataset = self.create_eval_dataset()
+
+ if self.train_dataparser_outputs is not None:
+ cameras = self.train_dataparser_outputs.cameras
+ if len(cameras) > 1:
+ for i in range(1, len(cameras)):
+ if cameras[0].width != cameras[i].width or cameras[0].height != cameras[i].height:
+ CONSOLE.print("Variable resolution, using variable_res_collate")
+ self.config.collate_fn = variable_res_collate
+ break
+
super().__init__()
def create_train_dataset(self) -> InputDataset:
diff --git a/nerfstudio/data/datamanagers/variable_res_datamanager.py b/nerfstudio/data/datamanagers/variable_res_datamanager.py
deleted file mode 100644
index b821bc034..000000000
--- a/nerfstudio/data/datamanagers/variable_res_datamanager.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright 2022 The Nerfstudio Team. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Data loader for variable resolution datasets, where batching raw image tensors isn't possible.
-"""
-
-from __future__ import annotations
-
-from dataclasses import dataclass
-from typing import Dict, List
-
-from nerfstudio.data.datamanagers.base_datamanager import VanillaDataManagerConfig
-from nerfstudio.data.utils.nerfstudio_collate import nerfstudio_collate
-
-
-def variable_res_collate(batch: List[Dict]) -> Dict:
- """Default collate function for the cached dataloader.
- Args:
- batch: Batch of samples from the dataset.
- Returns:
- Collated batch.
- """
- images = []
- masks = []
- for data in batch:
- image = data.pop("image")
- mask = data.pop("mask", None)
- images.append(image)
- if mask:
- masks.append(mask)
-
- new_batch: dict = nerfstudio_collate(batch)
- new_batch["image"] = images
- if masks:
- new_batch["mask"] = masks
-
- return new_batch
-
-
-@dataclass
-class VariableResDataManagerConfig(VanillaDataManagerConfig):
- """A datamanager for variable resolution datasets, with presets to optimize
- for the fact that we are now dealing with lists of images and masks.
- """
-
- train_num_images_to_sample_from: int = 40
- """Number of images to sample during training iteration."""
- train_num_times_to_repeat_images: int = 100
- """When not training on all images, number of iterations before picking new
- images. If -1, never pick new images."""
- eval_num_images_to_sample_from: int = 40
- """Number of images to sample during eval iteration."""
- eval_num_times_to_repeat_images: int = 100
- collate_fn = staticmethod(variable_res_collate)
diff --git a/nerfstudio/data/dataparsers/phototourism_dataparser.py b/nerfstudio/data/dataparsers/phototourism_dataparser.py
index e5a88e10e..d470215ed 100644
--- a/nerfstudio/data/dataparsers/phototourism_dataparser.py
+++ b/nerfstudio/data/dataparsers/phototourism_dataparser.py
@@ -136,8 +136,8 @@ class Phototourism(DataParser):
) # 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
i_all = torch.tensor(i_all)
- i_train = torch.tensor(i_train)
- i_eval = torch.tensor(i_eval)
+ i_train = torch.tensor(i_train, dtype=torch.long)
+ i_eval = torch.tensor(i_eval, dtype=torch.long)
assert len(i_eval) == num_eval_images
if split == "train":
indices = i_train
diff --git a/nerfstudio/process_data/realitycapture_utils.py b/nerfstudio/process_data/realitycapture_utils.py
index 87ce9240c..4bc02417e 100644
--- a/nerfstudio/process_data/realitycapture_utils.py
+++ b/nerfstudio/process_data/realitycapture_utils.py
@@ -59,17 +59,6 @@ def realitycapture_to_json(
for column, value in row.items():
cameras.setdefault(column, []).append(value)
- for name in cameras["#name"]:
- camera_label = name.rpartition(".")[0]
- if camera_label in image_filename_map:
- img = np.array(Image.open(output_dir / image_filename_map[camera_label]))
- break
-
- height, width, _ = img.shape
-
- data["h"] = int(height)
- data["w"] = int(width)
-
missing_image_data = 0
for i, name in enumerate(cameras["#name"]):
@@ -79,7 +68,12 @@ def realitycapture_to_json(
CONSOLE.print(f"Missing image for camera data {basename}, Skipping")
missing_image_data += 1
continue
+
frame = {}
+ img = np.array(Image.open(output_dir / image_filename_map[basename]))
+ height, width, _ = img.shape
+ frame["h"] = int(height)
+ frame["w"] = int(width)
frame["file_path"] = image_filename_map[basename].as_posix()
frame["fl_x"] = float(cameras["f"][i]) * max(width, height) / 36
frame["fl_y"] = float(cameras["f"][i]) * max(width, height) / 36
From e6ee0292e01d00b9b7faf91a1b106a1f3dedd0c2 Mon Sep 17 00:00:00 2001
From: Alexander Kristoffersen
Date: Fri, 24 Mar 2023 07:22:24 -0700
Subject: [PATCH 21/35] Render Cell Colab Fix (#1655)
quick render fix.
---
colab/demo.ipynb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/colab/demo.ipynb b/colab/demo.ipynb
index 4a87f1718..541b9d741 100644
--- a/colab/demo.ipynb
+++ b/colab/demo.ipynb
@@ -7,7 +7,7 @@
"colab_type": "text"
},
"source": [
- ""
+ ""
]
},
{
@@ -329,7 +329,7 @@
"#@markdown
The rendered video should be at renders/output.mp4!
\n",
"\n",
"\n",
- "base_dir = \"/content/outputs/data-nerfstudio-\" + scene + \"/nerfacto/\"\n",
+ "base_dir = \"/content/outputs/unnamed/nerfacto/\"\n",
"training_run_dir = base_dir + os.listdir(base_dir)[0]\n",
"\n",
"from IPython.core.display import display, HTML\n",
From 78fbe6513ea41732fe22107c1487cedc8b6d6e1a Mon Sep 17 00:00:00 2001
From: Giodiro
Date: Fri, 24 Mar 2023 16:18:57 +0100
Subject: [PATCH 22/35] Add KPlanesEncoding encoding (#1658)
Co-authored-by: gmeanti
---
nerfstudio/field_components/encodings.py | 85 +++++++++++++++++++++++-
tests/field_components/test_encodings.py | 26 ++++++++
2 files changed, 109 insertions(+), 2 deletions(-)
diff --git a/nerfstudio/field_components/encodings.py b/nerfstudio/field_components/encodings.py
index f77c17f4d..462080287 100644
--- a/nerfstudio/field_components/encodings.py
+++ b/nerfstudio/field_components/encodings.py
@@ -16,8 +16,9 @@
Encoding functions
"""
+import itertools
from abc import abstractmethod
-from typing import Optional
+from typing import Optional, Sequence
import numpy as np
import torch
@@ -242,7 +243,6 @@ class HashEncoding(Encoding):
implementation: Literal["tcnn", "torch"] = "tcnn",
interpolation: Optional[Literal["Nearest", "Linear", "Smoothstep"]] = None,
) -> None:
-
super().__init__(in_dim=3)
self.num_levels = num_levels
self.features_per_level = features_per_level
@@ -557,6 +557,87 @@ class TriplaneEncoding(Encoding):
self.resolution = resolution
+class KPlanesEncoding(Encoding):
+ """Learned K-Planes encoding
+
+ A plane encoding supporting both 3D and 4D coordinates. With 3D coordinates this is similar to
+ :class:`TriplaneEncoding`. With 4D coordinates, the encoding at point ``[i,j,k,q]`` is
+ a n-dimensional vector computed as the elementwise product of 6 n-dimensional vectors at
+ ``planes[i,j]``, ``planes[i,k]``, ``planes[i,q]``, ``planes[j,k]``, ``planes[j,q]``,
+ ``planes[k,q]``.
+
+ Unlike :class:`TriplaneEncoding` this class supports different resolution along each axis.
+
+ This will return a tensor of shape (bs:..., num_components)
+
+ Args:
+ resolution: Resolution of the grid. Can be a sequence of 3 or 4 integers.
+ num_components: The number of scalar planes to use (ie: output feature size)
+ init_a: The lower-bound of the uniform distribution used to initialize the spatial planes
+ init_b: The upper-bound of the uniform distribution used to initialize the spatial planes
+ reduce: Whether to use the element-wise product of the planes or the sum
+ """
+
+ def __init__(
+ self,
+ resolution: Sequence[int] = (128, 128, 128),
+ num_components: int = 64,
+ init_a: float = 0.1,
+ init_b: float = 0.5,
+ reduce: Literal["sum", "product"] = "product",
+ ) -> None:
+ super().__init__(in_dim=len(resolution))
+
+ self.resolution = resolution
+ self.num_components = num_components
+ self.reduce = reduce
+ if self.in_dim not in {3, 4}:
+ raise ValueError(
+ f"The dimension of coordinates must be either 3 (static scenes) "
+ f"or 4 (dynamic scenes). Found resolution with {self.in_dim} dimensions."
+ )
+ has_time_planes = self.in_dim == 4
+
+ self.coo_combs = list(itertools.combinations(range(self.in_dim), 2))
+ # Unlike the Triplane encoding, we use a parameter list instead of batching all planes
+ # together to support uneven resolutions (especially useful for time).
+ # Dynamic models (in_dim == 4) will have 6 planes:
+ # (y, x), (z, x), (t, x), (z, y), (t, y), (t, z)
+ # static models (in_dim == 3) will only have the 1st, 2nd and 4th planes.
+ self.plane_coefs = nn.ParameterList()
+ for coo_comb in self.coo_combs:
+ new_plane_coef = nn.Parameter(
+ torch.empty([self.num_components] + [self.resolution[cc] for cc in coo_comb[::-1]])
+ )
+ if has_time_planes and 3 in coo_comb: # Time planes initialized to 1
+ nn.init.ones_(new_plane_coef)
+ else:
+ nn.init.uniform_(new_plane_coef, a=init_a, b=init_b)
+ self.plane_coefs.append(new_plane_coef)
+
+ def get_out_dim(self) -> int:
+ return self.num_components
+
+ def forward(self, in_tensor: TensorType["bs":..., "input_dim"]) -> TensorType["bs":..., "output_dim"]:
+ """Sample features from this encoder. Expects ``in_tensor`` to be in range [-1, 1]"""
+ original_shape = in_tensor.shape
+
+ output = 1.0 if self.reduce == "product" else 0.0 # identity for corresponding op
+ for ci, coo_comb in enumerate(self.coo_combs):
+ grid = self.plane_coefs[ci].unsqueeze(0) # [1, feature_dim, reso1, reso2]
+ coords = in_tensor[..., coo_comb].view(1, 1, -1, 2) # [1, 1, flattened_bs, 2]
+ interp = F.grid_sample(
+ grid, coords, align_corners=True, padding_mode="border"
+ ) # [1, output_dim, 1, flattened_bs]
+ interp = interp.view(self.num_components, -1).T # [flattened_bs, output_dim]
+ if self.reduce == "product":
+ output = output * interp
+ else:
+ output = output + interp
+
+ return output.reshape(*original_shape[:-1], self.num_components)
+
+
class SHEncoding(Encoding):
"""Spherical harmonic encoding
diff --git a/tests/field_components/test_encodings.py b/tests/field_components/test_encodings.py
index 271adddc9..dc59b79ff 100644
--- a/tests/field_components/test_encodings.py
+++ b/tests/field_components/test_encodings.py
@@ -171,6 +171,32 @@ def test_tensor_hash_encoder():
assert encoded.shape == (10, out_dim)
+def test_kplane_encoder():
+ """Test K-Planes encoder"""
+
+ out_dim = 8
+
+ encoder = encodings.KPlanesEncoding(
+ resolution=(32, 32, 32, 16),
+ num_components=out_dim,
+ reduce="product",
+ )
+ assert encoder.get_out_dim() == out_dim
+
+ in_tensor = torch.randn((10, 4))
+ encoded = encoder(in_tensor)
+ assert encoded.shape == (10, out_dim)
+
+ encoder = encodings.KPlanesEncoding(
+ resolution=(32, 32, 32),
+ num_components=out_dim,
+ reduce="product",
+ )
+ in_tensor = torch.randn((10, 3))
+ encoded = encoder(in_tensor)
+ assert encoded.shape == (10, out_dim)
+
+
if __name__ == "__main__":
test_scaling_and_offset()
test_nerf_encoder()
From f490d9a923486a04a79fc78605fd7f96e4e84e1b Mon Sep 17 00:00:00 2001
From: Yudong Jin
Date: Fri, 24 Mar 2023 23:19:59 +0800
Subject: [PATCH 23/35] Update README.md (#1657)
Explicitly set the pytorch version to match the `https://docs.nerf.studio/en/latest/quickstart/installation.html`
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8730cb20a..b8bea621b 100644
--- a/README.md
+++ b/README.md
@@ -107,7 +107,7 @@ pip install git+https://github.com/NVlabs/tiny-cuda-nn/#subdirectory=bindings/to
For CUDA 11.7:
```bash
-pip install torch torchvision functorch --extra-index-url https://download.pytorch.org/whl/cu117
+pip install torch==1.13.1 torchvision functorch --extra-index-url https://download.pytorch.org/whl/cu117
pip install git+https://github.com/NVlabs/tiny-cuda-nn/#subdirectory=bindings/torch
```
From 2ff1462f30a30a6df3d7aaedd12590a494becd0a Mon Sep 17 00:00:00 2001
From: Matthew Tancik
Date: Fri, 24 Mar 2023 08:33:55 -0700
Subject: [PATCH 24/35] Fix render window size (#1659)
---
.../viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/nerfstudio/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx b/nerfstudio/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx
index 551aac3a0..7f38ad584 100644
--- a/nerfstudio/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx
+++ b/nerfstudio/viewer/app/src/modules/ViewerWindow/ViewerWindow.jsx
@@ -169,6 +169,12 @@ export default function ViewerWindow(props) {
});
};
+ setDimensions({
+ height: get_window_height(),
+ width: get_window_width(),
+ });
+ render();
+
window.addEventListener('resize', handleNewDimensions);
return () => {
window.removeEventListener('resize', handleNewDimensions);
From 4145f010d2269c996f68163f9883d996b71547d3 Mon Sep 17 00:00:00 2001
From: Pythonix Huang <370048201@qq.com>
Date: Mon, 27 Mar 2023 11:33:54 +0800
Subject: [PATCH 25/35] Update pixsfm with better param (#1661)
Previous setting will easily go to oom
---
nerfstudio/process_data/hloc_utils.py | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/nerfstudio/process_data/hloc_utils.py b/nerfstudio/process_data/hloc_utils.py
index 6ec71edd0..f04f6c32c 100644
--- a/nerfstudio/process_data/hloc_utils.py
+++ b/nerfstudio/process_data/hloc_utils.py
@@ -117,7 +117,13 @@ def run_hloc(
camera_model=camera_model.value
)
if refine_pixsfm:
- sfm = PixSfM({"dense_features": {"max_edge": 1024}})
+ sfm = PixSfM(
+ conf={
+ "dense_features": {"use_cache": True},
+ "KA": {"dense_features": {"use_cache": True}, "max_kps_per_problem": 1000},
+ "BA": {"strategy": "costmaps"},
+ }
+ )
refined, _ = sfm.reconstruction(
sfm_dir,
image_dir,
From 35cd06ef363287d9efd02521516f852b0563985e Mon Sep 17 00:00:00 2001
From: henrypearce4D <106480554+henrypearce4D@users.noreply.github.com>
Date: Mon, 27 Mar 2023 15:26:13 +0100
Subject: [PATCH 26/35] Update realitycapture_utils.py (#1663)
corrected the description from polycam data to reality capture data
---
nerfstudio/process_data/realitycapture_utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nerfstudio/process_data/realitycapture_utils.py b/nerfstudio/process_data/realitycapture_utils.py
index 4bc02417e..918aa84a5 100644
--- a/nerfstudio/process_data/realitycapture_utils.py
+++ b/nerfstudio/process_data/realitycapture_utils.py
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Helper utils for processing polycam data into the nerfstudio format."""
+"""Helper utils for processing reality capture data into the nerfstudio format."""
import csv
import json
From 24b3db8eb068ba21cea4c5bd617d67e10240fdbc Mon Sep 17 00:00:00 2001
From: wang shuai <212106734@qq.com>
Date: Tue, 28 Mar 2023 22:58:21 +0800
Subject: [PATCH 27/35] Fix bug: sh render is not correct (#1667)
* Update renderers.py
revise the SH renderer
* Update test_renderers.py
change the test target for sh render
---
nerfstudio/model_components/renderers.py | 2 +-
tests/model_components/test_renderers.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/nerfstudio/model_components/renderers.py b/nerfstudio/model_components/renderers.py
index 5e488bc35..43f81404c 100644
--- a/nerfstudio/model_components/renderers.py
+++ b/nerfstudio/model_components/renderers.py
@@ -180,7 +180,7 @@ class SHRenderer(nn.Module):
components = components_from_spherical_harmonics(levels=levels, directions=directions)
rgb = sh * components[..., None, :] # [..., num_samples, 3, sh_components]
- rgb = torch.sum(sh, dim=-1) + 0.5 # [..., num_samples, 3]
+ rgb = torch.sum(rgb, dim=-1) # [..., num_samples, 3]
if self.activation is not None:
rgb = self.activation(rgb)
diff --git a/tests/model_components/test_renderers.py b/tests/model_components/test_renderers.py
index 984a45e7c..a1e098388 100644
--- a/tests/model_components/test_renderers.py
+++ b/tests/model_components/test_renderers.py
@@ -40,7 +40,7 @@ def test_sh_renderer():
sh_renderer = renderers.SHRenderer()
rgb = sh_renderer(sh=sh, directions=directions, weights=weights)
- assert torch.max(rgb) > 0.9
+ assert torch.max(rgb) > 0.7
def test_acc_renderer():
From bde853bd148bb7b90354d3c9adfce31e13a40ddf Mon Sep 17 00:00:00 2001
From: Pythonix Huang <370048201@qq.com>
Date: Sat, 1 Apr 2023 04:39:49 +0800
Subject: [PATCH 28/35] Update scheduler for nerfacto (#1662)
* Update scheduler for nerfacto
Add scheduler (lr decay) in nerfacto is also effective for preventing long-time training nan and good for convergence
* refactor
refactor
* refactor
refactor
refactor
* refactor
refactor
* Update method_configs.py
* Revert "Update method_configs.py"
This reverts commit feaff61819d44fdc8d1aee957ac3b80bf1b948a9.
* Revert "refactor"
This reverts commit cf7c3153a2070c70f5786405e3e3c79c87438e85.
* Revert "Merge branch 'update-scheduler' of https://github.com/HrsPythonix/nerfstudio into update-scheduler"
This reverts commit b31575385ca0c79c77a6a16c8ca78e0ec02abac4, reversing
changes made to 6b78039059d7115eeb471f0f98ba28beb7f263bf.
* Revert "refactor"
This reverts commit 6b78039059d7115eeb471f0f98ba28beb7f263bf.
* Update method_configs.py
---
nerfstudio/configs/method_configs.py | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/nerfstudio/configs/method_configs.py b/nerfstudio/configs/method_configs.py
index 1e5a24ec0..0bc56f6da 100644
--- a/nerfstudio/configs/method_configs.py
+++ b/nerfstudio/configs/method_configs.py
@@ -92,7 +92,9 @@ method_configs["nerfacto"] = TrainerConfig(
train_num_rays_per_batch=4096,
eval_num_rays_per_batch=4096,
camera_optimizer=CameraOptimizerConfig(
- mode="SO3xR3", optimizer=AdamOptimizerConfig(lr=6e-4, eps=1e-8, weight_decay=1e-2)
+ mode="SO3xR3",
+ optimizer=AdamOptimizerConfig(lr=6e-4, eps=1e-8, weight_decay=1e-2),
+ scheduler=ExponentialDecaySchedulerConfig(lr_final=6e-6, max_steps=200000),
),
),
model=NerfactoModelConfig(eval_num_rays_per_chunk=1 << 15),
@@ -100,11 +102,11 @@ method_configs["nerfacto"] = TrainerConfig(
optimizers={
"proposal_networks": {
"optimizer": AdamOptimizerConfig(lr=1e-2, eps=1e-15),
- "scheduler": None,
+ "scheduler": ExponentialDecaySchedulerConfig(lr_final=0.0001, max_steps=200000),
},
"fields": {
"optimizer": AdamOptimizerConfig(lr=1e-2, eps=1e-15),
- "scheduler": None,
+ "scheduler": ExponentialDecaySchedulerConfig(lr_final=0.0001, max_steps=200000),
},
},
viewer=ViewerConfig(num_rays_per_chunk=1 << 15),
From b0dca720a83e9bf97875fc540379835863f4101d Mon Sep 17 00:00:00 2001
From: Alex-Zhang <71915735+pixel-alex@users.noreply.github.com>
Date: Fri, 31 Mar 2023 18:00:53 -0500
Subject: [PATCH 29/35] Update installation.md (#1675)
* Update installation.md
install ninja to speed up installation of tinycuda-nn
* Update installation.md
* update readme and installation
---
README.md | 4 ++--
docs/quickstart/installation.md | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index b8bea621b..ddd0873e0 100644
--- a/README.md
+++ b/README.md
@@ -101,14 +101,14 @@ For CUDA 11.3:
```bash
pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html
-pip install git+https://github.com/NVlabs/tiny-cuda-nn/#subdirectory=bindings/torch
+pip install ninja git+https://github.com/NVlabs/tiny-cuda-nn/#subdirectory=bindings/torch
```
For CUDA 11.7:
```bash
pip install torch==1.13.1 torchvision functorch --extra-index-url https://download.pytorch.org/whl/cu117
-pip install git+https://github.com/NVlabs/tiny-cuda-nn/#subdirectory=bindings/torch
+pip install ninja git+https://github.com/NVlabs/tiny-cuda-nn/#subdirectory=bindings/torch
```
See [Dependencies](https://github.com/nerfstudio-project/nerfstudio/blob/main/docs/quickstart/installation.md#dependencies)
diff --git a/docs/quickstart/installation.md b/docs/quickstart/installation.md
index 32542ea9c..dafb9f0ee 100644
--- a/docs/quickstart/installation.md
+++ b/docs/quickstart/installation.md
@@ -79,10 +79,10 @@ pip install torch==1.13.1 torchvision functorch --extra-index-url https://downlo
### tinycudann
-After pytorch, install the torch bindings for tinycudann:
+After pytorch and ninja, install the torch bindings for tinycudann:
```bash
-pip install git+https://github.com/NVlabs/tiny-cuda-nn/#subdirectory=bindings/torch
+pip install ninja git+https://github.com/NVlabs/tiny-cuda-nn/#subdirectory=bindings/torch
```
## Installing nerfstudio
From 2c12f5f205581cb99d2d4140bb14eefe7842129e Mon Sep 17 00:00:00 2001
From: Zhensheng Yuan <564361+yzslab@users.noreply.github.com>
Date: Sat, 1 Apr 2023 14:07:01 +0800
Subject: [PATCH 30/35] add SO_REUSEADDR option to reuse the port in TIME-WAIT
status (#1673)
---
nerfstudio/viewer/server/subprocess.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/nerfstudio/viewer/server/subprocess.py b/nerfstudio/viewer/server/subprocess.py
index b50038561..17a4cdb7a 100644
--- a/nerfstudio/viewer/server/subprocess.py
+++ b/nerfstudio/viewer/server/subprocess.py
@@ -44,6 +44,7 @@ def is_port_open(port: int):
"""
try:
sock = socket.socket()
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
_ = sock.bind(("", port))
sock.close()
return True
From e104a3029e1387b183184866de8220392eaeda44 Mon Sep 17 00:00:00 2001
From: Zhensheng Yuan <564361+yzslab@users.noreply.github.com>
Date: Sun, 2 Apr 2023 06:56:47 +0800
Subject: [PATCH 31/35] fix checkpoint generated by ddp training cannot be
loaded (#1682)
* fix checkpoint generated by ddp training cannot be loaded
* fix typo
* reformatted nerfstudio/pipelines/base_pipeline.py
* rewritten as for loop
* add test case
* reformatted
* fix pylint test failed
---
nerfstudio/pipelines/base_pipeline.py | 19 ++++++++++++++++---
tests/pipelines/test_vanilla_pipeline.py | 20 ++++++++++++++++++++
2 files changed, 36 insertions(+), 3 deletions(-)
diff --git a/nerfstudio/pipelines/base_pipeline.py b/nerfstudio/pipelines/base_pipeline.py
index 1da017ebc..c572bebeb 100644
--- a/nerfstudio/pipelines/base_pipeline.py
+++ b/nerfstudio/pipelines/base_pipeline.py
@@ -108,9 +108,22 @@ class Pipeline(nn.Module):
return self.model.device
def load_state_dict(self, state_dict: Mapping[str, Any], strict: bool = True):
- model_state = {key[len("_model.") :]: value for key, value in state_dict.items() if key.startswith("_model.")}
+ is_ddp_model_state = True
+ model_state = {}
+ for key, value in state_dict.items():
+ if key.startswith("_model."):
+ # remove the "_model." prefix from key
+ model_state[key[len("_model.") :]] = value
+ # make sure that the "module." prefix comes from DDP,
+ # rather than an attribute of the model named "module"
+ if not key.startswith("_model.module."):
+ is_ddp_model_state = False
+ # remove "module." prefix added by DDP
+ if is_ddp_model_state:
+ 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)
+ self.model.load_state_dict(model_state, strict=strict)
super().load_state_dict(pipeline_state, strict=False)
@profiler.time_function
@@ -373,7 +386,7 @@ class VanillaPipeline(Pipeline):
state = {
(key[len("module.") :] if key.startswith("module.") else key): value for key, value in loaded_state.items()
}
- self._model.update_to_step(step)
+ self.model.update_to_step(step)
self.load_state_dict(state, strict=True)
def get_training_callbacks(
diff --git a/tests/pipelines/test_vanilla_pipeline.py b/tests/pipelines/test_vanilla_pipeline.py
index 69581ce27..c51038135 100644
--- a/tests/pipelines/test_vanilla_pipeline.py
+++ b/tests/pipelines/test_vanilla_pipeline.py
@@ -57,6 +57,9 @@ def test_load_state_dict():
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.register_parameter("param", nn.Parameter(torch.ones((3,))))
+ self.register_module(
+ "module", Model(*args, **kwargs)
+ ) # make sure that non-DDP checkpoint won't be processed as DDP one
def load_state_dict(self, state_dict, strict=True):
"""Mocked load_state_dict"""
@@ -64,6 +67,7 @@ def test_load_state_dict():
was_called = True
assert strict
assert "param" in state_dict
+ assert "module.device_indicator_param" in state_dict
config = VanillaPipelineConfig(
datamanager=VanillaDataManagerConfig(
@@ -77,3 +81,19 @@ def test_load_state_dict():
pipeline.load_pipeline(state_dict, 0)
assert was_called
assert pipeline.model.param[0].item() == 2
+
+ # preparation for another test
+ state_dict["_model.param"].mul_(2) # pylint: disable=unsubscriptable-object
+ was_called = False
+ # pretends to be a DDP checkpoint
+ ddp_state_dict = {}
+ for key, value in state_dict.items():
+ if key.startswith("_model."):
+ # replace the prefix "_model" with "_model.module"
+ ddp_state_dict["_model.module." + key[len("_model.") :]] = value
+ else:
+ ddp_state_dict[key] = value
+ # load DDP checkpoint
+ pipeline.load_pipeline(ddp_state_dict, 0)
+ assert was_called
+ assert pipeline.model.param[0].item() == 4
From 4564ade1fe5f149b763b9ddebc13fd774f560d6f Mon Sep 17 00:00:00 2001
From: Jackson Waschura <35237687+jackson-waschura@users.noreply.github.com>
Date: Sat, 1 Apr 2023 16:29:33 -0700
Subject: [PATCH 32/35] Exposed number of actual frames to extract with ffmpeg
for more transparency (#1683)
* Exposed number of actual frames to extract for more transparency
* Fixed off-by-one error in calculation of frames to extract.
* Added math import
* Switched print statements to CONSOLE.print
---------
Co-authored-by: jackson-waschura
---
nerfstudio/process_data/process_data_utils.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/nerfstudio/process_data/process_data_utils.py b/nerfstudio/process_data/process_data_utils.py
index 817e2c6cd..a977df8b2 100644
--- a/nerfstudio/process_data/process_data_utils.py
+++ b/nerfstudio/process_data/process_data_utils.py
@@ -14,6 +14,7 @@
"""Helper utils for processing data into the nerfstudio format."""
+import math
import os
import shutil
import sys
@@ -141,7 +142,7 @@ def convert_video_to_images(
if num_frames == 0:
CONSOLE.print(f"[bold red]Error: Video has no frames: {video_path}")
sys.exit(1)
- print("Number of frames in video:", num_frames)
+ CONSOLE.print("Number of frames in video:", num_frames)
out_filename = image_dir / "frame_%05d.png"
ffmpeg_cmd = f'ffmpeg -i "{video_path}"'
@@ -157,6 +158,7 @@ def convert_video_to_images(
spacing = num_frames // num_frames_target
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))
else:
CONSOLE.print("[bold red]Can't satisfy requested number of frames. Extracting all frames.")
ffmpeg_cmd += " -pix_fmt bgr8"
From f6b67a0ad0d3fe843c41711825e40da5d99f9f85 Mon Sep 17 00:00:00 2001
From: Matthew Tancik
Date: Mon, 3 Apr 2023 17:38:56 -0700
Subject: [PATCH 33/35] Optimize viewer UI (#1692)
Prevent multiple renders
---
.../src/modules/ConfigPanel/ConfigPanel.jsx | 21 +-
.../SidePanel/ExportPanel/ExportPanel.jsx | 2 +-
.../app/src/modules/SidePanel/SidePanel.jsx | 183 +++++++++---------
nerfstudio/viewer/app/src/reducer.js | 2 +
4 files changed, 109 insertions(+), 99 deletions(-)
diff --git a/nerfstudio/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx b/nerfstudio/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx
index 66b92458e..5b20b545e 100644
--- a/nerfstudio/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx
+++ b/nerfstudio/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx
@@ -1,6 +1,8 @@
-import { buttonGroup, useControls } from 'leva';
+import * as React from 'react';
+import { Leva, buttonGroup, useControls } from 'leva';
import { useContext, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
+import LevaTheme from '../../themes/leva_theme.json';
import { WebSocketContext } from '../WebSocket/WebSocket';
@@ -22,7 +24,7 @@ function dispatch_and_send(websocket, dispatch, path, data) {
}
}
-export function RenderControls() {
+function ControlsLeva() {
// connection status indicators
const websocket = useContext(WebSocketContext).socket;
const outputOptions = useSelector(
@@ -326,3 +328,18 @@ export function RenderControls() {
return null;
}
+
+export function RenderControls() {
+ return (
+
+
+
+
+ );
+}
diff --git a/nerfstudio/viewer/app/src/modules/SidePanel/ExportPanel/ExportPanel.jsx b/nerfstudio/viewer/app/src/modules/SidePanel/ExportPanel/ExportPanel.jsx
index 51617dfc8..30e390b34 100644
--- a/nerfstudio/viewer/app/src/modules/SidePanel/ExportPanel/ExportPanel.jsx
+++ b/nerfstudio/viewer/app/src/modules/SidePanel/ExportPanel/ExportPanel.jsx
@@ -49,7 +49,7 @@ const CLIPPING_BOX_NAME = 'Clipping Box';
export default function ExportPanel(props) {
// unpack relevant information
const sceneTree = props.sceneTree;
- const showExportBox = props.showExportBox;
+ const showExportBox = useSelector((state) => state.show_export_box);
const [type_value, setTypeValue] = React.useState(false);
diff --git a/nerfstudio/viewer/app/src/modules/SidePanel/SidePanel.jsx b/nerfstudio/viewer/app/src/modules/SidePanel/SidePanel.jsx
index 35dd126d6..292c88a85 100644
--- a/nerfstudio/viewer/app/src/modules/SidePanel/SidePanel.jsx
+++ b/nerfstudio/viewer/app/src/modules/SidePanel/SidePanel.jsx
@@ -5,19 +5,16 @@ import * as THREE from 'three';
import Box from '@mui/material/Box';
import Divider from '@mui/material/Divider';
-import { Leva } from 'leva';
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
-import Typography from '@mui/material/Typography';
import {
CameraAltRounded,
TuneRounded,
WidgetsRounded,
ImportExportRounded,
} from '@mui/icons-material/';
-import { useSelector } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
import StatusPanel from './StatusPanel';
-import LevaTheme from '../../themes/leva_theme.json';
import CameraPanel from './CameraPanel';
import ScenePanel from './ScenePanel';
import { RenderControls } from '../ConfigPanel/ConfigPanel';
@@ -39,31 +36,6 @@ export const snap_to_camera = (sceneTree, camera, matrix) => {
);
};
-interface TabPanelProps {
- children: React.ReactNode;
- index: number;
- value: number;
-}
-
-function TabPanel(props: TabPanelProps) {
- const { children, value, index, ...other } = props;
-
- return (
-
);
diff --git a/nerfstudio/viewer/app/src/reducer.js b/nerfstudio/viewer/app/src/reducer.js
index 5aefe9c8a..21279a547 100644
--- a/nerfstudio/viewer/app/src/reducer.js
+++ b/nerfstudio/viewer/app/src/reducer.js
@@ -13,6 +13,8 @@ const initialState = {
render_img: null, // The rendered images
+ show_export_box: false, // whether to show the export box
+
// the rendering state
renderingState: {
// cameras
From 4e6c52c8fdbcd520027585a58d8103f1edf3f619 Mon Sep 17 00:00:00 2001
From: Matthew Tancik
Date: Tue, 4 Apr 2023 11:08:09 -0700
Subject: [PATCH 34/35] Update sphinx docs (#1695)
---
docs/quickstart/custom_dataset.md | 21 +++++++++++++++++++++
docs/reference/cli/index.md | 21 +++++++++++----------
docs/reference/cli/ns_install_cli.md | 9 +++++++++
scripts/completions/install.py | 3 +++
4 files changed, 44 insertions(+), 10 deletions(-)
create mode 100644 docs/reference/cli/ns_install_cli.md
diff --git a/docs/quickstart/custom_dataset.md b/docs/quickstart/custom_dataset.md
index 09e4d1930..65e6c22d4 100644
--- a/docs/quickstart/custom_dataset.md
+++ b/docs/quickstart/custom_dataset.md
@@ -20,6 +20,7 @@ We Currently support the following custom data types:
| π± [KIRI Engine](kiri) | IOS or Android | [KIRI Engine App](https://www.kiriengine.com/) | π |
| π± [Record3D](record3d) | IOS with LiDAR | [Record3D app](https://record3d.app/) | π |
| π₯ [Metashape](metashape) | Any | [Metashape](https://www.agisoft.com/) | π |
+| π₯ [RealityCapture](realitycapture) | Any | [RealityCapture](https://www.capturingreality.com/realitycapture) | π |
(images_and_video)=
@@ -286,6 +287,26 @@ ns-process-data metashape --data {data directory} --xml {xml file} --output-dir
ns-train nerfacto --data {output directory}
```
+(realitycapture)=
+
+## RealityCapture
+
+1. Align your images using RealityCapture. `ALIGNMENT -> Align Images`
+
+2. Export the camera alignment as a `csv` file. Choose `Internal/External camera parameters`
+
+3. Convert the data to the nerfstudio format.
+
+```bash
+ns-process-data realitycapture --data {data directory} --csv {csv file} --output-dir {output directory}
+```
+
+4. Train with nerfstudio!
+
+```bash
+ns-train nerfacto --data {output directory}
+```
+
(360_data)=
## 360 Data (Equirectangular)
diff --git a/docs/reference/cli/index.md b/docs/reference/cli/index.md
index 9e1b8fae4..7bc95834f 100644
--- a/docs/reference/cli/index.md
+++ b/docs/reference/cli/index.md
@@ -6,21 +6,22 @@ We provide a command line interface for training your own NeRFs (no coding neces
Here are the popular commands that we offer. If you've cloned the repo, you can also look at the [pyproject.toml file](https://github.com/nerfstudio-project/nerfstudio/blob/main/pyproject.toml) at the `[project.scripts]` section for details.
-| Command | Description | Filename |
-| ---------------- | -------------------------------------- | ---------------------------------- |
-| ns-install-cli | Install tab completion for all scripts | scripts/completions/install.py |
-| ns-process-data | Generate a dataset from your own data | scripts/process_data.py |
-| ns-download-data | Download existing captures | scripts/downloads/download_data.py |
-| ns-train | Generate a NeRF | scripts/train.py |
-| ns-viewer | View a trained NeRF | scripts/viewer/run_viewer.py |
-| ns-eval | Run evaluation metrics for your Model | scripts/eval.py |
-| ns-render | Render out a video of your NeRF | scripts/render.py |
-| ns-export | Export a NeRF into other formats | scripts/exporter.py |
+| Command | Description | Filename |
+| ------------------------------------ | -------------------------------------- | ---------------------------------- |
+| [ns-install-cli](ns_install_cli) | Install tab completion for all scripts | scripts/completions/install.py |
+| [ns-process-data](ns_process_data) | Generate a dataset from your own data | scripts/process_data.py |
+| [ns-download-data](ns_download_data) | Download existing captures | scripts/downloads/download_data.py |
+| [ns-train](ns_train) | Generate a NeRF | scripts/train.py |
+| [ns-viewer](ns_viewer) | View a trained NeRF | scripts/viewer/run_viewer.py |
+| [ns-eval](ns_eval) | Run evaluation metrics for your Model | scripts/eval.py |
+| [ns-render](ns_render) | Render out a video of your NeRF | scripts/render.py |
+| [ns-export](ns_export) | Export a NeRF into other formats | scripts/exporter.py |
```{toctree}
:maxdepth: 1
:hidden:
+ns_install_cli
ns_process_data
ns_download_data
ns_train
diff --git a/docs/reference/cli/ns_install_cli.md b/docs/reference/cli/ns_install_cli.md
new file mode 100644
index 000000000..e9c1a9541
--- /dev/null
+++ b/docs/reference/cli/ns_install_cli.md
@@ -0,0 +1,9 @@
+# ns-install-cli
+
+```{eval-rst}
+.. argparse::
+ :module: scripts.completions.install
+ :func: get_parser_fn
+ :prog: ns-install-cli
+ :nodefault:
+```
diff --git a/scripts/completions/install.py b/scripts/completions/install.py
index 98e67fea2..067bea2bd 100644
--- a/scripts/completions/install.py
+++ b/scripts/completions/install.py
@@ -399,3 +399,6 @@ def entrypoint():
if __name__ == "__main__":
entrypoint()
+
+# For sphinx docs
+get_parser_fn = lambda: tyro.extras.get_parser(main) # noqa
From 7126334976119ff5ebd3356ada0449de11458f10 Mon Sep 17 00:00:00 2001
From: Matthew Tancik
Date: Tue, 4 Apr 2023 12:49:15 -0700
Subject: [PATCH 35/35] Docs for adding new methods (#1697)
---
README.md | 21 ++++----
docs/developer_guides/config.md | 30 -----------
docs/developer_guides/new_methods.md | 75 ++++++++++++++++++++++++++++
docs/index.md | 1 +
4 files changed, 87 insertions(+), 40 deletions(-)
create mode 100644 docs/developer_guides/new_methods.md
diff --git a/README.md b/README.md
index ddd0873e0..57d77724b 100644
--- a/README.md
+++ b/README.md
@@ -196,16 +196,17 @@ ns-export pointcloud --help
Using an existing dataset is great, but likely you want to use your own data! We support various methods for using your own data. Before it can be used in nerfstudio, the camera location and orientations must be determined and then converted into our format using `ns-process-data`. We rely on external tools for this, instructions and information can be found in the documentation.
-| Data | Capture Device | Requirements | `ns-process-data` Speed |
-| ------------------------------------------------------------------------------------------------- | -------------- | ----------------------------------------------- | ----------------------- |
-| π· [Images](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#images-and-video) | Any | [COLMAP](https://colmap.github.io/install.html) | π’ |
-| πΉ [Video](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#images-and-video) | Any | [COLMAP](https://colmap.github.io/install.html) | π’ |
-| π [360 Data](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#360_data) | Any | [COLMAP](https://colmap.github.io/install.html) | π’ |
-| π± [Polycam](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#polycam-capture) | IOS with LiDAR | [Polycam App](https://poly.cam/) | π |
-| π± [KIRI Engine](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#kiri-capture) | IOS or Android | [KIRI Engine App](https://www.kiriengine.com/) | π |
-| π± [Record3D](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#record3d-capture) | IOS with LiDAR | [Record3D app](https://record3d.app/) | π |
-| π₯ [Metashape](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#metashape) | Any | [Metashape](https://www.agisoft.com/) | π |
-| π [Custom](https://docs.nerf.studio/en/latest/quickstart/data_conventions.html) | Any | Camera Poses | π |
+| Data | Capture Device | Requirements | `ns-process-data` Speed |
+| ---------------------------------------------------------------------------------------------------- | -------------- | ----------------------------------------------------------------- | ----------------------- |
+| π· [Images](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#images-and-video) | Any | [COLMAP](https://colmap.github.io/install.html) | π’ |
+| πΉ [Video](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#images-and-video) | Any | [COLMAP](https://colmap.github.io/install.html) | π’ |
+| π [360 Data](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#360_data) | Any | [COLMAP](https://colmap.github.io/install.html) | π’ |
+| π± [Polycam](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#polycam-capture) | IOS with LiDAR | [Polycam App](https://poly.cam/) | π |
+| π± [KIRI Engine](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#kiri-capture) | IOS or Android | [KIRI Engine App](https://www.kiriengine.com/) | π |
+| π± [Record3D](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#record3d-capture) | IOS with LiDAR | [Record3D app](https://record3d.app/) | π |
+| π₯ [Metashape](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#metashape) | Any | [Metashape](https://www.agisoft.com/) | π |
+| π₯ [RealityCapture](https://docs.nerf.studio/en/latest/quickstart/custom_dataset.html#realitycapture) | Any | [RealityCapture](https://www.capturingreality.com/realitycapture) | π |
+| π [Custom](https://docs.nerf.studio/en/latest/quickstart/data_conventions.html) | Any | Camera Poses | π |
## 5. Advanced Options
diff --git a/docs/developer_guides/config.md b/docs/developer_guides/config.md
index 29d093f9d..bf216278f 100644
--- a/docs/developer_guides/config.md
+++ b/docs/developer_guides/config.md
@@ -118,33 +118,3 @@ Often times, you just want to play with the parameters of an existing model with
# NOTE: the dataparser and associated configurations go at the end of the command
ns-train {METHOD_NAME} --vis viewer {DATA_PARSER} --scale-factor 0.5
```
-
-### Extending Nerfstudio with custom methods
-In order to extend the Nerfstudio and register your own methods, you can package your code as a python package
-and register it with Nerfstudio as a `nerfstudio.method_configs` entrypoint in the `pyproject.toml` file.
-The Nerfstudio will automatically look for all registered methods and will register them to be used
-by method such as `ns-train`.
-
-Here is an example:
-```python
-"""my_package/my_config.py"""
-
-from nerfstudio.engine.trainer import TrainerConfig
-from nerfstudio.plugins.types import MethodSpecification
-
-MyMethod = MethodSpecification(
- config=TrainerConfig(
- method_name="my-method",
- ...
- ),
- description="Custom description"
-)
-
-"""pyproject.toml"""
-[project]
-name = "my_package"
-...
-
-[project.entry-points.'nerfstudio.method_configs']
-my-method = 'my_package.my_config:MyMethod'
-```
\ No newline at end of file
diff --git a/docs/developer_guides/new_methods.md b/docs/developer_guides/new_methods.md
new file mode 100644
index 000000000..c0484ca9e
--- /dev/null
+++ b/docs/developer_guides/new_methods.md
@@ -0,0 +1,75 @@
+# Adding a New Method
+
+Nerfstudio aims to offer researchers a codebase that they can utilize to extend and develop novel methods. Our vision is for users to establish a distinct repository that imports nerfstudio and overrides pipeline components to cater to specific functionality requirements of the new approach. If any of the new features require modifications to the core nerfstudio repository and can be generally useful, we encourage you to submit a PR to enable others to benefit from it.
+
+Examples are often the best way to learn, take a look at the [LERF](https://github.com/kerrj/lerf) repository for good example of how to use nerfstudio in your projects.
+
+## File Structure
+
+We recommend the following file structure:
+
+```
+βββ my_method
+β βββ my_config.py
+β βββ custom_pipeline.py [optional]
+β βββ custom_model.py [optional]
+β βββ custom_field.py [optional]
+β βββ custom_datamanger.py [optional]
+β βββ ...
+βββ pyproject.toml
+```
+
+## Registering custom model with nerfstudio
+
+In order to extend the Nerfstudio and register your own methods, you can package your code as a python package
+and register it with Nerfstudio as a `nerfstudio.method_configs` entrypoint in the `pyproject.toml` file.
+Nerfstudio will automatically look for all registered methods and will register them to be used
+by methods such as `ns-train`.
+
+First create a config file:
+
+```python
+"""my_method/my_config.py"""
+
+from nerfstudio.engine.trainer import TrainerConfig
+from nerfstudio.plugins.types import MethodSpecification
+
+MyMethod = MethodSpecification(
+ config=TrainerConfig(
+ method_name="my-method",
+ pipeline=...
+ ...
+ ),
+ description="Custom description"
+)
+```
+
+Then create a `pyproject.toml` file. This is where the entrypoint to your method is set and also where you can specify additional dependencies required by your codebase.
+
+```python
+"""pyproject.toml"""
+
+[project]
+name = "my_method"
+
+dependencies = [
+ "nerfstudio" # you may want to consider pinning the version, ie "nerfstudio==0.1.19"
+]
+
+[project.entry-points.'nerfstudio.method_configs']
+my-method = 'my_method.my_config:MyMethod'
+```
+
+finally run the following to register the method,
+
+```
+pip install -e .
+```
+
+## Running custom method
+
+After registering your method you should be able to run the method with,
+
+```
+ns-train my-method --data DATA_DIR
+```
diff --git a/docs/index.md b/docs/index.md
index b373097e5..d28393883 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -75,6 +75,7 @@ nerfology/model_components/index
:hidden:
:caption: Developer Guides
+developer_guides/new_methods
developer_guides/pipelines/index
developer_guides/viewer/viewer_overview
developer_guides/config