From 9e992b9550ff6ead33519973c4123303be626c7f Mon Sep 17 00:00:00 2001 From: Ethan Weber Date: Mon, 30 Jan 2023 19:49:17 +0000 Subject: [PATCH] fixing linting --- nerfstudio/cameras/camera_optimizers.py | 2 +- .../data/datamanagers/base_datamanager.py | 1 - .../data/dataparsers/mipnerf360_dataparser.py | 15 +- nerfstudio/data/pixel_samplers.py | 64 ++++++-- nerfstudio/engine/optimizers.py | 3 +- 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 +- nerfstudio/pipelines/base_pipeline.py | 30 ++-- .../benchmark_nerfstudio_paper.py | 149 ------------------ scripts/downloads/download_data.py | 13 +- scripts/render.py | 7 +- 14 files changed, 84 insertions(+), 210 deletions(-) delete mode 100644 scripts/benchmarking/benchmark_nerfstudio_paper.py diff --git a/nerfstudio/cameras/camera_optimizers.py b/nerfstudio/cameras/camera_optimizers.py index 1d49a02df..7d0e045bc 100644 --- a/nerfstudio/cameras/camera_optimizers.py +++ b/nerfstudio/cameras/camera_optimizers.py @@ -81,7 +81,7 @@ class CameraOptimizer(nn.Module): self.initialize_parameters() def initialize_parameters(self) -> None: - # Initialize learnable parameters. + """Initialize learnable parameters.""" if self.config.mode == "off": pass elif self.config.mode in ("SO3xR3", "SE3"): diff --git a/nerfstudio/data/datamanagers/base_datamanager.py b/nerfstudio/data/datamanagers/base_datamanager.py index 7fcc6b44a..c4a6d35e3 100644 --- a/nerfstudio/data/datamanagers/base_datamanager.py +++ b/nerfstudio/data/datamanagers/base_datamanager.py @@ -398,7 +398,6 @@ class VanillaDataManager(DataManager): # pylint: disable=abstract-method num_cameras=self.eval_dataset.cameras.size, device=self.device ) self.eval_ray_generator = RayGenerator(self.eval_dataset.cameras.to(self.device), self.eval_camera_optimizer) - # TODO(ethan): remove use of self.eval_dataloader from the spiral trajectory code def next_train(self, step: int) -> Tuple[RayBundle, Dict]: """Returns the next batch of data from the train dataloader.""" diff --git a/nerfstudio/data/dataparsers/mipnerf360_dataparser.py b/nerfstudio/data/dataparsers/mipnerf360_dataparser.py index 5b4634dce..123ade13e 100644 --- a/nerfstudio/data/dataparsers/mipnerf360_dataparser.py +++ b/nerfstudio/data/dataparsers/mipnerf360_dataparser.py @@ -15,7 +15,6 @@ from __future__ import annotations -import math from dataclasses import dataclass, field from pathlib import Path, PurePath from typing import Optional, Type @@ -60,15 +59,15 @@ class Mipnerf360DataParserConfig(DataParserConfig): """Whether to center the poses.""" auto_scale_poses: bool = True """Whether to automatically scale the poses to fit in +/- 1 bounding box.""" - train_split_percentage: float = 0.9 - """The percent of images to use for training. The remaining images are for eval.""" + eval_interval: int = 8 + """The interval which eval images will be sampled.""" @dataclass class Mipnerf360(DataParser): """Mipnerf360 DatasetParser""" - config: NerfstudioDataParserConfig + config: Mipnerf360DataParserConfig downscale_factor: Optional[int] = None def _generate_dataparser_outputs(self, split="train"): @@ -106,6 +105,7 @@ class Mipnerf360(DataParser): distort = [] # sort the frames by fname + # they do this in mipnerf360 code fnames = [] for frame in meta["frames"]: filepath = PurePath(frame["file_path"]) @@ -181,11 +181,10 @@ class Mipnerf360(DataParser): # filter image_filenames and poses based on train/eval split percentage num_images = len(image_filenames) - # mipnerf360 code... - # TODO: add parameters for this... + # this chunk of code is very similar to the mipnerf360 code all_indices = np.arange(num_images) - train_indices = all_indices[all_indices % 8 != 0] - eval_indices = all_indices[all_indices % 8 == 0] + train_indices = all_indices[all_indices % self.config.eval_interval != 0] + eval_indices = all_indices[all_indices % self.config.eval_interval == 0] i_train = train_indices i_eval = eval_indices diff --git a/nerfstudio/data/pixel_samplers.py b/nerfstudio/data/pixel_samplers.py index 29db22dd5..07b72c88e 100644 --- a/nerfstudio/data/pixel_samplers.py +++ b/nerfstudio/data/pixel_samplers.py @@ -70,7 +70,9 @@ class PixelSampler: # pylint: disable=too-few-public-methods return indices - def collate_image_dataset_batch(self, batch: Dict, num_rays_per_batch: int, keep_full_image: bool = False): + def collate_image_dataset_batch( + self, batch: Dict, num_rays_per_batch: int, keep_full_image: bool = False, sample_all_pixels: bool = False + ): """ Operates on a batch of images and samples pixels to use for generating rays. Returns a collated batch which is input to the Graph. @@ -79,35 +81,52 @@ class PixelSampler: # pylint: disable=too-few-public-methods batch: batch of images to sample from num_rays_per_batch: number of rays to sample per batch keep_full_image: whether or not to include a reference to the full image in returned batch + sample_all_pixels: if True, will sample all the pixels in the image """ device = batch["image"].device num_images, image_height, image_width, _ = batch["image"].shape - if "mask" in batch: - indices = self.sample_method( - num_rays_per_batch, num_images, image_height, image_width, mask=batch["mask"], device=device + if sample_all_pixels: + image_indices = (torch.ones(num_images, device=device) * batch["image_idx"].to(device)).to(torch.int64) + collated_batch = {key: value for key, value in batch.items() if key != "image_idx" and value is not None} + collated_batch["indices"] = torch.stack( + torch.meshgrid( + image_indices, + torch.arange(image_height, device=device), + torch.arange(image_width, device=device), + indexing="ij", + ), + dim=-1, ) else: - indices = self.sample_method(num_rays_per_batch, num_images, image_height, image_width, device=device) + # if the mask is in the batch, then only sample within the mask + if "mask" in batch: + indices = self.sample_method( + num_rays_per_batch, num_images, image_height, image_width, mask=batch["mask"], device=device + ) + else: + indices = self.sample_method(num_rays_per_batch, num_images, image_height, image_width, device=device) - c, y, x = (i.flatten() for i in torch.split(indices, 1, dim=-1)) - collated_batch = { - key: value[c, y, x] for key, value in batch.items() if key != "image_idx" and value is not None - } + c, y, x = (i.flatten() for i in torch.split(indices, 1, dim=-1)) + collated_batch = { + key: value[c, y, x] for key, value in batch.items() if key != "image_idx" and value is not None + } - assert collated_batch["image"].shape == (num_rays_per_batch, 3), collated_batch["image"].shape + assert collated_batch["image"].shape == (num_rays_per_batch, 3), collated_batch["image"].shape - # Needed to correct the random indices to their actual camera idx locations. - indices[:, 0] = batch["image_idx"][c] - collated_batch["indices"] = indices # with the abs camera indices + # Needed to correct the random indices to their actual camera idx locations. + indices[:, 0] = batch["image_idx"][c] + collated_batch["indices"] = indices # with the abs camera indices if keep_full_image: collated_batch["full_image"] = batch["image"] return collated_batch - def collate_image_dataset_batch_list(self, batch: Dict, num_rays_per_batch: int, keep_full_image: bool = False): + def collate_image_dataset_batch_list( + self, batch: Dict, num_rays_per_batch: int, keep_full_image: bool = False, sample_all_pixels: bool = False + ): """ Does the same as collate_image_dataset_batch, except it will operate over a list of images / masks inside a list. @@ -118,6 +137,7 @@ class PixelSampler: # pylint: disable=too-few-public-methods batch: batch of images to sample from num_rays_per_batch: number of rays to sample per batch keep_full_image: whether or not to include a reference to the full image in returned batch + sample_all_pixels: if True, will sample all the pixels in the image """ device = batch["image"][0].device @@ -127,6 +147,9 @@ class PixelSampler: # pylint: disable=too-few-public-methods all_indices = [] all_images = [] + if sample_all_pixels: + raise NotImplementedError("sample_all_pixels is not implemented for collate_image_dataset_batch_list") + if "mask" in batch: num_rays_in_batch = num_rays_per_batch // num_images for i in range(num_images): @@ -176,19 +199,26 @@ class PixelSampler: # pylint: disable=too-few-public-methods return collated_batch - def sample(self, image_batch: Dict): + def sample(self, image_batch: Dict, sample_all_pixels: bool = False): """Sample an image batch and return a pixel batch. Args: image_batch: batch of images to sample from + sample_all_pixels: if True, will sample all pixels in the image """ if isinstance(image_batch["image"], list): image_batch = dict(image_batch.items()) # copy the dictionary so we don't modify the original pixel_batch = self.collate_image_dataset_batch_list( - image_batch, self.num_rays_per_batch, keep_full_image=self.keep_full_image + image_batch, + self.num_rays_per_batch, + keep_full_image=self.keep_full_image, + sample_all_pixels=sample_all_pixels, ) elif isinstance(image_batch["image"], torch.Tensor): pixel_batch = self.collate_image_dataset_batch( - image_batch, self.num_rays_per_batch, keep_full_image=self.keep_full_image + image_batch, + self.num_rays_per_batch, + keep_full_image=self.keep_full_image, + sample_all_pixels=sample_all_pixels, ) else: raise ValueError("image_batch['image'] must be a list or torch.Tensor") diff --git a/nerfstudio/engine/optimizers.py b/nerfstudio/engine/optimizers.py index c27ca4751..1622206c6 100644 --- a/nerfstudio/engine/optimizers.py +++ b/nerfstudio/engine/optimizers.py @@ -79,7 +79,8 @@ class Optimizers: for param_group_name, params in param_groups.items(): if param_group_name not in config: CONSOLE.print( - f"[bold yellow]Warning: Skipping param group '{param_group_name}' because optimizer and scheduler are not in the config." + f"[bold yellow]Warning: Skipping param group '{param_group_name}'", + "[bold yellow]because optimizer and scheduler aren't in config.", ) continue lr_init = config[param_group_name]["optimizer"].lr diff --git a/nerfstudio/models/mipnerf.py b/nerfstudio/models/mipnerf.py index b8c3bcc4f..cbc5139f2 100644 --- a/nerfstudio/models/mipnerf.py +++ b/nerfstudio/models/mipnerf.py @@ -86,7 +86,7 @@ class MipNerfModel(Model): # metrics self.psnr = PeakSignalNoiseRatio(data_range=1.0) self.ssim = structural_similarity_index_measure - self.lpips = LearnedPerceptualImagePatchSimilarity() + self.lpips = LearnedPerceptualImagePatchSimilarity(net_type="vgg", normalize=True) def get_param_groups(self) -> Dict[str, List[Parameter]]: param_groups = {} diff --git a/nerfstudio/models/nerfacto.py b/nerfstudio/models/nerfacto.py index 5e5ff03e9..04fff130d 100644 --- a/nerfstudio/models/nerfacto.py +++ b/nerfstudio/models/nerfacto.py @@ -55,7 +55,7 @@ from nerfstudio.model_components.renderers import ( ) from nerfstudio.model_components.scene_colliders import AABBBoxCollider, NearFarCollider from nerfstudio.models.base_model import Model, ModelConfig -from nerfstudio.utils import colormaps, colors +from nerfstudio.utils import colormaps @dataclass diff --git a/nerfstudio/models/semantic_nerfw.py b/nerfstudio/models/semantic_nerfw.py index 941716dc8..1361ff1af 100644 --- a/nerfstudio/models/semantic_nerfw.py +++ b/nerfstudio/models/semantic_nerfw.py @@ -137,7 +137,7 @@ class SemanticNerfWModel(Model): # metrics self.psnr = PeakSignalNoiseRatio(data_range=1.0) self.ssim = structural_similarity_index_measure - self.lpips = LearnedPerceptualImagePatchSimilarity() + self.lpips = LearnedPerceptualImagePatchSimilarity(net_type="vgg", normalize=True) def get_param_groups(self) -> Dict[str, List[Parameter]]: param_groups = {} diff --git a/nerfstudio/models/tensorf.py b/nerfstudio/models/tensorf.py index 60fec9706..9e0c28289 100644 --- a/nerfstudio/models/tensorf.py +++ b/nerfstudio/models/tensorf.py @@ -201,7 +201,7 @@ class TensoRFModel(Model): # metrics self.psnr = PeakSignalNoiseRatio(data_range=1.0) self.ssim = structural_similarity_index_measure - self.lpips = LearnedPerceptualImagePatchSimilarity() + self.lpips = LearnedPerceptualImagePatchSimilarity(net_type="vgg", normalize=True) # colliders if self.config.enable_collider: diff --git a/nerfstudio/models/vanilla_nerf.py b/nerfstudio/models/vanilla_nerf.py index 3e731524f..2970c106a 100644 --- a/nerfstudio/models/vanilla_nerf.py +++ b/nerfstudio/models/vanilla_nerf.py @@ -118,7 +118,7 @@ class NeRFModel(Model): # metrics self.psnr = PeakSignalNoiseRatio(data_range=1.0) self.ssim = structural_similarity_index_measure - self.lpips = LearnedPerceptualImagePatchSimilarity() + self.lpips = LearnedPerceptualImagePatchSimilarity(net_type="vgg", normalize=True) if getattr(self.config, "enable_temporal_distortion", False): params = self.config.temporal_distortion_params diff --git a/nerfstudio/pipelines/base_pipeline.py b/nerfstudio/pipelines/base_pipeline.py index 76e57cbb4..01ba182a5 100644 --- a/nerfstudio/pipelines/base_pipeline.py +++ b/nerfstudio/pipelines/base_pipeline.py @@ -33,12 +33,10 @@ from rich.progress import ( Progress, TextColumn, TimeElapsedColumn, - track, ) from torch import nn from torch.nn import Parameter from torch.nn.parallel import DistributedDataParallel as DDP -from tqdm import tqdm from typing_extensions import Literal from nerfstudio.configs import base_config as cfg @@ -331,8 +329,8 @@ class VanillaPipeline(Pipeline): self.train() return metrics_dict, images_dict - def get_eval_optimize_camera_camera_ray_bundle(self, idx): - # start optimizing camera pose + def eval_optimize_camera(self, idx): + """Optimize the camera pose.""" # TODO: raise error if the method is not Nerfacto # get the eval camera optimizer's parameters @@ -362,11 +360,11 @@ class VanillaPipeline(Pipeline): self.eval() # done optimizing camera pose - return camera_ray_bundle - - def get_eval_optimize_appearance_camera_ray_bundle(self, idx): - # start optimizing appearance embedding + def get_eval_optimize_appearance(self, idx): + """Optimize the appearance embedding.""" # TODO: raise error if the method is not Nerfacto + + # start optimizing appearance embedding self.datamanager.eval_ray_generator.cameras.rescale_output_resolution(self.config.eval_image_scale_factor) self.train() @@ -376,7 +374,6 @@ class VanillaPipeline(Pipeline): optimizer = torch.optim.Adam(appearance_parameters, lr=1e-3, eps=1e-15) side = random.choice([0, 1]) - half_width = width // 2 rgb_loss = MSELoss() for _ in range(self.config.eval_num_appearance_iters): @@ -387,6 +384,9 @@ class VanillaPipeline(Pipeline): ) outputs = self.model.get_outputs_for_camera_ray_bundle(camera_ray_bundle) + _, width = camera_ray_bundle.shape + half_width = width // 2 + # compute loss on half the image image = batch["image"].to(self.device) if side == 0: @@ -422,17 +422,17 @@ class VanillaPipeline(Pipeline): ) as progress: task = progress.add_task("[green]Evaluating all eval images...", total=num_images) for idx in range(num_images): - camera_ray_bundle, batch = self.datamanager.get_eval_image(idx) # time this the following line inner_start = time() - height, width = camera_ray_bundle.shape - num_rays = height * width if self.config.eval_optimize_cameras: - camera_ray_bundle = self.get_eval_optimize_camera_camera_ray_bundle(idx) + self.eval_optimize_camera(idx) if self.config.eval_optimize_appearance: - camera_ray_bundle = self.get_eval_optimize_appearance_camera_ray_bundle(idx) - # at this point, camera_ray_bundle might be optimized for pose and/or appearance embedding + self.get_eval_optimize_appearance(idx) + + camera_ray_bundle, batch = self.datamanager.get_eval_image(idx) + height, width = camera_ray_bundle.shape + num_rays = height * width with torch.no_grad(): outputs = self.model.get_outputs_for_camera_ray_bundle(camera_ray_bundle) diff --git a/scripts/benchmarking/benchmark_nerfstudio_paper.py b/scripts/benchmarking/benchmark_nerfstudio_paper.py deleted file mode 100644 index cd3cd730c..000000000 --- a/scripts/benchmarking/benchmark_nerfstudio_paper.py +++ /dev/null @@ -1,149 +0,0 @@ -""" -Benchmarking script for nerfstudio paper. - -- nerfacto and instant-ngp methods on mipnerf360 data -- nerfacto ablations -""" - -import threading -import time -from pathlib import Path - -import GPUtil - -from nerfstudio.utils.scripts import run_command - -# for the mipnerf360 experiments -mipnerf360_capture_names = ["bicycle", "garden", "stump", "room", "counter", "kitchen", "bonsai"] # 7 splits -mipnerf360_table_rows = [ - # nerfacto method - ( - "nerfacto-w/o-pose-app", - "nerfacto", - "--pipeline.eval_optimize_cameras False --pipeline.eval_optimize_appearance False --pipeline.datamanager.camera-optimizer.mode off --pipeline.model.use-appearance-embedding False nerfstudio-data --downscale-factor 4 --train-split-percentage 0.875 mipnerf360-data", - ), -] - -# for the ablation experiments -ablations_capture_names = [ - "Egypt", - "person", - "kitchen", - "plane", - "dozer", - "floating-tree", - "aspen", - "stump", - "sculpture", - "Giannini-Hall", -] - -ablations_table_rows = [ - ("nerfacto", "nerfacto", "--pipeline.eval_optimize_cameras True --pipeline.eval_optimize_appearance True"), - ( - "w/o-pose", - "nerfacto", - "--pipeline.eval_optimize_cameras True --pipeline.eval_optimize_appearance True --pipeline.datamanager.camera-optimizer.mode off", - ), - ( - "w/o-app", - "nerfacto", - "--pipeline.eval_optimize_cameras True --pipeline.eval_optimize_appearance False --pipeline.model.use-appearance-embedding False", - ), - ( - "w/o-pose-app", - "nerfacto", - "--pipeline.eval_optimize_cameras True --pipeline.eval_optimize_appearance False --pipeline.datamanager.camera-optimizer.mode off --pipeline.model.use-appearance-embedding False", - ), - ( - "1-prop-network", - "nerfacto", - '--pipeline.eval_optimize_cameras True --pipeline.eval_optimize_appearance True --pipeline.model.num-proposal-samples-per-ray "256" --pipeline.model.num_proposal_iterations 1', - ), - ( - "l2-contraction", - "nerfacto", - "--pipeline.eval_optimize_cameras True --pipeline.eval_optimize_appearance True --pipeline.model.scene-contraction-norm l2", - ), - ( - "shared-prop-network", - "nerfacto", - "--pipeline.eval_optimize_cameras True --pipeline.eval_optimize_appearance True --pipeline.model.use-same-proposal-network True", - ), - ( - "random-background-color", - "nerfacto", - "--pipeline.eval_optimize_cameras True --pipeline.eval_optimize_appearance True --pipeline.model.background-color random", - ), - ( - "no-contraction", - "nerfacto", - "--pipeline.eval_optimize_cameras True --pipeline.eval_optimize_appearance True --pipeline.model.use-bounded True --pipeline.model.use-scene-contraction False nerfstudio-data --scale_factor 0.125", - ), - ( - "synthetic-on-real", - "nerfacto", - "--pipeline.eval_optimize_cameras True --pipeline.eval_optimize_appearance False --pipeline.datamanager.camera-optimizer.mode off --pipeline.model.use-appearance-embedding False --pipeline.model.use-bounded True --pipeline.model.use-scene-contraction False nerfstudio-data --scale_factor 0.125", - ), -] - - -def main(capture_names, table_rows, data_path: Path = Path("data/nerfstudio")): - """Main method.""" - # 30K iterations - - # make a list of all the jobs that need to be fun - jobs = [] - for capture_name in capture_names: - - for table_row_name, method, table_row_command in table_rows: - command = " ".join( - ( - f"ns-train {method}", - "--vis wandb", - f"--data { data_path / capture_name}", - "--output-dir outputs/nerfacto-ablations", - "--trainer.steps-per-eval-batch 0 --trainer.steps-per-eval-image 0", - "--trainer.steps-per-eval-all-images 5000 --trainer.max-num-iterations 30001", - f"--wandb-name {capture_name}_{table_row_name}", - f"--experiment-name {capture_name}_{table_row_name}", - # extra_string, - table_row_command, - ) - ) - jobs.append(command) - - while jobs: - # get GPUs that capacity to run these jobs - gpu_devices_available = GPUtil.getAvailable(order="first", limit=10, maxMemory=0.1) - - print("Available GPUs: ", gpu_devices_available) - - # thread list - threads = [] - while gpu_devices_available and jobs: - gpu = gpu_devices_available.pop(0) - command = f"CUDA_VISIBLE_DEVICES={gpu} " + jobs.pop(0) - - def task(): - print("Starting command: ", command) - # out = run_command(command, verbose=False) - # print("Finished command: ", command) - - threads.append(threading.Thread(target=task)) - threads[-1].start() - - # NOTE(ethan): here we need a delay, otherwise the wandb/tensorboard naming is messed up... not sure why - time.sleep(5) - - # wait for all threads to finish - for t in threads: - t.join() - - print("Finished all threads") - - -if __name__ == "__main__": - # pass - # main(mipnerf360_capture_names, mipnerf360_table_rows, data_path=Path("data/nerfstudio-data-mipnerf360")) - main(ablations_capture_names, ablations_table_rows) diff --git a/scripts/downloads/download_data.py b/scripts/downloads/download_data.py index aea0ddf59..0afdae14f 100755 --- a/scripts/downloads/download_data.py +++ b/scripts/downloads/download_data.py @@ -65,8 +65,6 @@ class FriendsDownload(DatasetDownload): """Download the friends dataset.""" def download(self, save_dir: Path): - """Download the friends dataset.""" - # https://drive.google.com/file/d/1sgKr0ZO7BQC0FYinAnRSxobIWNucAST5/view?usp=sharing friends_file_id = "1sgKr0ZO7BQC0FYinAnRSxobIWNucAST5" @@ -81,13 +79,10 @@ class FriendsDownload(DatasetDownload): @dataclass class Mipnerf360Download(DatasetDownload): - """Download the friends dataset.""" + """Download the MIP-NeRF 360 dataset.""" def download(self, save_dir: Path): - """Download the friends dataset.""" - - # https://drive.google.com/file/d/1sgKr0ZO7BQC0FYinAnRSxobIWNucAST5/view?usp=sharing - friends_file_id = "1sgKr0ZO7BQC0FYinAnRSxobIWNucAST5" + """Download the MIP-NeRF 360 dataset.""" # Download the files url = "https://data.nerf.studio/nerfstudio-data-mipnerf360.zip" @@ -275,13 +270,11 @@ PhototourismCaptureName = tyro.extras.literal_type_from_choices(phototourism_dow @dataclass class PhototourismDownload(DatasetDownload): - """Download the phototourism dataset.""" + """Download the phototourism dataset: https://www.cs.ubc.ca/~kmyi/imw2020/data.html""" capture_name: PhototourismCaptureName = "brandenburg-gate" def download(self, save_dir: Path): - """Download a PhotoTourism dataset: https://www.cs.ubc.ca/~kmyi/imw2020/data.html""" - if self.capture_name == "all": for capture_name in phototourism_downloads: if capture_name != "all": diff --git a/scripts/render.py b/scripts/render.py index 4466c5385..78d8e5524 100644 --- a/scripts/render.py +++ b/scripts/render.py @@ -283,10 +283,11 @@ class RenderTrajectory: seconds = self.seconds crop_data = None - # TODO(ethan): use camera information from parsing args if self.traj == "spiral": - camera_start = pipeline.datamanager.eval_dataloader.get_camera(image_idx=0).flatten() - # TODO(ethan): pass in the up direction of the camera + # TODO(ethan): use camera information provided from arguments + # - camera index instead of defaulting to 0 + # - up direction of the camera + camera_start = pipeline.datamanager.eval_dataset.cameras[0].flatten() camera_type = CameraType.PERSPECTIVE camera_path = get_spiral_path(camera_start, steps=30, radius=0.1) elif self.traj == "filename":