Пример #1
0
def hloc_pipeline_from_kapture_dataset(
        kapture_path_map: str, kapture_path_query: str,
        pairsfile_path_map: str, pairsfile_path_query: str, output_dir: str,
        feature_conf_str: str, matcher_conf_str: str,
        covisibility_clustering: bool, bins_as_str: List[str],
        benchmark_format_style: BenchmarkFormatStyle, colmap_binary: str,
        python_binary: Optional[str], skip_list: List[str]) -> None:
    """
    run hloc on kapture data
    """
    feature_conf = extract_features.confs[feature_conf_str]
    matcher_conf = match_features.confs[matcher_conf_str]
    images_map = get_record_fullpath(kapture_path_map)
    images_query = get_record_fullpath(kapture_path_query)

    os.makedirs(output_dir, exist_ok=True)
    if "convert_pairsfile_map" not in skip_list:
        map_pairs_hloc = path.join(
            output_dir, 'pairfiles/db_pairs',
            path.basename(pairsfile_path_map) + "_hloc.txt")
        convert_pairs_to_hloc_format(pairsfile_path_map, map_pairs_hloc)
        pairsfile_path_map = map_pairs_hloc
    if "convert_pairsfile_query" not in skip_list:
        query_pairs_hloc = path.join(
            output_dir, 'pairfiles/query',
            path.basename(pairsfile_path_query) + "_hloc.txt")
        convert_pairs_to_hloc_format(pairsfile_path_query, query_pairs_hloc)
        pairsfile_path_query = query_pairs_hloc

    feature_path = Path(output_dir, feature_conf['output'] + '.h5')
    if "extract_features_map" not in skip_list:
        image_list_map_path = path.join(output_dir, 'image_list_map.txt')
        convert_kapture_to_hloc_image_list(kapture_path_map,
                                           image_list_map_path)
        feature_path_map = extract_features.main(
            feature_conf,
            Path(images_map),
            Path(output_dir),
            image_list=Path(image_list_map_path))
        assert feature_path_map.resolve() == feature_path.resolve()
    if "extract_features_query" not in skip_list:
        image_list_query_path = path.join(output_dir, 'image_list_query.txt')
        convert_kapture_to_hloc_image_list(kapture_path_query,
                                           image_list_query_path)
        feature_path_query = extract_features.main(
            feature_conf,
            Path(images_query),
            Path(output_dir),
            image_list=Path(image_list_query_path))
        assert feature_path_query.resolve() == feature_path.resolve()

    pairsfile_path_map_pathlib = Path(pairsfile_path_map)
    match_name_map = feature_conf['output'] + '_' + matcher_conf[
        "output"] + f'_{pairsfile_path_map_pathlib.stem}'
    map_match_path = Path(output_dir, match_name_map + '.h5')
    if 'match_map_pairs' not in skip_list:
        map_match_path_actual = match_features.main(
            matcher_conf, pairsfile_path_map_pathlib, feature_conf['output'],
            Path(output_dir))
        assert map_match_path_actual.resolve() == map_match_path.resolve()

    exported_mapping_path = path.join(output_dir,
                                      '3D-models/exported_from_kapture')
    if 'kapture_export_map_to_colmap' not in skip_list:
        export_colmap(kapture_path_map,
                      path.join(exported_mapping_path, 'colmap.db'),
                      exported_mapping_path,
                      force_overwrite_existing=True)
        # convert .txt to .bin
        run_model_converter(colmap_binary, exported_mapping_path,
                            exported_mapping_path, 'BIN')

    triangulate_path = path.join(
        output_dir, 'sfm_' + feature_conf_str + '_' + matcher_conf_str)
    if 'triangulate' not in skip_list:
        triangulation.main(Path(triangulate_path), Path(exported_mapping_path),
                           Path(images_map), pairsfile_path_map_pathlib,
                           feature_path, map_match_path, colmap_binary)

    pairsfile_path_query_pathlib = Path(pairsfile_path_query)
    match_name_query = feature_conf['output'] + '_' + matcher_conf[
        "output"] + f'_{pairsfile_path_query_pathlib.stem}'
    query_match_path = Path(output_dir, match_name_query + '.h5')
    if 'match_query_pairs' not in skip_list:
        query_match_path_actual = match_features.main(
            matcher_conf, pairsfile_path_query_pathlib, feature_conf['output'],
            Path(output_dir))
        assert query_match_path_actual.resolve() == query_match_path.resolve()

    query_as_txt = path.join(output_dir, 'image_list_with_intrinsics.txt')
    export_image_list(kapture_path_query, query_as_txt)
    results_file = path.join(
        output_dir, f'results_{feature_conf_str}_{matcher_conf_str}.txt')
    if 'localize' not in skip_list:
        localize_sfm.main(Path(triangulate_path),
                          Path(query_as_txt),
                          pairsfile_path_query_pathlib,
                          feature_path,
                          query_match_path,
                          Path(results_file),
                          covisibility_clustering=covisibility_clustering)

    results_full = path.join(
        output_dir,
        f'results_{feature_conf_str}_{matcher_conf_str}_fullnames.txt')
    results_kapture = path.join(
        output_dir, f'results_{feature_conf_str}_{matcher_conf_str}_kapture')
    if 'convert_results' not in skip_list:
        convert_results_format(query_as_txt, results_file, results_full)
        convert_results_to_kapture(kapture_path_query, results_full,
                                   results_kapture)
    if 'evaluate' not in skip_list and path.isfile(
            get_csv_fullpath(kapture.Trajectories, kapture_path_query)):
        local_evaluate_path = path.join(pipeline_import_paths.HERE_PATH,
                                        '../tools/kapture_evaluate.py')
        evaluate_args = [
            '-v',
            str(logger.level), '-i', results_kapture, '--labels',
            f'hloc_{feature_conf_str}_{matcher_conf_str}', '-gt',
            kapture_path_query, '-o',
            path.join(results_kapture, 'eval')
        ]
        evaluate_args += ['--bins'] + bins_as_str
        evaluate_args.append('-f')
        run_python_command(local_evaluate_path, evaluate_args, python_binary)

    LTVL2020_output_path = path.join(
        output_dir,
        f'results_{feature_conf_str}_{matcher_conf_str}_LTVL2020_style.txt')
    if 'export_LTVL2020' not in skip_list:
        export_LTVL2020_script_name, export_LTVL2020_args = get_benchmark_format_command(
            benchmark_format_style, results_kapture, LTVL2020_output_path,
            True, logger)
        local_export_LTVL2020_path = path.join(
            pipeline_import_paths.HERE_PATH,
            f'../../kapture/tools/{export_LTVL2020_script_name}')
        run_python_command(local_export_LTVL2020_path, export_LTVL2020_args,
                           python_binary)
