4
0
mirror of https://github.com/nerfstudio-project/nerfstudio.git synced 2026-11-11 02:24:38 +08:00
nerfstudio/nerfactory/data/dataloader.py
Matthew Tancik 6dc704c551
Rename repo to nerfactory (#204)
* Rename to nerfactory

* Fix missing files
2022-07-25 13:52:20 -07:00

197 lines
7.5 KiB
Python

# Copyright 2022 The Plenoptix Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Data loader.
"""
import random
from typing import Dict, List, Optional, Tuple, Union
from omegaconf import ListConfig
from torchtyping import TensorType
from nerfactory.cameras.cameras import Camera, get_camera
from nerfactory.cameras.rays import RayBundle
from nerfactory.data.image_dataset import ImageDataset
from nerfactory.data.image_sampler import ImageSampler
from nerfactory.data.pixel_sampler import PixelSampler
from nerfactory.data.structs import DatasetInputs
from nerfactory.data.utils import get_dataset_inputs_from_dataset_config
from nerfactory.utils import profiler
from nerfactory.utils.config import DataConfig
from nerfactory.utils.misc import get_dict_to_torch, instantiate_from_dict_config
@profiler.time_function
def setup_dataset_train(config: DataConfig, device: str) -> Tuple[DatasetInputs, "TrainDataloader"]:
"""Helper method to load train dataset
Args:
config (DataConfig): Configuration of training dataset.
device (str): device to load the dataset to
Returns:
Tuple[DatasetInputs, "TrainDataloader"]: returns both the dataset input information and associated dataloader
"""
dataset_inputs_train = get_dataset_inputs_from_dataset_config(**config.dataset_inputs_train, split="train")
# ImageDataset
image_dataset_train = instantiate_from_dict_config(config.image_dataset_train, **dataset_inputs_train.as_dict())
# ImageSampler
image_sampler_train = instantiate_from_dict_config(
config.dataloader_train.image_sampler, dataset=image_dataset_train, device=device
)
# PixelSampler
pixel_sampler_train = instantiate_from_dict_config(config.dataloader_train.pixel_sampler)
# Dataloader
dataloader_train = TrainDataloader(image_sampler_train, pixel_sampler_train)
return dataset_inputs_train, dataloader_train
@profiler.time_function
def setup_dataset_eval(config: DataConfig, test_mode: bool, device: str) -> Tuple[DatasetInputs, "EvalDataloader"]:
"""Helper method to load test or val dataset based on test/train mode
Args:
config (DataConfig): Configuration of training dataset.
test_mode (bool): specifies whether you are training/testing mode, to load validation/test data
device (str): device to load the dataset to
Returns:
Tuple[DatasetInputs, "TrainDataloader"]: returns both the dataset input information and associated dataloader
"""
eval_split = "test" if test_mode else "val"
dataset_inputs_eval = get_dataset_inputs_from_dataset_config(**config.dataset_inputs_eval, split=eval_split)
image_dataset_eval = instantiate_from_dict_config(config.image_dataset_eval, **dataset_inputs_eval.as_dict())
dataloader_eval = instantiate_from_dict_config(
config.dataloader_eval,
image_dataset=image_dataset_eval,
device=device,
**dataset_inputs_eval.as_dict(),
)
return dataset_inputs_eval, dataloader_eval
class TrainDataloader:
"""Training dataloader base class."""
def __init__(self, image_sampler: ImageSampler, pixel_sampler: PixelSampler):
self.image_sampler = image_sampler
self.pixel_sampler = pixel_sampler
self.iter_image_sampler = iter(self.image_sampler)
self.count = 0
def __iter__(self):
self.count = 0
return self
def __next__(self) -> Tuple[TensorType["num_rays", 3], Dict]:
self.count += 1
image_batch = next(self.iter_image_sampler)
batch = self.pixel_sampler.sample(image_batch)
ray_indices = batch["indices"]
return ray_indices, batch
class EvalDataloader: # pylint: disable=too-few-public-methods
"""Evaluation dataloader base class"""
def __init__(
self, image_dataset: ImageDataset, intrinsics, camera_to_world, num_rays_per_chunk: int, device="cpu", **kwargs
):
super().__init__()
self.image_dataset = image_dataset
self.intrinsics = intrinsics
self.camera_to_world = camera_to_world
self.num_rays_per_chunk = num_rays_per_chunk
self.device = device
self.kwargs = kwargs
def get_camera(self, image_idx) -> Camera:
"""Get camera for the given image index"""
intrinsics = self.intrinsics[image_idx].to(self.device)
camera_to_world = self.camera_to_world[image_idx].to(self.device)
camera = get_camera(intrinsics, camera_to_world, camera_index=image_idx)
return camera
def get_data_from_image_idx(self, image_idx) -> Tuple[RayBundle, Dict]:
"""Returns the data for a specific image index."""
camera = self.get_camera(image_idx)
ray_bundle = camera.get_camera_ray_bundle(device=self.device)
ray_bundle.num_rays_per_chunk = self.num_rays_per_chunk
batch = self.image_dataset[image_idx]
batch = get_dict_to_torch(batch, device=self.device)
return ray_bundle, batch
class FixedIndicesEvalDataloader(EvalDataloader):
"""Dataloader that returns a fixed set of indices."""
def __init__(
self,
image_dataset: ImageDataset,
intrinsics,
camera_to_world,
num_rays_per_chunk: int,
image_indices: Optional[Union[List[int], ListConfig]] = None,
device="cpu",
**kwargs,
):
"""
Args:
image_dataset: ImageDataset to load data from
image_indices: List of image indices to load data from. If None, then use all images.
"""
super().__init__(image_dataset, intrinsics, camera_to_world, num_rays_per_chunk, device, **kwargs)
if image_indices is None:
self.image_indices = list(range(len(image_dataset)))
else:
self.image_indices = image_indices
self.num_rays_per_chunk = num_rays_per_chunk
self.count = 0
def __iter__(self):
self.count = 0
return self
def __next__(self):
if self.count < len(self.image_indices):
image_idx = self.image_indices[self.count]
ray_bundle, batch = self.get_data_from_image_idx(image_idx)
ray_bundle.num_rays_per_chunk = self.num_rays_per_chunk
self.count += 1
return ray_bundle, batch
raise StopIteration
class RandIndicesEvalDataloader(EvalDataloader):
"""Dataloader that returns random images."""
def __init__(
self, image_dataset: ImageDataset, intrinsics, camera_to_world, num_rays_per_chunk: int, device="cpu", **kwargs
):
super().__init__(image_dataset, intrinsics, camera_to_world, num_rays_per_chunk, device, **kwargs)
self.count = 0
def __iter__(self):
self.count = 0
return self
def __next__(self):
if self.count < 1:
image_indices = range(len(self.camera_to_world))
image_idx = random.choice(image_indices)
ray_bundle, batch = self.get_data_from_image_idx(image_idx)
self.count += 1
return ray_bundle, batch
raise StopIteration