Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
PreemptiveRANSAC.hh
1 /* This file is part of the BIAS library (Basic ImageAlgorithmS).
2 
3  Copyright (C) 2003, 2004 (see file CONTACTS for details)
4  Multimediale Systeme der Informationsverarbeitung
5  Institut fuer Informatik
6  Christian-Albrechts-Universitaet Kiel
7 
8  BIAS is free software; you can redistribute it and/or modify
9  it under the terms of the GNU Lesser General Public License as published by
10  the Free Software Foundation; either version 2.1 of the License, or
11  (at your option) any later version.
12 
13  BIAS is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU Lesser General Public License for more details.
17 
18  You should have received a copy of the GNU Lesser General Public License
19  along with BIAS; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
21 #ifndef __PreemptiveRANSAC_hh__
22 #define __PreemptiveRANSAC_hh__
23 
24 #include <bias_config.h>
25 #include <Base/Common/BIASpragmaStart.hh>
26 
27 #include <Base/Debug/Debug.hh>
28 #include <Base/Math/Random.hh>
29 #include <MathAlgo/Median1D.hh>
30 #include <MathAlgo/RANSACEvaluatorInterface.hh>
31 
32 #include <list>
33 #include <vector>
34 #include <limits>
35 #include <algorithm>
36 
37 #ifndef NAN
38 # define NAN sqrt(-1.0)
39 #endif
40 
41 #define PRANSAC_NO_SOLUTION -5
42 #define PRANSAC_SUBOPTIMAL_PARAMETERS -4
43 #define PRANSAC_INVALID_PARAMETERS -3
44 #define PRANSAC_NOTE_ENOUGH_SAMPLES -2
45 #define PRANSAC_NO_SOLUTION_LEFT -1
46 #define PRANSAC_OK 0
47 #define PRANSAC_NOT_ENOUGH_INLIERS 1
48 #define PRANSAC_SCORE_TOO_BIG 2
49 #define PRANSAC_ 2
50 
51 namespace BIAS {
52 
53 
54  /** @class PreemptiveRANSAC
55  @ingroup g_mathalgo
56  @brief Fast RANSAC after David Nister, "Preemptive RANSAC for Live
57  Structure And Motion Estimation", Internation Conference on Computer
58  Vision (ICCV) 2003
59 
60  The algorithm (preemptive RANSAC) is decoupled from the problem (for
61  example HMatrix estimation) using a common interface class, the
62  RANSACEvaluatorInterace. To use this class it is therefore necessary to
63  derive from RANSACEvaluatorInterface and overload the functions:
64 
65  bool IsInlier(const SolutionType& solution,
66  const unsigned data_index,
67  double& score) = 0;
68 
69  int GetSampleSolutions(const std::vector<unsigned> &which_samples,
70  std::vector<SolutionType> &solutions)= 0;
71 
72  bool RefineSolution(const std::vector<bool>& inliers,
73  SolutionType& solution) = 0;
74 
75  Instantiate PreemptiveRANSAC with a pointer to the sibling of
76  RANSACEvaluatorInterface as argment to the constructor and call
77  alternatingly
78 
79  Init() and
80  SolveMaster()
81 
82  @author woelk 2006? */
83 
84  template <class SolutionType>
85  class PreemptiveRANSAC : virtual public Debug
86  {
87  public:
89 
90  virtual ~PreemptiveRANSAC() {};
91 
92  void Init();
93 
94  void AddSolutionGuesses(const std::vector<SolutionType>& guesses);
95 
96  /** @returns PRANSAC_OK when a solution could be found,
97  a positive number when a solution could be found whcih did not met the
98  minimum qulaity criteria (i.e. min_num_inliers and maximum score)
99  and a negative number when absolutly no solution could be found */
100  int SolveMaster(const double inlying_data_fraction, const double max_score,
101  SolutionType &solution, std::vector<bool> &inliers,
102  unsigned &num_inliers, double& score,
103  const bool auto_compute_max_samples = false);
104 
105 
106  /** @brief Set max number of samples.
107 
108  Ransacing will stop after max_samples samples, regardless
109  of solution quality. Normally, more than a few thousand samples
110  signifies a problem. */
111  inline void SetMaxSamples(const unsigned max_samples)
112  { _uiMaxSamples = max_samples; }
113 
114  /** @brief Set min number of samples.
115 
116  Ransacing will evaluate and compare solutions using sets of
117  MinSamples solutions. */
118  inline void SetMinSamples(const unsigned min_samples)
119  { _uiMinSamples = min_samples; }
120 
121  inline void SetGreedy(const bool greedy)
122  { _bGreedy = greedy; }
123 
124  /** the number of measurements that is initially evaluated */
125  inline void SetNumInitialChecked(const unsigned num_initial_checked)
126  { _uiNumInitialChecked = num_initial_checked; }
127 
128  /** when less than MinNumInitialInliers are found, the solution is
129  rejected straight away */
130  inline void SetMinNumInitialInliers(const unsigned min_num_initial_inl)
131  { _uiMinNumInitialInliers = min_num_initial_inl; }
132 
133  inline void SetRefineSolutions(const bool refine_solutions)
134  { _bRefineSolutions = refine_solutions; }
135 
136  /** after each EvaluationBlockSize-th measurement, the solutions are
137  compared and only the better half is kept */
138  inline void SetEvaluationBlockSize(const unsigned eval_block_size)
139  { _uiEvaluationBlockSize = eval_block_size; }
140 
141  protected:
142  /// randomizer instance used for evaluation order computation
144  /// the helper class for solution computation and sample evaluation
146  /// the number of samples needed by GetSampleSolutions
147  unsigned _uiSampleSize;
148  /// the minimu and maximum number of samples investigated
150  /// the number of data for evaluation
151  unsigned _uiDataSize;
152  /// should termination be aborted when first good solution is found?
153  bool _bGreedy;
154  /// the number of datat points that is initially checked
156  /// the minimum number of inlier from _uiNumInitialChecked
158  /// should the solutions be refined?
160  /// after EvaluationBlockSize, bad solutions are removed
162  /// just to check if the class is used correctly
164  /// prevents that calls to GenerateSample_() generate the same random
165  /// sample if sample solution creation fails!
167 
168  struct SolStruct {
169  SolutionType solution;
170  /// evaluation of the solution has taken place up to eval_index
171  unsigned eval_index;
172  // the accumulated score of the so-far evaluated datas
173  double score;
174  // the number of inliers of the so-far evaluated datas
175  unsigned num_inliers;
176  /** inlier indicator for the data in the order given by _EvaluationOrder
177  i.e. inliers[i] indicates if data[_EvaluationOrder[i]] is an inlier*/
178  std::vector<bool> inliers;
179  /// has a refined version of this solution lareday be added?
180  bool refined;
181  };
182 
183  /// all (remaining) solutions
184  std::list<SolStruct> _Solutions;
185  /// the order in which data is used for solutions evaluation
186  std::vector<unsigned> _EvaluationOrder;
187 
188  void GenerateEvaluationOrder_(const unsigned data_size);
189 
190  /** returns the number of generated solution */
191  int GenerateSolutions_(const unsigned block_num,
192  const unsigned sample_size,
193  const unsigned data_size);
194 
195  /** returns the number of succesfully refined solutions */
196  int GenerateRefinedSolutions_(const unsigned data_size);
197 
198  /** returns the number of rejected solutions */
199  int InitialRejectTcd_(const unsigned min_num_inliers,
200  const unsigned num_checked);
201 
202  void EvaluateSolutions_(const unsigned start_index,
203  const unsigned data_size);
204 
205  /** returns the number of rejected solutions */
206  int RejectSolutions_(const unsigned eval_index);
207 
208  inline bool IsInlier_(typename std::list<SolStruct>::iterator solution,
209  const unsigned data_index, double& score);
210 
211  int GetSolution_(const unsigned min_num_inliers, const double max_score,
212  SolutionType& solution, std::vector<bool>& inliers,
213  unsigned& num_inliers, double & score) const;
214 
215  inline void ConvertInliers_(const std::vector<bool>& inliers_in_eval_order,
216  std::vector<bool>& inliers) const;
217 
218  const struct SolStruct *
219  GetSolutionMaxInliers_(const unsigned min_num_inliers,
220  const double max_score) const
221  {
222  SolStruct const *best = NULL;
223  unsigned max_inliers = 0;
224  double min_score = std::numeric_limits<double>::max();
225  typename std::list<SolStruct>::const_iterator it;
226  for (it=_Solutions.begin(); it!=_Solutions.end();it++){
227  BCDOUT(D_PRANSAC_GetSolution, "solution: "<<it->solution<<std::endl);
228  if (it->num_inliers>max_inliers){
229  max_inliers = it->num_inliers;
230  min_score = it->score;
231  BCDOUT(D_PRANSAC_GetSolution, " is best (num inliers "<<max_inliers
232  <<", score "<<min_score<<")"<<std::endl);
233  best = &(*it);
234  } else {
235  BCDOUT(D_PRANSAC_GetSolution, " (num inliers "<<max_inliers
236  <<", score "<<min_score<<")"<<std::endl);
237  }
238  if (it->num_inliers==max_inliers && it->score < min_score){
239  min_score = it->score;
240  best = &(*it);
241  BCDOUT(D_PRANSAC_GetSolution, " new best score "<<min_score
242  <<std::endl);
243  }
244  }
245 
246  return best;
247  }
248 
249  const struct SolStruct *
250  GetSolutionMinScore_(const unsigned min_num_inliers,
251  const double max_score) const
252  {
253  SolStruct const *best = NULL;
254  double min_score = std::numeric_limits<double>::max();
255  typename std::list<SolStruct>::const_iterator it;
256  for (it=_Solutions.begin(); it!=_Solutions.end(); it++){
257  BCDOUT(D_PRANSAC_GetSolution, "solution: "<<it->solution
258  <<std::endl);
259  if (it->score < min_score){
260  min_score = it->score;
261  best = &(*it);
262  BCDOUT(D_PRANSAC_GetSolution, " is best (min_score "<<min_score
263  <<" / num inliers: "<<it->num_inliers<<" )"<<std::endl);
264  } else {
265  BCDOUT(D_PRANSAC_GetSolution, " score: "<<it->score
266  <<" / num inliers: "<<it->num_inliers<<std::endl);
267  }
268  }
269  return best;
270  }
271 
272  };
273 
274  ///////////////////////////////////////////////////////////////
275 
276  template <class SolutionType>
279  : _RANSACEvaluator(shi), _uiSampleSize(0u), _uiMinSamples(50),
280  _uiMaxSamples(1000),
281  _uiDataSize(0), _bGreedy(true), _uiNumInitialChecked(8),
282  _uiMinNumInitialInliers(1),
283  _bRefineSolutions(true), _uiEvaluationBlockSize(50),
284  sample_offset(0)
285  {
286  if (!_RANSACEvaluator){
287  BEXCEPTION("PreemptiveRANSAC::PreemptiveRANSAC(): Invalid "
288  <<"(null) argument.");
289  }
290  _uiSampleSize = _RANSACEvaluator->GetMinNumSamplesForSolutionComputation();
291 
292  NewDebugLevel("D_PRANSAC_SolveMaster");
293  NewDebugLevel("D_PRANSAC_EvalOrder");
294  NewDebugLevel("D_PRANSAC_Evaluate");
295  NewDebugLevel("D_PRANSAC_InitialReject");
296  NewDebugLevel("D_PRANSAC_Inlier");
297  NewDebugLevel("D_PRANSAC_GetSolution");
298  NewDebugLevel("D_PRANSAC_GenerateSolutions");
299  }
300 
301 
302  template <class SolutionType>
305  {
306  BIASASSERT(_RANSACEvaluator);
307  _uiDataSize = _RANSACEvaluator->GetNumMeasurements();
308  _Solutions.clear();
309  _EvaluationOrder.clear();
310  GenerateEvaluationOrder_(_uiDataSize);
311  _bInitialised = true;
312  }
313 
314 
315  template <class SolutionType>
317  AddSolutionGuesses(const std::vector<SolutionType>& guesses)
318  {
319  BIASASSERT(_uiDataSize!=0);
320  typename std::vector<SolutionType>::const_iterator it;
321  SolStruct st;
322  for (it=guesses.begin(); it!=guesses.end(); it++){
323  st.solution = *it;
324  st.eval_index = 0;
325  st.score = 0.0;
326  st.num_inliers = 0;
327  st.inliers.resize(_uiDataSize);
328  st.refined = false;
329  _Solutions.push_back(st);
330  }
331  }
332 
333 
334  template <class SolutionType>
336  SolveMaster(const double inlying_data_fraction, const double max_score,
337  SolutionType &solution, std::vector<bool> &inliers,
338  unsigned &num_inliers, double& score,
339  const bool auto_compute_max_samples)
340  {
341  BIASASSERT(_RANSACEvaluator);
342  if (!_bInitialised){
343  BIASERR("PreemptiveRANSAC::SolveMaster(): Call Init() before calling "
344  <<"SolveMAster!");
345  }
346  BIASASSERT(_bInitialised);
347  _bInitialised = false;
348 
349  unsigned oldMinSamples = _uiMinSamples;
350  if (auto_compute_max_samples){
351  // the following euqation stems from Phil Torr's thesis
352  const double prob_of_good_solution = 0.9999;
353  _uiMaxSamples =
354  (unsigned)ceil(log(1.0-prob_of_good_solution)/
355  log(1.0-pow(inlying_data_fraction,
356  (double)(_uiSampleSize))));
357  if (_uiMinSamples>_uiMaxSamples){
358  _uiMinSamples = _uiMaxSamples;
359  }
360  }
361 
362  if (_uiDataSize<_uiSampleSize){
363  // need at least _uiSampleSize samples to generate a single solution
364  return PRANSAC_NOTE_ENOUGH_SAMPLES;
365  }
366  const int min_num_inliers =
367  (int)rint(inlying_data_fraction * (double)_uiDataSize);
368  BIASASSERT(_uiDataSize!=0);
369  BIASASSERT(_uiMaxSamples>=_uiMinSamples);
370  const unsigned num_blocks = _uiMaxSamples / _uiMinSamples;
371 
372  unsigned oldNumInitialChecked = _uiNumInitialChecked;
373  unsigned oldMinNumInitialInliers = _uiMinNumInitialInliers;
374  if (_uiDataSize<_uiNumInitialChecked){
375  std::cerr << "Only "<<_uiDataSize<<" measurements are available, but the "
376  <<"initial check has been requested for "<<_uiNumInitialChecked
377  <<" measurements. Checking all "<<_uiDataSize
378  <<" available measurememts\n" ;
379  _uiNumInitialChecked = _uiDataSize;
380  unsigned num_initial_expected_inliers =
381  (unsigned)floor(inlying_data_fraction * (double) _uiNumInitialChecked);
382  if (_uiMinNumInitialInliers>=num_initial_expected_inliers){
383  _uiMinNumInitialInliers = num_initial_expected_inliers;
384  if (_uiMinNumInitialInliers>0){
385  _uiMinNumInitialInliers--;
386  }
387  }
388  }
389  unsigned num_initial_expected_inliers =
390  (unsigned)floor(inlying_data_fraction * (double) _uiNumInitialChecked);
391  if (_uiMinNumInitialInliers>num_initial_expected_inliers){
392  std::cerr << "the parameters inyling_data_fraction ("
393  <<inlying_data_fraction<<") and the ratio of "
394  <<"MinNumInitialInliers / NumInitialChecked ("
395  <<(double)_uiMinNumInitialInliers/(double)_uiNumInitialChecked
396  <<") are not consistent. The latter must be smaller than the "
397  <<"former.\n";
398  return PRANSAC_INVALID_PARAMETERS;
399  }
400 
401  unsigned num_generated_solutions = 0;
402  unsigned num_initially_rejected_solutions = 0;
403  for (unsigned block=0; block<num_blocks; block++){
404  BCDOUT(D_PRANSAC_SolveMaster,"\n########## block "<<block<<" #######\n");
405  // generate and evaluate raw hypothesis
406  num_generated_solutions +=
407  GenerateSolutions_(block, _uiSampleSize, _uiDataSize);
408  BCDOUT(D_PRANSAC_SolveMaster,"initially generated "<<_Solutions.size()
409  <<" solutions\n");
410 
411  num_initially_rejected_solutions +=
412  InitialRejectTcd_(_uiMinNumInitialInliers, _uiNumInitialChecked);
413  BCDOUT(D_PRANSAC_SolveMaster, _Solutions.size()<<" solutions remained "
414  "after initial rejection\n");
415  if (_Solutions.empty()) continue;
416 
417  EvaluateSolutions_(_uiNumInitialChecked, _uiDataSize);
418  BCDOUT(D_PRANSAC_SolveMaster, _Solutions.size()<<" solutions remained "
419  "after evaluation of unrefined solutions\n");
420  if (_Solutions.empty()) continue;
421 
422  // generate and evaluate the refined hypothesis
423  if (_bRefineSolutions && !_Solutions.empty()){
424  int num_refined = GenerateRefinedSolutions_(_uiDataSize);
425  if (num_refined>0){
426  BCDOUT(D_PRANSAC_SolveMaster, _Solutions.size()<<" solutions "
427  "after adding refinements\n");
428 
429  int num_rejected =
430  InitialRejectTcd_(_uiMinNumInitialInliers, _uiNumInitialChecked);
431  BCDOUT(D_PRANSAC_SolveMaster, _Solutions.size()<<" solutions "
432  "after initial rejection of refinements\n");
433  if (_Solutions.empty()) continue;
434 
435  if (num_rejected!=num_refined){
436  EvaluateSolutions_(_uiNumInitialChecked, _uiDataSize);
437  BCDOUT(D_PRANSAC_SolveMaster, _Solutions.size()<<" solutions "
438  "after evaluation of refinements\n");
439  if (_Solutions.empty()) continue;
440  } else {
441  BCDOUT(D_PRANSAC_SolveMaster, "no refined solution passed the "
442  "initial test\n");
443  }
444  } else {
445  BCDOUT(D_PRANSAC_SolveMaster, "no refined solutions\n");
446  }
447  }
448 
449  int solution_res = GetSolution_(min_num_inliers, max_score,
450  solution, inliers, num_inliers, score);
451  if (_bGreedy && solution_res==PRANSAC_OK){
452  BCDOUT(D_PRANSAC_SolveMaster, "solution found!\n");
453  // restore internal state
454  _uiNumInitialChecked = oldNumInitialChecked;
455  _uiMinNumInitialInliers = oldMinNumInitialInliers;
456  _uiMinSamples = oldMinSamples;
457  return 0;
458  }
459  } /// for (int block=0; block<num_blocks; block++){
460 
461  // restore internal state
462  _uiNumInitialChecked = oldNumInitialChecked;
463  _uiMinNumInitialInliers = oldMinNumInitialInliers;
464  _uiMinSamples = oldMinSamples;
465 
466  const double initially_rejected_solutions_ratio =
467  (double)(num_initially_rejected_solutions) /
468  (double)(num_generated_solutions);
469  const double max_initially_rejected_solution_ratio = 0.95;
470  if (initially_rejected_solutions_ratio >
471  max_initially_rejected_solution_ratio){
472  BIASERR("More than "<<max_initially_rejected_solution_ratio*100
473  << "% of the solutions have been rejected by the Tcd test "
474  << "(c="<<_uiMinNumInitialInliers<<", d="
475  <<_uiNumInitialChecked<<").\n");
476  // retry when the parameters are reasonable
477  static int num_retries = 0;
478  const unsigned reasonable_min_num_initial_checked = 5;
479  const unsigned reasonable_min_num_initial_inliers = 2;
480  if ( _uiMinNumInitialInliers <= reasonable_min_num_initial_inliers &&
481  _uiNumInitialChecked >= reasonable_min_num_initial_checked){
482  BIASERR("Retrying with different evaluation order\n");
483  if (num_retries < 1 ){
484  num_retries++;
485  GenerateEvaluationOrder_(_uiDataSize);
486  SolveMaster(inlying_data_fraction, max_score, solution,
487  inliers, num_inliers, score, auto_compute_max_samples);
488  }
489  } else {
490  BIASERR("Rerunning PreemptiveRANSAC with more reasonable parameters "
491  <<"for \"NumInitialChecked\" (is "<<_uiNumInitialChecked
492  <<", should be at least "<<reasonable_min_num_initial_checked
493  <<") and \"MinNumInitialInliers\" (is "
494  <<_uiMinNumInitialInliers <<" an should be at most "
495  <<reasonable_min_num_initial_inliers
496  <<") could be successfull.");
497  return PRANSAC_SUBOPTIMAL_PARAMETERS;
498  }
499  }
500 
501  // check if any solution is left
502  int solution_res = GetSolution_(min_num_inliers, max_score,
503  solution, inliers, num_inliers, score);
504  if (solution_res==PRANSAC_OK){
505  BCDOUT(D_PRANSAC_SolveMaster, "solution found!\n");
506  return 0;
507  } else if (solution_res>0){
508  BCDOUT(D_PRANSAC_SolveMaster, "solution found, but minimum quality "
509  <<"criteria not matched! Best score "<<score<<" (expected max "
510  <<max_score<<") / num inliers "<<num_inliers<<" (expected min "
511  <<min_num_inliers<<")\n");
512  BIASERR("solution found, but minimum quality "
513  <<"criteria not matched! Best score "<<score<<" (expected max "
514  <<max_score<<") / num inliers "<<num_inliers<<" (expected min "
515  <<min_num_inliers<<")");
516  return solution_res;
517  }
518 
519 
520  BIASERR("PreemptiveRANSAC found no solution");
521  return PRANSAC_NO_SOLUTION;
522  }
523 
524  template <class SolutionType>
526  GenerateEvaluationOrder_(const unsigned data_size)
527  {
528  _EvaluationOrder.clear();
529  const unsigned ds = data_size;
530  std::list<unsigned> mlist;
531  for (unsigned i=0; i<ds; i++)
532  mlist.push_back(i);
533 
534  unsigned index;
535  typename std::list<unsigned>::iterator it;
536  BCDOUT(D_PRANSAC_EvalOrder, "evaluation order: ");
537  for (unsigned i=0; i<ds; i++){
538  index = randomizer_.GetUniformDistributedInt(0u, ds-i-1);
539  BIASASSERT(index>=0 && index<ds-i);
540  it = mlist.begin();
541  for (unsigned k=0; k<index; k++) it++;
542  _EvaluationOrder.push_back(*it);
543  BCDOUT(D_PRANSAC_EvalOrder, _EvaluationOrder.back()<<" ");
544  mlist.erase(it);
545  }
546  BCDOUT(D_PRANSAC_EvalOrder, std::endl);
547  }
548 
549 
550  template <class SolutionType>
552  GenerateSolutions_(const unsigned block_num, const unsigned sample_size,
553  const unsigned data_size)
554  {
555  const unsigned max_sample = (block_num+1)*_uiMinSamples;
556  std::vector<unsigned> which_samples;
557  std::vector<SolutionType> solutions;
558  typename std::vector<SolutionType>::const_iterator it;
559  SolStruct st;
560 
561  if (block_num == 0) sample_offset = 0;
562  const int max_tries = 25;
563  int num, tries;
564  int num_solutions = 0;
565  BIASASSERT(_RANSACEvaluator);
566  BCDOUT(D_PRANSAC_GenerateSolutions, "generating "<<block_num
567  <<"-th block with "<<_uiMinSamples <<" solutions\n");
568  for (unsigned sample=block_num * _uiMinSamples;
569  sample<max_sample; sample++){
570  tries = num = 0;
571  do {
572  if (_RANSACEvaluator->GenerateSamples(sample+sample_offset, sample_size,
573  data_size, which_samples)) {
574  num = _RANSACEvaluator->GetSampleSolutions(which_samples, solutions);
575  BCDOUT(D_PRANSAC_GenerateSolutions, "samples: ");
576  for (unsigned i=0; i<which_samples.size(); i++){
577  BCDOUT(D_PRANSAC_GenerateSolutions, which_samples[i]<<" ");
578  }
579  BCDOUT(D_PRANSAC_GenerateSolutions, " resulted in "
580  <<solutions.size()<<" solutions\n");
581  for (it=solutions.begin(); it!=solutions.end(); it++){
582  st.solution = *it;
583  st.eval_index = 0;
584  st.score = 0.0;
585  st.num_inliers = 0;
586  st.inliers.resize(data_size);
587  st.refined = false;
588  _Solutions.push_back(st);
589  num_solutions++;
590  }
591  } else {
592  BIASERR("sample generation failed!");
593  return -1;
594  }
595  if (num == 0) {
596  tries++;
597  sample_offset++;
598  }
599  } while (num==0 && tries<max_tries);
600  }
601  BCDOUT(D_PRANSAC_GenerateSolutions, "generated "<<block_num
602  <<"-th block containing "<<num_solutions<<" solutions\n");
603  return num_solutions;
604  }
605 
606  template <class SolutionType>
608  GenerateRefinedSolutions_(const unsigned data_size)
609  {
610  typename std::list<SolStruct>::iterator it;
611  SolStruct st;
612  st.refined = true;
613  std::vector<bool> inliers;
614  int num_refined = 0;
615  BIASASSERT(_RANSACEvaluator);
616  for (it=_Solutions.begin(); it!=_Solutions.end(); it++){
617  if (!it->refined){
618  BIASASSERT(it->eval_index == data_size);
619  ConvertInliers_(it->inliers, inliers);
620  st.solution = it->solution;
621  if (_RANSACEvaluator->RefineSolution(inliers, st.solution)){
622  st.eval_index = 0;
623  st.score = 0.0;
624  st.num_inliers = 0;
625  st.inliers.resize(data_size);
626  _Solutions.push_back(st);
627  num_refined++;
628  }
629  }
630  }
631  return num_refined;
632  }
633 
634  template <class SolutionType>
636  InitialRejectTcd_(const unsigned min_num_inliers,
637  const unsigned num_checked)
638  {
639  BIASASSERT(min_num_inliers<=num_checked);
640  typename std::list<SolStruct>::iterator it;
641  unsigned data = 0;
642  double score = 0.0;
643  int num_rejected = 0;
644  BCDOUT(D_PRANSAC_InitialReject, "initial reject, checking samples: ");
645  for (data=0; data<num_checked; data++){
646  BCDOUT(D_PRANSAC_InitialReject, _EvaluationOrder[data]<<" ");
647  }
648  BCDOUT(D_PRANSAC_InitialReject, std::endl);
649 
650  for (it=_Solutions.begin(); it!=_Solutions.end();){
651  if (it->eval_index>num_checked) { it++; continue; }
652  BCDOUT(D_PRANSAC_InitialReject, " (");
653  for (data=0; data<num_checked; data++){
654  IsInlier_(it, data, score);
655  BCDOUT(D_PRANSAC_InitialReject, ((it->inliers[data])?("+"):("-")));
656  }
657  if (it->num_inliers<min_num_inliers){
658  it = _Solutions.erase(it);
659  num_rejected++;
660  BCDOUT(D_PRANSAC_InitialReject, ")*");
661  } else {
662  it++;
663  BCDOUT(D_PRANSAC_InitialReject, ").");
664  }
665  }
666  BCDOUT(D_PRANSAC_InitialReject, "\n");
667  if (_Solutions.empty()){
668  std::cout << "initially checked "<<num_checked
669  <<" samples and expected at least "<<min_num_inliers
670  <<" inliers. No solutions remaining.\n";
671  }
672  return num_rejected;
673  }
674 
675  template <class SolutionType>
677  IsInlier_(typename std::list<SolStruct>::iterator solution,
678  const unsigned eval_index, double& score)
679  {
680  BCDOUT(D_PRANSAC_Inlier, "eval_index: "<<eval_index<<" ");
681  BCDOUT(D_PRANSAC_Inlier, "solution->eval_index: "<<solution->eval_index);
682  if (solution->eval_index<=eval_index){
683  // cannot leave indices unevaluated
684  //std::cerr<<solution->eval_index<<" "<<eval_index<<std::endl;
685  BIASASSERT(solution->eval_index==eval_index);
686  BIASASSERT(solution->inliers.size()>eval_index);
687  BIASASSERT(_EvaluationOrder.size()>eval_index);
688  BCDOUT(D_PRANSAC_Inlier, " (new score) "<<std::endl);
689  BCDOUT(D_PRANSAC_Inlier, "_EvaluationOrder[eval_index] = "
690  <<_EvaluationOrder[eval_index]<<std::endl);
691  BIASASSERT(_RANSACEvaluator);
692  solution->inliers[eval_index] =
693  _RANSACEvaluator->IsInlier(solution->solution,
694  _EvaluationOrder[eval_index], score);
695 
696  if (solution->inliers[eval_index]){
697  solution->score += score;
698  solution->num_inliers++;
699  }
700  solution->eval_index++;
701  } else {
702  BCDOUT(D_PRANSAC_Inlier, " (already scored) "<<std::endl);
703  score = NAN;
704  }
705  BCDOUT(D_PRANSAC_Inlier, "inlier: "<<std::boolalpha
706  <<solution->inliers[eval_index]<<std::endl);
707  return solution->inliers[eval_index];
708  }
709 
710 
711  template <class SolutionType>
713  EvaluateSolutions_(const unsigned start_index, const unsigned data_size)
714  {
715  if (start_index>=data_size){
716  return;
717  }
718  typename std::list<SolStruct>::iterator it;
719  double score = 0.0;
720  for (unsigned data=start_index; data<data_size; data++){
721  for (it=_Solutions.begin(); it!=_Solutions.end(); it++){
722  if (it->eval_index>data) { continue; }
723  IsInlier_(it, data, score);
724  }
725  if ( ( ((int)data+1-(int)start_index) % (int)_uiEvaluationBlockSize)
726  == 0){
727  BCDOUT(D_PRANSAC_Evaluate, "--- eval "<<data<<" ---\n");
728  RejectSolutions_(data);
729  }
730  }
731  }
732 
733  template <class SolutionType>
735  RejectSolutions_(const unsigned eval_index)
736  {
737  // always keep the better half
738  std::vector<double> scores;
739  typename std::list<SolStruct>::iterator it;
740  for (it=_Solutions.begin(); it!=_Solutions.end(); it++){
741  if (it->eval_index>eval_index+1) { continue; }
742  scores.push_back(it->score);
743  }
744  if (scores.empty()) {
745  BCDOUT(D_PRANSAC_Evaluate, "no solutions left to evaluate\n");
746  return 0;
747  }
748  Median1D<double> med;
749  med.Compute(scores);
750  const double thresh = med.GetMedian();
751  BCDOUT(D_PRANSAC_Evaluate, "score threshold = "<<thresh<<std::endl);
752  int num_rejected=0;
753  for (it=_Solutions.begin(); it!=_Solutions.end();){
754  if (it->score > thresh){
755  it = _Solutions.erase(it);
756  BCDOUT(D_PRANSAC_Evaluate, "*");
757  num_rejected++;
758  } else {
759  it++;
760  BCDOUT(D_PRANSAC_Evaluate, ".");
761  }
762  }
763  BCDOUT(D_PRANSAC_Evaluate, std::endl);
764  BCDOUT(D_PRANSAC_Evaluate, _Solutions.size()<<" solutions remaining\n");
765 
766  return num_rejected;
767  }
768 
769 
770  template <class SolutionType>
772  GetSolution_(const unsigned min_num_inliers, const double max_score,
773  SolutionType& solution, std::vector<bool>& inliers,
774  unsigned& num_inliers, double & score) const
775  {
776  if (_Solutions.empty()) {
777  return PRANSAC_NO_SOLUTION_LEFT;
778  }
779 
780  const SolStruct *best_sol =
781  GetSolutionMinScore_(min_num_inliers, max_score);
782  //GetSolutionMaxInliers_(min_num_inliers, max_score);
783 
784  if (best_sol){
785  score = best_sol->score;
786  num_inliers = best_sol->num_inliers;
787  solution = best_sol->solution;
788  BCDOUT(D_PRANSAC_GetSolution, "\nbest solution is: "
789  <<solution<<" score: "<<score<<" / num inliers: "
790  <<num_inliers<<std::endl);
791  ConvertInliers_(best_sol->inliers, inliers);
792  bool enough_inliers = (best_sol->num_inliers >= min_num_inliers);
793  bool score_smaller_than_max_score = (best_sol->score <= max_score);
794  if (enough_inliers && score_smaller_than_max_score){
795  BCDOUT(D_PRANSAC_GetSolution, "num_inliers: "<<num_inliers
796  <<"\nscore: "<<score<<"\n");
797  return PRANSAC_OK;
798  } else {
799  int res = 0;
800  BCDOUT(D_PRANSAC_GetSolution, "minimum quality criteria not met "
801  "(max_score: "<<max_score<<" / min inliers "<<min_num_inliers
802  <<")\n");
803  if (!enough_inliers){
804  BCDOUT(D_PRANSAC_GetSolution, "not enough inliers "
805  <<"(found: "<<best_sol->num_inliers<<" / expected: "
806  <<min_num_inliers<<")\n");
807  res = res | PRANSAC_NOT_ENOUGH_INLIERS;
808  }
809  if (!score_smaller_than_max_score){
810  BCDOUT(D_PRANSAC_GetSolution, "score too big "
811  <<"(found: "<<best_sol->score<<" / expected: "
812  <<max_score<<")\n");
813  res = res | PRANSAC_SCORE_TOO_BIG;
814  }
815  return res;
816  }
817  } else {
818  BIASERR("no best solution returned");
819  BIASABORT;
820  }
821 
822  return PRANSAC_NO_SOLUTION_LEFT;
823  }
824 
825 
826  template <class SolutionType>
828  ConvertInliers_(const std::vector<bool>& inliers_in_eval_order,
829  std::vector<bool>& inliers) const
830  {
831  // convert the inlier vector
832  inliers.resize(inliers_in_eval_order.size());
833  const unsigned data_size = (unsigned)inliers.size();
834  BIASASSERT(data_size == _EvaluationOrder.size());
835  for (unsigned i=0; i<data_size; i++){
836  BIASASSERT(_EvaluationOrder[i]<data_size);
837  inliers[_EvaluationOrder[i]] = inliers_in_eval_order[i];
838  }
839  }
840 
841 }
842 #endif // __PreemptiveRANSAC_hh__
bool _bInitialised
just to check if the class is used correctly
void GenerateEvaluationOrder_(const unsigned data_size)
Interface for computation of solutions and evaluation of measurements.
void SetMinSamples(const unsigned min_samples)
Set min number of samples.
unsigned _uiMinNumInitialInliers
the minimum number of inlier from _uiNumInitialChecked
void AddSolutionGuesses(const std::vector< SolutionType > &guesses)
DataType GetMedian() const
Return computed median.
Definition: Median1D.hh:56
struct SolStruct * GetSolutionMaxInliers_(const unsigned min_num_inliers, const double max_score) const
void SetEvaluationBlockSize(const unsigned eval_block_size)
after each EvaluationBlockSize-th measurement, the solutions are compared and only the better half is...
BIAS::Random randomizer_
randomizer instance used for evaluation order computation
unsigned _uiNumInitialChecked
the number of datat points that is initially checked
RANSACEvaluatorInterface< SolutionType > * _RANSACEvaluator
the helper class for solution computation and sample evaluation
std::vector< bool > inliers
inlier indicator for the data in the order given by _EvaluationOrder i.e.
struct SolStruct * GetSolutionMinScore_(const unsigned min_num_inliers, const double max_score) const
void SetMinNumInitialInliers(const unsigned min_num_initial_inl)
when less than MinNumInitialInliers are found, the solution is rejected straight away ...
unsigned eval_index
evaluation of the solution has taken place up to eval_index
Computes the median and p-quantile of a vector.
Definition: Median1D.hh:41
void SetNumInitialChecked(const unsigned num_initial_checked)
the number of measurements that is initially evaluated
bool refined
has a refined version of this solution lareday be added?
void EvaluateSolutions_(const unsigned start_index, const unsigned data_size)
void SetMaxSamples(const unsigned max_samples)
Set max number of samples.
int GenerateSolutions_(const unsigned block_num, const unsigned sample_size, const unsigned data_size)
returns the number of generated solution
bool IsInlier_(typename std::list< SolStruct >::iterator solution, const unsigned data_index, double &score)
bool _bGreedy
should termination be aborted when first good solution is found?
void ConvertInliers_(const std::vector< bool > &inliers_in_eval_order, std::vector< bool > &inliers) const
void Compute(const std::vector< DataType > &vec)
Compute median and store sorted vector internally.
Definition: Median1D.cpp:32
void SetRefineSolutions(const bool refine_solutions)
long int NewDebugLevel(const std::string &name)
creates a new debuglevel
Definition: Debug.hh:474
unsigned _uiEvaluationBlockSize
after EvaluationBlockSize, bad solutions are removed
unsigned _uiSampleSize
the number of samples needed by GetSampleSolutions
int InitialRejectTcd_(const unsigned min_num_inliers, const unsigned num_checked)
returns the number of rejected solutions
int GetSolution_(const unsigned min_num_inliers, const double max_score, SolutionType &solution, std::vector< bool > &inliers, unsigned &num_inliers, double &score) const
std::list< SolStruct > _Solutions
all (remaining) solutions
int SolveMaster(const double inlying_data_fraction, const double max_score, SolutionType &solution, std::vector< bool > &inliers, unsigned &num_inliers, double &score, const bool auto_compute_max_samples=false)
std::vector< unsigned > _EvaluationOrder
the order in which data is used for solutions evaluation
int sample_offset
prevents that calls to GenerateSample_() generate the same random sample if sample solution creation ...
int RejectSolutions_(const unsigned eval_index)
returns the number of rejected solutions
Fast RANSAC after David Nister, &quot;Preemptive RANSAC for Live Structure And Motion Estimation&quot;, Internation Conference on Computer Vision (ICCV) 2003.
int GenerateRefinedSolutions_(const unsigned data_size)
returns the number of succesfully refined solutions
unsigned _uiDataSize
the number of data for evaluation
class for producing random numbers from different distributions
Definition: Random.hh:51
PreemptiveRANSAC(RANSACEvaluatorInterface< SolutionType > *shi)
unsigned _uiMinSamples
the minimu and maximum number of samples investigated
bool _bRefineSolutions
should the solutions be refined?
void SetGreedy(const bool greedy)