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