Files
nerfstudio/nerfactory/datamanagers/dataparsers/nerfactory_parser.py
T
2022-09-25 18:21:26 -07:00

160 lines
6.0 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 parser for nerfactory datasets. """
from __future__ import annotations
import logging
import math
from dataclasses import dataclass, field
from pathlib import Path
from typing import Literal, Type
import numpy as np
import torch
from nerfactory.cameras import utils as camera_utils
from nerfactory.cameras.cameras import CAMERA_MODEL_TO_TYPE, Cameras, CameraType
from nerfactory.datamanagers.dataparsers.base import DataParser, DataParserConfig
from nerfactory.datamanagers.structs import DatasetInputs, SceneBounds
from nerfactory.utils.io import get_absolute_path, load_from_json
@dataclass
class NerfactoryDataParserConfig(DataParserConfig):
"""Nerfactory dataset config"""
_target: Type = field(default_factory=lambda: Nerfactory)
"""target class to instantiate"""
data_directory: Path = Path("data/ours/posters_v3")
"""directory specifying location of data"""
scale_factor: float = 1.0
"""How much to scale the camera origins by."""
downscale_factor: int = 1
"""How much to downscale images."""
scene_scale: float = 4.0
"""How much to scale the scene."""
orientation_method: Literal["pca", "up"] = "up"
"""The method to use for orientation."""
train_split_percentage: float = 0.9
"""The percent of images to use for training. The remaining images are for eval.
"""
@dataclass
class Nerfactory(DataParser):
"""Nerfactory Dataset"""
config: NerfactoryDataParserConfig
def _generate_dataset_inputs(self, split="train"):
abs_dir = get_absolute_path(self.config.data_directory)
meta = load_from_json(abs_dir / "transforms.json")
image_filenames = []
poses = []
num_skipped_image_filenames = 0
for frame in meta["frames"]:
fname = abs_dir / Path(frame["file_path"])
if self.config.downscale_factor > 1:
fname = abs_dir / f"images_{self.config.downscale_factor}" / Path(frame["file_path"]).name
else:
fname = abs_dir / Path(frame["file_path"])
if not fname:
num_skipped_image_filenames += 1
else:
image_filenames.append(fname)
poses.append(np.array(frame["transform_matrix"]))
if num_skipped_image_filenames >= 0:
logging.info("Skipping %s files in dataset split %s.", num_skipped_image_filenames, split)
assert (
len(image_filenames) != 0
), """
No image files found.
You should check the file_paths in the transforms.json file to make sure they are correct.
"""
# filter image_filenames and poses based on train/eval split percentage
num_images = len(image_filenames)
num_train_images = math.ceil(num_images * self.config.train_split_percentage)
num_eval_images = num_images - num_train_images
i_all = np.arange(num_images)
i_train = np.linspace(
0, num_images - 1, num_train_images, dtype=int
) # 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
assert len(i_eval) == num_eval_images
if split == "train":
indices = i_train
elif split in ["val", "test"]:
indices = i_eval
else:
raise ValueError(f"Unknown dataparser split {split}")
poses = torch.from_numpy(np.array(poses).astype(np.float32))
poses = camera_utils.auto_orient_poses(poses, method=self.config.orientation_method)
# Scale poses
scale_factor = 1.0 / torch.max(torch.abs(poses[:, :3, 3]))
poses[:, :3, 3] *= scale_factor * self.config.scale_factor
# Choose image_filenames and poses based on split, but after auto orient and scaling the poses.
image_filenames = [image_filenames[i] for i in indices]
poses = poses[indices]
# in x,y,z order
# assumes that the scene is centered at the origin
aabb_scale = self.config.scene_scale
scene_bounds = SceneBounds(
aabb=torch.tensor(
[[-aabb_scale, -aabb_scale, -aabb_scale], [aabb_scale, aabb_scale, aabb_scale]], dtype=torch.float32
)
)
if "camera_model" in meta:
camera_type = CAMERA_MODEL_TO_TYPE[meta["camera_model"]]
else:
camera_type = CameraType.PERSPECTIVE
distortion_params = camera_utils.get_distortion_params(
k1=float(meta["k1"]) if "k1" in meta else 0.0,
k2=float(meta["k2"]) if "k2" in meta else 0.0,
k3=float(meta["k3"]) if "k3" in meta else 0.0,
k4=float(meta["k4"]) if "k4" in meta else 0.0,
p1=float(meta["p1"]) if "p1" in meta else 0.0,
p2=float(meta["p2"]) if "p2" in meta else 0.0,
)
cameras = Cameras(
fx=float(meta["fl_x"]),
fy=float(meta["fl_y"]),
cx=float(meta["cx"]),
cy=float(meta["cy"]),
distortion_params=distortion_params,
height=int(meta["h"]),
width=int(meta["w"]),
camera_to_worlds=poses[:, :3, :4],
camera_type=camera_type,
)
cameras.rescale_output_resolution(scaling_factor=1.0 / self.config.downscale_factor)
dataset_inputs = DatasetInputs(
image_filenames=image_filenames,
cameras=cameras,
scene_bounds=scene_bounds,
)
return dataset_inputs