Skip to content

Commit

Permalink
INGP data converter
Browse files Browse the repository at this point in the history
  • Loading branch information
sxyu committed Jan 15, 2023
1 parent 9fab277 commit c57f556
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ https://drive.google.com/file/d/1PG-KllCv4vSRPO7n5lpBjyTjlUyT8Nag/view?usp=shari
**Note: we currently do not support the instant-ngp format data (since the project was released before NGP). Using it will trigger the nerf-synthetic (Blender) data loader
due to similarity, but will not train properly. For real data we use the NSVF format.**

To help convert instant-ngp data, please try
```
cd opt/scripts
python ingp2nsvf.py <ingp_data_dir> <output_data_dir>
```

## Voxel Optimization (aka Training)

For training a single scene, see `opt/opt.py`. The launch script makes this easier.
Expand Down
141 changes: 141 additions & 0 deletions opt/scripts/ingp2nsvf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"""
Convert NeRF-iNGP data to NSVF
python ingp2nsvf.py <ngp_data_dir> <our_data_dir>
"""
import os
import shutil
from glob import glob
import json

import numpy as np
from PIL import Image
import argparse

def convert(data_dir : str, out_data_dir : str):
"""
Convert Instant-NGP (modified NeRF) data to NSVF
:param data_dir: the dataset dir (NeRF-NGP format) to convert
:param out_data_dir: output dataset directory NSVF
"""

images_dir_name = os.path.join(out_data_dir, "images")
pose_dir_name = os.path.join(out_data_dir, "pose")

os.makedirs(images_dir_name, exist_ok=True)
os.makedirs(pose_dir_name, exist_ok=True)

def get_subdir(name):
if name.endswith("_train.json"):
return "train"
elif name.endswith("_val.json"):
return "val"
elif name.endswith("_test.json"):
return "test"
return ""

def get_out_prefix(name):
if name.endswith("_train.json"):
return "0_"
elif name.endswith("_val.json"):
return "1_"
elif name.endswith("_test.json"):
return "2_"
return ""

jsons = {
x: (get_subdir(x), get_out_prefix(x))
for x in glob(os.path.join(data_dir, "*.json"))
}

# OpenGL -> OpenCV
cam_trans = np.diag(np.array([1.0, -1.0, -1.0, 1.0]))

# fmt: off
world_trans = np.array(
[
[0.0, -1.0, 0.0, 0.0],
[0.0, 0.0, -1.0, 0.0],
[1.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
]
)
# fmt: on

assert len(jsons) > 0, f"No jsons found in {data_dir}, can't convert"
cnt = 0

example_fpath = None
tj = {}
for tj_path, (tj_subdir, tj_out_prefix) in jsons.items():
with open(tj_path, "r") as f:
tj = json.load(f)
if "frames" not in tj:
print(f"No frames in json {tj_path}, skipping")
continue

for frame in tj["frames"]:
# Try direct relative path (used in newer NGP datasets)
fpath = os.path.join(data_dir, frame["file_path"])
if not os.path.isfile(fpath):
# Legacy path (NeRF)
fpath = os.path.join(
data_dir, tj_subdir, os.path.basename(frame["file_path"]) + ".png"
)
example_fpath = fpath
if not os.path.isfile(fpath):
print("Could not find image:", frame["file_path"], "(this may be ok)")
continue

ext = os.path.splitext(fpath)[1]

c2w = np.array(frame["transform_matrix"])
c2w = world_trans @ c2w @ cam_trans # To OpenCV

image_fname = tj_out_prefix + f"{cnt:05d}"

pose_path = os.path.join(pose_dir_name, image_fname + ".txt")

# Save 4x4 OpenCV C2W pose
np.savetxt(pose_path, c2w)

# Copy images
new_fpath = os.path.join(images_dir_name, image_fname + ext)
shutil.copyfile(fpath, new_fpath)
cnt += 1

assert len(tj) > 0, f"No valid jsons found in {data_dir}, can't convert"

w = tj.get("w")
h = tj.get("h")

if w is None or h is None:
assert example_fpath is not None
# Pose not available so load a image and get the size
w, h = Image.open(example_fpath).size

fx = float(0.5 * w / np.tan(0.5 * tj["camera_angle_x"]))
if "camera_angle_y" in tj:
fy = float(0.5 * h / np.tan(0.5 * tj["camera_angle_y"]))
else:
fy = fx

cx = tj.get("cx", w * 0.5)
cy = tj.get("cy", h * 0.5)

intrin_mtx = np.array([
[fx, 0.0, cx, 0.0],
[0.0, fy, cy, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
])
# Write intrinsics
np.savetxt(os.path.join(out_data_dir, "intrinsics.txt"), intrin_mtx)


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("data_dir", type=str, help="NeRF-NGP data directory")
parser.add_argument("out_data_dir", type=str, help="Output NSVF data directory")
args = parser.parse_args()
convert(args.data_dir, args.out_data_dir)
4 changes: 4 additions & 0 deletions opt/util/nerf_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
class NeRFDataset(DatasetBase):
"""
NeRF dataset loader
WARNING: this is only intended for use with NeRF Blender data!!!!
"""

focal: float
Expand Down Expand Up @@ -58,6 +60,8 @@ def __init__(
data_json = path.join(root, "transforms_" + split_name + ".json")

print("LOAD DATA", data_path)
print("WARNING: This data loader is ONLY intended for use with NeRF-synthetic Blender data!!!!")
print("If you want to try running this code on Instant-NGP data please use scripts/ingp2nsvf.py")

j = json.load(open(data_json, "r"))

Expand Down

0 comments on commit c57f556

Please sign in to comment.