Пример #2
0
def colmap_localize_from_loaded_data(kapture_data: kapture.Kapture,
                                     kapture_path: str,
                                     tar_handlers: Optional[TarCollection],
                                     colmap_path: str,
                                     input_database_path: str,
                                     input_reconstruction_path: str,
                                     colmap_binary: str,
                                     keypoints_type: Optional[str],
                                     use_colmap_matches_importer: bool,
                                     image_registrator_options: List[str],
                                     skip_list: List[str],
                                     force: bool) -> None:
    """
    Localize images on a colmap model with the kapture data.

    :param kapture_data: kapture data to use
    :param kapture_path: path to the kapture to use
    :param tar_handler: collection of preloaded tar archives
    :param colmap_path: path to the colmap build
    :param input_database_path: path to the map colmap.db
    :param input_database_path: path to the map colmap.db
    :param input_reconstruction_path: path to the map reconstruction folder
    :param colmap_binary: path to the colmap binary executable
    :param keypoints_type: type of keypoints, name of the keypoints subfolder
    :param use_colmap_matches_importer: bool,
    :param image_registrator_options: options for the image registrator
    :param skip_list: list of steps to skip
    :param force: Silently overwrite kapture files if already exists.
    """
    os.makedirs(colmap_path, exist_ok=True)

    if not (kapture_data.records_camera and kapture_data.sensors and kapture_data.keypoints and kapture_data.matches):
        raise ValueError('records_camera, sensors, keypoints, matches are mandatory')

    if kapture_data.trajectories:
        logger.warning("Input data contains trajectories: they will be ignored")
        kapture_data.trajectories.clear()
    else:
        kapture_data.trajectories = kapture.Trajectories()

    # COLMAP does not fully support rigs.
    if kapture_data.rigs is not None and kapture_data.trajectories is not None:
        # make sure, rigs are not used in trajectories.
        logger.info('remove rigs notation.')
        rigs_remove_inplace(kapture_data.trajectories, kapture_data.rigs)
        kapture_data.rigs.clear()

    # Prepare output
    # Set fixed name for COLMAP database
    colmap_db_path = path.join(colmap_path, 'colmap.db')
    image_list_path = path.join(colmap_path, 'images.list')
    reconstruction_path = path.join(colmap_path, "reconstruction")
    if 'delete_existing' not in skip_list:
        safe_remove_file(colmap_db_path, force)
        safe_remove_file(image_list_path, force)
        safe_remove_any_path(reconstruction_path, force)
    os.makedirs(reconstruction_path, exist_ok=True)

    # Copy colmap db to output
    if not os.path.exists(colmap_db_path):
        shutil.copy(input_database_path, colmap_db_path)

    # find correspondences between the colmap db and the kapture data
    images_all = {image_path: (ts, cam_id)
                  for ts, shot in kapture_data.records_camera.items()
                  for cam_id, image_path in shot.items()}

    colmap_db = COLMAPDatabase.connect(colmap_db_path)
    colmap_image_ids = database_extra.get_colmap_image_ids_from_db(colmap_db)
    colmap_images = database_extra.get_images_from_database(colmap_db)
    colmap_db.close()

    # dict ( kapture_camera -> colmap_camera_id )
    colmap_camera_ids = {images_all[image_path][1]: colmap_cam_id
                         for image_path, colmap_cam_id in colmap_images if image_path in images_all}

    images_to_add = {image_path: value
                     for image_path, value in images_all.items()
                     if image_path not in colmap_image_ids}

    flatten_images_to_add = [(ts, kapture_cam_id, image_path)
                             for image_path, (ts, kapture_cam_id) in images_to_add.items()]

    if 'import_to_db' not in skip_list:
        logger.info("Step 1: Add precomputed keypoints and matches to colmap db")

        if keypoints_type is None:
            keypoints_type = try_get_only_key_from_collection(kapture_data.keypoints)
        assert keypoints_type is not None
        assert keypoints_type in kapture_data.keypoints
        assert keypoints_type in kapture_data.matches

        cameras_to_add = kapture.Sensors()
        for _, (_, kapture_cam_id) in images_to_add.items():
            if kapture_cam_id not in colmap_camera_ids:
                kapture_cam = kapture_data.sensors[kapture_cam_id]
                cameras_to_add[kapture_cam_id] = kapture_cam
        colmap_db = COLMAPDatabase.connect(colmap_db_path)
        colmap_added_camera_ids = database_extra.add_cameras_to_database(cameras_to_add, colmap_db)
        colmap_camera_ids.update(colmap_added_camera_ids)

        colmap_added_image_ids = database_extra.add_images_to_database_from_flatten(
            colmap_db, flatten_images_to_add, kapture_data.trajectories, colmap_camera_ids)
        colmap_image_ids.update(colmap_added_image_ids)

        colmap_image_ids_reversed = {v: k for k, v in colmap_image_ids.items()}  # colmap_id : name

        # add new features
        colmap_keypoints = database_extra.get_keypoints_set_from_database(colmap_db, colmap_image_ids_reversed)

        keypoints_all = kapture_data.keypoints[keypoints_type]
        keypoints_to_add = {name for name in keypoints_all if name not in colmap_keypoints}
        keypoints_to_add = kapture.Keypoints(keypoints_all.type_name, keypoints_all.dtype, keypoints_all.dsize,
                                             keypoints_to_add)
        database_extra.add_keypoints_to_database(colmap_db, keypoints_to_add,
                                                 keypoints_type, kapture_path,
                                                 tar_handlers,
                                                 colmap_image_ids)

        # add new matches
        colmap_matches = kapture.Matches(database_extra.get_matches_set_from_database(colmap_db,
                                                                                      colmap_image_ids_reversed))
        colmap_matches.normalize()

        matches_all = kapture_data.matches[keypoints_type]
        matches_to_add = kapture.Matches({pair for pair in matches_all if pair not in colmap_matches})
        # print(list(matches_to_add))
        database_extra.add_matches_to_database(colmap_db, matches_to_add,
                                               keypoints_type, kapture_path,
                                               tar_handlers,
                                               colmap_image_ids,
                                               export_two_view_geometry=not use_colmap_matches_importer)
        colmap_db.close()

    if use_colmap_matches_importer:
        logger.info('Step 2: Run geometric verification')
        logger.debug('running colmap matches_importer...')

        if keypoints_type is None:
            keypoints_type = try_get_only_key_from_collection(kapture_data.matches)
        assert keypoints_type is not None
        assert keypoints_type in kapture_data.matches

        # compute two view geometry
        colmap_lib.run_matches_importer_from_kapture_matches(
            colmap_binary,
            colmap_use_cpu=True,
            colmap_gpu_index=None,
            colmap_db_path=colmap_db_path,
            kapture_matches=kapture_data.matches[keypoints_type],
            force=force)
    else:
        logger.info('Step 2: Run geometric verification - skipped')
    if 'image_registrator' not in skip_list:
        logger.info("Step 3: Run image_registrator")
        # run image_registrator
        colmap_lib.run_image_registrator(
            colmap_binary,
            colmap_db_path,
            input_reconstruction_path,
            reconstruction_path,
            image_registrator_options
        )

    # run model_converter
    if 'model_converter' not in skip_list:
        logger.info("Step 4: Export reconstruction results to txt")
        colmap_lib.run_model_converter(
            colmap_binary,
            reconstruction_path,
            reconstruction_path
        )
