nvs2colmap 0.5.1__tar.gz → 0.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/PKG-INFO +1 -1
- nvs2colmap-0.5.2/nvs2colmap/blender/sample_cameras.py +101 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap.egg-info/PKG-INFO +1 -1
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap.egg-info/SOURCES.txt +2 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap.egg-info/entry_points.txt +1 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/pyproject.toml +2 -1
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/LICENSE +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/README.md +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/__init__.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/blender/__main__.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/blender/render.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/colmap.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/dynamic3dgs/__main__.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/dynamic3dgs/camera_meta.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/dynamic3dgs/link_frames.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/dynamic3dgs/points.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/extract_videos.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/n3dv/__main__.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/n3dv/extract_videos.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/n3dv/poses_bounds.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/stnerf/__init__.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/utils/__init__.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/utils/colmap.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/utils/ffmpeg.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/utils/rotation.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/write_model.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap/write_pcd.py +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap.egg-info/dependency_links.txt +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap.egg-info/requires.txt +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/nvs2colmap.egg-info/top_level.txt +0 -0
- {nvs2colmap-0.5.1 → nvs2colmap-0.5.2}/setup.cfg +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Sample cameras on a spherical cap and write poses_bounds.npy."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def cap_direction(axis, cap_angle, rng):
|
|
11
|
+
z = rng.uniform(np.cos(cap_angle), 1.0)
|
|
12
|
+
phi = rng.uniform(0, 2 * np.pi)
|
|
13
|
+
xy = np.sqrt(1 - z * z)
|
|
14
|
+
local = np.array([xy * np.cos(phi), xy * np.sin(phi), z])
|
|
15
|
+
helper = np.array([0.0, 1.0, 0.0]) if abs(axis[2]) > 0.999 else np.array([0.0, 0.0, 1.0])
|
|
16
|
+
tangent = np.cross(helper, axis)
|
|
17
|
+
tangent /= np.linalg.norm(tangent)
|
|
18
|
+
bitangent = np.cross(axis, tangent)
|
|
19
|
+
return tangent * local[0] + bitangent * local[1] + axis * local[2]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main() -> None:
|
|
23
|
+
parser = argparse.ArgumentParser()
|
|
24
|
+
parser.add_argument("--output", type=Path, required=True)
|
|
25
|
+
parser.add_argument("--n-cameras", type=int, required=True)
|
|
26
|
+
parser.add_argument("--height", type=int, required=True)
|
|
27
|
+
parser.add_argument("--width", type=int, required=True)
|
|
28
|
+
parser.add_argument("--focal", type=float, required=True, help="Focal length in pixels. fx = fy, principal point at the image center.")
|
|
29
|
+
parser.add_argument("--center", type=float, nargs=3, required=True, help="Shell center and look-at point.")
|
|
30
|
+
parser.add_argument("--radius-min", type=float, default=10.0)
|
|
31
|
+
parser.add_argument("--radius-max", type=float, default=12.0)
|
|
32
|
+
parser.add_argument("--azimuth", type=float, default=0.0, help="Cap axis azimuth in degrees.")
|
|
33
|
+
parser.add_argument("--elevation", type=float, default=90.0, help="Cap axis elevation in degrees. 90 looks straight up.")
|
|
34
|
+
parser.add_argument("--cap-angle", type=float, default=90.0, help="Cap half-angle in degrees. 90 is a hemisphere; smaller clusters cameras on one side.")
|
|
35
|
+
parser.add_argument("--min-angle", type=float, default=10.0, help="Minimum angle in degrees between a camera and its nearest neighbor.")
|
|
36
|
+
parser.add_argument("--max-angle", type=float, default=45.0, help="Maximum angle in degrees between a camera and its nearest neighbor.")
|
|
37
|
+
argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else sys.argv[1:]
|
|
38
|
+
args = parser.parse_args(argv)
|
|
39
|
+
|
|
40
|
+
azimuth = np.deg2rad(args.azimuth)
|
|
41
|
+
elevation = np.deg2rad(args.elevation)
|
|
42
|
+
axis = np.array([
|
|
43
|
+
np.cos(elevation) * np.cos(azimuth),
|
|
44
|
+
np.cos(elevation) * np.sin(azimuth),
|
|
45
|
+
np.sin(elevation),
|
|
46
|
+
])
|
|
47
|
+
cap_angle = np.deg2rad(args.cap_angle)
|
|
48
|
+
min_angle = np.deg2rad(args.min_angle)
|
|
49
|
+
max_angle = np.deg2rad(args.max_angle)
|
|
50
|
+
center = np.array(args.center)
|
|
51
|
+
rng = np.random.default_rng()
|
|
52
|
+
|
|
53
|
+
directions = []
|
|
54
|
+
radii = []
|
|
55
|
+
tries = 0
|
|
56
|
+
while len(directions) < args.n_cameras:
|
|
57
|
+
tries += 1
|
|
58
|
+
if tries > 100:
|
|
59
|
+
raise RuntimeError("Could not place that many cameras on this cap.")
|
|
60
|
+
direction = cap_direction(axis, cap_angle, rng)
|
|
61
|
+
if directions:
|
|
62
|
+
nearest = np.arccos(np.clip(np.stack(directions) @ direction, -1.0, 1.0)).min()
|
|
63
|
+
if nearest < min_angle or nearest > max_angle:
|
|
64
|
+
continue
|
|
65
|
+
directions.append(direction)
|
|
66
|
+
radii.append(rng.uniform(args.radius_min, args.radius_max))
|
|
67
|
+
|
|
68
|
+
rows = []
|
|
69
|
+
for direction, radius in zip(directions, radii):
|
|
70
|
+
position = center + direction * radius
|
|
71
|
+
forward = center - position
|
|
72
|
+
forward /= np.linalg.norm(forward)
|
|
73
|
+
up = np.array([0.0, 0.0, 1.0])
|
|
74
|
+
right = np.cross(forward, up)
|
|
75
|
+
if np.dot(right, right) == 0:
|
|
76
|
+
up = np.array([0.0, 1.0, 0.0])
|
|
77
|
+
right = np.cross(forward, up)
|
|
78
|
+
right /= np.linalg.norm(right)
|
|
79
|
+
down = np.cross(forward, right)
|
|
80
|
+
c2w = np.eye(4)
|
|
81
|
+
c2w[:3, 0] = right
|
|
82
|
+
c2w[:3, 1] = down
|
|
83
|
+
c2w[:3, 2] = forward
|
|
84
|
+
c2w[:3, 3] = position
|
|
85
|
+
# Inverse of read_camera_meta_n3dv: poses_bounds columns are [down, right, back, position, hwf].
|
|
86
|
+
pose = np.zeros((3, 5))
|
|
87
|
+
pose[:3, 0] = c2w[:3, 1]
|
|
88
|
+
pose[:3, 1] = c2w[:3, 0]
|
|
89
|
+
pose[:3, 2] = -c2w[:3, 2]
|
|
90
|
+
pose[:3, 3] = c2w[:3, 3]
|
|
91
|
+
pose[:, 4] = (args.height, args.width, args.focal)
|
|
92
|
+
rows.append(np.concatenate([pose.reshape(-1), [radius * 0.1, radius * 2]]))
|
|
93
|
+
|
|
94
|
+
args.output.mkdir(parents=True, exist_ok=True)
|
|
95
|
+
path = args.output / "poses_bounds.npy"
|
|
96
|
+
np.save(path, np.stack(rows))
|
|
97
|
+
print(f"Done: {path}")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == "__main__":
|
|
101
|
+
main()
|
|
@@ -8,6 +8,7 @@ pyproject.toml
|
|
|
8
8
|
./nvs2colmap/write_pcd.py
|
|
9
9
|
./nvs2colmap/blender/__main__.py
|
|
10
10
|
./nvs2colmap/blender/render.py
|
|
11
|
+
./nvs2colmap/blender/sample_cameras.py
|
|
11
12
|
./nvs2colmap/dynamic3dgs/__main__.py
|
|
12
13
|
./nvs2colmap/dynamic3dgs/camera_meta.py
|
|
13
14
|
./nvs2colmap/dynamic3dgs/link_frames.py
|
|
@@ -33,6 +34,7 @@ nvs2colmap.egg-info/requires.txt
|
|
|
33
34
|
nvs2colmap.egg-info/top_level.txt
|
|
34
35
|
nvs2colmap/blender/__main__.py
|
|
35
36
|
nvs2colmap/blender/render.py
|
|
37
|
+
nvs2colmap/blender/sample_cameras.py
|
|
36
38
|
nvs2colmap/dynamic3dgs/__main__.py
|
|
37
39
|
nvs2colmap/dynamic3dgs/camera_meta.py
|
|
38
40
|
nvs2colmap/dynamic3dgs/link_frames.py
|
|
@@ -3,3 +3,4 @@ nvs2colmap-blender = nvs2colmap.blender.__main__:main
|
|
|
3
3
|
nvs2colmap-dynamic3dgs = nvs2colmap.dynamic3dgs.__main__:main
|
|
4
4
|
nvs2colmap-extract-videos = nvs2colmap.extract_videos:main
|
|
5
5
|
nvs2colmap-n3dv = nvs2colmap.n3dv.__main__:main
|
|
6
|
+
nvs2colmap-sample-cameras = nvs2colmap.blender.sample_cameras:main
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nvs2colmap"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.2"
|
|
8
8
|
description = "Utilities for converting novel view synthesis datasets to COLMAP format."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Howard Yin", email = "yindaheng98@gmail.com" }]
|
|
@@ -46,6 +46,7 @@ nvs2colmap-extract-videos = "nvs2colmap.extract_videos:main"
|
|
|
46
46
|
nvs2colmap-n3dv = "nvs2colmap.n3dv.__main__:main"
|
|
47
47
|
nvs2colmap-dynamic3dgs = "nvs2colmap.dynamic3dgs.__main__:main"
|
|
48
48
|
nvs2colmap-blender = "nvs2colmap.blender.__main__:main"
|
|
49
|
+
nvs2colmap-sample-cameras = "nvs2colmap.blender.sample_cameras:main"
|
|
49
50
|
|
|
50
51
|
[tool.setuptools]
|
|
51
52
|
packages = { find = { include = ["nvs2colmap*"] } }
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|