ransac.h

Go to the documentation of this file.
00001 
00015 #ifndef DLR_COMPUTERVISION_RANSAC_H
00016 #define DLR_COMPUTERVISION_RANSAC_H
00017 
00018 #include <vector>
00019 #include <dlrComputerVision/randomSampleSelector.h>
00020 
00021 namespace dlr {
00022 
00023   namespace computerVision {
00024 
00031     enum RansacInlierStrategy {
00032       DLR_CV_NAIVE_ERROR_THRESHOLD
00033     };
00034       
00035     
00051     template <class Problem>
00052     class Ransac {
00053     public:
00054 
00058       typedef Problem ProblemType;
00059 
00060       
00066       typedef typename Problem::ModelType ResultType;
00067 
00068       
00100       Ransac(ProblemType const& problem,
00101              size_t minimumConsensusSize = 0,
00102              double requiredConfidence = 0.99,
00103              double inlierProbability = 0.5);
00104 
00105 
00109       virtual
00110       ~Ransac() {}
00111 
00112 
00120       virtual ResultType
00121       getResult() {
00122         // xxx
00123         ResultType result;
00124         this->estimate(result);
00125         return result;
00126       }
00127       
00128 
00129     protected:
00130 
00131       void
00132       computeConsensusSet(ResultType& model, std::vector<bool>& consensusFlags);
00133 
00134       bool
00135       estimate(ResultType& model);
00136 
00137       bool
00138       isConverged(std::vector<bool> const& consensusFlags,
00139                   std::vector<bool>& previousConsensusFlags,
00140                   size_t& consensusSetSize,
00141                   size_t& previousConsensusSetSize,
00142                   size_t& strikes);
00143 
00144 
00145       size_t m_minimumConsensusSize;
00146       size_t m_numberOfRandomSampleSets;
00147       ProblemType m_problem;
00148     };
00149 
00150 
00171     template <class Sample, class Model>
00172     class RansacProblem
00173       : public RandomSampleSelector<Sample>
00174     {
00175     public:
00176       
00177       // ========= Typedefs that must be present in order   =========
00178       // ========= to work with the Ransac class template.  =========
00179       // ========= You probably don't need to change these. =========
00180 
00185       typedef Model ModelType;
00186 
00187 
00192       typedef Sample SampleType;
00193 
00194 
00221       typedef typename RandomSampleSelector<SampleType>::SampleSequenceType
00222         SampleSequenceType;
00223       
00224 
00225       // ========= Member functions that must be provided by user =========
00226       // ========= You definitely do need to change these.        =========
00227 
00242       virtual ModelType
00243       estimateModel(SampleSequenceType const& sampleSequence) = 0;
00244 
00245 
00264       template <class IterType>
00265       void
00266       computeError(ModelType const& model,
00267                    SampleSequenceType const& sampleSequence,
00268                    IterType ouputIter) {};
00269 
00270 
00286       virtual double 
00287       getNaiveErrorThreshold() = 0;
00288       
00289 
00290       // ========= Public constructors, destructors, and    =========
00291       // ========= predefined member functions.             =========
00292       // ========= You probably don't need to change these. =========
00293 
00312       template <class IterType>
00313       RansacProblem(size_t sampleSize, IterType beginIter, IterType endIter)
00314         : RandomSampleSelector<SampleType>(beginIter, endIter),
00315           m_sampleSize(sampleSize) {}
00316 
00317       
00321       virtual
00322       ~RansacProblem() {}
00323 
00324 
00334       virtual size_t
00335       getSampleSize() {return m_sampleSize;}
00336 
00337 
00345       RansacInlierStrategy
00346       getInlierStrategy() {
00347         return DLR_CV_NAIVE_ERROR_THRESHOLD;
00348       }
00349 
00350     protected:
00351 
00352       size_t m_sampleSize;
00353       
00354     }; // class RansacProblem
00355     
00356   } // namespace computerVision
00357   
00358 } // namespace dlr
00359 
00360 
00361 
00362 /* ============ Definitions of inline & template functions ============ */
00363 
00364 
00365 #include <cmath>
00366 #include <algorithm>
00367 #include <functional>
00368 #include <dlrCommon/exception.h>
00369 #include <dlrNumeric/maxRecorder.h>
00370 
00371 namespace dlr {
00372 
00373   namespace computerVision {
00374 
00375     // The default constructor currently does nothing.
00376     template <class Problem>
00377     Ransac<Problem>::
00378     Ransac(Problem const& problem,
00379            size_t minimumConsensusSize,
00380            double requiredConfidence,
00381            double inlierProbability)
00382       : m_minimumConsensusSize(minimumConsensusSize),
00383         m_numberOfRandomSampleSets(),
00384         m_problem(problem)
00385     {
00386       size_t sampleSize = m_problem.getSampleSize();
00387       
00388       // The probability that any particular random sample is all inliers.
00389       double singlePickConfidence =
00390         std::pow(inlierProbability, static_cast<double>(sampleSize));
00391 
00392       // The probability that any particular random sample contains at
00393       // least one outlier.
00394       double singlePickDisconfidence = 1.0 - singlePickConfidence;
00395 
00396       // Number of times we have to sample in order to get a set of
00397       // all inliers with probability requiredConfidence.  This
00398       // follows Fischler's paper exactly.
00399       m_numberOfRandomSampleSets =
00400         std::log(1.0 - requiredConfidence) / std::log(singlePickDisconfidence);
00401       
00402       if(m_minimumConsensusSize == 0) {
00403         // User didn't specify a minimum consensus size.  Compute one
00404         // assuming that the probability of a sample matching an
00405         // incorrect model is <= 0.5, and using requiredConfidence as
00406         // a rough measure of how confident we have to be that we've
00407         // found the right model.  This follows Fischler's paper
00408         // approximately.
00409         int extraSamples = static_cast<int>(
00410           std::log(1.0 - requiredConfidence) / std::log(0.5) + 0.5);
00411         if(extraSamples < 0) {
00412           extraSamples = 0;
00413         }
00414         m_minimumConsensusSize = sampleSize + extraSamples;
00415       }
00416       
00417     }
00418 
00419 
00420     template <class Problem>
00421     void
00422     Ransac<Problem>::
00423     computeConsensusSet(typename Ransac<Problem>::ResultType& model,
00424                         std::vector<bool>& consensusFlags)
00425     {
00426       if(m_problem.getInlierStrategy() != DLR_CV_NAIVE_ERROR_THRESHOLD) {
00427         DLR_THROW(NotImplementedException, "Ransac::computeConsensusSet()",
00428                   "Currently only naive error thresholding is supported.");
00429       }
00430       if(consensusFlags.size() != m_problem.getPoolSize()) {
00431         consensusFlags.resize(m_problem.getPoolSize());
00432       }
00433       
00434       // Apply error function to entire set.
00435       typename Problem::SampleSequenceType testSet = m_problem.getPool();
00436       std::vector<double> errorMetrics(m_problem.getPoolSize());
00437       m_problem.computeError(model, testSet, errorMetrics.begin());
00438 
00439       // Find out which samples are within tolerance.
00440       double threshold = m_problem.getNaiveErrorThreshold();
00441       std::transform(
00442         errorMetrics.begin(), errorMetrics.end(), consensusFlags.begin(),
00443         std::bind2nd(std::less<double>(), threshold));
00444     }
00445     
00446     
00447     template <class Problem>
00448     bool
00449     Ransac<Problem>::
00450     estimate(typename Ransac<Problem>::ResultType& model)
00451     {
00452       dlr::numeric::MaxRecorder<size_t, ResultType> maxRecorder;
00453       for(size_t iteration = 0; iteration < m_numberOfRandomSampleSets;
00454           ++iteration) {
00455 
00456         // Select samples
00457         typename ProblemType::SampleSequenceType trialSet =
00458           m_problem.getRandomSample(m_problem.getSampleSize());
00459 
00460         std::vector<bool> consensusFlags(m_problem.getPoolSize());
00461         std::vector<bool> previousConsensusFlags(m_problem.getPoolSize(),
00462                                                  false);
00463         size_t consensusSetSize = 0;
00464         size_t previousConsensusSetSize = 0;
00465         size_t strikes = 0;
00466         while(1) {
00467           // Fit the model to the reduced (randomly sampled) set.
00468           model = m_problem.estimateModel(trialSet);
00469 
00470           // Identify the consensus set, made up of samples that are
00471           // sufficiently consistent with the model estimate.
00472           this->computeConsensusSet(model, consensusFlags);
00473 
00474           // See if this iteration has converged yet.
00475           if(this->isConverged(consensusFlags, previousConsensusFlags,
00476                                consensusSetSize, previousConsensusSetSize,
00477                                strikes)) {
00478             break;
00479           }
00480 
00481           // Not converged yet... loop so we can recompute the model
00482           // using the new consensus set.
00483           trialSet = m_problem.getSubset(
00484             consensusFlags.begin(), consensusFlags.end());
00485         }
00486 
00487         // OK, we've converged to a "best" result for this iteration.
00488         // Is it good enough to terminate?
00489         if(consensusSetSize > m_minimumConsensusSize) {
00490           // Reference argument model is already set appropriately.
00491           return true;
00492         }
00493 
00494         // Not ready to terminate yet, but remember this model (if
00495         // it's the best so far) in case we don't find any better.
00496         maxRecorder.test(consensusSetSize, model);
00497       }
00498 
00499       // Looks like we never found a gold plated correct answer.  Just
00500       // return report the best we found, and return false to indicate
00501       // our frustration.
00502       model = maxRecorder.getPayload();
00503       return false;
00504     }
00505 
00506 
00507     template <class Problem>
00508     bool
00509     Ransac<Problem>::
00510     isConverged(std::vector<bool> const& consensusFlags,
00511                 std::vector<bool>& previousConsensusFlags,
00512                 size_t& consensusSetSize,
00513                 size_t& previousConsensusSetSize,
00514                 size_t& strikes)
00515     {
00516       // Do we even have enough matching points to continue
00517       // iteration?
00518       consensusSetSize = std::count(
00519         consensusFlags.begin(), consensusFlags.end(), true);
00520       if(consensusSetSize < m_problem.getSampleSize()) {
00521         // No. The model is so bad that not even the points we used to
00522         // estimate it are within the consensus set!  We're converged,
00523         // after a fashion.
00524         return true;
00525       }
00526 
00527       // If previousConsensusFlags isn't initialized yet, then we're
00528       // clearly not converged, and can skip the remaining tests.
00529       if(consensusFlags.size() == previousConsensusFlags.size()) {
00530 
00531         // Does it look like we're in a cycle of adding/subtracting the
00532         // same points?
00533         if(consensusSetSize <= previousConsensusSetSize) {
00534           ++strikes;
00535         }
00536         if(strikes > 10) {
00537           return true;
00538         }
00539 
00540         // Hmm.  Are we selecting the same set as last time?  If so,
00541         // we're converged.
00542         if(std::equal(consensusFlags.begin(), consensusFlags.end(),
00543                       previousConsensusFlags.begin())) {
00544           return true;
00545         }
00546       }
00547       
00548       // Finally, do the bookkeeping so that previousConsensusFlags
00549       // gets updated.
00550       previousConsensusFlags = consensusFlags;
00551       previousConsensusSetSize = consensusSetSize;
00552 
00553       return false;
00554     }
00555     
00556   } // namespace computerVision
00557   
00558 } // namespace dlr
00559 
00560 #endif /* #ifndef DLR_COMPUTERVISION_RANSAC_H */

Generated on Wed Nov 25 12:15:05 2009 for dlrComputerVision Utility Library by  doxygen 1.5.8