def colmap_build_sift_map(kapture_path: str, colmap_path: str,
                          colmap_binary: str, colmap_use_cpu: bool,
                          colmap_gpu_index: str, vocab_tree_path: str,
                          point_triangulator_options: List[str],
                          skip_list: List[str], force: bool) -> None:
    """
    Build a colmap model using default SIFT features with the kapture data.

    :param kapture_path: path to the kapture to use
    :param colmap_path: path to the colmap build
    :param colmap_binary: path to the colmap executable
    :param colmap_use_cpu: to use cpu only (and ignore gpu) or to use also gpu
    :param colmap_gpu_index: gpu index for sift extractor and mapper
    :param vocab_tree_path: path to the colmap vocabulary tree file
    :param point_triangulator_options: options for the point triangulator
    :param skip_list: list of steps to skip
    :param force: Silently overwrite kapture files if already exists.
    """
    os.makedirs(colmap_path, exist_ok=True)

    # Load input files first to make sure it is OK
    logger.info('loading kapture files...')
    kapture_data = kapture.io.csv.kapture_from_dir(kapture_path)

    if not (kapture_data.records_camera and kapture_data.sensors):
        raise ValueError('records_camera, sensors are mandatory')
    if not kapture_data.trajectories:
        logger.info(
            'there are no trajectories, running mapper instead of point_triangulator'
        )

    if not os.path.isfile(vocab_tree_path):
        raise ValueError(
            f'Vocabulary Tree file does not exist: {vocab_tree_path}')

    # COLMAP does not fully support rigs.
    if kapture_data.rigs is not None and kapture_data.trajectories is not None:
        # make sure, rigs are not used in trajectories.
        logger.info('remove rigs notation.')
        rigs_remove_inplace(kapture_data.trajectories, kapture_data.rigs)
        kapture_data.rigs.clear()

    # Set fixed name for COLMAP database
    colmap_db_path = path.join(colmap_path, 'colmap.db')
    image_list_path = path.join(colmap_path, 'images.list')
    reconstruction_path = path.join(colmap_path, "reconstruction")
    if 'delete_existing' not in skip_list:
        safe_remove_file(colmap_db_path, force)
        safe_remove_file(image_list_path, force)
        safe_remove_any_path(reconstruction_path, force)
    os.makedirs(reconstruction_path, exist_ok=True)

    if 'feature_extract' not in skip_list:
        logger.info("Step 1: Feature extraction using colmap")
        with open(image_list_path, 'w') as fid:
            for timestamp, sensor_id in sorted(
                    kapture_data.records_camera.key_pairs()):
                fid.write(kapture_data.records_camera[timestamp][sensor_id] +
                          "\n")

        colmap_lib.run_feature_extractor(colmap_binary, colmap_use_cpu,
                                         colmap_gpu_index, colmap_db_path,
                                         get_image_fullpath(kapture_path),
                                         image_list_path)

    # Update cameras in COLMAP:
    # - use only one camera for all images taken with the same camera (update all camera IDs)
    # - import camera intrinsics
    # - import camera pose
    if 'update_db_cameras' not in skip_list:
        logger.info("Step 2: Populate COLMAP DB with cameras and poses")
        colmap_db = COLMAPDatabase.connect(colmap_db_path)
        database_extra.update_DB_cameras_and_poses(colmap_db, kapture_data)
        # close db before running colmap processes in order to avoid locks
        colmap_db.close()

    # Extract matches with COLMAP
    if 'matches' not in skip_list:
        logger.info("Step 3: Compute matches with colmap")

        colmap_lib.run_vocab_tree_matcher(colmap_binary, colmap_use_cpu,
                                          colmap_gpu_index, colmap_db_path,
                                          vocab_tree_path)

    if kapture_data.trajectories is not None:
        # Generate priors for reconstruction
        txt_path = path.join(colmap_path, "priors_for_reconstruction")
        os.makedirs(txt_path, exist_ok=True)
        if 'priors_for_reconstruction' not in skip_list:
            logger.info('Step 4: Exporting priors for reconstruction.')
            colmap_db = COLMAPDatabase.connect(colmap_db_path)
            database_extra.generate_priors_for_reconstruction(
                kapture_data, colmap_db, txt_path)
            colmap_db.close()

        # Point triangulator
        reconstruction_path = path.join(colmap_path, "reconstruction")
        os.makedirs(reconstruction_path, exist_ok=True)
        if 'triangulation' not in skip_list:
            logger.info("Step 5: Triangulation")
            colmap_lib.run_point_triangulator(colmap_binary, colmap_db_path,
                                              get_image_fullpath(kapture_path),
                                              txt_path, reconstruction_path,
                                              point_triangulator_options)
    else:
        # mapper
        reconstruction_path = path.join(colmap_path, "reconstruction")
        os.makedirs(reconstruction_path, exist_ok=True)
        if 'triangulation' not in skip_list:
            logger.info("Step 5: Triangulation")
            colmap_lib.run_mapper(colmap_binary, colmap_db_path,
                                  get_image_fullpath(kapture_path), None,
                                  reconstruction_path,
                                  point_triangulator_options)
            # use reconstruction 0 as main
            first_reconstruction = os.path.join(reconstruction_path, '0')
            files = os.listdir(first_reconstruction)
            for f in files:
                shutil.move(os.path.join(first_reconstruction, f),
                            os.path.join(reconstruction_path, f))
            shutil.rmtree(first_reconstruction)

    # run model_converter
    if 'model_converter' not in skip_list:
        logger.info("Step 6: Export reconstruction results to txt")
        colmap_lib.run_model_converter(colmap_binary, reconstruction_path,
                                       reconstruction_path)
