ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
scene_clustering.cc
Go to the documentation of this file.
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/scene_clustering.h"
33 
34 #include <set>
35 
36 #include "base/graph_cut.h"
37 #include "util/random.h"
38 
39 namespace colmap {
40 
44  return true;
45 }
46 
47 SceneClustering::SceneClustering(const Options& options) : options_(options) {
48  CHECK(options_.Check());
49 }
50 
52  const std::vector<std::pair<image_t, image_t>>& image_pairs,
53  const std::vector<int>& num_inliers) {
54  CHECK(!root_cluster_);
55  CHECK_EQ(image_pairs.size(), num_inliers.size());
56 
57  std::set<image_t> image_ids;
58  std::vector<std::pair<int, int>> edges;
59  edges.reserve(image_pairs.size());
60  for (const auto& image_pair : image_pairs) {
61  image_ids.insert(image_pair.first);
62  image_ids.insert(image_pair.second);
63  edges.emplace_back(image_pair.first, image_pair.second);
64  }
65 
66  root_cluster_.reset(new Cluster());
67  root_cluster_->image_ids.insert(root_cluster_->image_ids.end(),
68  image_ids.begin(), image_ids.end());
69  if (options_.is_hierarchical) {
70  PartitionHierarchicalCluster(edges, num_inliers, root_cluster_.get());
71  } else {
72  PartitionFlatCluster(edges, num_inliers);
73  }
74 }
75 
76 void SceneClustering::PartitionHierarchicalCluster(
77  const std::vector<std::pair<int, int>>& edges,
78  const std::vector<int>& weights, Cluster* cluster) {
79  CHECK_EQ(edges.size(), weights.size());
80 
81  // If the cluster is small enough, we return from the recursive clustering.
82  if (edges.size() == 0 ||
83  cluster->image_ids.size() <=
84  static_cast<size_t>(options_.leaf_max_num_images)) {
85  return;
86  }
87 
88  // Partition the cluster using a normalized cut on the scene graph.
89  const auto labels =
90  ComputeNormalizedMinGraphCut(edges, weights, options_.branching);
91 
92  // Assign the images to the clustered child clusters.
93  cluster->child_clusters.resize(options_.branching);
94  for (const auto image_id : cluster->image_ids) {
95  if (labels.count(image_id)) {
96  auto& child_cluster = cluster->child_clusters.at(labels.at(image_id));
97  child_cluster.image_ids.push_back(image_id);
98  } else {
99  std::cout << "WARN: Graph cut failed to assign cluster label to image "
100  << image_id << "; assigning to cluster 0" << std::endl;
101  cluster->child_clusters.at(0).image_ids.push_back(image_id);
102  }
103  }
104 
105  // Collect the edges based on whether they are inter or intra child clusters.
106  std::vector<std::vector<std::pair<int, int>>> child_edges(options_.branching);
107  std::vector<std::vector<int>> child_weights(options_.branching);
108  std::vector<std::vector<std::pair<std::pair<int, int>, int>>>
109  overlapping_edges(options_.branching);
110  for (size_t i = 0; i < edges.size(); ++i) {
111  const int label1 = labels.at(edges[i].first);
112  const int label2 = labels.at(edges[i].second);
113  if (label1 == label2) {
114  child_edges.at(label1).push_back(edges[i]);
115  child_weights.at(label1).push_back(weights[i]);
116  } else {
117  overlapping_edges.at(label1).emplace_back(edges[i], weights[i]);
118  overlapping_edges.at(label2).emplace_back(edges[i], weights[i]);
119  }
120  }
121 
122  // Recursively partition all the child clusters.
123  for (int i = 0; i < options_.branching; ++i) {
124  PartitionHierarchicalCluster(child_edges[i], child_weights[i],
125  &cluster->child_clusters[i]);
126  }
127 
128  if (options_.image_overlap > 0) {
129  for (int i = 0; i < options_.branching; ++i) {
130  // Sort the overlapping edges by the number of inlier matches, such
131  // that we add overlapping images with many common observations.
132  std::sort(overlapping_edges[i].begin(), overlapping_edges[i].end(),
133  [](const std::pair<std::pair<int, int>, int>& edge1,
134  const std::pair<std::pair<int, int>, int>& edge2) {
135  return edge1.second > edge2.second;
136  });
137 
138  // Select overlapping edges at random and add image to cluster.
139  std::set<int> overlapping_image_ids;
140  for (const auto& edge : overlapping_edges[i]) {
141  if (labels.at(edge.first.first) == i) {
142  overlapping_image_ids.insert(edge.first.second);
143  } else {
144  overlapping_image_ids.insert(edge.first.first);
145  }
146  if (overlapping_image_ids.size() >=
147  static_cast<size_t>(options_.image_overlap)) {
148  break;
149  }
150  }
151 
152  // Recursively append the overlapping images to cluster and its children.
153  std::function<void(Cluster*)> InsertOverlappingImageIds =
154  [&](Cluster* cluster) {
155  cluster->image_ids.insert(cluster->image_ids.end(),
156  overlapping_image_ids.begin(),
157  overlapping_image_ids.end());
158  for (auto& child_cluster : cluster->child_clusters) {
159  InsertOverlappingImageIds(&child_cluster);
160  }
161  };
162 
163  InsertOverlappingImageIds(&cluster->child_clusters[i]);
164  }
165  }
166 }
167 
168 void SceneClustering::PartitionFlatCluster(
169  const std::vector<std::pair<int, int>>& edges,
170  const std::vector<int>& weights) {
171  CHECK_EQ(edges.size(), weights.size());
172 
173  // Partition the cluster using a normalized cut on the scene graph.
174  const auto labels =
175  ComputeNormalizedMinGraphCut(edges, weights, options_.branching);
176 
177  // Assign the images to the clustered child clusters.
178  root_cluster_->child_clusters.resize(options_.branching);
179  for (const auto image_id : root_cluster_->image_ids) {
180  if (labels.count(image_id)) {
181  auto& child_cluster =
182  root_cluster_->child_clusters.at(labels.at(image_id));
183  child_cluster.image_ids.push_back(image_id);
184  }
185  }
186 
187  // Sort child clusters by descending size of images and secondarily by lowest
188  // image id.
189  std::sort(root_cluster_->child_clusters.begin(),
190  root_cluster_->child_clusters.end(),
191  [](const Cluster& first, const Cluster& second) {
192  return first.image_ids.size() >= second.image_ids.size() &&
193  *std::min_element(first.image_ids.begin(),
194  first.image_ids.end()) <
195  *std::min_element(second.image_ids.begin(),
196  second.image_ids.end());
197  });
198 
199  // For each image find all related images with their weights
200  std::unordered_map<int, std::vector<std::pair<int, int>>> related_images;
201  for (size_t i = 0; i < edges.size(); ++i) {
202  related_images[edges[i].first].emplace_back(edges[i].second, weights[i]);
203  related_images[edges[i].second].emplace_back(edges[i].first, weights[i]);
204  }
205 
206  // Sort related images by decreasing weights
207  for (auto& image : related_images) {
208  std::sort(image.second.begin(), image.second.end(),
209  [](const std::pair<int, int>& first,
210  const std::pair<int, int>& second) {
211  return first.second > second.second;
212  });
213  }
214 
215  // For each cluster add as many of the needed matching images up to
216  // the max image overal allowance
217  // We do the process sequentially for each image to ensure that at
218  // least we get the best matches firat
219  for (int i = 0; i < options_.branching; ++i) {
220  auto& orig_image_ids = root_cluster_->child_clusters[i].image_ids;
221  std::set<int> cluster_images(
222  root_cluster_->child_clusters[i].image_ids.begin(),
223  root_cluster_->child_clusters[i].image_ids.end());
224  const size_t max_size = cluster_images.size() + options_.image_overlap;
225  // check up to all the desired matches
226  for (size_t j = 0; j < static_cast<size_t>(options_.num_image_matches) &&
227  cluster_images.size() < max_size;
228  ++j) {
229  for (const image_t image_id : orig_image_ids) {
230  const auto& images = related_images[image_id];
231  if (j >= images.size()) {
232  continue;
233  }
234  // image not exists in cluster so we add it in the overlap set
235  const int related_id = images[j].first;
236  if (cluster_images.count(related_id) == 0) {
237  cluster_images.insert(related_id);
238  }
239  if (cluster_images.size() >= max_size) {
240  break;
241  }
242  }
243  }
244  orig_image_ids.clear();
245  orig_image_ids.insert(orig_image_ids.end(), cluster_images.begin(),
246  cluster_images.end());
247  }
248 }
249 
251  return root_cluster_.get();
252 }
253 
254 std::vector<const SceneClustering::Cluster*> SceneClustering::GetLeafClusters()
255  const {
256  CHECK(root_cluster_);
257 
258  std::vector<const Cluster*> leaf_clusters;
259 
260  if (!root_cluster_) {
261  return leaf_clusters;
262  } else if (root_cluster_->child_clusters.empty()) {
263  leaf_clusters.push_back(root_cluster_.get());
264  return leaf_clusters;
265  }
266 
267  std::vector<const Cluster*> non_leaf_clusters;
268  non_leaf_clusters.push_back(root_cluster_.get());
269 
270  while (!non_leaf_clusters.empty()) {
271  const auto cluster = non_leaf_clusters.back();
272  non_leaf_clusters.pop_back();
273 
274  for (const auto& child_cluster : cluster->child_clusters) {
275  if (child_cluster.child_clusters.empty()) {
276  leaf_clusters.push_back(&child_cluster);
277  } else {
278  non_leaf_clusters.push_back(&child_cluster);
279  }
280  }
281  }
282 
283  return leaf_clusters;
284 }
285 
287  const Database& database) {
288  std::cout << "Reading scene graph..." << std::endl;
289  std::vector<std::pair<image_t, image_t>> image_pairs;
290  std::vector<int> num_inliers;
291  database.ReadTwoViewGeometryNumInliers(&image_pairs, &num_inliers);
292 
293  std::cout << "Partitioning scene graph..." << std::endl;
294  SceneClustering scene_clustering(options);
295  scene_clustering.Partition(image_pairs, num_inliers);
296  return scene_clustering;
297 }
298 
299 } // namespace colmap
std::shared_ptr< core::Tensor > image
void ReadTwoViewGeometryNumInliers(std::vector< std::pair< image_t, image_t >> *image_pairs, std::vector< int > *num_inliers) const
Definition: database.cc:579
const Cluster * GetRootCluster() const
SceneClustering(const Options &options)
void Partition(const std::vector< std::pair< image_t, image_t >> &image_pairs, const std::vector< int > &num_inliers)
static SceneClustering Create(const Options &options, const Database &database)
std::vector< const Cluster * > GetLeafClusters() const
#define CHECK_OPTION_GT(val1, val2)
Definition: logging.h:34
#define CHECK_OPTION_GE(val1, val2)
Definition: logging.h:33
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
uint32_t image_t
Definition: types.h:61
std::unordered_map< int, int > ComputeNormalizedMinGraphCut(const std::vector< std::pair< int, int >> &edges, const std::vector< int > &weights, const int num_parts)
Definition: graph_cut.cc:186