4
0
mirror of https://github.com/nerfstudio-project/nerfstudio.git synced 2026-11-11 10:34:29 +08:00
nerfstudio/docs/tutorials/pipelines/creating_pipelines.ipynb
Ethan Weber 6879a8a9bd
Reorganizing docs for pipeline restructure (#279)
* starting docs for pipelines

* working on an example

* clearing output

* starting to implement config changes and add documentation

* removing diff

* format

* templated pipeline notebook a bit

* black
2022-08-16 12:10:24 -07:00

277 lines
9.9 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Creating Custom Pipelines\n",
"\n",
"Here we explain how to create custom pipelines in nerfactory. Pipelines are composed of two components, namely a Dataloader and a Model. We'll show how to make an incremental dataloader with multiple scenes, a model with scene conditioning, and importance sampling at pixels with high loss.\n",
"\n",
"The extensible features that we show in this tutorial are the following:\n",
"- a Dataloader that incrementally adds cameras\n",
"- a Dataloader that uses multiple scenes\n",
"- a Model that is conditioned on the scene\n",
"- a Dataloader PixelSampler that samples more densely in regions with high loss\n",
"\n",
"Our goal is to enable Nerfactory users to create an entire custom Pipeline by only editing one file.\n",
"\n",
"```text\n",
"#TODO(ethan): show a figure depicting the different modules that go into a pipeline\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tags": [
"remove-cell",
"hide-input"
]
},
"outputs": [],
"source": [
"# COLLAPSED\n",
"%load_ext autoreload\n",
"%autoreload 2\n",
"\n",
"from __future__ import annotations\n",
"\n",
"from typing import Dict, Optional, Tuple, List\n",
"import torch\n",
"\n",
"from nerfactory.utils.config import DataloaderConfig, ModelConfig, PipelineConfig, ViewerConfig\n",
"\n",
"from nerfactory.dataloaders.base import Dataloader\n",
"from nerfactory.dataloaders.structs import DatasetInputs\n",
"from nerfactory.models.base import Model\n",
"from nerfactory.pipelines.base import Pipeline\n",
"\n",
"from nerfactory.models.instant_ngp import NGPModel\n",
"from nerfactory.cameras.rays import RayBundle"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### RayBundle class\n",
"\n",
"In the RayBundle class, we allow uses to add non-default attributes for conditioning.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class CustomDataloaderConfig(DataloaderConfig):\n",
" _target = CustomDataloader\n",
" scene_names: List[str] = [\"chair\", \"drums\", \"fern\", \"lego\"]\n",
" steps_per_add_cameras: int = 100 # every X iterations, add more cameras\n",
" num_cameras_per_add: int = 10 # number of cameras to add\n",
"\n",
"\n",
"class CustomDataloader(Dataloader):\n",
" \"\"\"A custom dataloader that incrementally adds cameras and has multiple scenes.\"\"\"\n",
"\n",
" config: CustomDataloaderConfig\n",
"\n",
" def populate_train_modules(self):\n",
" \"\"\"Populate the train dataloader modules.\"\"\"\n",
" self.scene_name_to_image_sampler = {}\n",
" self.scene_name_to_collider = {} # TODO(ethan): move colliders to the dataloader\n",
" for scene_name in self.config.scene_names:\n",
" dataset_inputs: DatasetInputs = None # TODO\n",
" image_dataset = ImageDataset(dataset_inputs)\n",
" image_sampler = CacheImageSampler(image_dataset=image_dataset) # a torch dataloader\n",
" self.scene_name_to_image_sampler[scene_name] = image_sampler\n",
" self.train_pixel_sampler = PixelSampler\n",
" self.train_ray_generator = RayGenerator # nn.Module\n",
"\n",
" def populate_eval_modules(self):\n",
" \"\"\"Populate the eval dataloader modules.\"\"\"\n",
" # we'll mostly rely on the train dataloader modules\n",
" self.eval_pixel_sampler = EvalPixelSampler # this will sample entire images\n",
"\n",
" def sample_images(self) -> List[Image]:\n",
" images = []\n",
" for scene_name in self.scene_name_to_image_sampler.keys():\n",
" image_sampler = self.scene_name_to_image_sampler[scene_name]\n",
" x = image_sampler.forward() # note that empty forward calls next()\n",
" images.append(x)\n",
" return images\n",
"\n",
" def next_train(self, step: int) -> Tuple[RayBundle, Dict]:\n",
" \"\"\"Get the next batch of training data by stringing together the train modules.\"\"\"\n",
" # grab some images from each scene\n",
" images = self.sample_images()\n",
" pixels = self.train_pixel_sampler.forward(images)\n",
" ray_bundle = self.train_ray_generator(pixels)\n",
" # shape should be (H, W, :)\n",
" ray_bundle.metadata[\"scene_indices\"] = None # TODO(ethan): populate this with a tensor of scene indices\n",
" dict_ = {}\n",
" return ray_bundle, dict_\n",
"\n",
" def next_eval(self, step: int) -> Tuple[RayBundle, Dict]:\n",
" \"\"\"Get the next batch of eval data by stringing together the eval modules.\"\"\"\n",
" # grab some images from each scene\n",
" images = self.sample_images()\n",
" pixels = self.train_pixel_sampler.forward(images)\n",
" ray_bundle = self.eval_ray_generator(pixels) # notice this is eval and not train\n",
" # shape should be (H, W, :)\n",
" ray_bundle.metadata[\"scene_indices\"] = None # TODO(ethan): populate this with a tensor of scene indices\n",
" dict_ = {}\n",
" return ray_bundle, dict_\n",
"\n",
"\n",
"class CustomNGPModelConfig(ModelConfig):\n",
" _target = CustomNGPModel\n",
" coarse_field: str = \"temp\"\n",
" fine_field: str = \"temp2\"\n",
"\n",
"\n",
"class CustomNGPModel(Model):\n",
" \"\"\"An instant ngp model modified slightly to output semantics.\"\"\"\n",
"\n",
" config: SceneConditionNGPModelConfig\n",
"\n",
" def populate_modules(self):\n",
" self.ngp_model = NGPModel(coarse_field=self.config.coarse_field, fine_field=self.config.fine_field)\n",
"\n",
" def get_outputs(self, ray_bundle: RayBundle) -> Dict[str, torch.Tensor]:\n",
" # TODO(ethan): pass in batch from the forward function\n",
" # TODO(ethan): rename batch to something else\n",
" outputs = self.ngp_model.forward(ray_bundle)\n",
" outputs[\"semantics\"] = torch.rand_like(outputs[\"rgb\"])\n",
" return outputs\n",
"\n",
" def get_loss_dict(self):\n",
" return {}\n",
"\n",
"\n",
"class CustomPipeline(Pipeline):\n",
" \"\"\"The Instant NGP pipeline.\"\"\"\n",
"\n",
" config: PipelineConfig\n",
"\n",
" def train_step(self, step: int):\n",
" ray_bundle, batch = self.dataloader.next_train(step=step)\n",
" # TODO: maybe run a CNN on the data before passing into model\n",
" model_outputs, loss_dict, metrics_dict = self.model(ray_bundle, batch)\n",
" # TODO: update pixel sampler state with loss map to show the flexibilty of our Pipeline\n",
" self.dataloader.pixel_sampler.update_loss_map()\n",
" return model_outputs, loss_dict, metrics_dict\n",
"\n",
" def eval_step(self):\n",
" ray_bundle, batch = self.dataloader.next_eval(step=step)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"dataloader = Dataloader()\n",
"model = SceneConditionNGPModel()\n",
"pipeline = CustomPipeline.from_dataloader_and_model(dataloader=dataloader, model=model)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Creating pipelines from a config\n",
"\n",
"Now we show how to create a pipeline from a config, which has the following form:\n",
"\n",
"```python\n",
"@dataclass\n",
"class PipelineConfig:\n",
" \"\"\"Configuration for pipeline instantiation\"\"\"\n",
"\n",
" _target: ClassVar[Type] = Pipeline\n",
" dataloader: DataloaderConfig = MISSING\n",
" model: ModelConfig = MISSING\n",
"```\n",
"\n",
"See `nerfactory/utils/config.py` for more details. In this example, we will simply load from an existing configuration from `configs/graph_instant_ngp.yaml`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pprint\n",
"import hydra\n",
"\n",
"hydra.core.global_hydra.GlobalHydra.instance().clear()\n",
"from hydra import compose, initialize\n",
"\n",
"initialize(version_base=\"1.2\", config_path=\"../configs/\")\n",
"config_name = \"graph_instant_ngp.yaml\"\n",
"config = compose(config_name)\n",
"pipeline_config = config.pipeline\n",
"pprint.pprint(pipeline_config)\n",
"print(\"----------------------------------------------------\")\n",
"\n",
"# from nerfactory.pipelines.base import setup_pipeline\n",
"# pipeline = setup_pipeline(pipeline_config, device=\"cuda\")\n",
"pipeline = pipeline_config.setup()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pipeline.get_train_loss_dict()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.8.13 ('nerfactory')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.13"
},
"vscode": {
"interpreter": {
"hash": "e691f033e0f2f1b9c0a11e7e81375a1e27aec87a71fd1b1eaa545f7a5e61b3f6"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}