Пример #4
0
def colmap_localize_sift(kapture_path: str, colmap_path: str,
                         input_database_path: str,
                         input_reconstruction_path: str, colmap_binary: str,
                         colmap_use_cpu: bool, colmap_gpu_index: str,
                         vocab_tree_path: str,
                         image_registrator_options: List[str],
                         skip_list: List[str], force: bool) -> None:
    """
    Localize images on a colmap model using default SIFT features with the kapture data.

    :param kapture_path: path to the kapture to use
    :param colmap_path: path to the colmap build
    :param input_database_path: path to the map colmap.db
    :param input_database_path: path to the map colmap.db
    :param input_reconstruction_path: path to the map reconstruction folder
    :param colmap_binary: path to the colmap binary executable
    :param colmap_use_cpu: to use cpu only (and ignore gpu) or to use also gpu
    :param colmap_gpu_index: gpu index for sift extractor and mapper
    :param vocab_tree_path: path to the colmap vocabulary tree file
    :param image_registrator_options: options for the image registrator
    :param skip_list: list of steps to skip
    :param force: Silently overwrite kapture files if already exists.
    """
    os.makedirs(colmap_path, exist_ok=True)
    # Set fixed name for COLMAP database

    # Load input files first to make sure it is OK
    logger.info('loading kapture files...')
    kapture_data = kapture.io.csv.kapture_from_dir(kapture_path)

    if not (kapture_data.records_camera and kapture_data.sensors):
        raise ValueError('records_camera, sensors are mandatory')

    if kapture_data.trajectories:
        logger.warning(
            "Input data contains trajectories: they will be ignored")
        kapture_data.trajectories.clear()
    else:
        kapture_data.trajectories = kapture.Trajectories()

    if not os.path.isfile(vocab_tree_path):
        raise ValueError(
            f'Vocabulary Tree file does not exist: {vocab_tree_path}')

    # COLMAP does not fully support rigs.
    if kapture_data.rigs is not None and kapture_data.trajectories is not None:
        # make sure, rigs are not used in trajectories.
        logger.info('remove rigs notation.')
        rigs_remove_inplace(kapture_data.trajectories, kapture_data.rigs)
        kapture_data.rigs.clear()

    # Prepare output
    # Set fixed name for COLMAP database
    colmap_db_path = path.join(colmap_path, 'colmap.db')
    image_list_path = path.join(colmap_path, 'images.list')
    reconstruction_path = path.join(colmap_path, "reconstruction")
    if 'delete_existing' not in skip_list:
        safe_remove_file(colmap_db_path, force)
        safe_remove_file(image_list_path, force)
        safe_remove_any_path(reconstruction_path, force)
    os.makedirs(reconstruction_path, exist_ok=True)

    # Copy colmap db to output
    if not os.path.exists(colmap_db_path):
        shutil.copy(input_database_path, colmap_db_path)

    # find correspondences between the colmap db and the kapture data
    images_all = {
        image_path: (ts, cam_id)
        for ts, shot in kapture_data.records_camera.items()
        for cam_id, image_path in shot.items()
    }

    colmap_db = COLMAPDatabase.connect(colmap_db_path)
    colmap_image_ids = database_extra.get_colmap_image_ids_from_db(colmap_db)
    colmap_cameras = database_extra.get_camera_ids_from_database(colmap_db)
    colmap_images = database_extra.get_images_from_database(colmap_db)
    colmap_db.close()

    # dict ( kapture_camera -> colmap_camera_id )
    colmap_camera_ids = {
        images_all[image_path][1]: colmap_cam_id
        for image_path, colmap_cam_id in colmap_images
        if image_path in images_all
    }

    images_to_add = {
        image_path: value
        for image_path, value in images_all.items()
        if image_path not in colmap_image_ids
    }

    flatten_images_to_add = [
        (ts, kapture_cam_id, image_path)
        for image_path, (ts, kapture_cam_id) in images_to_add.items()
    ]

    if 'feature_extract' not in skip_list:
        logger.info("Step 1: Feature extraction using colmap")
        with open(image_list_path, 'w') as fid:
            for image in images_to_add.keys():
                fid.write(image + "\n")

        colmap_lib.run_feature_extractor(colmap_binary, colmap_use_cpu,
                                         colmap_gpu_index, colmap_db_path,
                                         get_image_fullpath(kapture_path),
                                         image_list_path)

    if 'matches' not in skip_list:
        logger.info("Step 2: Compute matches with colmap")
        colmap_lib.run_vocab_tree_matcher(colmap_binary, colmap_use_cpu,
                                          colmap_gpu_index, colmap_db_path,
                                          vocab_tree_path, image_list_path)

    if 'fix_db_cameras' not in skip_list:
        logger.info(
            "Step 3: Replace colmap generated cameras with kapture cameras")
        colmap_db = COLMAPDatabase.connect(colmap_db_path)
        database_extra.foreign_keys_off(colmap_db)

        # remove colmap generated cameras
        after_feature_extraction_colmap_cameras = database_extra.get_camera_ids_from_database(
            colmap_db)
        colmap_cameras_to_remove = [
            cam_id for cam_id in after_feature_extraction_colmap_cameras
            if cam_id not in colmap_cameras
        ]
        for cam_id in colmap_cameras_to_remove:
            database_extra.remove_camera(colmap_db, cam_id)

        # put the correct cameras and image extrinsic back into the database
        cameras_to_add = kapture.Sensors()
        for image_path, (ts, kapture_cam_id) in images_to_add.items():
            if kapture_cam_id not in colmap_camera_ids:
                kapture_cam = kapture_data.sensors[kapture_cam_id]
                cameras_to_add[kapture_cam_id] = kapture_cam
        colmap_added_camera_ids = database_extra.add_cameras_to_database(
            cameras_to_add, colmap_db)
        colmap_camera_ids.update(colmap_added_camera_ids)

        database_extra.update_images_in_database_from_flatten(
            colmap_db, flatten_images_to_add, kapture_data.trajectories,
            colmap_camera_ids)

        database_extra.foreign_keys_on(colmap_db)
        colmap_db.commit()
        colmap_db.close()

    if 'image_registrator' not in skip_list:
        logger.info("Step 4: Run image_registrator")
        # run image_registrator
        colmap_lib.run_image_registrator(colmap_binary, colmap_db_path,
                                         input_reconstruction_path,
                                         reconstruction_path,
                                         image_registrator_options)

    # run model_converter
    if 'model_converter' not in skip_list:
        logger.info("Step 5: Export reconstruction results to txt")
        colmap_lib.run_model_converter(colmap_binary, reconstruction_path,
                                       reconstruction_path)
