nvs2colmap 0.1.0__tar.gz → 0.2.0__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.1.0 → nvs2colmap-0.2.0}/PKG-INFO +15 -2
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/README.md +14 -1
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/n3dv/__main__.py +17 -1
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/n3dv/extract_videos.py +16 -12
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/utils/__init__.py +2 -1
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/utils/ffmpeg.py +40 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap.egg-info/PKG-INFO +15 -2
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/pyproject.toml +1 -1
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/LICENSE +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/__init__.py +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/colmap.py +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/dynamic3dgs/__init__.py +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/n3dv/poses_bounds.py +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/stnerf/__init__.py +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/utils/colmap.py +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/utils/rotation.py +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/write_model.py +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap.egg-info/SOURCES.txt +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap.egg-info/dependency_links.txt +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap.egg-info/entry_points.txt +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap.egg-info/requires.txt +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap.egg-info/top_level.txt +0 -0
- {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nvs2colmap
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Utilities for converting novel view synthesis datasets to COLMAP format.
|
|
5
5
|
Author-email: Howard Yin <yindaheng98@gmail.com>
|
|
6
6
|
Maintainer-email: Howard Yin <yindaheng98@gmail.com>
|
|
@@ -72,6 +72,17 @@ python -m nvs2colmap.n3dv \
|
|
|
72
72
|
--n-frames 300
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
Start from source frame 10 and keep output frame numbering aligned with it:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
python -m nvs2colmap.n3dv \
|
|
79
|
+
--path data/coffee_martini \
|
|
80
|
+
--ffmpeg ffmpeg \
|
|
81
|
+
--ffprobe ffprobe \
|
|
82
|
+
--start-number 10 \
|
|
83
|
+
--n-frames 300
|
|
84
|
+
```
|
|
85
|
+
|
|
75
86
|
Run the full COLMAP pipeline for each frame:
|
|
76
87
|
|
|
77
88
|
```bash
|
|
@@ -89,4 +100,6 @@ python -m nvs2colmap.n3dv \
|
|
|
89
100
|
By default, decoded frames are written to `frame*/images`, and the command also
|
|
90
101
|
writes `frame*/sparse/0` text models. With `--use-colmap`, decoded frames are
|
|
91
102
|
written to `frame*/input`, and each frame additionally gets the standard
|
|
92
|
-
COLMAP outputs such as `distorted/`, `images/`, `sparse/`, and `stereo/`.
|
|
103
|
+
COLMAP outputs such as `distorted/`, `images/`, `sparse/`, and `stereo/`. When
|
|
104
|
+
`--start-number N` is provided, decoding starts from source video frame `N`, and
|
|
105
|
+
the generated folders/images are also numbered from `N`.
|
|
@@ -44,6 +44,17 @@ python -m nvs2colmap.n3dv \
|
|
|
44
44
|
--n-frames 300
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
Start from source frame 10 and keep output frame numbering aligned with it:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
python -m nvs2colmap.n3dv \
|
|
51
|
+
--path data/coffee_martini \
|
|
52
|
+
--ffmpeg ffmpeg \
|
|
53
|
+
--ffprobe ffprobe \
|
|
54
|
+
--start-number 10 \
|
|
55
|
+
--n-frames 300
|
|
56
|
+
```
|
|
57
|
+
|
|
47
58
|
Run the full COLMAP pipeline for each frame:
|
|
48
59
|
|
|
49
60
|
```bash
|
|
@@ -61,4 +72,6 @@ python -m nvs2colmap.n3dv \
|
|
|
61
72
|
By default, decoded frames are written to `frame*/images`, and the command also
|
|
62
73
|
writes `frame*/sparse/0` text models. With `--use-colmap`, decoded frames are
|
|
63
74
|
written to `frame*/input`, and each frame additionally gets the standard
|
|
64
|
-
COLMAP outputs such as `distorted/`, `images/`, `sparse/`, and `stereo/`.
|
|
75
|
+
COLMAP outputs such as `distorted/`, `images/`, `sparse/`, and `stereo/`. When
|
|
76
|
+
`--start-number N` is provided, decoding starts from source video frame `N`, and
|
|
77
|
+
the generated folders/images are also numbered from `N`.
|
|
@@ -30,6 +30,12 @@ def parse_args() -> argparse.Namespace:
|
|
|
30
30
|
type=int,
|
|
31
31
|
help="Number of frames to extract and write. Defaults to the video frame count when extracting.",
|
|
32
32
|
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--start-number",
|
|
35
|
+
type=int,
|
|
36
|
+
default=1,
|
|
37
|
+
help="1-based source frame to start extracting from; output frame folders and image files use the same starting number.",
|
|
38
|
+
)
|
|
33
39
|
parser.add_argument(
|
|
34
40
|
"--ffmpeg",
|
|
35
41
|
dest="ffmpeg_executable",
|
|
@@ -42,6 +48,12 @@ def parse_args() -> argparse.Namespace:
|
|
|
42
48
|
default="ffprobe",
|
|
43
49
|
help="ffprobe executable.",
|
|
44
50
|
)
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"--ffmpeg-processes",
|
|
53
|
+
type=int,
|
|
54
|
+
default=1,
|
|
55
|
+
help="Number of ffmpeg processes to run in parallel while extracting videos.",
|
|
56
|
+
)
|
|
45
57
|
parser.add_argument(
|
|
46
58
|
"--image-extension",
|
|
47
59
|
default=".png",
|
|
@@ -93,20 +105,23 @@ def main() -> None:
|
|
|
93
105
|
folder=folder,
|
|
94
106
|
output_pattern=frame_output_pattern / image_dir_name,
|
|
95
107
|
cameras=cameras,
|
|
108
|
+
start_number=args.start_number,
|
|
96
109
|
n_frames=n_frames,
|
|
97
110
|
ffmpeg_executable=args.ffmpeg_executable,
|
|
98
111
|
ffprobe_executable=args.ffprobe_executable,
|
|
112
|
+
ffmpeg_processes=args.ffmpeg_processes,
|
|
99
113
|
video_extension=args.video_extension,
|
|
100
114
|
image_extension=args.image_extension,
|
|
101
115
|
)
|
|
102
116
|
elif n_frames is None:
|
|
103
|
-
n_frames = count_frame_dirs(frame_output_pattern)
|
|
117
|
+
n_frames = count_frame_dirs(frame_output_pattern, start_number=args.start_number)
|
|
104
118
|
|
|
105
119
|
if not args.use_colmap:
|
|
106
120
|
write_video_colmap_text_model(
|
|
107
121
|
output_pattern=frame_output_pattern / "sparse" / "0",
|
|
108
122
|
cameras=cameras,
|
|
109
123
|
n_frames=n_frames,
|
|
124
|
+
start_number=args.start_number,
|
|
110
125
|
image_extension=args.image_extension,
|
|
111
126
|
)
|
|
112
127
|
else:
|
|
@@ -114,6 +129,7 @@ def main() -> None:
|
|
|
114
129
|
output_pattern=frame_output_pattern,
|
|
115
130
|
cameras=cameras,
|
|
116
131
|
n_frames=n_frames,
|
|
132
|
+
start_number=args.start_number,
|
|
117
133
|
image_extension=args.image_extension,
|
|
118
134
|
colmap_executable=args.colmap_executable,
|
|
119
135
|
use_gpu=args.colmap_use_gpu,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from nvs2colmap.write_model import CameraModel
|
|
6
|
-
from nvs2colmap.utils import
|
|
6
|
+
from nvs2colmap.utils import extract_video_frames_parallel
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def count_frame_dirs(output_pattern: Path, start_number: int = 1) -> int:
|
|
@@ -21,29 +21,33 @@ def extract_videos(
|
|
|
21
21
|
folder: Path,
|
|
22
22
|
output_pattern: Path,
|
|
23
23
|
cameras: list[CameraModel],
|
|
24
|
+
start_number: int = 1,
|
|
24
25
|
n_frames: int | None = None,
|
|
25
26
|
ffmpeg_executable: str = "ffmpeg",
|
|
26
27
|
ffprobe_executable: str = "ffprobe",
|
|
28
|
+
ffmpeg_processes: int = 1,
|
|
27
29
|
video_extension: str = ".mp4",
|
|
28
30
|
image_extension: str = ".png",
|
|
29
31
|
) -> int:
|
|
30
32
|
if not video_extension.startswith("."):
|
|
31
33
|
video_extension = f".{video_extension}"
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
jobs = []
|
|
34
36
|
for camera in cameras:
|
|
35
37
|
video_path = folder / f"{camera.name}{video_extension}"
|
|
36
38
|
if not video_path.is_file():
|
|
37
39
|
raise FileNotFoundError(f"Missing camera video: {video_path}")
|
|
38
40
|
camera_output_pattern = output_pattern / f"{camera.name}{image_extension}"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
output_pattern=str(camera_output_pattern),
|
|
42
|
-
n_frames=n_frames,
|
|
43
|
-
ffmpeg_executable=ffmpeg_executable,
|
|
44
|
-
ffprobe_executable=ffprobe_executable,
|
|
45
|
-
)
|
|
46
|
-
extracted_n_frames = max(extracted_n_frames, camera_n_frames)
|
|
47
|
-
if extracted_n_frames == 0:
|
|
41
|
+
jobs.append((video_path, str(camera_output_pattern)))
|
|
42
|
+
if not jobs:
|
|
48
43
|
raise ValueError("No cameras to extract.")
|
|
49
|
-
|
|
44
|
+
|
|
45
|
+
extracted_frame_counts = extract_video_frames_parallel(
|
|
46
|
+
jobs,
|
|
47
|
+
start_number=start_number,
|
|
48
|
+
n_frames=n_frames,
|
|
49
|
+
ffmpeg_executable=ffmpeg_executable,
|
|
50
|
+
ffprobe_executable=ffprobe_executable,
|
|
51
|
+
process_count=ffmpeg_processes,
|
|
52
|
+
)
|
|
53
|
+
return max(extracted_frame_counts)
|
|
@@ -11,7 +11,7 @@ from .colmap import (
|
|
|
11
11
|
point_triangulator,
|
|
12
12
|
read_db,
|
|
13
13
|
)
|
|
14
|
-
from .ffmpeg import count_video_frames, extract_video_frames
|
|
14
|
+
from .ffmpeg import count_video_frames, extract_video_frames, extract_video_frames_parallel
|
|
15
15
|
from .rotation import matrix_to_quaternion, standardize_quaternion
|
|
16
16
|
|
|
17
17
|
__all__ = [
|
|
@@ -19,6 +19,7 @@ __all__ = [
|
|
|
19
19
|
"execute",
|
|
20
20
|
"exhaustive_matcher",
|
|
21
21
|
"extract_video_frames",
|
|
22
|
+
"extract_video_frames_parallel",
|
|
22
23
|
"feature_extractor",
|
|
23
24
|
"image_undistorter",
|
|
24
25
|
"mapper",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""ffmpeg helpers."""
|
|
2
2
|
|
|
3
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
3
4
|
import os
|
|
4
5
|
import subprocess
|
|
5
6
|
|
|
@@ -59,6 +60,8 @@ def extract_video_frames(
|
|
|
59
60
|
"error",
|
|
60
61
|
"-i",
|
|
61
62
|
str(video_path),
|
|
63
|
+
"-vf",
|
|
64
|
+
f"trim=start_frame={start_number - 1}",
|
|
62
65
|
"-frames:v", str(frame_count),
|
|
63
66
|
"-start_number", str(start_number),
|
|
64
67
|
str(output_pattern), "-y",
|
|
@@ -66,3 +69,40 @@ def extract_video_frames(
|
|
|
66
69
|
print(" ".join(str(part) for part in cmd))
|
|
67
70
|
subprocess.run(cmd, check=True)
|
|
68
71
|
return frame_count
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def extract_video_frames_task(args) -> int:
|
|
75
|
+
return extract_video_frames(*args)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def extract_video_frames_parallel(
|
|
79
|
+
jobs,
|
|
80
|
+
start_number: int = 1,
|
|
81
|
+
n_frames: int | None = None,
|
|
82
|
+
ffmpeg_executable: str = "ffmpeg",
|
|
83
|
+
ffprobe_executable: str = "ffprobe",
|
|
84
|
+
process_count: int = 1,
|
|
85
|
+
) -> list[int]:
|
|
86
|
+
"""Extract frames from multiple videos using multiple ffmpeg processes."""
|
|
87
|
+
assert process_count >= 1, f"process_count must be >= 1, got {process_count}."
|
|
88
|
+
|
|
89
|
+
tasks = [
|
|
90
|
+
(
|
|
91
|
+
video_path,
|
|
92
|
+
output_pattern,
|
|
93
|
+
start_number,
|
|
94
|
+
n_frames,
|
|
95
|
+
ffmpeg_executable,
|
|
96
|
+
ffprobe_executable,
|
|
97
|
+
)
|
|
98
|
+
for video_path, output_pattern in jobs
|
|
99
|
+
]
|
|
100
|
+
if process_count == 1:
|
|
101
|
+
return [extract_video_frames_task(task) for task in tasks]
|
|
102
|
+
|
|
103
|
+
if not tasks:
|
|
104
|
+
return []
|
|
105
|
+
|
|
106
|
+
max_workers = min(process_count, len(tasks))
|
|
107
|
+
with ProcessPoolExecutor(max_workers=max_workers) as executor:
|
|
108
|
+
return list(executor.map(extract_video_frames_task, tasks))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nvs2colmap
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Utilities for converting novel view synthesis datasets to COLMAP format.
|
|
5
5
|
Author-email: Howard Yin <yindaheng98@gmail.com>
|
|
6
6
|
Maintainer-email: Howard Yin <yindaheng98@gmail.com>
|
|
@@ -72,6 +72,17 @@ python -m nvs2colmap.n3dv \
|
|
|
72
72
|
--n-frames 300
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
Start from source frame 10 and keep output frame numbering aligned with it:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
python -m nvs2colmap.n3dv \
|
|
79
|
+
--path data/coffee_martini \
|
|
80
|
+
--ffmpeg ffmpeg \
|
|
81
|
+
--ffprobe ffprobe \
|
|
82
|
+
--start-number 10 \
|
|
83
|
+
--n-frames 300
|
|
84
|
+
```
|
|
85
|
+
|
|
75
86
|
Run the full COLMAP pipeline for each frame:
|
|
76
87
|
|
|
77
88
|
```bash
|
|
@@ -89,4 +100,6 @@ python -m nvs2colmap.n3dv \
|
|
|
89
100
|
By default, decoded frames are written to `frame*/images`, and the command also
|
|
90
101
|
writes `frame*/sparse/0` text models. With `--use-colmap`, decoded frames are
|
|
91
102
|
written to `frame*/input`, and each frame additionally gets the standard
|
|
92
|
-
COLMAP outputs such as `distorted/`, `images/`, `sparse/`, and `stereo/`.
|
|
103
|
+
COLMAP outputs such as `distorted/`, `images/`, `sparse/`, and `stereo/`. When
|
|
104
|
+
`--start-number N` is provided, decoding starts from source video frame `N`, and
|
|
105
|
+
the generated folders/images are also numbered from `N`.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nvs2colmap"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
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" }]
|
|
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
|