ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ransac.h
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #pragma once
9 
10 #include <cfloat>
11 #include <random>
12 #include <stdexcept>
13 #include <vector>
14 
15 #include "optim/random_sampler.h"
17 #include "util/alignment.h"
18 #include "util/logging.h"
19 
20 namespace colmap {
21 
22 struct RANSACOptions {
23  // Maximum error for a sample to be considered as an inlier. Note that
24  // the residual of an estimator corresponds to a squared error.
25  double max_error = 0.0;
26 
27  // A priori assumed minimum inlier ratio, which determines the maximum
28  // number of iterations. Only applies if smaller than `max_num_trials`.
29  double min_inlier_ratio = 0.1;
30 
31  // Abort the iteration if minimum probability that one sample is free from
32  // outliers is reached.
33  double confidence = 0.99;
34 
35  // The num_trials_multiplier to the dynamically computed maximum number of
36  // iterations based on the specified confidence value.
38 
39  // Number of random trials to estimate model from random subset.
40  size_t min_num_trials = 0;
41  size_t max_num_trials = std::numeric_limits<size_t>::max();
42 
43  void Check() const {
44  CHECK_GT(max_error, 0);
45  CHECK_GE(min_inlier_ratio, 0);
46  CHECK_LE(min_inlier_ratio, 1);
47  CHECK_GE(confidence, 0);
48  CHECK_LE(confidence, 1);
49  CHECK_LE(min_num_trials, max_num_trials);
50  }
51 };
52 
53 template <typename Estimator,
54  typename SupportMeasurer = InlierSupportMeasurer,
55  typename Sampler = RandomSampler>
56 class RANSAC {
57 public:
58  struct Report {
59  // Whether the estimation was successful.
60  bool success = false;
61 
62  // The number of RANSAC trials / iterations.
63  size_t num_trials = 0;
64 
65  // The support of the estimated model.
66  typename SupportMeasurer::Support support;
67 
68  // Boolean mask which is true if a sample is an inlier.
69  std::vector<char> inlier_mask;
70 
71  // The estimated model.
72  typename Estimator::M_t model;
73  };
74 
75  explicit RANSAC(const RANSACOptions& options);
76 
77  // Determine the maximum number of trials required to sample at least one
78  // outlier-free random set of samples with the specified confidence,
79  // given the inlier ratio.
80  //
81  // @param num_inliers The number of inliers.
82  // @param num_samples The total number of
83  // samples.
84  // @param confidence Confidence that one
85  // sample is
86  // outlier-free.
87  // @param num_trials_multiplier Multiplication factor to the computed
88  // number of trials.
89  //
90  // @return The required number of iterations.
91  static size_t ComputeNumTrials(const size_t num_inliers,
92  const size_t num_samples,
93  const double confidence,
94  const double num_trials_multiplier);
95 
96  // Robustly estimate model with RANSAC (RANdom SAmple Consensus).
97  //
98  // @param X Independent variables.
99  // @param Y Dependent variables.
100  //
101  // @return The report with the results of the estimation.
102  Report Estimate(const std::vector<typename Estimator::X_t>& X,
103  const std::vector<typename Estimator::Y_t>& Y);
104 
105  // Objects used in RANSAC procedure. Access useful to define custom behavior
106  // through options or e.g. to compute residuals.
107  Estimator estimator;
109  SupportMeasurer support_measurer;
110 
111 protected:
113 };
114 
116 // Implementation
118 
119 template <typename Estimator, typename SupportMeasurer, typename Sampler>
121  const RANSACOptions& options)
122  : sampler(Sampler(Estimator::kMinNumSamples)), options_(options) {
123  options.Check();
124 
125  // Determine max_num_trials based on assumed `min_inlier_ratio`.
126  const size_t kNumSamples = 100000;
127  const size_t dyn_max_num_trials = ComputeNumTrials(
128  static_cast<size_t>(options_.min_inlier_ratio * kNumSamples),
129  kNumSamples, options_.confidence,
132  std::min<size_t>(options_.max_num_trials, dyn_max_num_trials);
133 }
134 
135 template <typename Estimator, typename SupportMeasurer, typename Sampler>
137  const size_t num_inliers,
138  const size_t num_samples,
139  const double confidence,
140  const double num_trials_multiplier) {
141  const double inlier_ratio = num_inliers / static_cast<double>(num_samples);
142 
143  const double nom = 1 - confidence;
144  if (nom <= 0) {
145  return std::numeric_limits<size_t>::max();
146  }
147 
148  const double denom = 1 - std::pow(inlier_ratio, Estimator::kMinNumSamples);
149  if (denom <= 0) {
150  return 1;
151  }
152 
153  return static_cast<size_t>(
154  std::ceil(std::log(nom) / std::log(denom) * num_trials_multiplier));
155 }
156 
157 template <typename Estimator, typename SupportMeasurer, typename Sampler>
160  const std::vector<typename Estimator::X_t>& X,
161  const std::vector<typename Estimator::Y_t>& Y) {
162  CHECK_EQ(X.size(), Y.size());
163 
164  const size_t num_samples = X.size();
165 
166  Report report;
167  report.success = false;
168  report.num_trials = 0;
169 
170  if (num_samples < Estimator::kMinNumSamples) {
171  return report;
172  }
173 
174  typename SupportMeasurer::Support best_support;
175  typename Estimator::M_t best_model;
176 
177  bool abort = false;
178 
179  const double max_residual = options_.max_error * options_.max_error;
180 
181  std::vector<double> residuals(num_samples);
182 
183  std::vector<typename Estimator::X_t> X_rand(Estimator::kMinNumSamples);
184  std::vector<typename Estimator::Y_t> Y_rand(Estimator::kMinNumSamples);
185 
186  sampler.Initialize(num_samples);
187 
188  size_t max_num_trials = options_.max_num_trials;
189  max_num_trials = std::min<size_t>(max_num_trials, sampler.MaxNumSamples());
190  size_t dyn_max_num_trials = max_num_trials;
191 
192  for (report.num_trials = 0; report.num_trials < max_num_trials;
193  ++report.num_trials) {
194  if (abort) {
195  report.num_trials += 1;
196  break;
197  }
198 
199  sampler.SampleXY(X, Y, &X_rand, &Y_rand);
200 
201  // Estimate model for current subset.
202  const std::vector<typename Estimator::M_t> sample_models =
203  estimator.Estimate(X_rand, Y_rand);
204 
205  // Iterate through all estimated models.
206  for (const auto& sample_model : sample_models) {
207  estimator.Residuals(X, Y, sample_model, &residuals);
208  CHECK_EQ(residuals.size(), num_samples);
209 
210  const auto support =
211  support_measurer.Evaluate(residuals, max_residual);
212 
213  // Save as best subset if better than all previous subsets.
214  if (support_measurer.Compare(support, best_support)) {
215  best_support = support;
216  best_model = sample_model;
217 
218  dyn_max_num_trials =
219  ComputeNumTrials(best_support.num_inliers, num_samples,
220  options_.confidence,
221  options_.dyn_num_trials_multiplier);
222  }
223 
224  if (report.num_trials >= dyn_max_num_trials &&
225  report.num_trials >= options_.min_num_trials) {
226  abort = true;
227  break;
228  }
229  }
230  }
231 
232  report.support = best_support;
233  report.model = best_model;
234 
235  // No valid model was found.
236  if (report.support.num_inliers < estimator.kMinNumSamples) {
237  return report;
238  }
239 
240  report.success = true;
241 
242  // Determine inlier mask. Note that this calculates the residuals for the
243  // best model twice, but saves to copy and fill the inlier mask for each
244  // evaluated model. Some benchmarking revealed that this approach is faster.
245 
246  estimator.Residuals(X, Y, report.model, &residuals);
247  CHECK_EQ(residuals.size(), num_samples);
248 
249  report.inlier_mask.resize(num_samples);
250  for (size_t i = 0; i < residuals.size(); ++i) {
251  report.inlier_mask[i] = residuals[i] <= max_residual;
252  }
253 
254  return report;
255 }
256 
257 } // namespace colmap
void * X
Definition: SmallVector.cpp:45
RANSAC(const RANSACOptions &options)
Definition: ransac.h:120
SupportMeasurer support_measurer
Definition: ransac.h:109
Estimator estimator
Definition: ransac.h:107
Sampler sampler
Definition: ransac.h:108
RANSACOptions options_
Definition: ransac.h:112
static size_t ComputeNumTrials(const size_t num_inliers, const size_t num_samples, const double confidence, const double num_trials_multiplier)
Definition: ransac.h:136
Report Estimate(const std::vector< typename Estimator::X_t > &X, const std::vector< typename Estimator::Y_t > &Y)
Definition: ransac.h:159
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
static const int kMinNumSamples
size_t min_num_trials
Definition: ransac.h:40
void Check() const
Definition: ransac.h:43
double min_inlier_ratio
Definition: ransac.h:29
size_t max_num_trials
Definition: ransac.h:41
double dyn_num_trials_multiplier
Definition: ransac.h:37
std::vector< char > inlier_mask
Definition: ransac.h:69
Estimator::M_t model
Definition: ransac.h:72
SupportMeasurer::Support support
Definition: ransac.h:66