def colmap_build_map_from_loaded_data(kapture_data: kapture.Kapture,
                                      kapture_path: str, colmap_path: str,
                                      colmap_binary: str,
                                      use_colmap_matches_importer: bool,
                                      point_triangulator_options: List[str],
                                      skip_list: List[str],
                                      force: bool) -> None:
    """
    Build a colmap model using custom features with the kapture data.

    :param kapture_data: kapture data to use
    :param kapture_path: path to the kapture to use
    :param colmap_path: path to the colmap build
    :param colmap_binary: path to the colmap executable
    :param use_colmap_matches_importer: bool,
    :param point_triangulator_options: options for the point triangulator
    :param skip_list: list of steps to skip
    :param force: Silently overwrite kapture files if already exists.
    """
    os.makedirs(colmap_path, exist_ok=True)

    if not (kapture_data.records_camera and kapture_data.sensors
            and kapture_data.keypoints and kapture_data.matches):
        raise ValueError(
            'records_camera, sensors, keypoints, matches are mandatory')
    if not kapture_data.trajectories:
        logger.info(
            'there are no trajectories, running mapper instead of point_triangulator'
        )

    # COLMAP does not fully support rigs.
    if kapture_data.rigs is not None and kapture_data.trajectories is not None:
        # make sure, rigs are not used in trajectories.
        logger.info('remove rigs notation.')
        rigs_remove_inplace(kapture_data.trajectories, kapture_data.rigs)
        kapture_data.rigs.clear()

    # Set fixed name for COLMAP database
    colmap_db_path = path.join(colmap_path, 'colmap.db')
    reconstruction_path = path.join(colmap_path, "reconstruction")
    priors_txt_path = path.join(colmap_path, "priors_for_reconstruction")
    if 'delete_existing' not in skip_list:
        safe_remove_file(colmap_db_path, force)
        safe_remove_any_path(reconstruction_path, force)
        safe_remove_any_path(priors_txt_path, force)
    os.makedirs(reconstruction_path, exist_ok=True)

    if 'colmap_db' not in skip_list:
        logger.info('Using precomputed keypoints and matches')
        logger.info('Step 1: Export kapture format to colmap')

        colmap_db = COLMAPDatabase.connect(colmap_db_path)
        if kapture_data.descriptors is not None:
            kapture_data.descriptors.clear()
        database_extra.kapture_to_colmap(
            kapture_data,
            kapture_path,
            colmap_db,
            export_two_view_geometry=not use_colmap_matches_importer)
        # close db before running colmap processes in order to avoid locks
        colmap_db.close()

        if use_colmap_matches_importer:
            logger.info('Step 2: Run geometric verification')
            logger.debug('running colmap matches_importer...')
            colmap_lib.run_matches_importer_from_kapture(
                colmap_binary,
                colmap_use_cpu=True,
                colmap_gpu_index=None,
                colmap_db_path=colmap_db_path,
                kapture_data=kapture_data,
                force=force)
        else:
            logger.info('Step 2: Run geometric verification - skipped')

    if kapture_data.trajectories is not None:
        # Generate priors for reconstruction
        os.makedirs(priors_txt_path, exist_ok=True)
        if 'priors_for_reconstruction' not in skip_list:
            logger.info('Step 3: Exporting priors for reconstruction.')
            colmap_db = COLMAPDatabase.connect(colmap_db_path)
            database_extra.generate_priors_for_reconstruction(
                kapture_data, colmap_db, priors_txt_path)
            colmap_db.close()

        # Point triangulator
        reconstruction_path = path.join(colmap_path, "reconstruction")
        os.makedirs(reconstruction_path, exist_ok=True)
        if 'triangulation' not in skip_list:
            logger.info("Step 4: Triangulation")
            colmap_lib.run_point_triangulator(colmap_binary, colmap_db_path,
                                              get_image_fullpath(kapture_path),
                                              priors_txt_path,
                                              reconstruction_path,
                                              point_triangulator_options)
    else:
        # mapper
        reconstruction_path = path.join(colmap_path, "reconstruction")
        os.makedirs(reconstruction_path, exist_ok=True)
        if 'triangulation' not in skip_list:
            logger.info("Step 4: Triangulation")
            colmap_lib.run_mapper(colmap_binary, colmap_db_path,
                                  get_image_fullpath(kapture_path), None,
                                  reconstruction_path,
                                  point_triangulator_options)
            # use reconstruction 0 as main
            first_reconstruction = os.path.join(reconstruction_path, '0')
            files = os.listdir(first_reconstruction)
            for f in files:
                shutil.move(os.path.join(first_reconstruction, f),
                            os.path.join(reconstruction_path, f))
            shutil.rmtree(first_reconstruction)

    # run model_converter
    if 'model_converter' not in skip_list:
        logger.info("Step 5: Export reconstruction results to txt")
        colmap_lib.run_model_converter(colmap_binary, reconstruction_path,
                                       reconstruction_path)
    def reconstruct(self, kapture_data):
        os.makedirs(self._colmap_path, exist_ok=True)

        if not (kapture_data.records_camera and kapture_data.sensors
                and kapture_data.keypoints and kapture_data.matches
                and kapture_data.trajectories):
            raise ValueError(
                'records_camera, sensors, keypoints, matches, trajectories are mandatory'
            )

        # Set fixed name for COLMAP database
        colmap_db_path = path.join(self._colmap_path, 'colmap.db')
        reconstruction_path = path.join(self._colmap_path, "reconstruction")
        priors_txt_path = path.join(self._colmap_path,
                                    "priors_for_reconstruction")

        safe_remove_file(colmap_db_path, True)
        safe_remove_any_path(reconstruction_path, True)
        safe_remove_any_path(priors_txt_path, True)
        os.makedirs(reconstruction_path, exist_ok=True)

        # COLMAP does not fully support rigs.
        print("Step 1. Remove rigs")
        if kapture_data.rigs is not None and kapture_data.trajectories is not None:
            # make sure, rigs are not used in trajectories.
            rigs_remove_inplace(kapture_data.trajectories, kapture_data.rigs)
            kapture_data.rigs.clear()

        print("Step 2. Kapture to colmap")
        colmap_db = COLMAPDatabase.connect(colmap_db_path)
        database_extra.kapture_to_colmap(kapture_data,
                                         kapture_data.kapture_path,
                                         colmap_db,
                                         export_two_view_geometry=True)
        colmap_db.close()

        os.makedirs(priors_txt_path, exist_ok=True)

        print("Step 3. Generate priors for reconstruction")
        colmap_db = COLMAPDatabase.connect(colmap_db_path)
        database_extra.generate_priors_for_reconstruction(
            kapture_data, colmap_db, priors_txt_path)
        colmap_db.close()

        # Point triangulator
        print("Step 4. Point triangulator")
        reconstruction_path = path.join(self._colmap_path, "reconstruction")
        os.makedirs(reconstruction_path, exist_ok=True)
        run_point_triangulator(self._colmap_binary, colmap_db_path,
                               kapture_data.image_path, priors_txt_path,
                               reconstruction_path,
                               self._point_triangulator_options)
        print("Step 5. Model converter")
        run_model_converter(self._colmap_binary, reconstruction_path,
                            reconstruction_path)
        print("Step 5. Reconstruction import")
        points3d, observations = import_from_colmap_points3d_txt(
            os.path.join(reconstruction_path, "points3D.txt"),
            kapture_data.image_names)
        kapture_data.observations = observations
        kapture_data.points3d = points3d