1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 //     * Redistributions of source code must retain the above copyright
8 //       notice, this list of conditions and the following disclaimer.
9 //
10 //     * Redistributions in binary form must reproduce the above copyright
11 //       notice, this list of conditions and the following disclaimer in the
12 //       documentation and/or other materials provided with the distribution.
13 //
14 //     * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 //       its contributors may be used to endorse or promote products derived
16 //       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, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // 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 THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31 
32 #include "base/database_cache.h"
33 
34 #include <unordered_set>
35 
36 #include "feature/utils.h"
37 #include "util/string.h"
38 #include "util/timer.h"
39 
40 namespace colmap {
41 
DatabaseCache()42 DatabaseCache::DatabaseCache() {}
43 
AddCamera(const class Camera & camera)44 void DatabaseCache::AddCamera(const class Camera& camera) {
45   CHECK(!ExistsCamera(camera.CameraId()));
46   cameras_.emplace(camera.CameraId(), camera);
47 }
48 
AddImage(const class Image & image)49 void DatabaseCache::AddImage(const class Image& image) {
50   CHECK(!ExistsImage(image.ImageId()));
51   images_.emplace(image.ImageId(), image);
52   correspondence_graph_.AddImage(image.ImageId(), image.NumPoints2D());
53 }
54 
Load(const Database & database,const size_t min_num_matches,const bool ignore_watermarks,const std::unordered_set<std::string> & image_names)55 void DatabaseCache::Load(const Database& database, const size_t min_num_matches,
56                          const bool ignore_watermarks,
57                          const std::unordered_set<std::string>& image_names) {
58   //////////////////////////////////////////////////////////////////////////////
59   // Load cameras
60   //////////////////////////////////////////////////////////////////////////////
61 
62   Timer timer;
63 
64   timer.Start();
65   std::cout << "Loading cameras..." << std::flush;
66 
67   {
68     const std::vector<class Camera> cameras = database.ReadAllCameras();
69     cameras_.reserve(cameras.size());
70     for (const auto& camera : cameras) {
71       cameras_.emplace(camera.CameraId(), camera);
72     }
73   }
74 
75   std::cout << StringPrintf(" %d in %.3fs", cameras_.size(),
76                             timer.ElapsedSeconds())
77             << std::endl;
78 
79   //////////////////////////////////////////////////////////////////////////////
80   // Load matches
81   //////////////////////////////////////////////////////////////////////////////
82 
83   timer.Restart();
84   std::cout << "Loading matches..." << std::flush;
85 
86   std::vector<image_pair_t> image_pair_ids;
87   std::vector<TwoViewGeometry> two_view_geometries;
88   database.ReadTwoViewGeometries(&image_pair_ids, &two_view_geometries);
89 
90   std::cout << StringPrintf(" %d in %.3fs", image_pair_ids.size(),
91                             timer.ElapsedSeconds())
92             << std::endl;
93 
94   auto UseInlierMatchesCheck = [min_num_matches, ignore_watermarks](
95                                    const TwoViewGeometry& two_view_geometry) {
96     return static_cast<size_t>(two_view_geometry.inlier_matches.size()) >=
97                min_num_matches &&
98            (!ignore_watermarks ||
99             two_view_geometry.config != TwoViewGeometry::WATERMARK);
100   };
101 
102   //////////////////////////////////////////////////////////////////////////////
103   // Load images
104   //////////////////////////////////////////////////////////////////////////////
105 
106   timer.Restart();
107   std::cout << "Loading images..." << std::flush;
108 
109   std::unordered_set<image_t> image_ids;
110 
111   {
112     const std::vector<class Image> images = database.ReadAllImages();
113 
114     // Determines for which images data should be loaded.
115     if (image_names.empty()) {
116       for (const auto& image : images) {
117         image_ids.insert(image.ImageId());
118       }
119     } else {
120       for (const auto& image : images) {
121         if (image_names.count(image.Name()) > 0) {
122           image_ids.insert(image.ImageId());
123         }
124       }
125     }
126 
127     // Collect all images that are connected in the correspondence graph.
128     std::unordered_set<image_t> connected_image_ids;
129     connected_image_ids.reserve(image_ids.size());
130     for (size_t i = 0; i < image_pair_ids.size(); ++i) {
131       if (UseInlierMatchesCheck(two_view_geometries[i])) {
132         image_t image_id1;
133         image_t image_id2;
134         Database::PairIdToImagePair(image_pair_ids[i], &image_id1, &image_id2);
135         if (image_ids.count(image_id1) > 0 && image_ids.count(image_id2) > 0) {
136           connected_image_ids.insert(image_id1);
137           connected_image_ids.insert(image_id2);
138         }
139       }
140     }
141 
142     // Load images with correspondences and discard images without
143     // correspondences, as those images are useless for SfM.
144     images_.reserve(connected_image_ids.size());
145     for (const auto& image : images) {
146       if (image_ids.count(image.ImageId()) > 0 &&
147           connected_image_ids.count(image.ImageId()) > 0) {
148         images_.emplace(image.ImageId(), image);
149         const FeatureKeypoints keypoints =
150             database.ReadKeypoints(image.ImageId());
151         const std::vector<Eigen::Vector2d> points =
152             FeatureKeypointsToPointsVector(keypoints);
153         images_[image.ImageId()].SetPoints2D(points);
154       }
155     }
156 
157     std::cout << StringPrintf(" %d in %.3fs (connected %d)", images.size(),
158                               timer.ElapsedSeconds(),
159                               connected_image_ids.size())
160               << std::endl;
161   }
162 
163   //////////////////////////////////////////////////////////////////////////////
164   // Build correspondence graph
165   //////////////////////////////////////////////////////////////////////////////
166 
167   timer.Restart();
168   std::cout << "Building correspondence graph..." << std::flush;
169 
170   for (const auto& image : images_) {
171     correspondence_graph_.AddImage(image.first, image.second.NumPoints2D());
172   }
173 
174   size_t num_ignored_image_pairs = 0;
175   for (size_t i = 0; i < image_pair_ids.size(); ++i) {
176     if (UseInlierMatchesCheck(two_view_geometries[i])) {
177       image_t image_id1;
178       image_t image_id2;
179       Database::PairIdToImagePair(image_pair_ids[i], &image_id1, &image_id2);
180       if (image_ids.count(image_id1) > 0 && image_ids.count(image_id2) > 0) {
181         correspondence_graph_.AddCorrespondences(
182             image_id1, image_id2, two_view_geometries[i].inlier_matches);
183       } else {
184         num_ignored_image_pairs += 1;
185       }
186     } else {
187       num_ignored_image_pairs += 1;
188     }
189   }
190 
191   correspondence_graph_.Finalize();
192 
193   // Set number of observations and correspondences per image.
194   for (auto& image : images_) {
195     image.second.SetNumObservations(
196         correspondence_graph_.NumObservationsForImage(image.first));
197     image.second.SetNumCorrespondences(
198         correspondence_graph_.NumCorrespondencesForImage(image.first));
199   }
200 
201   std::cout << StringPrintf(" in %.3fs (ignored %d)", timer.ElapsedSeconds(),
202                             num_ignored_image_pairs)
203             << std::endl;
204 }
205 
FindImageWithName(const std::string & name) const206 const class Image* DatabaseCache::FindImageWithName(
207     const std::string& name) const {
208   for (const auto& image : images_) {
209     if (image.second.Name() == name) {
210       return &image.second;
211     }
212   }
213   return nullptr;
214 }
215 
216 }  // namespace colmap
217