diff --git a/docs/quickstart/custom_dataset.md b/docs/quickstart/custom_dataset.md index 6444ca4456..7c9556008c 100644 --- a/docs/quickstart/custom_dataset.md +++ b/docs/quickstart/custom_dataset.md @@ -141,6 +141,29 @@ cd vcpkg (polycam)= +### Installing GLOMAP + +GLOMAP is a global structure-from-motion method that is up to 100x faster than COLMAP. It is highly recommended if you would like to extract poses from video. The best way to install GLOMAP is through the official instructions, which have been copied below: + +::::::{tab-set} +:::::{tab-item} Linux + +Clone the glomap repository and build GLOMAP: + +``` +git clone https://github.com/colmap/glomap.git +mkdir build +cd build +cmake .. -GNinja +ninja && sudo ninja install +``` + +Check that GLOMAP is successfully installed: + +``` +glomap -h +``` + ## Polycam Capture Nerfstudio can also be trained directly from captures from the [Polycam app](https://poly.cam//). This avoids the need to use COLMAP. Polycam's poses are globally optimized which make them more robust to drift (an issue with ARKit or SLAM methods). diff --git a/nerfstudio/process_data/colmap_converter_to_nerfstudio_dataset.py b/nerfstudio/process_data/colmap_converter_to_nerfstudio_dataset.py index ec82f0ad7f..98c40c3029 100644 --- a/nerfstudio/process_data/colmap_converter_to_nerfstudio_dataset.py +++ b/nerfstudio/process_data/colmap_converter_to_nerfstudio_dataset.py @@ -35,7 +35,7 @@ class ColmapConverterToNerfstudioDataset(BaseConverterToNerfstudioDataset): """Feature matching method to use. Vocab tree is recommended for a balance of speed and accuracy. Exhaustive is slower but more accurate. Sequential is faster but should only be used for videos.""" - sfm_tool: Literal["any", "colmap", "hloc"] = "any" + sfm_tool: Literal["any", "colmap", "glomap", "hloc"] = "any" """Structure from motion tool to use. Colmap will use sift features, hloc can use many modern methods such as superpoint features and superglue matcher""" refine_pixsfm: bool = False @@ -222,6 +222,19 @@ def _run_colmap(self, mask_path: Optional[Path] = None): refine_intrinsics=self.refine_intrinsics, colmap_cmd=self.colmap_cmd, ) + elif sfm_tool == "glomap": + colmap_utils.run_colmap( + image_dir=image_dir, + colmap_dir=self.absolute_colmap_path, + camera_model=CAMERA_MODELS[self.camera_type], + camera_mask_path=mask_path, + gpu=self.gpu, + verbose=self.verbose, + matching_method=self.matching_method, + refine_intrinsics=self.refine_intrinsics, + colmap_cmd=self.colmap_cmd, + glomap_toggle=True, + ) elif sfm_tool == "hloc": if mask_path is not None: raise RuntimeError("Cannot use a mask with hloc. Please remove the cropping options " "and try again.") diff --git a/nerfstudio/process_data/colmap_utils.py b/nerfstudio/process_data/colmap_utils.py index 1d9405c81a..619580b4a3 100644 --- a/nerfstudio/process_data/colmap_utils.py +++ b/nerfstudio/process_data/colmap_utils.py @@ -99,6 +99,7 @@ def run_colmap( matching_method: Literal["vocab_tree", "exhaustive", "sequential"] = "vocab_tree", refine_intrinsics: bool = True, colmap_cmd: str = "colmap", + glomap_toggle: bool = False, ) -> None: """Runs COLMAP on the images. @@ -154,23 +155,26 @@ def run_colmap( sparse_dir = colmap_dir / "sparse" sparse_dir.mkdir(parents=True, exist_ok=True) mapper_cmd = [ - f"{colmap_cmd} mapper", + f"{'glomap' if glomap_toggle else colmap_cmd} mapper", f"--database_path {colmap_dir / 'database.db'}", f"--image_path {image_dir}", f"--output_path {sparse_dir}", ] - if colmap_version >= Version("3.7"): + if colmap_version >= Version("3.7") and not glomap_toggle: mapper_cmd.append("--Mapper.ba_global_function_tolerance=1e-6") - + if glomap_toggle: + mapper_cmd.append("--TrackEstablishment.max_num_tracks 5000") mapper_cmd = " ".join(mapper_cmd) with status( - msg="[bold yellow]Running COLMAP bundle adjustment... (This may take a while)", + msg="[bold yellow]Running GLOMAP bundle adjustment..." + if glomap_toggle + else "[bold yellow]Running COLMAP bundle adjustment... (This may take a while)", spinner="circle", verbose=verbose, ): run_command(mapper_cmd, verbose=verbose) - CONSOLE.log("[bold green]:tada: Done COLMAP bundle adjustment.") + CONSOLE.log(f"[bold green]:tada: Done {'GLOMAP' if glomap_toggle else 'COLMAP'} bundle adjustment.") if refine_intrinsics: with status(msg="[bold yellow]Refine intrinsics...", spinner="dqpb", verbose=verbose): diff --git a/nerfstudio/process_data/process_data_utils.py b/nerfstudio/process_data/process_data_utils.py index 3c9013abe3..77795aff5b 100644 --- a/nerfstudio/process_data/process_data_utils.py +++ b/nerfstudio/process_data/process_data_utils.py @@ -490,7 +490,7 @@ def downscale_images( def find_tool_feature_matcher_combination( - sfm_tool: Literal["any", "colmap", "hloc"], + sfm_tool: Literal["any", "colmap", "glomap", "hloc"], feature_type: Literal[ "any", "sift", @@ -518,7 +518,7 @@ def find_tool_feature_matcher_combination( ) -> Union[ Tuple[None, None, None], Tuple[ - Literal["colmap", "hloc"], + Literal["colmap", "glomap", "hloc"], Literal[ "sift", "superpoint_aachen", @@ -564,6 +564,12 @@ def find_tool_feature_matcher_combination( if (feature_type not in ("any", "sift")) or (matcher_type not in ("any", "NN")): return (None, None, None) return ("colmap", "sift", "NN") + + if sfm_tool == "glomap": + if (feature_type not in ("any", "sift")) or (matcher_type not in ("any", "NN")): + return (None, None, None) + return ("glomap", "sift", "NN") + if sfm_tool == "hloc": if feature_type in ("any", "superpoint"): feature_type = "superpoint_aachen"