// Copyright (c) 2022, ETH Zurich and UNC Chapel Hill. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de) #ifndef COLMAP_SRC_BASE_DATABASE_H_ #define COLMAP_SRC_BASE_DATABASE_H_ #include #include #include #include #include "SQLite/sqlite3.h" #include "base/camera.h" #include "base/image.h" #include "estimators/two_view_geometry.h" #include "feature/types.h" #include "util/types.h" namespace colmap { // Database class to read and write images, features, cameras, matches, etc. // from a SQLite database. The class is not thread-safe and must not be accessed // concurrently. The class is optimized for single-thread speed and for optimal // performance, wrap multiple method calls inside a leading `BeginTransaction` // and trailing `EndTransaction`. class Database { public: const static int kSchemaVersion = 1; // The maximum number of images, that can be stored in the database. // This limitation arises due to the fact, that we generate unique IDs for // image pairs manually. Note: do not change this to // another type than `size_t`. const static size_t kMaxNumImages; Database(); explicit Database(const std::string& path); ~Database(); // Open and close database. The same database should not be opened // concurrently in multiple threads or processes. void Open(const std::string& path); void Close(); // Check if entry already exists in database. For image pairs, the order of // `image_id1` and `image_id2` does not matter. bool ExistsCamera(const camera_t camera_id) const; bool ExistsImage(const image_t image_id) const; bool ExistsImageWithName(std::string name) const; bool ExistsKeypoints(const image_t image_id) const; bool ExistsDescriptors(const image_t image_id) const; bool ExistsMatches(const image_t image_id1, const image_t image_id2) const; bool ExistsInlierMatches(const image_t image_id1, const image_t image_id2) const; // Number of rows in `cameras` table. size_t NumCameras() const; // Number of rows in `images` table. size_t NumImages() const; // Sum of `rows` column in `keypoints` table, i.e. number of total keypoints. size_t NumKeypoints() const; // The number of keypoints for the image with most features. size_t MaxNumKeypoints() const; // Number of descriptors for specific image. size_t NumKeypointsForImage(const image_t image_id) const; // Sum of `rows` column in `descriptors` table, // i.e. number of total descriptors. size_t NumDescriptors() const; // The number of descriptors for the image with most features. size_t MaxNumDescriptors() const; // Number of descriptors for specific image. size_t NumDescriptorsForImage(const image_t image_id) const; // Sum of `rows` column in `matches` table, i.e. number of total matches. size_t NumMatches() const; // Sum of `rows` column in `two_view_geometries` table, // i.e. number of total inlier matches. size_t NumInlierMatches() const; // Number of rows in `matches` table. size_t NumMatchedImagePairs() const; // Number of rows in `two_view_geometries` table. size_t NumVerifiedImagePairs() const; // Each image pair is assigned an unique ID in the `matches` and // `two_view_geometries` table. We intentionally avoid to store the pairs in a // separate table by using e.g. AUTOINCREMENT, since the overhead of querying // the unique pair ID is significant. inline static image_pair_t ImagePairToPairId(const image_t image_id1, const image_t image_id2); inline static void PairIdToImagePair(const image_pair_t pair_id, image_t* image_id1, image_t* image_id2); // Return true if image pairs should be swapped. Used to enforce a specific // image order to generate unique image pair identifiers independent of the // order in which the image identifiers are used. inline static bool SwapImagePair(const image_t image_id1, const image_t image_id2); // Read an existing entry in the database. The user is responsible for making // sure that the entry actually exists. For image pairs, the order of // `image_id1` and `image_id2` does not matter. Camera ReadCamera(const camera_t camera_id) const; std::vector ReadAllCameras() const; Image ReadImage(const image_t image_id) const; Image ReadImageWithName(const std::string& name) const; std::vector ReadAllImages() const; FeatureKeypoints ReadKeypoints(const image_t image_id) const; FeatureDescriptors ReadDescriptors(const image_t image_id) const; FeatureMatches ReadMatches(const image_t image_id1, const image_t image_id2) const; std::vector> ReadAllMatches() const; TwoViewGeometry ReadTwoViewGeometry(const image_t image_id1, const image_t image_id2) const; void ReadTwoViewGeometries( std::vector* image_pair_ids, std::vector* two_view_geometries) const; // Read all image pairs that have an entry in the `NumVerifiedImagePairs` // table with at least one inlier match and their number of inlier matches. void ReadTwoViewGeometryNumInliers( std::vector>* image_pairs, std::vector* num_inliers) const; // Add new camera and return its database identifier. If `use_camera_id` // is false a new identifier is automatically generated. camera_t WriteCamera(const Camera& camera, const bool use_camera_id = false) const; // Add new image and return its database identifier. If `use_image_id` // is false a new identifier is automatically generated. image_t WriteImage(const Image& image, const bool use_image_id = false) const; // Write a new entry in the database. The user is responsible for making sure // that the entry does not yet exist. For image pairs, the order of // `image_id1` and `image_id2` does not matter. void WriteKeypoints(const image_t image_id, const FeatureKeypoints& keypoints) const; void WriteDescriptors(const image_t image_id, const FeatureDescriptors& descriptors) const; void WriteMatches(const image_t image_id1, const image_t image_id2, const FeatureMatches& matches) const; void WriteTwoViewGeometry(const image_t image_id1, const image_t image_id2, const TwoViewGeometry& two_view_geometry) const; // Update an existing camera in the database. The user is responsible for // making sure that the entry already exists. void UpdateCamera(const Camera& camera) const; // Update an existing image in the database. The user is responsible for // making sure that the entry already exists. void UpdateImage(const Image& image) const; // Delete matches of an image pair. void DeleteMatches(const image_t image_id1, const image_t image_id2) const; // Delete inlier matches of an image pair. void DeleteInlierMatches(const image_t image_id1, const image_t image_id2) const; // Clear all database tables void ClearAllTables() const; // Clear the entire cameras table void ClearCameras() const; // Clear the entire images, keypoints, and descriptors tables void ClearImages() const; // Clear the entire descriptors table void ClearDescriptors() const; // Clear the entire keypoints table void ClearKeypoints() const; // Clear the entire matches table. void ClearMatches() const; // Clear the entire inlier matches table. void ClearTwoViewGeometries() const; // Merge two databases into a single, new database. static void Merge(const Database& database1, const Database& database2, Database* merged_database); private: friend class DatabaseTransaction; // Combine multiple queries into one transaction by wrapping a code section // into a `BeginTransaction` and `EndTransaction`. You can create a scoped // transaction with `DatabaseTransaction` that ends when the transaction // object is destructed. Combining queries results in faster transaction time // due to reduced locking of the database etc. void BeginTransaction() const; void EndTransaction() const; // Prepare SQL statements once at construction of the database, and reuse // the statements for multiple queries by resetting their states. void PrepareSQLStatements(); void FinalizeSQLStatements(); // Create database tables, if not existing, called when opening a database. void CreateTables() const; void CreateCameraTable() const; void CreateImageTable() const; void CreateKeypointsTable() const; void CreateDescriptorsTable() const; void CreateMatchesTable() const; void CreateTwoViewGeometriesTable() const; void UpdateSchema() const; bool ExistsTable(const std::string& table_name) const; bool ExistsColumn(const std::string& table_name, const std::string& column_name) const; bool ExistsRowId(sqlite3_stmt* sql_stmt, const sqlite3_int64 row_id) const; bool ExistsRowString(sqlite3_stmt* sql_stmt, const std::string& row_entry) const; size_t CountRows(const std::string& table) const; size_t CountRowsForEntry(sqlite3_stmt* sql_stmt, const sqlite3_int64 row_id) const; size_t SumColumn(const std::string& column, const std::string& table) const; size_t MaxColumn(const std::string& column, const std::string& table) const; sqlite3* database_ = nullptr; // Check if elements got removed from the database to only apply // the VACUUM command in such case mutable bool database_cleared_ = false; // Ensure that only one database object at a time updates the schema of a // database. Since the schema is updated every time a database is opened, this // is to ensure that there are no race conditions ("database locked" error // messages) when the user actually only intends to read from the database, // which requires to open it. static std::mutex update_schema_mutex_; // Used to ensure that only one transaction is active at the same time. std::mutex transaction_mutex_; // A collection of all `sqlite3_stmt` objects for deletion in the destructor. std::vector sql_stmts_; // num_* sqlite3_stmt* sql_stmt_num_keypoints_ = nullptr; sqlite3_stmt* sql_stmt_num_descriptors_ = nullptr; // exists_* sqlite3_stmt* sql_stmt_exists_camera_ = nullptr; sqlite3_stmt* sql_stmt_exists_image_id_ = nullptr; sqlite3_stmt* sql_stmt_exists_image_name_ = nullptr; sqlite3_stmt* sql_stmt_exists_keypoints_ = nullptr; sqlite3_stmt* sql_stmt_exists_descriptors_ = nullptr; sqlite3_stmt* sql_stmt_exists_matches_ = nullptr; sqlite3_stmt* sql_stmt_exists_two_view_geometry_ = nullptr; // add_* sqlite3_stmt* sql_stmt_add_camera_ = nullptr; sqlite3_stmt* sql_stmt_add_image_ = nullptr; // update_* sqlite3_stmt* sql_stmt_update_camera_ = nullptr; sqlite3_stmt* sql_stmt_update_image_ = nullptr; // read_* sqlite3_stmt* sql_stmt_read_camera_ = nullptr; sqlite3_stmt* sql_stmt_read_cameras_ = nullptr; sqlite3_stmt* sql_stmt_read_image_id_ = nullptr; sqlite3_stmt* sql_stmt_read_image_name_ = nullptr; sqlite3_stmt* sql_stmt_read_images_ = nullptr; sqlite3_stmt* sql_stmt_read_keypoints_ = nullptr; sqlite3_stmt* sql_stmt_read_descriptors_ = nullptr; sqlite3_stmt* sql_stmt_read_matches_ = nullptr; sqlite3_stmt* sql_stmt_read_matches_all_ = nullptr; sqlite3_stmt* sql_stmt_read_two_view_geometry_ = nullptr; sqlite3_stmt* sql_stmt_read_two_view_geometries_ = nullptr; sqlite3_stmt* sql_stmt_read_two_view_geometry_num_inliers_ = nullptr; // write_* sqlite3_stmt* sql_stmt_write_keypoints_ = nullptr; sqlite3_stmt* sql_stmt_write_descriptors_ = nullptr; sqlite3_stmt* sql_stmt_write_matches_ = nullptr; sqlite3_stmt* sql_stmt_write_two_view_geometry_ = nullptr; // delete_* sqlite3_stmt* sql_stmt_delete_matches_ = nullptr; sqlite3_stmt* sql_stmt_delete_two_view_geometry_ = nullptr; // clear_* sqlite3_stmt* sql_stmt_clear_cameras_ = nullptr; sqlite3_stmt* sql_stmt_clear_images_ = nullptr; sqlite3_stmt* sql_stmt_clear_descriptors_ = nullptr; sqlite3_stmt* sql_stmt_clear_keypoints_ = nullptr; sqlite3_stmt* sql_stmt_clear_matches_ = nullptr; sqlite3_stmt* sql_stmt_clear_two_view_geometries_ = nullptr; }; // This class automatically manages the scope of a database transaction by // calling `BeginTransaction` and `EndTransaction` during construction and // destruction, respectively. class DatabaseTransaction { public: explicit DatabaseTransaction(Database* database); ~DatabaseTransaction(); private: NON_COPYABLE(DatabaseTransaction) NON_MOVABLE(DatabaseTransaction) Database* database_; std::unique_lock database_lock_; }; //////////////////////////////////////////////////////////////////////////////// // Implementation //////////////////////////////////////////////////////////////////////////////// image_pair_t Database::ImagePairToPairId(const image_t image_id1, const image_t image_id2) { CHECK_GE(image_id1, 0); CHECK_GE(image_id2, 0); CHECK_LT(image_id1, kMaxNumImages); CHECK_LT(image_id2, kMaxNumImages); if (SwapImagePair(image_id1, image_id2)) { return static_cast(kMaxNumImages) * image_id2 + image_id1; } else { return static_cast(kMaxNumImages) * image_id1 + image_id2; } } void Database::PairIdToImagePair(const image_pair_t pair_id, image_t* image_id1, image_t* image_id2) { *image_id2 = static_cast(pair_id % kMaxNumImages); *image_id1 = static_cast((pair_id - *image_id2) / kMaxNumImages); CHECK_GE(*image_id1, 0); CHECK_GE(*image_id2, 0); CHECK_LT(*image_id1, kMaxNumImages); CHECK_LT(*image_id2, kMaxNumImages); } // Return true if image pairs should be swapped. Used to enforce a specific // image order to generate unique image pair identifiers independent of the // order in which the image identifiers are used. bool Database::SwapImagePair(const image_t image_id1, const image_t image_id2) { return image_id1 > image_id2; } } // namespace colmap #endif // COLMAP_SRC_BASE_DATABASE_H_