#!/usr/bin/env python3 # Copyright (C) 2024-present Naver Corporation. All rights reserved. # Licensed under CC BY-NC-SA 4.0 (non-commercial use only). # # -------------------------------------------------------- # mast3r exec for running standard sfm # -------------------------------------------------------- import pycolmap import os import os.path as path import argparse from mast3r.model import AsymmetricMASt3R from mast3r.colmap.mapping import (kapture_import_image_folder_or_list, run_mast3r_matching, pycolmap_run_triangulator, pycolmap_run_mapper, glomap_run_mapper) from kapture.io.csv import kapture_from_dir from kapture.converter.colmap.database_extra import kapture_to_colmap, generate_priors_for_reconstruction from kapture_localization.utils.pairsfile import get_pairs_from_file from kapture.io.records import get_image_fullpath from kapture.converter.colmap.database import COLMAPDatabase def get_argparser(): parser = argparse.ArgumentParser(description='point triangulator with mast3r from kapture data') parser_weights = parser.add_mutually_exclusive_group(required=True) parser_weights.add_argument("--weights", type=str, help="path to the model weights", default=None) parser_weights.add_argument("--model_name", type=str, help="name of the model weights", choices=["MASt3R_ViTLarge_BaseDecoder_512_catmlpdpt_metric"]) parser_input = parser.add_mutually_exclusive_group(required=True) parser_input.add_argument('-i', '--input', default=None, help='kdata') parser_input.add_argument('--dir', default=None, help='image dir (individual intrinsics)') parser_input.add_argument('--dir_same_camera', default=None, help='image dir (shared intrinsics)') parser.add_argument('-o', '--output', required=True, help='output path to reconstruction') parser.add_argument('--pairsfile_path', required=True, help='pairsfile') parser.add_argument('--glomap_bin', default='glomap', type=str, help='glomap bin') parser_mapper = parser.add_mutually_exclusive_group() parser_mapper.add_argument('--ignore_pose', action='store_true', default=False) parser_mapper.add_argument('--use_glomap_mapper', action='store_true', default=False) parser_matching = parser.add_mutually_exclusive_group() parser_matching.add_argument('--dense_matching', action='store_true', default=False) parser_matching.add_argument('--pixel_tol', default=0, type=int) parser.add_argument('--device', default='cuda') parser.add_argument('--conf_thr', default=1.001, type=float) parser.add_argument('--skip_geometric_verification', action='store_true', default=False) parser.add_argument('--min_len_track', default=5, type=int) return parser if __name__ == '__main__': parser = get_argparser() args = parser.parse_args() if args.weights is not None: weights_path = args.weights else: weights_path = "naver/" + args.model_name model = AsymmetricMASt3R.from_pretrained(weights_path).to(args.device) maxdim = max(model.patch_embed.img_size) patch_size = model.patch_embed.patch_size if args.input is not None: kdata = kapture_from_dir(args.input) records_data_path = get_image_fullpath(args.input) else: if args.dir_same_camera is not None: use_single_camera = True records_data_path = args.dir_same_camera elif args.dir is not None: use_single_camera = False records_data_path = args.dir else: raise ValueError('all inputs choices are None') kdata = kapture_import_image_folder_or_list(records_data_path, use_single_camera) has_pose = kdata.trajectories is not None image_pairs = get_pairs_from_file(args.pairsfile_path, kdata.records_camera, kdata.records_camera) colmap_db_path = path.join(args.output, 'colmap.db') reconstruction_path = path.join(args.output, "reconstruction") priors_txt_path = path.join(args.output, "priors_for_reconstruction") for path_i in [reconstruction_path, priors_txt_path]: os.makedirs(path_i, exist_ok=True) assert not os.path.isfile(colmap_db_path) colmap_db = COLMAPDatabase.connect(colmap_db_path) try: kapture_to_colmap(kdata, args.input, tar_handler=None, database=colmap_db, keypoints_type=None, descriptors_type=None, export_two_view_geometry=False) if has_pose: generate_priors_for_reconstruction(kdata, colmap_db, priors_txt_path) colmap_image_pairs = run_mast3r_matching(model, maxdim, patch_size, args.device, kdata, records_data_path, image_pairs, colmap_db, args.dense_matching, args.pixel_tol, args.conf_thr, args.skip_geometric_verification, args.min_len_track) colmap_db.close() except Exception as e: print(f'Error {e}') colmap_db.close() exit(1) if len(colmap_image_pairs) == 0: raise Exception("no matches were kept") # colmap db is now full, run colmap colmap_world_to_cam = {} if not args.skip_geometric_verification: print("verify_matches") f = open(args.output + '/pairs.txt', "w") for image_path1, image_path2 in colmap_image_pairs: f.write("{} {}\n".format(image_path1, image_path2)) f.close() pycolmap.verify_matches(colmap_db_path, args.output + '/pairs.txt') print("running mapping") if has_pose and not args.ignore_pose and not args.use_glomap_mapper: pycolmap_run_triangulator(colmap_db_path, priors_txt_path, reconstruction_path, records_data_path) elif not args.use_glomap_mapper: pycolmap_run_mapper(colmap_db_path, reconstruction_path, records_data_path) else: glomap_run_mapper(args.glomap_bin, colmap_db_path, reconstruction_path, records_data_path)