Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
RANSAC.hh
1 /*
2 This file is part of the BIAS library (Basic ImageAlgorithmS).
3 
4 Copyright (C) 2003-2009 (see file CONTACT for details)
5  Multimediale Systeme der Informationsverarbeitung
6  Institut fuer Informatik
7  Christian-Albrechts-Universitaet Kiel
8 
9 
10 BIAS is free software; you can redistribute it and/or modify
11 it under the terms of the GNU Lesser General Public License as published by
12 the Free Software Foundation; either version 2.1 of the License, or
13 (at your option) any later version.
14 
15 BIAS is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Lesser General Public License for more details.
19 
20 You should have received a copy of the GNU Lesser General Public License
21 along with BIAS; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24 
25 
26 #ifndef __RANSAC_hh__
27 #define __RANSAC_hh__
28 
29 #include <bias_config.h>
30 #include <Base/Common/BIASpragmaStart.hh>
31 
32 #include <Base/Debug/Debug.hh>
33 #include <Base/Math/Random.hh>
34 #include <Base/Geometry/HomgPoint2D.hh>
35 #include <Base/Math/Matrix.hh>
36 
37 #include <algorithm>
38 #include <float.h>
39 #include <iterator>
40 #include <vector>
41 #include <iomanip>
42 
43 #define RANSAC_SAMPLE_PROGRESS_EVERY 100
44 
45 #define RANSAC_DEFAULT_PROBOFGOODSOLUTION 0.98
46 #define RANSAC_DEFAULT_SOSMALLSOLUTIONCHANGE 0.02
47 
48 namespace BIAS {
49 
50  /** @class RANSAC
51  @ingroup g_mathalgo
52  @brief Generic abstract base class for RANSAC (RANdom SAmple Consensus)
53  estimators.
54 
55  To use this class, derive a class from it in which the following methods
56  are overloaded:
57  - RANSAC::GetSampleSolutions : Computes solution(s) from a set of input
58  samples.
59  - RANSAC::EvaluateSolution : Evaluate input samples with respect to a
60  given solution and classify inliers/outliers
61  - RANSAC::RefineSolution : Refine a given solution using all inliers
62 
63  RANSAC::GenerateSamples can be overloaded e.g. if for an instance an even-
64  spaced sampling is desired.
65 
66  For examples how to use this class see
67  - MathAlgo/Examples/ExampleRANSAC_double.cpp
68  - MathAlgo/Examples/ExampleRANSACPlane.cpp
69 
70  The function SolveMaster was ported from TargetJR -> PMatrixRANSAC by
71  frahm and from there on further ported to this class from woelk.
72 
73  @note Since this is a generic templated class the implementation
74  has to go into the header file.
75 
76  @author frahm, woelk
77  */
78 
79  template <class SolutionType>
80  class /*BIASMathAlgo_EXPORT*/ RANSAC : virtual public Debug
81  {
82 
83  public:
84 
85  RANSAC();
86 
87  /** Construct a RANSAC object that takes samples of size sample_size,
88  returning at most max_solutions_per_sample solutions each time.
89  If the refine_solution flag is true, a routine has been supplied to
90  refine the initial fits before evaluation.
91 
92  In order to use the RANSAC, a child class from class RANSAC or class
93  RANSACPreKnowledge has to be implemented with the methods Evalulate Solution,
94  GetSampleSolutions, and RefineSolution.
95  A Compute method needs to do all the preparations and call the SolveMaster
96  method.
97  See ExampleRANSACPlane to get an idea of how to use the parameters.
98  */
99  RANSAC(unsigned int sample_size, unsigned int max_solutions_per_sample = 1,
100  bool refine_solution = false, bool greedy = false);
101 
102  virtual ~RANSAC();
103 
104  inline void Init(unsigned int sample_size, unsigned int max_solutions_per_sample = 1,
105  bool refine_solution = false, bool greedy = false);
106 
107  /** @brief Explicitly set max number of samples
108 
109  Ransacing will stop after max_samples samples, regardless
110  of solution quality. Normally, more than a few thousand samples
111  signifies a problem. */
112  inline void SetMaxSamples(unsigned int max_samples)
113  { _uiMaxSamples = max_samples; }
114 
115  /** @brief Explicitly set number of samples
116 
117  Explicitly set number of samples to make in SolveMaster, i.e.
118  Value of ComputeSampleCount() is ignored if this function is called
119  with a strictly positive value uiSamples.
120  Note:
121  SolveMaster performs at most min(_uiMaxSamples, uiSamples) iterations
122  @author koeser
123  */
124  inline void SetSampleCount(unsigned int uiSamples) {
125  _uiExplicitSampleNo = uiSamples;
126  }
127 
128  /** @brief Main computation function called by user.
129 
130  This function is called to run the computation.
131  On output, solution is set to the best solution found and
132  inliers indicate which data were inliers to that solution.
133  The inlying_data_fraction is responsible for the maximal number of
134  sample sets taken from the data. The smaller the
135  inlying_data_fraction, the more sample sets are taken
136 
137  @attention Do NOT override this function in derived classes, unless
138  you want to do modifications in the RANSAC _algorithm_ itself (and
139  usually you dont want to do this ...)
140  input : inlying_data_fraction expected inlier fraction
141  output: solution solution
142  inliers inlier flags
143  @return <0 indicates an error, otherwise number of chosen sample
144  @author frahm, woelk */
145  inline virtual int SolveMaster(double inlying_data_fraction,
146  SolutionType &solution,
147  std::vector<bool> &inliers);
148 
149  /** @brief Compute solution(s) for the given set of samples
150  @return the number of solutions found.
151 
152  The which_samples array will be of length sample_size, selecting the
153  indices to be fit to from the subclass's data array. */
154  virtual inline int GetSampleSolutions(std::vector<unsigned int> &which_samples,
155  std::vector<SolutionType> &solutions);
156 
157  /** @brief evaluate the goodness of a solution
158  *
159  * For the specified solution, set
160  * - an accept count indicating the number of data which are inliers
161  * to the given solution,
162  * - the inlier flag for each datum to true if the datum is inlying
163  * - an evaluate score (typically the average residual) indicating
164  * the goodness of fit of the inliers - the solver code requires a
165  * positive number where zero is a perfect solution, larger positive
166  * scores mean a worse solution.
167  * - an ok_to_terminate flag which is set TRUE if the solution
168  * is good enough that the robust solver can discontinue search for
169  * a better solution. Only happens, if greedy is set to true.
170  * - The return value tells the algorithm, if the solution is
171  * good enough to be refined further - meaning, if set to true and
172  * if RefineSolution_ is set to true, the solution will be refined.
173  * In the example below, true is returned even if only one samples
174  * has been found to be an inlier. Because refinement usually takes
175  * quite a while, it is sensible to use a higher threshold. For example
176  * the use of a minimum inlier fraction via a private variable in the
177  * child class can be tested on.
178  *
179  * If existing_solution_flag is true, then the best accept count
180  * so far is in accept_count. The function can terminate early if
181  * it is not going to beat this.
182  *
183  * A skeleton implementation of this function would be written as follows:
184  * \verbatim
185  * int my_accept_count = 0;
186  * for (unsigned int i=0; i<_uiDataSize; i++) {
187  * inlier[i] = Consistent(corner_matches[i], solution);
188  * if (inlier[i]) {
189  * my_accept_count++;
190  * evaluate_score += Residual(corner_matches[i], solution);
191  * if (existing_solution_flag && my_accept_count>accept_count)
192  * break;
193  * }
194  * }
195  * accept_count=my_accept_count;
196  * evaluate_score=(accept_count!=0)?(evaluate_score/accept_count):
197  * (DBL_MAX);
198  * if (evaluate_score < solution_error_threshold &&
199  * (double)accept_count/(double)_uiDataSize > min_inlier_fraction)
200  * ok_to_terminate_flag=true;
201  * else
202  * ok_to_terminate_flag=false;
203  * return (accept_count!=0) // maybe even (accept_count>inl_frac)
204  * \endverbatim
205  *
206  * The line if (existing_solution_flag && my_accept_count>accept_count)
207  * break;
208  * causes the algorithm to not determine the score and acceptcount correctly,
209  * only so far as to top the so far best solution. It can be used for very fast
210  * solutions, however if the exact inliers are needed and refineSolution_ is set
211  * to false, the evaluation should not be omnitted.
212  *
213  * @param solution (in) solution to be evaluated
214  * @param existing_solution_flag (in) if true the best accept_cout so far
215  * is given in accept_count
216  * @param accept_count (in/out) input: best accept_cout so far if
217  * existing_solution_flag==true, output: number of
218  * inliers to the current solution
219  * @param inlier (out) inlier[i] ==> datum i is inlying
220  * @param evaluate_score (out) a score for the solution, 0 means ideal
221  * solution, larger score means worse solution
222  * @param ok_to_termiate_flag (out) if true, RANSACing will stop */
223  virtual inline
224  bool EvaluateSolution(const SolutionType& solution,
225  bool existing_solution_flag,
226  std::vector<bool>& inlier, int& accept_count,
227  double& evaluate_score, bool& ok_to_terminate_flag);
228 
229  /** @brief refine previously computed solution based on its inliers
230 
231  If implemented, this function should compute a more accurate solution,
232  e.g. a least squares on all inliers, possibly using as an initial
233  estimate the solution in solution.
234  The output array new_inliers should be set to reflect the inliers to
235  the new solution.
236  @return false if no better solution than the initial could be found
237  */
238  virtual inline
239  bool RefineSolution(std::vector<unsigned int>& which_samples,
240  SolutionType& solution,
241  std::vector<bool>& new_inliers);
242 
243  /** @brief pick a set of samples
244 
245  Override this to specialize the technique used to generate samples.
246  By default, random sampling is assumed but implementations may wish to
247  use the latin-square sampling provided by generate_sample_evenspace
248  (below), or some other problem-specific sampling strategy. */
249  virtual inline
250  bool GenerateSamples(int sample_index,
251  std::vector<unsigned int>& which_samples);
252 
253  inline bool GenerateSamplesEvenspace(int sample_index,
254  std::vector<unsigned int>& sample_table);
255 
256  inline bool GenerateSamplesRandom(int sample_index,
257  std::vector<unsigned int>& sampletable);
258 
259  // Return the best EvaluateScore obtained by a previous call of SolveMaster
260  inline double GetFinalScore() const { return _dLastBestSampleEvaluateScore; }
261 
262  protected:
263  /// number of samples available
264  unsigned _uiSampleSize;
265 
266  /// max number of solutions that are to be computed for one sample set
268 
269  /// number of data elements needed to compute a solution
270  unsigned _uiDataSize;
271 
272  /// do we want to refine a good sample (one for which evaluate solution returned true)?
274 
275  /// sampled best values
279 
280  /// refined best values
283 
284  /// absolute max number of samples
285  unsigned int _uiMaxSamples;
286 
287  /// use this number of samples
288  unsigned int _uiExplicitSampleNo;
289 
290  /// prob. for computing number of samples, set close to 1
292 
293  /// If all other conditions are right, and the fractional change in the
294  /// solution statistics is less than this, stop sampling.
296 
297  /// if we already have a solution for which EvaluateSolution returned true
299 
300  /// if true, the first acceptable solution is returned
301  bool _greedy;
302 
303  protected:
304  static inline int ComputeSampleCount(double inlying_data_fraction,
305  double desired_prob_good,
306  unsigned int sample_size);
307  };
308 
309 
310 
311  /************************************************************/
312  /* now the implementation */
313  /************************************************************/
314 
315 
316  template <class SolutionType> inline
318  {
319  _dProbabilityOfGoodSolution = RANSAC_DEFAULT_PROBOFGOODSOLUTION;
320  _dSoSmallSolutionChange = RANSAC_DEFAULT_SOSMALLSOLUTIONCHANGE;
321  _uiSampleSize=_uiMaxSolutionsPerSample=_uiDataSize=_uiMaxSamples=0;
322  _uiExplicitSampleNo = 0;
323  _bRefineSolution = false;
324  _iBestSampleAcceptCount = _iBestInlierAcceptCount = -1;
325  _dBestSampleEvaluateScore = _dBestInlierEvaluateScore = -1.0;
326  _got_solution_flag = false;
327  _greedy = false;
328  NewDebugLevel("D_RANSAC_INLIERS");
329  NewDebugLevel("D_RANSAC_SAMPLES");
330  NewDebugLevel("D_RANSAC_SOLVE_MASTER");
331  NewDebugLevel("D_RANSAC_SAMPLE_QUALITY");
332  NewDebugLevel("D_RANSAC_SAMPLE_PROGRESS");
333  NewDebugLevel("D_RANSAC_SOLUTION");
334  //AddDebugLevel("D_RANSAC_SOLUTION");
335  }
336 
337  template <class SolutionType> inline
338  RANSAC<SolutionType>::RANSAC(unsigned int sample_size,
339  unsigned int max_solutions_per_sample,
340  bool refine_solution, bool greedy)
341  {
342  _dProbabilityOfGoodSolution = RANSAC_DEFAULT_PROBOFGOODSOLUTION;
343  _dSoSmallSolutionChange = RANSAC_DEFAULT_SOSMALLSOLUTIONCHANGE;
344  Init(sample_size, max_solutions_per_sample, refine_solution);
345  _uiDataSize = 0;
346  _iBestSampleAcceptCount = _iBestInlierAcceptCount = -1;
347  _dBestSampleEvaluateScore = _dBestInlierEvaluateScore = -1.0;
348  _got_solution_flag = false;
349  _greedy = greedy;
350  NewDebugLevel("D_RANSAC_INLIERS");
351  NewDebugLevel("D_RANSAC_SAMPLES");
352  NewDebugLevel("D_RANSAC_SOLVE_MASTER");
353  NewDebugLevel("D_RANSAC_SAMPLE_QUALITY");
354  NewDebugLevel("D_RANSAC_SAMPLE_PROGRESS");
355  NewDebugLevel("D_RANSAC_SOLUTION");
356  //AddDebugLevel("D_RANSAC_SOLUTION");
357  }
358 
359  template <class SolutionType> inline
360  void RANSAC<SolutionType>::Init(unsigned int sample_size,
361  unsigned int max_solutions_per_sample,
362  bool refine_solution, bool greedy)
363  {
364  _uiSampleSize=sample_size;
365  _uiMaxSolutionsPerSample=max_solutions_per_sample;
366  _bRefineSolution=refine_solution;
367  _uiMaxSamples = 0;
368  _uiExplicitSampleNo = 0;
369  _greedy = greedy;
370  }
371 
372  template <class SolutionType> inline
374  {}
375 
376  /* @author frahm, woelk */
377  template <class SolutionType> inline
378  int RANSAC<SolutionType>::SolveMaster(double inlying_data_fraction,
379  SolutionType& solution,
380  std::vector<bool>& inliers)
381  {
382  BCDOUT(D_RANSAC_SOLVE_MASTER, "RANSAC::SolveMaster() --------------------"
383  "--------------------------------------\n");
384 
385 #ifdef BIAS_DEBUG
386  if (_uiDataSize==0){
387  BIASERR("_uiDataSize should be set in derived class first "
388  <<_uiDataSize);
389  BIASABORT;
390  }
391  if (inlying_data_fraction>1.0 || inlying_data_fraction<0.0){
392  BIASERR("invalid inlying_data_fraction: "<<inlying_data_fraction);
393  BIASABORT;
394  }
395 #endif
396  if (_uiDataSize < _uiSampleSize) {
397  BIASERR("SolveMaster: _uiDataSize < _uiSampleSize !");
398  return -1;
399  }
400 
401  std::vector<bool> inlier_set(_uiDataSize, false);
402 
403  std::vector<SolutionType> multisoln_table(_uiMaxSolutionsPerSample);
404 
405  // check whether _uiExplicitSampleNo has been set (to a positive value)
406  int sample_count;
407  if (_uiExplicitSampleNo>0) {
408  // yes the user specified to make exactly _uiExplicitSampleNo samples
409  sample_count = _uiExplicitSampleNo;
410  BCDOUT(D_RANSAC_SOLVE_MASTER, "RANSAC: Using "<< sample_count
411  <<" samples (explicitly specified).");
412  } else {
413  // compute the number of samples needed using Torr's thesis
414  sample_count = ComputeSampleCount(inlying_data_fraction,
415  _dProbabilityOfGoodSolution,
416  _uiSampleSize);
417 
418  BCDOUT(D_RANSAC_SOLVE_MASTER, "RANSAC: Need at most "<< sample_count
419  <<" samples for "<< inlying_data_fraction <<" inlier fraction");
420 
421  }
422 
423  // Override computed bailout if specified
424  if (_uiMaxSamples != 0 && (unsigned)sample_count > _uiMaxSamples) {
425  sample_count = _uiMaxSamples;
426  BCDOUT(D_RANSAC_SOLVE_MASTER,"RANSAC: But will bail out after "
427  << sample_count );
428  }
429 
430  // offsets of samples for current solution
431  std::vector<unsigned int> which_samples;
432 
433  // number of solutions per sample
434  int nsolutions;
435 
436  // number of inliers for current solution
437  int sample_accept_count;
438 
439  // score for sample set(resp. current solution), is set by EvaluateSolution
440  double sample_evaluate_score;
441 
442  // found a solution that good that we can finish, set by EvaluateSolution
443  bool ok_to_terminate_flag;
444 
445  // current solution is accepted by EvaluateSolution
446  bool good_flag;
447 
448  // number of inliers for current solution after refinement
449  int inlier_accept_count;
450 
451  // score for current solution after refinement
452  double inlier_evaluate_score;
453 
454  // indicates that the current is the best solution so far
455  bool best_so_far_flag;
456 
457  // indicate whether we have ever reached the checkpoint before
458  // fast termination can only happen at the second time we reach the
459  // checkpoint (or if _greedy is set)
460  bool checkpoint_set_flag = false;
461 
462  // backup of solution evaluation at last time we were at checkpoint
463  int sample_checkpoint_count = 0;
464  int inlier_checkpoint_count = 0;
465 
466  // if ok_to_terminate is set true, fast termination is enabled if
467  // termination_check_active_flag is true.
468  bool termination_check_active_flag = false;
469  BCDOUT(D_RANSAC_SAMPLE_QUALITY, "\nchecking at most "<<sample_count
470  <<" samples\n");
471 
472  // index of best sample so far
473  int best_sample_index = -1;
474 
475  int sample_index;
476  for (sample_index = 0; sample_index < sample_count; sample_index++){
477 
478  if (sample_index % RANSAC_SAMPLE_PROGRESS_EVERY ==
479  RANSAC_SAMPLE_PROGRESS_EVERY-1) {
480  BCDOUT(D_RANSAC_SAMPLE_PROGRESS, "RANSAC sample "
481  <<sample_index+1<<" of "<<sample_count << std::endl);
482  }
483 
484  // jmf for skipping if we haven't enough matches
485  if (GenerateSamples(sample_index, which_samples)){
486 // std::cout << std::setw(3)<<sample_index<<" : ";
487 // copy(which_samples.begin(), which_samples.end(),
488 // std::ostream_iterator<int>(std::cout, " "));
489 // std::cout << std::endl;
490  nsolutions = GetSampleSolutions(which_samples, multisoln_table);
491  if (nsolutions == 0) {
492  BCDOUT(D_RANSAC_SAMPLE_QUALITY, "o"<<std::flush);
493  }
494  for (int solution_index=0; solution_index<nsolutions;solution_index++){
495  BCDOUT(D_RANSAC_SOLVE_MASTER, "evaluating solution "
496  <<solution_index+1<<"/"<<nsolutions<<std::endl);
497  SolutionType* active_solution_ptr = &multisoln_table[solution_index];
498 
499  if (_got_solution_flag) // ?
500  sample_accept_count = _iBestSampleAcceptCount;
501 
502  ok_to_terminate_flag = false;
503  sample_evaluate_score = DBL_MAX;
504  good_flag =
505  EvaluateSolution(*active_solution_ptr, _got_solution_flag,
506  inlier_set, sample_accept_count,
507  sample_evaluate_score, ok_to_terminate_flag);
508 
509  BCDOUT(D_RANSAC_SOLVE_MASTER,"sample: "<<sample_index<<"/"
510  <<sample_count<<" ("
511  <<solution_index<<")"
512  <<" solution: "<<(*active_solution_ptr)
513  <<" inliers: " <<sample_accept_count<<"/"<<_uiDataSize
514  <<" score: "<< sample_evaluate_score<<" good_flag: "
515  <<std::boolalpha<<good_flag<<std::endl);
516  inlier_accept_count=-1;
517  inlier_evaluate_score = 1e20;
518  if (good_flag && _bRefineSolution) {
519  // backup linear solution in case refining doesnt improve results
520  SolutionType samplesolution = *active_solution_ptr;
521  bool ok_to_terminate_with_linear = ok_to_terminate_flag;
522 
523  which_samples.clear();
524  for (unsigned int i=0; i<_uiDataSize; i++)
525  if (inlier_set[i]) which_samples.push_back(i);
526  BCDOUT(D_RANSAC_SOLVE_MASTER, "refining solution "
527  <<*active_solution_ptr<<std::endl);
528  if (RefineSolution(which_samples, *active_solution_ptr,
529  inlier_set)){
530  if (_got_solution_flag)
531  inlier_accept_count = _iBestInlierAcceptCount;
532  BCDOUT(D_RANSAC_SOLVE_MASTER, "evaluating refined solution: "
533  <<*active_solution_ptr<<std::endl);
534 
535  good_flag =
536  EvaluateSolution (*active_solution_ptr, _got_solution_flag,
537  inlier_set, inlier_accept_count,
538  inlier_evaluate_score, ok_to_terminate_flag);
539  if (good_flag) {
540  BCDOUT(D_RANSAC_SOLVE_MASTER, "RANSAC: minimal/refined "
541  <<sample_accept_count<<"/"
542  <<inlier_accept_count<<" "
543  <<sample_evaluate_score<<"/"
544  <<inlier_evaluate_score<<std::endl);
545  } else {
546  BCDOUT(D_RANSAC_SOLVE_MASTER, "RANSAC: (note) "
547  <<"EvaluateSolution returned false\n");
548  }
549  } else {
550  // RefineSolution did not succeed, we do not use the solution
551  good_flag = false;
552  BCDOUT(D_RANSAC_SOLVE_MASTER, "RANSAC: (note) RefineSolution"
553  <<" returned false\n");
554 
555  }
556  // refining didnt improve results, use linear solution:
557  if (!good_flag || (inlier_accept_count < sample_accept_count)) {
558  BCDOUT(D_RANSAC_SOLVE_MASTER, "RANSAC: (note) Linear solution"
559  <<" overrides refined !!! \n");
560  good_flag = true;
561  inlier_accept_count = sample_accept_count;
562  inlier_evaluate_score = sample_evaluate_score;
563  *active_solution_ptr = samplesolution;
564  ok_to_terminate_flag = ok_to_terminate_with_linear;
565  }
566  } else { // if (good_flag && _bRefineSolution) {
567  BCDOUT(D_RANSAC_SOLVE_MASTER, "RANSAC: (note) either good_flag"
568  <<"=false or _bRefineSolution=false\n");
569  }
570 
571  // the criteria for deciding the best solution is the following -
572  // (a) if the accept_count is higher than the previous best, take it,
573  // (b) if the accept_count is equal to the previous best, and
574  // the evaluate score is smaller than the previous best, take it.
575  if (good_flag) {
576  BCDOUT(D_RANSAC_SAMPLE_QUALITY, "."<<std::flush);
577  best_so_far_flag = false;
578 
579  if (!_got_solution_flag) {
580  BCDOUT(D_RANSAC_SOLVE_MASTER, "setting _got_solution_flag to "
581  "true\n");
582  _got_solution_flag = true;
583  best_so_far_flag = true;
584  } else {
585  if (!_bRefineSolution) {
586  if (sample_accept_count > _iBestSampleAcceptCount ||
587  (sample_accept_count == _iBestSampleAcceptCount &&
588  sample_evaluate_score < _dBestSampleEvaluateScore))
589  best_so_far_flag = true;
590  } else {
591  if (inlier_accept_count > _iBestInlierAcceptCount ||
592  (inlier_accept_count == _iBestInlierAcceptCount &&
593  inlier_evaluate_score < _dBestInlierEvaluateScore))
594  best_so_far_flag = true;
595  }
596  }
597 
598  // save all the scores associated with this result.
599  // future evaluations will be tested against these accept scores.
600  if (best_so_far_flag) {
601  BCDOUT(D_RANSAC_SAMPLE_QUALITY, "b"<<std::flush);
602  _iBestSampleAcceptCount = sample_accept_count;
603  _dBestSampleEvaluateScore =
604  (sample_evaluate_score<inlier_evaluate_score) ?
605  sample_evaluate_score : inlier_evaluate_score;
606  _iBestInlierAcceptCount = inlier_accept_count;
607  _dBestInlierEvaluateScore = inlier_evaluate_score;
608  best_sample_index = sample_index;
609 
610  // save inliers and solution
611  inliers = inlier_set;
612  solution = *active_solution_ptr;
613 
614  BCDOUT(D_RANSAC_SOLVE_MASTER, "RANSAC: Sample "<<sample_index
615  <<" is best so far. " << "Inliers/score "
616  << _iBestSampleAcceptCount << "/"
617  <<_dBestSampleEvaluateScore<<": "
618  <<solution<<std::endl);
619  if (_bRefineSolution){
620  BCDOUT(D_RANSAC_SOLVE_MASTER," refined inliers/score "
621  <<_iBestInlierAcceptCount << "/"
622  <<_dBestInlierEvaluateScore<<std::endl);
623  }
624 
625 
626  if (ok_to_terminate_flag){
627  BCDOUT(D_RANSAC_SAMPLE_QUALITY, "x"<<std::flush);
628  termination_check_active_flag = true;
629  if (_greedy)
630  break; // exit for (int solution_index=0; solution_index<...
631  BCDOUT(D_RANSAC_SOLVE_MASTER,
632  "RANSAC: termination_check_active_flag = true");
633  }
634  } // if (best_so_far_flag) {
635  } else { // if (good_flag) {
636  BCDOUT(D_RANSAC_SAMPLE_QUALITY, "*"<< std::endl << std::flush);
637  }
638  } // for (int solution_index=0; solution_index<nsolutions ...
639 
640  // this is all fast termination stuff
641  if (_got_solution_flag) {
642  // in greedy mode, we dont want to check the improvement
643  if (_greedy && termination_check_active_flag) {
644  BCDOUT(D_RANSAC_SAMPLE_QUALITY, "X"<<std::flush);
645  BCDOUT(D_RANSAC_SOLVE_MASTER," found solution, stopping sample "
646  "generation\n");
647  break;
648  }
649  if (!checkpoint_set_flag) {
650  checkpoint_set_flag = true;
651  } else if (_greedy) {
652  // check if we have already found such a good solution that we can
653  // terminate (termination_check_active_flag)
654  // and if the best-so-far-solution has not increased
655  // significantly compared to the previous solution
656  if (!_bRefineSolution) {
657  if (termination_check_active_flag &&
658  ((double) (_iBestSampleAcceptCount - sample_checkpoint_count)
659  /(double)sample_checkpoint_count <_dSoSmallSolutionChange)){
660  BCDOUT(D_RANSAC_SAMPLE_QUALITY, "S"<<std::flush);
661  break;
662  }
663  } else {
664  if (termination_check_active_flag &&
665  ((double) (_iBestInlierAcceptCount - inlier_checkpoint_count)
666  /(double)inlier_checkpoint_count <_dSoSmallSolutionChange)){
667  BCDOUT(D_RANSAC_SAMPLE_QUALITY, "S"<<std::flush);
668  break;
669  }
670  }
671  }
672 
673  // save the current best values to measure the improvement next time
674  inlier_checkpoint_count = _iBestInlierAcceptCount;
675  sample_checkpoint_count = _iBestSampleAcceptCount;
676  }
677  } else { // if (GenerateSamples
678  BIASERR("GenerateSamples failed");
679  }
680 
681  // -------------- debug output (kk) ------------------
682 
683  if (sample_index % RANSAC_SAMPLE_PROGRESS_EVERY ==
684  RANSAC_SAMPLE_PROGRESS_EVERY-1) {
685  if (_bRefineSolution){
686  BCDOUT(D_RANSAC_SAMPLE_PROGRESS,"Refined inliers/score "
687  <<_iBestInlierAcceptCount << "/"
688  <<_dBestInlierEvaluateScore<<"\n");
689  } else {
690  BCDOUT(D_RANSAC_SAMPLE_PROGRESS, " Unrefined inliers/score "
691  << _iBestSampleAcceptCount << "/"
692  <<_dBestSampleEvaluateScore<<" as solution : "
693  <<solution<<"\n");
694  }
695  }
696  // -------------- debug output ---------------------
697 
698  } // for (int sample_index = 0 ...
699  BCDOUT(D_RANSAC_SAMPLE_QUALITY, "\n");
700  BCDOUT(D_RANSAC_SAMPLE_QUALITY, "stopped after "<<sample_index
701  <<" sampels\n");
702 
703  if (!_got_solution_flag) {
704  BCDOUT(D_RANSAC_SOLUTION, "RANSAC: no solution found\n");
705  BIASWARN("RANSAC: no solution found, return -2");
706  // prepare return value "failed"
707  best_sample_index = -2;
708  } else {
709  if (_greedy && sample_index==sample_count){
710  BCDOUT(D_RANSAC_SOLUTION,
711  "RANSAC: early termination not successful\n");
712  BCDOUT(D_RANSAC_SOLUTION,
713  "RANSAC: no sufficient solution found\n");
714  }
715  BCDOUT(D_RANSAC_SOLVE_MASTER, "\nRANSAC: using sample "<<best_sample_index
716  <<" with inliers/score "<< _iBestSampleAcceptCount << "/"
717  <<_dBestSampleEvaluateScore<<" as solution : "
718  <<solution<<"\n");
719  if (_bRefineSolution){
720  BCDOUT(D_RANSAC_SOLVE_MASTER," refined inliers/score "
721  <<_iBestInlierAcceptCount << "/"
722  <<_dBestInlierEvaluateScore<<"\n");
723  }
724  }
725  _dLastBestSampleEvaluateScore = _dBestSampleEvaluateScore;
726  // reset best so far values
727  _iBestSampleAcceptCount = -1;
728  _dBestSampleEvaluateScore = -1;
729  _iBestInlierAcceptCount = -1;
730  _dBestInlierEvaluateScore = -1;
731  _got_solution_flag = false;
732  return best_sample_index;
733  }
734 
735 
736  template <class SolutionType> inline
738  GetSampleSolutions(std::vector<unsigned int> &which_samples,
739  std::vector<SolutionType> &solutions)
740  {
741  BIASERR("this function should be implemented in derived class");
742  return -1;
743  }
744 
745 
746  template <class SolutionType> inline
748  EvaluateSolution(const SolutionType& solution, bool existing_solution_flag,
749  std::vector<bool>& out_inlier_flags, int& accept_count_ptr,
750  double& evaluate_score_ptr, bool& ok_to_terminate_flag_ptr)
751  {
752  BIASERR("this function should be implemented in derived class");
753  return false;
754  }
755 
756  template <class SolutionType> inline
758  RefineSolution(std::vector<unsigned int>& which_samples,
759  SolutionType& solution,
760  std::vector<bool>& new_inliers)
761  {
762  BIASERR("this function should be implemented in derived class");
763  return false;
764  }
765 
766  template <class SolutionType> inline
768  GenerateSamples(int sample_index, std::vector<unsigned int>& which_samples)
769  {
770  return GenerateSamplesRandom(sample_index, which_samples);
771  }
772 
773  template <class SolutionType> inline
775  GenerateSamplesEvenspace(int sample_index,
776  std::vector<unsigned int>& which_samples)
777  {
778  bool res=false;
779  BIASERR("unfinished code");
780  /*
781  // from sa_generate_sample_evenspace
782 
783  assert((int)_sample_size <= sample_table.length());
784  assert((int)_sample_size < data_size);
785 
786  // basic_offset starts sample_size samples evenly spaced from the data_size set.
787  unsigned basic_offset = data_size / _sample_size;
788  unsigned offset = basic_offset + sample_index / basic_offset;
789  unsigned data_index = sample_index % data_size;
790 
791  if (offset < basic_offset + data_size) {
792  for(unsigned count = 0; count < _sample_size; ++count) {
793  sample_table[count] = data_index;
794 
795  data_index += offset;
796 
797  if ((int)data_index >= data_size)
798  data_index = data_index % data_size;
799  }
800  }
801  else {
802  if (offset == basic_offset + data_size && (sample_index % basic_offset == 0))
803  cerr << "RANSAC: Evenspace sampling has reached repetition stage, changing to random sampling\n";
804 
805  generate_sample_random (sample_index, sample_table);
806  }
807  */
808  return res;
809  }
810 
811  template <class SolutionType> inline
813  GenerateSamplesRandom(int sample_index,
814  std::vector<unsigned int>& which_samples)
815  {
816  bool res=true;
817 #ifdef BIAS_DEBUG
818  if (_uiSampleSize==0){
819  BIASERR("call Init or appropriate constructor before using RANSAC "
820  <<"(_uiSampleSize==0");
821  res=false;
822  BIASABORT;
823  }
824  if (_uiSampleSize>=_uiDataSize){
825  BIASERR("_uiDataSize should be set in derived class before using RANSAC "
826  <<"\t_uiSampleSize: "<<_uiSampleSize<<"\t_uiDataSize: "
827  <<_uiDataSize);
828  res=false;
829  BIASABORT;
830  }
831 #endif
832  // initialzing random with 0 and 1 does not seem to work well
833  Random ran(sample_index+rand());
834  unsigned int count=0;
835  unsigned int index;
836  which_samples.clear();
837  do {
838  index = ran.GetUniformDistributedInt((unsigned int)0, _uiDataSize-1);
839  if (find(which_samples.begin(), which_samples.end(), index)==
840  which_samples.end()){
841  which_samples.push_back(index);
842  count++;
843  //std::cout << index << " ";
844  }
845  } while (count<_uiSampleSize);
846  //std::cout << std::endl;
847 #ifdef BIAS_DEBUG
848  if (DebugLevelIsSet("D_RANSAC_SAMPLES")){
849  BCDOUT(D_RANSAC_SAMPLES, "---------- sample "<<sample_index
850  <<" ---------------------------------"<<"\nnow using data: ");
851  std::ostream_iterator<int> oi(GetDebugStream(), " ");
852  std::copy(which_samples.begin(), which_samples.end(), oi);
853  GetDebugStream() << std::endl;
854  }
855 #endif
856 
857  return res;
858  }
859 
860 
861  template <class SolutionType> inline
863  ComputeSampleCount(double inlying_data_fraction, double desired_prob_good,
864  unsigned int sample_size)
865  {
866  if (inlying_data_fraction == 1.0)
867  // the analytic expression below cant cope with this so do it explicitly.
868  return 1;
869 
870  // from Torr's thesis.
871  return int(log (1.0 - desired_prob_good) /
872  log (1.0 - pow (inlying_data_fraction, (double) sample_size)));
873  }
874 
875 
876 } // namespace
877 
878 #include <Base/Common/BIASpragmaEnd.hh>
879 
880 #endif //__RANSAC_hh__
unsigned int _uiMaxSamples
absolute max number of samples
Definition: RANSAC.hh:285
unsigned _uiSampleSize
number of samples available
Definition: RANSAC.hh:264
double _dLastBestSampleEvaluateScore
Definition: RANSAC.hh:278
bool GenerateSamplesEvenspace(int sample_index, std::vector< unsigned int > &sample_table)
Definition: RANSAC.hh:775
bool GenerateSamplesRandom(int sample_index, std::vector< unsigned int > &sampletable)
Definition: RANSAC.hh:813
double _dBestSampleEvaluateScore
Definition: RANSAC.hh:277
unsigned _uiMaxSolutionsPerSample
max number of solutions that are to be computed for one sample set
Definition: RANSAC.hh:267
virtual ~RANSAC()
Definition: RANSAC.hh:373
virtual bool GenerateSamples(int sample_index, std::vector< unsigned int > &which_samples)
pick a set of samples
Definition: RANSAC.hh:768
unsigned int _uiExplicitSampleNo
use this number of samples
Definition: RANSAC.hh:288
bool _bRefineSolution
do we want to refine a good sample (one for which evaluate solution returned true)?
Definition: RANSAC.hh:273
virtual int SolveMaster(double inlying_data_fraction, SolutionType &solution, std::vector< bool > &inliers)
Main computation function called by user.
Definition: RANSAC.hh:378
double _dBestInlierEvaluateScore
Definition: RANSAC.hh:282
int GetUniformDistributedInt(const int min, const int max)
get uniform distributed random variable including min/max
Definition: Random.cpp:139
void Init(unsigned int sample_size, unsigned int max_solutions_per_sample=1, bool refine_solution=false, bool greedy=false)
Definition: RANSAC.hh:360
bool _got_solution_flag
if we already have a solution for which EvaluateSolution returned true
Definition: RANSAC.hh:298
double _dSoSmallSolutionChange
If all other conditions are right, and the fractional change in the solution statistics is less than ...
Definition: RANSAC.hh:295
double GetFinalScore() const
Definition: RANSAC.hh:260
int _iBestSampleAcceptCount
sampled best values
Definition: RANSAC.hh:276
void SetMaxSamples(unsigned int max_samples)
Explicitly set max number of samples.
Definition: RANSAC.hh:112
static int ComputeSampleCount(double inlying_data_fraction, double desired_prob_good, unsigned int sample_size)
Definition: RANSAC.hh:863
Generic abstract base class for RANSAC (RANdom SAmple Consensus) estimators.
Definition: RANSAC.hh:80
bool _greedy
if true, the first acceptable solution is returned
Definition: RANSAC.hh:301
virtual int GetSampleSolutions(std::vector< unsigned int > &which_samples, std::vector< SolutionType > &solutions)
Compute solution(s) for the given set of samples.
Definition: RANSAC.hh:738
int _iBestInlierAcceptCount
refined best values
Definition: RANSAC.hh:281
virtual bool RefineSolution(std::vector< unsigned int > &which_samples, SolutionType &solution, std::vector< bool > &new_inliers)
refine previously computed solution based on its inliers
Definition: RANSAC.hh:758
unsigned _uiDataSize
number of data elements needed to compute a solution
Definition: RANSAC.hh:270
void SetSampleCount(unsigned int uiSamples)
Explicitly set number of samples.
Definition: RANSAC.hh:124
double _dProbabilityOfGoodSolution
prob. for computing number of samples, set close to 1
Definition: RANSAC.hh:291
virtual bool EvaluateSolution(const SolutionType &solution, bool existing_solution_flag, std::vector< bool > &inlier, int &accept_count, double &evaluate_score, bool &ok_to_terminate_flag)
evaluate the goodness of a solution
Definition: RANSAC.hh:748
class for producing random numbers from different distributions
Definition: Random.hh:51