CoolFace
Modelpublic

beverley-gorry/colmap-vslamlab

sourceHugging Faceupdated 3mo agoView on Hugging Face
0likes
database.py469 linesDownload Raw Back to python
1# Copyright (c), ETH Zurich and UNC Chapel Hill.2# All rights reserved.3#4# Redistribution and use in source and binary forms, with or without5# modification, are permitted provided that the following conditions are met:6#7#     * Redistributions of source code must retain the above copyright8#       notice, this list of conditions and the following disclaimer.9#10#     * Redistributions in binary form must reproduce the above copyright11#       notice, this list of conditions and the following disclaimer in the12#       documentation and/or other materials provided with the distribution.13#14#     * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of15#       its contributors may be used to endorse or promote products derived16#       from this software without specific prior written permission.17#18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE21# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE22# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE28# POSSIBILITY OF SUCH DAMAGE.29 30 31# This script is based on an original implementation by True Price.32 33import sqlite334import sys35 36import numpy as np37 38IS_PYTHON3 = sys.version_info[0] >= 339 40MAX_IMAGE_ID = 2**31 - 141 42CREATE_CAMERAS_TABLE = """CREATE TABLE IF NOT EXISTS cameras (43    camera_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,44    model INTEGER NOT NULL,45    width INTEGER NOT NULL,46    height INTEGER NOT NULL,47    params BLOB,48    prior_focal_length INTEGER NOT NULL)"""49 50CREATE_DESCRIPTORS_TABLE = """CREATE TABLE IF NOT EXISTS descriptors (51    image_id INTEGER PRIMARY KEY NOT NULL,52    rows INTEGER NOT NULL,53    cols INTEGER NOT NULL,54    data BLOB,55    FOREIGN KEY(image_id) REFERENCES images(image_id) ON DELETE CASCADE)"""56 57CREATE_IMAGES_TABLE = """CREATE TABLE IF NOT EXISTS images (58    image_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,59    name TEXT NOT NULL UNIQUE,60    camera_id INTEGER NOT NULL,61    CONSTRAINT image_id_check CHECK(image_id >= 0 and image_id < {}),62    FOREIGN KEY(camera_id) REFERENCES cameras(camera_id))63""".format(MAX_IMAGE_ID)64 65CREATE_POSE_PRIORS_TABLE = """CREATE TABLE IF NOT EXISTS pose_priors (66    image_id INTEGER PRIMARY KEY NOT NULL,67    position BLOB,68    coordinate_system INTEGER NOT NULL,69    position_covariance BLOB,70    FOREIGN KEY(image_id) REFERENCES images(image_id) ON DELETE CASCADE)"""71 72CREATE_TWO_VIEW_GEOMETRIES_TABLE = """73CREATE TABLE IF NOT EXISTS two_view_geometries (74    pair_id INTEGER PRIMARY KEY NOT NULL,75    rows INTEGER NOT NULL,76    cols INTEGER NOT NULL,77    data BLOB,78    config INTEGER NOT NULL,79    F BLOB,80    E BLOB,81    H BLOB,82    qvec BLOB,83    tvec BLOB)84"""85 86CREATE_KEYPOINTS_TABLE = """CREATE TABLE IF NOT EXISTS keypoints (87    image_id INTEGER PRIMARY KEY NOT NULL,88    rows INTEGER NOT NULL,89    cols INTEGER NOT NULL,90    data BLOB,91    FOREIGN KEY(image_id) REFERENCES images(image_id) ON DELETE CASCADE)92"""93 94CREATE_MATCHES_TABLE = """CREATE TABLE IF NOT EXISTS matches (95    pair_id INTEGER PRIMARY KEY NOT NULL,96    rows INTEGER NOT NULL,97    cols INTEGER NOT NULL,98    data BLOB)"""99 100CREATE_NAME_INDEX = (101    "CREATE UNIQUE INDEX IF NOT EXISTS index_name ON images(name)"102)103 104CREATE_ALL = "; ".join(105    [106        CREATE_CAMERAS_TABLE,107        CREATE_IMAGES_TABLE,108        CREATE_POSE_PRIORS_TABLE,109        CREATE_KEYPOINTS_TABLE,110        CREATE_DESCRIPTORS_TABLE,111        CREATE_MATCHES_TABLE,112        CREATE_TWO_VIEW_GEOMETRIES_TABLE,113        CREATE_NAME_INDEX,114    ]115)116 117 118def image_ids_to_pair_id(image_id1, image_id2):119    if image_id1 > image_id2:120        image_id1, image_id2 = image_id2, image_id1121    return image_id1 * MAX_IMAGE_ID + image_id2122 123 124def pair_id_to_image_ids(pair_id):125    image_id2 = pair_id % MAX_IMAGE_ID126    image_id1 = (pair_id - image_id2) / MAX_IMAGE_ID127    return image_id1, image_id2128 129 130def array_to_blob(array):131    if IS_PYTHON3:132        return array.tostring()133    else:134        return np.getbuffer(array)135 136 137def blob_to_array(blob, dtype, shape=(-1,)):138    if IS_PYTHON3:139        return np.fromstring(blob, dtype=dtype).reshape(*shape)140    else:141        return np.frombuffer(blob, dtype=dtype).reshape(*shape)142 143 144class COLMAPDatabase(sqlite3.Connection):145    @staticmethod146    def connect(database_path):147        return sqlite3.connect(database_path, factory=COLMAPDatabase)148 149    def __init__(self, *args, **kwargs):150        super(COLMAPDatabase, self).__init__(*args, **kwargs)151 152        self.create_tables = lambda: self.executescript(CREATE_ALL)153        self.create_cameras_table = lambda: self.executescript(154            CREATE_CAMERAS_TABLE155        )156        self.create_descriptors_table = lambda: self.executescript(157            CREATE_DESCRIPTORS_TABLE158        )159        self.create_images_table = lambda: self.executescript(160            CREATE_IMAGES_TABLE161        )162        self.create_pose_priors_table = lambda: self.executescript(163            CREATE_POSE_PRIORS_TABLE164        )165        self.create_two_view_geometries_table = lambda: self.executescript(166            CREATE_TWO_VIEW_GEOMETRIES_TABLE167        )168        self.create_keypoints_table = lambda: self.executescript(169            CREATE_KEYPOINTS_TABLE170        )171        self.create_matches_table = lambda: self.executescript(172            CREATE_MATCHES_TABLE173        )174        self.create_name_index = lambda: self.executescript(CREATE_NAME_INDEX)175 176    def add_camera(177        self,178        model,179        width,180        height,181        params,182        prior_focal_length=False,183        camera_id=None,184    ):185        params = np.asarray(params, np.float64)186        cursor = self.execute(187            "INSERT INTO cameras VALUES (?, ?, ?, ?, ?, ?)",188            (189                camera_id,190                model,191                width,192                height,193                array_to_blob(params),194                prior_focal_length,195            ),196        )197        return cursor.lastrowid198 199    def add_image(200        self,201        name,202        camera_id,203        image_id=None,204    ):205        cursor = self.execute(206            "INSERT INTO images VALUES (?, ?, ?)", (image_id, name, camera_id)207        )208        return cursor.lastrowid209 210    def add_pose_prior(211        self, image_id, position, coordinate_system=-1, position_covariance=None212    ):213        position = np.asarray(position, dtype=np.float64)214        if position_covariance is None:215            position_covariance = np.full((3, 3), np.nan, dtype=np.float64)216        self.execute(217            "INSERT INTO pose_priors VALUES (?, ?, ?, ?)",218            (219                image_id,220                array_to_blob(position),221                coordinate_system,222                array_to_blob(position_covariance),223            ),224        )225 226    def add_keypoints(self, image_id, keypoints):227        assert len(keypoints.shape) == 2228        assert keypoints.shape[1] in [2, 4, 6]229 230        keypoints = np.asarray(keypoints, np.float32)231        self.execute(232            "INSERT INTO keypoints VALUES (?, ?, ?, ?)",233            (image_id,) + keypoints.shape + (array_to_blob(keypoints),),234        )235 236    def add_descriptors(self, image_id, descriptors):237        descriptors = np.ascontiguousarray(descriptors, np.uint8)238        self.execute(239            "INSERT INTO descriptors VALUES (?, ?, ?, ?)",240            (image_id,) + descriptors.shape + (array_to_blob(descriptors),),241        )242 243    def add_matches(self, image_id1, image_id2, matches):244        assert len(matches.shape) == 2245        assert matches.shape[1] == 2246 247        if image_id1 > image_id2:248            matches = matches[:, ::-1]249 250        pair_id = image_ids_to_pair_id(image_id1, image_id2)251        matches = np.asarray(matches, np.uint32)252        self.execute(253            "INSERT INTO matches VALUES (?, ?, ?, ?)",254            (pair_id,) + matches.shape + (array_to_blob(matches),),255        )256 257    def add_two_view_geometry(258        self,259        image_id1,260        image_id2,261        matches,262        F=np.eye(3),263        E=np.eye(3),264        H=np.eye(3),265        qvec=np.array([1.0, 0.0, 0.0, 0.0]),266        tvec=np.zeros(3),267        config=2,268    ):269        assert len(matches.shape) == 2270        assert matches.shape[1] == 2271 272        if image_id1 > image_id2:273            matches = matches[:, ::-1]274 275        pair_id = image_ids_to_pair_id(image_id1, image_id2)276        matches = np.asarray(matches, np.uint32)277        F = np.asarray(F, dtype=np.float64)278        E = np.asarray(E, dtype=np.float64)279        H = np.asarray(H, dtype=np.float64)280        qvec = np.asarray(qvec, dtype=np.float64)281        tvec = np.asarray(tvec, dtype=np.float64)282        self.execute(283            "INSERT INTO two_view_geometries VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",284            (pair_id,)285            + matches.shape286            + (287                array_to_blob(matches),288                config,289                array_to_blob(F),290                array_to_blob(E),291                array_to_blob(H),292                array_to_blob(qvec),293                array_to_blob(tvec),294            ),295        )296 297 298def example_usage():299    import argparse300    import os301 302    parser = argparse.ArgumentParser()303    parser.add_argument("--database_path", default="database.db")304    args = parser.parse_args()305 306    if os.path.exists(args.database_path):307        print("ERROR: database path already exists -- will not modify it.")308        return309 310    # Open the database.311 312    db = COLMAPDatabase.connect(args.database_path)313 314    # For convenience, try creating all the tables upfront.315 316    db.create_tables()317 318    # Create dummy cameras.319 320    model1, width1, height1, params1 = (321        0,322        1024,323        768,324        np.array((1024.0, 512.0, 384.0)),325    )326    model2, width2, height2, params2 = (327        2,328        1024,329        768,330        np.array((1024.0, 512.0, 384.0, 0.1)),331    )332 333    camera_id1 = db.add_camera(model1, width1, height1, params1)334    camera_id2 = db.add_camera(model2, width2, height2, params2)335 336    # Create dummy images.337 338    image_id1 = db.add_image("image1.png", camera_id1)339    image_id2 = db.add_image("image2.png", camera_id1)340    image_id3 = db.add_image("image3.png", camera_id2)341    image_id4 = db.add_image("image4.png", camera_id2)342 343    # Create dummy keypoints.344    #345    # Note that COLMAP supports:346    #      - 2D keypoints: (x, y)347    #      - 4D keypoints: (x, y, theta, scale)348    #      - 6D affine keypoints: (x, y, a_11, a_12, a_21, a_22)349 350    num_keypoints = 1000351    keypoints1 = np.random.rand(num_keypoints, 2) * (width1, height1)352    keypoints2 = np.random.rand(num_keypoints, 2) * (width1, height1)353    keypoints3 = np.random.rand(num_keypoints, 2) * (width2, height2)354    keypoints4 = np.random.rand(num_keypoints, 2) * (width2, height2)355 356    db.add_keypoints(image_id1, keypoints1)357    db.add_keypoints(image_id2, keypoints2)358    db.add_keypoints(image_id3, keypoints3)359    db.add_keypoints(image_id4, keypoints4)360 361    # Create dummy matches.362 363    M = 50364    matches12 = np.random.randint(num_keypoints, size=(M, 2))365    matches23 = np.random.randint(num_keypoints, size=(M, 2))366    matches34 = np.random.randint(num_keypoints, size=(M, 2))367 368    db.add_matches(image_id1, image_id2, matches12)369    db.add_matches(image_id2, image_id3, matches23)370    db.add_matches(image_id3, image_id4, matches34)371 372    # Create dummy pose_priors.373 374    pos1 = np.random.rand(3, 1) * np.random.randint(10)375    pos2 = np.random.rand(3, 1) * np.random.randint(10)376    pos3 = np.random.rand(3, 1) * np.random.randint(10)377 378    cov3 = np.random.rand(3, 3) * np.random.randint(10)379 380    pose_prior1 = [image_id1, pos1, 1, None]381    pose_prior2 = [image_id2, pos2, -1, None]382    pose_prior3 = [image_id3, pos3, 0, cov3]383 384    db.add_pose_prior(*pose_prior1)385    db.add_pose_prior(*pose_prior2)386    db.add_pose_prior(*pose_prior3)387 388    # Convert unset covariance to nan matrix for later check389    pose_prior1[3] = np.full((3, 3), np.nan, dtype=np.float64)390    pose_prior2[3] = np.full((3, 3), np.nan, dtype=np.float64)391 392    # Commit the data to the file.393 394    db.commit()395 396    # Read and check cameras.397 398    rows = db.execute("SELECT * FROM cameras")399 400    camera_id, model, width, height, params, prior = next(rows)401    params = blob_to_array(params, np.float64)402    assert camera_id == camera_id1403    assert model == model1 and width == width1 and height == height1404    assert np.allclose(params, params1)405 406    camera_id, model, width, height, params, prior = next(rows)407    params = blob_to_array(params, np.float64)408    assert camera_id == camera_id2409    assert model == model2 and width == width2 and height == height2410    assert np.allclose(params, params2)411 412    # Read and check keypoints.413 414    keypoints = dict(415        (image_id, blob_to_array(data, np.float32, (-1, 2)))416        for image_id, data in db.execute("SELECT image_id, data FROM keypoints")417    )418 419    assert np.allclose(keypoints[image_id1], keypoints1)420    assert np.allclose(keypoints[image_id2], keypoints2)421    assert np.allclose(keypoints[image_id3], keypoints3)422    assert np.allclose(keypoints[image_id4], keypoints4)423 424    # Read and check matches.425 426    matches = dict(427        (pair_id_to_image_ids(pair_id), blob_to_array(data, np.uint32, (-1, 2)))428        for pair_id, data in db.execute("SELECT pair_id, data FROM matches")429    )430 431    assert np.all(matches[(image_id1, image_id2)] == matches12)432    assert np.all(matches[(image_id2, image_id3)] == matches23)433    assert np.all(matches[(image_id3, image_id4)] == matches34)434 435    # Read and check pose_priors436 437    rows = db.execute("SELECT * FROM pose_priors")438 439    img_id1, pos1, coord_sys1, cov1 = next(rows)440    img_id2, pos2, coord_sys2, cov2 = next(rows)441    img_id3, pos3, coord_sys3, cov3 = next(rows)442 443    assert pose_prior1[0] == img_id1444    assert pose_prior2[0] == img_id2445    assert pose_prior3[0] == img_id3446 447    assert pose_prior1[1].all() == blob_to_array(pos1, np.float64, (3, 1)).all()448    assert pose_prior2[1].all() == blob_to_array(pos2, np.float64, (3, 1)).all()449    assert pose_prior3[1].all() == blob_to_array(pos3, np.float64, (3, 1)).all()450 451    assert pose_prior1[2] == coord_sys1452    assert pose_prior2[2] == coord_sys2453    assert pose_prior3[2] == coord_sys3454 455    assert pose_prior1[3].all() == blob_to_array(cov1, np.float64, (3, 3)).all()456    assert pose_prior2[3].all() == blob_to_array(cov2, np.float64, (3, 3)).all()457    assert pose_prior3[3].all() == blob_to_array(cov3, np.float64, (3, 3)).all()458 459    # Clean up.460 461    db.close()462 463    if os.path.exists(args.database_path):464        os.remove(args.database_path)465 466 467if __name__ == "__main__":468    example_usage()469