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.
Files changed (23) hide show
  1. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/PKG-INFO +15 -2
  2. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/README.md +14 -1
  3. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/n3dv/__main__.py +17 -1
  4. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/n3dv/extract_videos.py +16 -12
  5. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/utils/__init__.py +2 -1
  6. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/utils/ffmpeg.py +40 -0
  7. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap.egg-info/PKG-INFO +15 -2
  8. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/pyproject.toml +1 -1
  9. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/LICENSE +0 -0
  10. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/__init__.py +0 -0
  11. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/colmap.py +0 -0
  12. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/dynamic3dgs/__init__.py +0 -0
  13. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/n3dv/poses_bounds.py +0 -0
  14. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/stnerf/__init__.py +0 -0
  15. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/utils/colmap.py +0 -0
  16. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/utils/rotation.py +0 -0
  17. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap/write_model.py +0 -0
  18. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap.egg-info/SOURCES.txt +0 -0
  19. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap.egg-info/dependency_links.txt +0 -0
  20. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap.egg-info/entry_points.txt +0 -0
  21. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap.egg-info/requires.txt +0 -0
  22. {nvs2colmap-0.1.0 → nvs2colmap-0.2.0}/nvs2colmap.egg-info/top_level.txt +0 -0
  23. {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.1.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 extract_video_frames
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
- extracted_n_frames = 0
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
- camera_n_frames = extract_video_frames(
40
- video_path=video_path,
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
- return extracted_n_frames
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.1.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.1.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