Skip to content

Commit 26804f8

Browse files
Jing1LingJing
and
Jing
authored
Make colmapDataParser compatible with 360_v2 dataset format (nerfstudio-project#2860)
* added an option to colmapdataparser to round up the image size when downscaling * add round mode and update ffmpeg command * [fix] wrong variable order * update format --------- Co-authored-by: Jing <[email protected]>
1 parent 911091c commit 26804f8

File tree

2 files changed

+53
-8
lines changed

2 files changed

+53
-8
lines changed

nerfstudio/cameras/cameras.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -984,12 +984,15 @@ def get_intrinsics_matrices(self) -> Float[Tensor, "*num_cameras 3 3"]:
984984
return K
985985

986986
def rescale_output_resolution(
987-
self, scaling_factor: Union[Shaped[Tensor, "*num_cameras"], Shaped[Tensor, "*num_cameras 1"], float, int]
987+
self,
988+
scaling_factor: Union[Shaped[Tensor, "*num_cameras"], Shaped[Tensor, "*num_cameras 1"], float, int],
989+
scale_rounding_mode: str = "floor",
988990
) -> None:
989991
"""Rescale the output resolution of the cameras.
990992
991993
Args:
992994
scaling_factor: Scaling factor to apply to the output resolution.
995+
scale_rounding_mode: round down or round up when calculating the scaled image height and width
993996
"""
994997
if isinstance(scaling_factor, (float, int)):
995998
scaling_factor = torch.tensor([scaling_factor]).to(self.device).broadcast_to((self.cx.shape))
@@ -1006,5 +1009,14 @@ def rescale_output_resolution(
10061009
self.fy = self.fy * scaling_factor
10071010
self.cx = self.cx * scaling_factor
10081011
self.cy = self.cy * scaling_factor
1009-
self.height = (self.height * scaling_factor).to(torch.int64)
1010-
self.width = (self.width * scaling_factor).to(torch.int64)
1012+
if scale_rounding_mode == "floor":
1013+
self.height = (self.height * scaling_factor).to(torch.int64)
1014+
self.width = (self.width * scaling_factor).to(torch.int64)
1015+
elif scale_rounding_mode == "round":
1016+
self.height = torch.floor(0.5 + (self.height * scaling_factor)).to(torch.int64)
1017+
self.width = torch.floor(0.5 + (self.width * scaling_factor)).to(torch.int64)
1018+
elif scale_rounding_mode == "ceil":
1019+
self.height = torch.ceil(self.height * scaling_factor).to(torch.int64)
1020+
self.width = torch.ceil(self.width * scaling_factor).to(torch.int64)
1021+
else:
1022+
raise ValueError("Scale rounding mode must be 'floor', 'round' or 'ceil'.")

nerfstudio/data/dataparsers/colmap_dataparser.py

+38-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from __future__ import annotations
1717

18+
import math
1819
import sys
1920
from dataclasses import dataclass, field
2021
from functools import partial
@@ -56,6 +57,8 @@ class ColmapDataParserConfig(DataParserConfig):
5657
"""How much to scale the camera origins by."""
5758
downscale_factor: Optional[int] = None
5859
"""How much to downscale images. If not set, images are chosen such that the max dimension is <1600px."""
60+
downscale_rounding_mode: Literal["floor", "round", "ceil"] = "floor"
61+
"""How to round downscale image height and Image width."""
5962
scene_scale: float = 1.0
6063
"""How much to scale the region of interest by."""
6164
orientation_method: Literal["pca", "up", "vertical", "none"] = "up"
@@ -355,7 +358,9 @@ def _generate_dataparser_outputs(self, split: str = "train", **kwargs):
355358
camera_type=camera_type,
356359
)
357360

358-
cameras.rescale_output_resolution(scaling_factor=1.0 / downscale_factor)
361+
cameras.rescale_output_resolution(
362+
scaling_factor=1.0 / downscale_factor, scale_rounding_mode=self.config.downscale_rounding_mode
363+
)
359364

360365
if "applied_transform" in meta:
361366
applied_transform = torch.tensor(meta["applied_transform"], dtype=transform_matrix.dtype)
@@ -452,18 +457,39 @@ def _load_3D_points(self, colmap_path: Path, transform_matrix: torch.Tensor, sca
452457
out["points3D_points2D_xy"] = torch.stack(points3D_image_xy, dim=0)
453458
return out
454459

455-
def _downscale_images(self, paths, get_fname, downscale_factor: int, nearest_neighbor: bool = False):
460+
def _downscale_images(
461+
self,
462+
paths,
463+
get_fname,
464+
downscale_factor: int,
465+
downscale_rounding_mode: str = "floor",
466+
nearest_neighbor: bool = False,
467+
):
468+
def calculate_scaled_size(original_width, original_height, downscale_factor, mode="floor"):
469+
if mode == "floor":
470+
return math.floor(original_width / downscale_factor), math.floor(original_height / downscale_factor)
471+
elif mode == "round":
472+
return round(original_width / downscale_factor), round(original_height / downscale_factor)
473+
elif mode == "ceil":
474+
return math.ceil(original_width / downscale_factor), math.ceil(original_height / downscale_factor)
475+
else:
476+
raise ValueError("Invalid mode. Choose from 'floor', 'round', or 'ceil'.")
477+
456478
with status(msg="[bold yellow]Downscaling images...", spinner="growVertical"):
457479
assert downscale_factor > 1
458480
assert isinstance(downscale_factor, int)
481+
filepath = next(iter(paths))
482+
img = Image.open(filepath)
483+
w, h = img.size
484+
w_scaled, h_scaled = calculate_scaled_size(w, h, downscale_factor, downscale_rounding_mode)
459485
# Using %05d ffmpeg commands appears to be unreliable (skips images).
460486
for path in paths:
461487
nn_flag = "" if not nearest_neighbor else ":flags=neighbor"
462488
path_out = get_fname(path)
463489
path_out.parent.mkdir(parents=True, exist_ok=True)
464490
ffmpeg_cmd = [
465491
f'ffmpeg -y -noautorotate -i "{path}" ',
466-
f"-q:v 2 -vf scale=iw/{downscale_factor}:ih/{downscale_factor}{nn_flag} ",
492+
f"-q:v 2 -vf scale={w_scaled}:{h_scaled}{nn_flag} ",
467493
f'"{path_out}"',
468494
]
469495
ffmpeg_cmd = " ".join(ffmpeg_cmd)
@@ -488,7 +514,7 @@ def get_fname(parent: Path, filepath: Path) -> Path:
488514
if self._downscale_factor is None:
489515
if self.config.downscale_factor is None:
490516
test_img = Image.open(filepath)
491-
h, w = test_img.size
517+
w, h = test_img.size
492518
max_res = max(h, w)
493519
df = 0
494520
while True:
@@ -508,12 +534,17 @@ def get_fname(parent: Path, filepath: Path) -> Path:
508534
CONSOLE.print(
509535
f"[bold red]Downscaled images do not exist for factor of {self._downscale_factor}.[/bold red]"
510536
)
511-
if Confirm.ask("\nWould you like to downscale the images now?", default=False, console=CONSOLE):
537+
if Confirm.ask(
538+
f"\nWould you like to downscale the images using '{self.config.downscale_rounding_mode}' rounding mode now?",
539+
default=False,
540+
console=CONSOLE,
541+
):
512542
# Install the method
513543
self._downscale_images(
514544
image_filenames,
515545
partial(get_fname, self.config.data / self.config.images_path),
516546
self._downscale_factor,
547+
self.config.downscale_rounding_mode,
517548
nearest_neighbor=False,
518549
)
519550
if len(mask_filenames) > 0:
@@ -522,6 +553,7 @@ def get_fname(parent: Path, filepath: Path) -> Path:
522553
mask_filenames,
523554
partial(get_fname, self.config.data / self.config.masks_path),
524555
self._downscale_factor,
556+
self.config.downscale_rounding_mode,
525557
nearest_neighbor=True,
526558
)
527559
if len(depth_filenames) > 0:
@@ -530,6 +562,7 @@ def get_fname(parent: Path, filepath: Path) -> Path:
530562
depth_filenames,
531563
partial(get_fname, self.config.data / self.config.depths_path),
532564
self._downscale_factor,
565+
self.config.downscale_rounding_mode,
533566
nearest_neighbor=True,
534567
)
535568
else:

0 commit comments

Comments
 (0)