mirror of
https://github.com/nerfstudio-project/nerfstudio.git
synced 2026-11-11 02:24:38 +08:00
* spiral traj working, specify version for hydra, better documentation * cleaned up spiral trajectory a bit
161 lines
6.1 KiB
Python
161 lines
6.1 KiB
Python
"""
|
|
run_eval.py
|
|
"""
|
|
import argparse
|
|
import os
|
|
from typing import Tuple
|
|
|
|
import hydra
|
|
import mediapy as media
|
|
import torch
|
|
from hydra import compose, initialize
|
|
from omegaconf import DictConfig
|
|
from tqdm import tqdm
|
|
|
|
from pyrad.cameras.camera_paths import get_spiral_path
|
|
from pyrad.cameras.cameras import SimplePinholeCamera
|
|
from pyrad.data.dataloader import setup_dataset_eval, setup_dataset_train
|
|
from pyrad.graphs.base import Graph, setup_graph
|
|
from pyrad.utils.writer import TimeWriter
|
|
|
|
|
|
def _update_avg(prev_avg: float, new_val: float, step: int) -> float:
|
|
"""helper to calculate the running average
|
|
|
|
Args:
|
|
prev_avg (float): previous average value
|
|
new_val (float): new value to update the average with
|
|
step (int): current step number
|
|
|
|
Returns:
|
|
float: new updated average
|
|
"""
|
|
return (step * prev_avg + new_val) / (step + 1)
|
|
|
|
|
|
def _load_checkpoint(config: DictConfig, graph: Graph) -> None:
|
|
"""Helper function to load checkpointed graph
|
|
|
|
Args:
|
|
config (DictConfig): Configuration of graph to load
|
|
graph (Graph): Graph instance of which to load weights
|
|
"""
|
|
assert config.load_dir is not None
|
|
if config.load_step is None:
|
|
print("Loading latest checkpoint from load_dir")
|
|
# NOTE: this is specific to the checkpoint name format
|
|
load_step = sorted([int(x[x.find("-") + 1 : x.find(".")]) for x in os.listdir(config.load_dir)])[-1]
|
|
else:
|
|
load_step = config.load_step
|
|
load_path = os.path.join(config.load_dir, f"step-{load_step:09d}.ckpt")
|
|
assert os.path.exists(load_path), f"Checkpoint {load_path} does not exist"
|
|
loaded_state = torch.load(load_path, map_location="cpu")
|
|
graph.load_graph(loaded_state)
|
|
print(f"done loading checkpoint from {load_path}")
|
|
|
|
|
|
def run_inference(config: DictConfig, local_rank: int = 0, world_size: int = 1) -> Tuple[float, float]:
|
|
"""helper function to run inference given config specifications (also used in benchmarking)
|
|
|
|
Args:
|
|
config (DictConfig): Configuration for loading the evaluation.
|
|
|
|
Returns:
|
|
Tuple[float, float]: returns both the avg psnr and avg rays per second
|
|
"""
|
|
device = "cpu" if world_size == 0 else f"cuda:{local_rank}"
|
|
# setup graph and dataset
|
|
dataset_inputs_train, _ = setup_dataset_train(config.data, device=device)
|
|
_, dataloader_eval = setup_dataset_eval(config.data, test_mode=True, device=device)
|
|
graph = setup_graph(config.graph, dataset_inputs_train, device=device)
|
|
|
|
# load checkpointed information
|
|
_load_checkpoint(config.trainer.resume_train, graph)
|
|
|
|
# calculate average psnr across test dataset
|
|
# TODO(ethan): trajector specification
|
|
avg_psnr = 0
|
|
avg_rays_per_sec = 0
|
|
for step, (camera_ray_bundle, batch) in tqdm(enumerate(dataloader_eval)):
|
|
with TimeWriter(writer=None, name=None, write=False) as t:
|
|
with torch.no_grad():
|
|
image_idx = int(camera_ray_bundle.camera_indices[0, 0])
|
|
outputs = graph.get_outputs_for_camera_ray_bundle(camera_ray_bundle)
|
|
psnr = graph.log_test_image_outputs(image_idx, step, batch, outputs)
|
|
avg_rays_per_sec = _update_avg(avg_rays_per_sec, camera_ray_bundle.origins.shape[0] / t.duration, step)
|
|
avg_psnr = _update_avg(avg_psnr, psnr, step)
|
|
return avg_psnr, avg_rays_per_sec
|
|
|
|
|
|
def create_spiral_video(
|
|
config: DictConfig, local_rank: int = 0, world_size: int = 1, output_filename: str = None
|
|
) -> None:
|
|
print("Creating spiral video")
|
|
device = "cpu" if world_size == 0 else f"cuda:{local_rank}"
|
|
# setup graph and dataset
|
|
dataset_inputs_train, _ = setup_dataset_train(config.data, device=device)
|
|
_, dataloader_eval = setup_dataset_eval(config.data, test_mode=True, device=device)
|
|
graph = setup_graph(config.graph, dataset_inputs_train, device=device)
|
|
|
|
# load checkpointed information
|
|
_load_checkpoint(config.trainer.resume_train, graph)
|
|
|
|
# get a trajecory
|
|
start_camera = dataloader_eval.get_camera(image_idx=0)
|
|
# TODO(ethan): replace with radius with radiuses, based on camera pose percentiles
|
|
# see original nerf paper code for details
|
|
camera_path = get_spiral_path(start_camera, steps=60, radius=0.5)
|
|
|
|
images = []
|
|
for camera in tqdm(camera_path.cameras):
|
|
camera.cx /= 4
|
|
camera.cy /= 4
|
|
camera.fx /= 4
|
|
camera.fy /= 4
|
|
camera_ray_bundle = camera.get_camera_ray_bundle().to(device)
|
|
camera_ray_bundle.num_rays_per_chunk = 4096
|
|
outputs = graph.get_outputs_for_camera_ray_bundle(camera_ray_bundle)
|
|
# TODO: don't hardcode the key! this will break for some nerf Graphs
|
|
image = outputs["rgb"].cpu().numpy()
|
|
images.append(image)
|
|
|
|
seconds = 5.0
|
|
fps = len(images) / seconds
|
|
media.write_video(output_filename, images, fps=fps)
|
|
|
|
|
|
def main():
|
|
"""Main function."""
|
|
|
|
parser = argparse.ArgumentParser(description="Run the evaluation of a model.")
|
|
parser.add_argument(
|
|
"--method",
|
|
type=str,
|
|
default="psnr",
|
|
choices=["psnr", "traj"],
|
|
help="Specify which type of evaluation method to run.",
|
|
)
|
|
parser.add_argument("--traj", type=str, default="spiral", choices=["spiral", "interp"])
|
|
parser.add_argument("--output-filename", type=str, default="output.mp4")
|
|
parser.add_argument("--config-name", type=str, default="graph_default.yaml")
|
|
parser.add_argument("overrides", nargs="*", default=[])
|
|
args = parser.parse_args()
|
|
|
|
config_path = "../configs"
|
|
initialize(version_base="1.2", config_path=config_path)
|
|
config = compose(args.config_name, overrides=args.overrides)
|
|
|
|
assert config.trainer.resume_train.load_dir, "Please specify checkpoint load path"
|
|
assert args.traj != "interp", "Camera pose interpolation trajectory isn't yet implemented."
|
|
|
|
if args.method == "psnr":
|
|
avg_psnr, avg_rays_per_sec = run_inference(config)
|
|
print(f"Avg. PSNR: {avg_psnr:0.4f}")
|
|
print(f"Avg. Rays per sec: {avg_rays_per_sec:0.4f}")
|
|
elif args.method == "traj":
|
|
create_spiral_video(config, output_filename=args.output_filename)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|