toon-members
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Toon-members] tag/tag ransac.h ransac_estimators.h


From: Ethan Eade
Subject: [Toon-members] tag/tag ransac.h ransac_estimators.h
Date: Tue, 13 Jun 2006 01:50:44 +0000

CVSROOT:        /cvsroot/toon
Module name:    tag
Changes by:     Ethan Eade <ethaneade>  06/06/13 01:50:44

Modified files:
        tag            : ransac.h ransac_estimators.h 

Log message:
        Updated RANSAC algorithms and estimators to fix bugs and improve 
efficiency.
        There are now (only) two estimators defined:  
        
        - EssentialMatrix estimates the essential matrix given at least 8 
          correspondences (using the Torr-Fitzgibbon algorithm) and gives 
          decomposition into SE(3)
        - AffineHomography estimates a transformation of the form Ax+T given at 
least 4 correspondences
        
        More estimators will be added as they are tested.  The required 
interface is
        straightforward.
        
        There are three RANSAC algorithms defined:
        
        - find_RANSAC_inliers is classic RANSAC
        - find_RANSAC_inliers_breadth_first is preemptive RANSAC
        - find_RANSAC_inliers_guided_breadth_first is preemptive RANSAC with a 
non-uniform sampling density
        
        The interfaces of estimators and correspondences are slightly changed; 
see
        the doxygen for details.

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/tag/tag/ransac.h?cvsroot=toon&r1=1.2&r2=1.3
http://cvs.savannah.gnu.org/viewcvs/tag/tag/ransac_estimators.h?cvsroot=toon&r1=1.2&r2=1.3

Patches:
Index: ransac.h
===================================================================
RCS file: /cvsroot/toon/tag/tag/ransac.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -b -r1.2 -r1.3
--- ransac.h    31 May 2006 16:46:20 -0000      1.2
+++ ransac.h    13 Jun 2006 01:50:44 -0000      1.3
@@ -12,207 +12,246 @@
 /// The functions are both templated on the correspondence data type and
 /// the estimator for the transformation.
 
-template <class T>
-unsigned int getSample(const std::vector<T>& cdf, const T& p){
-    return std::lower_bound(cdf.begin(), cdf.end(), p) - cdf.begin();
-}
-
-inline void randomTuple(std::vector<unsigned int>& t, unsigned int bound){
-    for (unsigned int i=0; i<t.size(); i++) {
+ template <class T> void randomTuple(const std::vector<T>& cdf, 
std::vector<unsigned int>& t, T maxp) {
+     for (size_t i=0; i<t.size(); i++) {
         try_again:
-        unsigned int r = rand()%bound;
-        for (unsigned int j=0; j<i; j++)
+        double x = drand48()* maxp;
+        size_t r = std::min(cdf.size()-1, 
(size_t)(std::lower_bound(cdf.begin(), cdf.end(), (T)x) - cdf.begin()));
+        for (size_t j=0; j<i; j++)
             if (r == t[j])
                 goto try_again;
         t[i] = r;
     }
-}
+ }
 
-template <class T>
-void randomTuple(const std::vector<T>& cdf, std::vector<unsigned int>& t, T 
maxp){
-    for (unsigned int i=0; i<t.size(); i++) {
+ template <class T> void randomTuple(T& t, unsigned int bound)
+ {
+     for (size_t i=0; i<t.size(); i++) {
         try_again:
-        double x = double(rand()*maxp)/RAND_MAX;
-        unsigned int r = std::min((unsigned int)cdf.size()-1, 
getSample(cdf,(T)x));
-        for (unsigned int j=0; j<i; j++)
+        size_t r = (size_t)(drand48() * bound);
+        for (size_t j=0; j<i; j++)
             if (r == t[j])
                 goto try_again;
         t[i] = r;
     }
-}
+ }
+ 
 
-/// basic RANSAC implementation. The function is templated on the 
correspondence data type
+/// basic RANSAC implementation. The function is templated on the observation 
data type
 /// and the transformation data type which must conform to the following 
interface:
 /// @code
 /// class Estimator {
-///     void estimate(const std::vector<Correspondence>& matches);
-///     double getSqError(const Correspondence& m);
+///     Estimator();
+///     // Estimate from a sequence of observations
+///     template <class It> bool estimate(It begin, It End); 
+///     // Check whether the given observation is an inlier for this estimate 
(with specified tolerance)
+///     template <class Obs, class Tol> bool isInlier(const Obs& obs, const 
Tol& tolerance) const;
 /// };
 /// @endcode
 /// see the file @ref ransac_estimators.h for some Estimator classes for 
various transformations.
-/// @param[in] matches a vector of matches of type Correspondence
-/// @param[in] dist threshold distance for inliers, the square of dist ist 
compared with Estimator::getSqError
-/// @param[in] sampleSize the number of correspondences for a single hypothesis
-/// @param[in] tries the number of hypotheses to test
-/// @param[in] bestT the Estimator object used to compute the transformation
-/// @param[out] isInlier a vector of bools that is set to describe the inlier 
set of the winning hypothesis
+/// @param[in] observations a vector of observations (usually point matches)
+/// @param[in] sample_size the number of samples used estimate a transformation
+/// @param[in] tolerance the tolerance (passed with each observation) to the 
transformation to check for inliers
+/// @param[in] N the number of hypotheses to test
+/// @param[out] best the transformation hypothesis with the highest inlier 
count
+/// @param[out] inlier a vector of bools that describes the inlier set of the 
winning hypothesis
 /// @return the number of inliers for the winning hypothesis
 /// @ingroup ransac
-template <class Correspondence, class Transform>
-int findRANSACInliers(const std::vector<Correspondence>& matches, double dist, 
unsigned int sampleSize, unsigned int tries, Transform& bestT, 
std::vector<bool>& isInlier){
-    assert(isInlier.size() >= matches.size());
-    std::vector<bool> tInlier(isInlier.size());
-    Transform T = bestT;
-    unsigned int i;
-    unsigned int most = 0;
-    std::vector<unsigned int> index(sampleSize);
-    std::vector<Correspondence> sample(sampleSize);
-    for (i=0; i<tries; i++) {
-        randomTuple(index, matches.size());
-        for (unsigned int j=0; j<sampleSize; j++) {
-            sample[j] = matches[index[j]];
-        }
-        T.estimate(sample);
-        unsigned int votes = 0;
-        for (unsigned int p=0; p<matches.size() && most+p < 
matches.size()+votes; p++) {
-            double d2 = T.getSqError(matches[p]);
-            if (d2 < dist*dist) {
-                tInlier[p] = true;
-                votes++;
+
+template <class Obs, class Trans, class Tol> size_t find_RANSAC_inliers(const 
std::vector<Obs>& observations, int sample_size, const Tol& tolerance, size_t N,
+                                                                       Trans& 
best, std::vector<bool>& inlier)
+{
+    std::vector<bool> thisInlier(observations.size(),best);
+    size_t bestScore = 0;
+    std::vector<size_t> sample_index(sample_size);
+    vector<Obs> sample(sample_size);
+    while (N--) {
+       randomTuple(sample_index, observations.size());
+       for (int i=0;i<sample_size; i++)
+           sample[i] = observations[sample_index[i]];
+       Trans thisT;
+       thisT.estimate(sample.begin(), sample.end());
+       size_t score = 0;
+       for (size_t i=0; i<observations.size(); i++) {
+           const Obs& o = observations[i];
+           if (thisT.isInlier(o, tolerance)) {
+               inlier[i] = true;
+               score++;
             } else
-                tInlier[p] = false;
-        }
-        if (votes > most) {
-            most = votes;
-            isInlier = tInlier;
-            bestT = T;
-            if (most == matches.size())
-            break;
+               inlier[i] = false;
         }
+       if (score > bestScore) {
+           bestScore = score;
+           inlier = thisInlier;
+           best = thisT;
     }
-    if (most > sampleSize) {
-        std::vector<Correspondence> sample(most);
-        unsigned int j=0;
-        for (i=0;i<matches.size();i++)
-            if (isInlier[i])
-        sample[j++] = matches[i];
-        //bestT.estimate(sample);
     }
-    return most;
+    return bestScore;
 }
 
-template <class Correspondence, class Transform, class Prob>
-int findRANSACInliersGuidedBFS(const std::vector<Correspondence>& matches, 
const Prob& prob, double dist, unsigned int sampleSize, unsigned int tries, 
unsigned int perPass, Transform& bestT, std::vector<bool>& isInlier)
-{
-    assert(isInlier.size() >= matches.size());
-    unsigned int i,j;
+
+ inline double getShrinkRatio(unsigned int H, unsigned int N, unsigned int B)
+ {
+     return pow(double(H), -double(B)/N);
+ }
+
+/// Guided breadth-first RANSAC implementation. The function is templated on 
the observation data type,
+/// the probability density for observation correctness, the tolerance for 
inliers,
+/// and the transformation data type which must conform to the following 
interface:
+/// @code
+/// class Estimator {
+///     Estimator();
+///     // Estimate from a sequence of observations
+///     template <class It> bool estimate(It begin, It End); 
+///     // Check whether the given observation is an inlier for this estimate 
(with specified tolerance)
+///     template <class Obs, class Tol> bool isInlier(const Obs& obs, const 
Tol& tolerance) const;
+/// };
+/// @endcode
+/// All hypotheses are generated first, and preemptively discarded as more 
observations are examined.
+/// The sample sets for hypotheses are drawn probabilistically using the 
specified probability density.
+/// see the file @ref ransac_estimators.h for some Estimator classes for 
various transformations.
+/// @param[in] observations a vector of observations (usually point matches)
+/// @param[in] prob the probability predicate applied (as a function object) 
to each observation to compute its prior probability of being correct
+/// @param[in] sample_size the number of samples used estimate a transformation
+/// @param[in] tolerance the tolerance (passed with each observation) to the 
transformation to check for inliers
+/// @param[in] N the number of hypotheses to test
+/// @param[in] block_size the number of hypotheses to test in a block (between 
culling hypotheses)
+/// @param[out] best the transformation hypothesis with the highest inlier 
count
+/// @param[out] inlier a vector of bools that describes the inlier set of the 
winning hypothesis
+/// @return the number of inliers for the winning hypothesis
+/// @ingroup ransac
+
+
+     template <class Obs, class Trans, class Tol, class Prob> 
+       size_t find_RANSAC_inliers_guided_breadth_first(const std::vector<Obs>& 
observations, const Prob& prob, int sample_size, const Tol& tolerance, size_t 
N, size_t block_size,
+                                                       Trans& best, 
std::vector<bool>& inlier)
+    {
+       std::vector<Trans> hypotheses(N,best);
+       std::vector<std::pair<int,size_t> > score(N);
+       std::vector<size_t> sample_index(sample_size);    
+       std::vector<Obs> sample(sample_size); 
     std::vector<double> cdf(matches.size());
-    double psum = 0;
-    for (i=0; i<matches.size(); i++) {
-        psum += prob(matches[i]);
-        cdf[i] = psum;
-    }
-    std::vector<Transform> T(tries,bestT);
-    std::vector<std::pair<int, unsigned int> > score(tries);
-    std::vector<Correspondence> sample(sampleSize);
-    for (i=0; i<tries; i++) {
-        std::vector<unsigned int> index(sampleSize);
-        randomTuple(cdf, index, psum);
-        for (j=0; j<sampleSize; j++)
-        sample[j] = matches[index[j]];
-        T[i].estimate(sample);
-        score[i].first = 0;
-        score[i].second = i;
-    }
-    unsigned int curr = 0;
-    double distSq = dist*dist;
-    int toCut = 1;
-    while (curr < matches.size()) {
-        if (matches.size() - curr < perPass)
-        perPass = matches.size() - curr;
-        for (i=0; i<score.size(); i++) {
-            const Transform& t = T[score[i].second];
-            for (unsigned int p=0; p<perPass; p++) {
-                double d2 = t.getSqError(matches[curr+p]);
-                if (d2 < distSq)
-                    score[i].first++;
-            }
-        }
-        curr += perPass;
-        int cutoff = (score.size()*2)/3;//tries-((toCut*tries)>>passes);
-        toCut *= 2;
-        if (cutoff > 1) {
-            nth_element(score.begin(), score.begin()+cutoff, score.end(), 
std::greater<std::pair<int, unsigned int> >());
-            score.resize(cutoff);
-        } else
+       cdf[0] = prob(observations[0]);
+       for (i=1; i<observations.size(); ++i)
+           cdf[i] = cdf[i-1] + prob(matches[i]);
+       const double psum = cdf.back();
+
+       for (size_t i=0; i<hypotheses.size(); i++) {
+           do {
+               randomTuple(cdf, sample_index, psum);
+               for (int s=0; s<sample_size; ++s)
+                   sample[s] = observations[sample_index[s]];
+           }
+           while (!hypotheses[i].estimate(sample.begin(), sample.end()));
+           score[i] = std::make_pair(0,i);
+       }
+       size_t m = 0;
+       const double factor = getShrinkRatio(N, observations.size(), 
block_size);
+       while (m < observations.size()) {
+           size_t end = std::min(observations.size(), m+block_size);
+           for (size_t i=0; i<score.size(); i++) {
+               const Trans& thisT = hypotheses[score[i].second];
+               size_t s = 0;
+               for (size_t j=m; j!=end; j++) {
+                   if (thisT.isInlier(observations[j], tolerance))
+                       ++s;
+               }
+               score[i].first += s;
+           }
+           unsigned int cutoff = (unsigned int)(score.size() * factor);
+           if (cutoff == 0)
             break;
+           std::nth_element(score.begin(), score.end(), score.begin()+cutoff, 
greater<std::pair<int,size_t> >());
+           score.resize(cutoff);
+           m = end;
     }
-    bestT = T[max_element(score.begin(), score.end())->second];
-    int count = 0;
-    for (i=0; i<matches.size(); i++) {
-        double d2 = bestT.getSqError(matches[i]);
-        if (d2 < distSq) {
-            isInlier[i] = true;
-            count++;
+       size_t best_index = std::max_element(score.begin(), 
score.end())->second;
+       best = hypotheses[best_index];
+       size_t count = 0;
+       inlier.resize(observations.size());
+       for (size_t i=0; i<observations.size(); i++) {
+           if (best.isInlier(observations[i], tolerance)) {
+               inlier[i] = true;
+               ++count;
         } else
-            isInlier[i] = false;
+               inlier[i] = false;
     }
     return count;
-}
+    }
 
-template <class Correspondence, class Transform>
-int findRANSACInliersBFS(const std::vector<Correspondence>& matches, double 
dist, unsigned int sampleSize, unsigned int tries, unsigned int perPass, 
Transform& bestT, std::vector<bool>& isInlier) {
-    assert(isInlier.size() >= matches.size());
-    unsigned int i,j;
-    std::vector<Transform> T(tries,bestT);
-    std::vector<std::pair<int, unsigned int> > score(tries);
-    std::vector<Correspondence> sample(sampleSize);
-    for (i=0; i<tries; i++) {
-        std::vector<unsigned int> index(sampleSize);
-        randomTuple(index, matches.size());
-        for (j=0; j<sampleSize; j++)
-            sample[j] = matches[index[j]];
-        T[i].estimate(sample);
-        score[i].first = 0;
-        score[i].second = i;
-    }
-    unsigned int curr = 0;
-    double distSq = dist*dist;
-    int toCut = 1;
-    unsigned int passes = tries/perPass + 1;
-    while (curr < matches.size()) {
-        if (matches.size() - curr < perPass)
-            perPass = matches.size() - curr;
-        for (i=0; i<score.size(); i++) {
-            unsigned int t = score[i].second;
-            for (unsigned int p=0; p<perPass; p++) {
-                double d2 = T[t].getSqError(matches[curr+p]);
-                if (d2 < distSq)
-                    score[i].first++;
-            }
-        }
-        curr += perPass;
-        int cutoff = tries-((toCut*tries)>>passes);
-        toCut *= 2;
-        if (cutoff > 1) {
-            nth_element(score.begin(), score.begin()+cutoff, score.end(), 
std::greater<std::pair<int, unsigned int> >());
-            score.resize(cutoff);
-        } else
+/// Breadth-first RANSAC implementation. The function is templated on the 
observation data type, the tolerance for inliers,
+/// and the transformation data type which must conform to the following 
interface:
+/// @code
+/// class Estimator {
+///     Estimator();
+///     // Estimate from a sequence of observations
+///     template <class It> bool estimate(It begin, It End); 
+///     // Check whether the given observation is an inlier for this estimate 
(with specified tolerance)
+///     template <class Obs, class Tol> bool isInlier(const Obs& obs, const 
Tol& tolerance) const;
+/// };
+/// @endcode
+/// All hypotheses are generated first, and preemptively discarded as more 
observations are examined.
+/// see the file @ref ransac_estimators.h for some Estimator classes for 
various transformations.
+/// @param[in] observations a vector of observations (usually point matches)
+/// @param[in] sample_size the number of samples used estimate a transformation
+/// @param[in] tolerance the tolerance (passed with each observation) to the 
transformation to check for inliers
+/// @param[in] N the number of hypotheses to test
+/// @param[in] block_size the number of hypotheses to test in a block (between 
culling hypotheses)
+/// @param[out] best the transformation hypothesis with the highest inlier 
count
+/// @param[out] inlier a vector of bools that describes the inlier set of the 
winning hypothesis
+/// @return the number of inliers for the winning hypothesis
+/// @ingroup ransac
+
+
+    template <class Obs, class Trans, class Tol> size_t 
find_RANSAC_inliers_breadth_first(const std::vector<Obs>& observations, int 
sample_size, const Tol& tolerance, size_t N, size_t block_size,
+                                                                               
          Trans& best, std::vector<bool>& inlier)
+    {
+       std::vector<Trans> hypotheses(N,best);
+       std::vector<std::pair<int,size_t> > score(N);
+       std::vector<size_t> sample_index(sample_size);    
+       std::vector<Obs> sample(sample_size); 
+       for (size_t i=0; i<hypotheses.size(); i++) {
+           do {
+               randomTuple(sample_index, observations.size());
+               for (int s=0; s<sample_size; ++s)
+                   sample[s] = observations[sample_index[s]];
+           }
+           while (!hypotheses[i].estimate(sample.begin(), sample.end()));
+           score[i] = std::make_pair(0,i);
+       }
+       size_t m = 0;
+       const double factor = getShrinkRatio(N, observations.size(), 
block_size);
+       while (m < observations.size()) {
+           size_t end = std::min(observations.size(), m+block_size);
+           for (size_t i=0; i<score.size(); i++) {
+               const Trans& thisT = hypotheses[score[i].second];
+               size_t s = 0;
+               for (size_t j=m; j!=end; j++) {
+                   if (thisT.isInlier(observations[j], tolerance))
+                       ++s;
+               }
+               score[i].first += s;
+           }
+           unsigned int cutoff = (unsigned int)(score.size() * factor);
+           if (cutoff == 0)
             break;
+           std::nth_element(score.begin(), score.end(), score.begin()+cutoff, 
greater<std::pair<int,size_t> >());
+           score.resize(cutoff);
+           m = end;
     }
-    int bestIndex = max_element(score.begin(), score.end())-score.begin();
-    bestT = T[score[bestIndex].second];
-    int count = 0;
-    for (i=0; i<matches.size(); i++) {
-        double d2 = bestT.getSqError(matches[i]);
-        if (d2 < distSq) {
-            isInlier[i] = true;
-            count++;
+       size_t best_index = std::max_element(score.begin(), 
score.end())->second;
+       best = hypotheses[best_index];
+       size_t count = 0;
+       inlier.resize(observations.size());
+       for (size_t i=0; i<observations.size(); i++) {
+           if (best.isInlier(observations[i], tolerance)) {
+               inlier[i] = true;
+               ++count;
         } else
-            isInlier[i] = false;
+               inlier[i] = false;
     }
     return count;
-}
+    }
 
 
 /// helper function for use with ransac functions. It removes elements

Index: ransac_estimators.h
===================================================================
RCS file: /cvsroot/toon/tag/tag/ransac_estimators.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -b -r1.2 -r1.3
--- ransac_estimators.h 31 May 2006 16:46:20 -0000      1.2
+++ ransac_estimators.h 13 Jun 2006 01:50:44 -0000      1.3
@@ -1,267 +1,242 @@
 #ifndef TAG_RANSAC_ESTIMATORS_H
 #define TAG_RANSAC_ESTIMATORS_H
 
+#include <vector>
+#include <cassert>
+
 #include <TooN/TooN.h>
+#include <TooN/helpers.h>
+#include <TooN/Cholesky.h>
+#include <TooN/wls.h>
+#include <TooN/LU.h>
 #include <TooN/SVD.h>
 #include <TooN/SymEigen.h>
 
 namespace tag {
 
-/// RANSAC estimator to compute an affine transformation between a set of 
2D-2D correspondences.
-/// The Correspondence datatype has to implement the following interface:
+    template <class T> inline const Vector<2>& first_point(const T& t) { 
return t.first; }
+    template <class T> inline const Vector<2>& second_point(const T& t) { 
return t.second; }
+    template <class T> inline double noise(const T& t) { return 1.0; }
+    
+
+namespace essential_matrix {
+
+    // For degenerate case
+    template <class It> void getProjectiveHomography(It begin, It end, 
TooN::Matrix<3>& H)
+    {
+       assert(std::distance(begin,end) >= 4);
+
+       TooN::WLS<8> wls;
+       for (It it=begin; it!=end; it++) {
+           const Vector<2>& a = first_point(*it);
+           const Vector<2>& b = first_point(*it);
+           const double rows[2][8] = {{a[0], a[1], 1, 0, 0, 0, -b[0]*a[0], 
-b[0]*a[1]},
+                                      {0, 0, 0, a[0], a[1], 1, -b[1]*a[0], 
-b[1]*a[1]}};
+           wls.add_df(b[0], Vector<8>(rows[0]));
+           wls.add_df(b[1], Vector<8>(rows[1]));
+       }
+       wls.compute();
+       TooN::Vector<8> h = wls.get_mu();
+       H[0] = h.template slice<0,3>();
+       H[1] = h.template slice<3,3>();
+       H[2][0] = h[6];
+       H[2][1] = h[7];
+       H[2][2] = 1;
+    }
+
+    
+    template <class M> inline int getValidPair(const TooN::Matrix<3>& R1, 
const TooN::Matrix<3>& R2, const TooN::Vector<2>& e, double z1, const M& m)
+    {
+       TooN::Vector<2> dm = m.b-e;
+       TooN::Vector<3> ha = TooN::unproject(m.a);
+       TooN::Vector<3> inf1 = R1*ha;
+       TooN::Vector<3> inf2 = R2*ha;
+       double zp1 = inf1[2];
+       double zp2 = inf2[2];
+       TooN::Vector<2> pinf1 = project(inf1);
+       TooN::Vector<2> pinf2 = project(inf2);
+       if (zp1*dm*(pinf1 - e) >= 0) {
+           // R1
+           if (zp1 < 0)
+               return z1 <= 0 ? 0 : 1;
+           // check for sign match
+           return  ((pinf1-m.b)*dm*z1 >= 0) ? 0 : 1;
+       } else {
+           //R2
+           if (zp2 < 0) 
+               return z1 <= 0 ? 2 : 3;
+           return ((pinf2-m.b)*dm*z1 >= 0) ? 2 : 3;
+       }
+    }
+
+    template <int N, class Accessor> double determinant(const 
TooN::FixedMatrix<N,N,Accessor>& M)
+    {
+       TooN::LU<N> lu(M);
+       double det = 1;
+       for (int i=0; i<N; i++)
+           det *= lu.get_lu()[i][i];
+       return det;
+    }
+    
+/// RANSAC estimator to compute the essential matrix from a set of 2D-2D 
correspondences
+/// The observations passed (via iterators) to the estimate method must allow:
 /// @code
-/// struct Correspondence {
-///   TooN::Vector<2> a;
-///   TooN::Vector<2> b;
-/// };
+/// TooN::Vector<2> a = first_point(*it); // default value is "(*it).first"
+/// TooN::Vector<2> b = second_point(*it); // default value is "(*it).second"
+/// double R = noise(*it); // default value is "1.0"
 /// @endcode
 /// The resulting transformation will map from a -> b.
 /// @ingroup ransac
-template <class Correspondence>
-struct AffineHomography {
-    /// the linear part of the resulting affine transformation
-    TooN::Matrix<2> A;
-    /// the translation part of the resulting affine transformation
-    TooN::Vector<2> t;
 
-    AffineHomography() { A[0][0] = A[0][1] = A[1][0] = A[1][1] = t[0] = t[1] = 
0; }
-
-    void estimate(const std::vector<Correspondence>& matches) {
-        TooN::Matrix<> Ad(matches.size(), 3);
-        TooN::Vector<> bx(matches.size()), by(matches.size());
-        assert(matches.size() >= 3);
-        for (unsigned int i=0; i<matches.size(); i++) {
-            const Correspondence& m = matches[i];
-            Ad[i].template slice<0,2>() =  m.a;
-            Ad[i][2] = 1;
-            bx[i] = m.b[0];
-            by[i] = m.b[1];
-        }
-        //TooN::Matrix<3> ATA = Ad.T()*Ad;
-        TooN::SVD<> svd(Ad);
-        TooN::Vector<3> coefsX = svd.backsub(bx);
-        TooN::Vector<3> coefsY = svd.backsub(by);
-        A[0][0] = coefsX[0];
-        A[0][1] = coefsX[1];
-        t[0] = coefsX[2];
-        A[1][0] = coefsY[0];
-        A[1][1] = coefsY[1];
-        t[1] = coefsY[2];
-    }
-
-    inline double getSqError(const Correspondence& m) const {
-        TooN::Vector<2> disp = A*m.a + t - m.b;
-        return disp*disp;
+    struct EssentialMatrix {
+       TooN::Matrix<3> E;
+       template <class It> bool estimate(It begin, It end) {
+           TooN::Matrix<9> M = zeros<9,9>();
+           for (It it=begin; it!= end; ++it) {
+               const TooN::Vector<2>& a = first_point(*it);
+               const TooN::Vector<2>& b = second_point(*it);
+               const double factor = 1.0/noise(*it);
+               const double m[9] = {b[0]*a[0], b[0]*a[1], b[1]*a[0], 
b[1]*a[1], b[0], b[1], a[0], a[1], 1};
+               for (int j=0; j<9; j++) {
+                   for (int k=j; k<9; k++) {
+                       M[j][k] += m[j]*m[k] * factor;
+                   }
+               }
+           }
+           for (int j=0; j<9;j++)
+               for (int k=j; k<9; k++)
+                   M[k][j] = M[j][k];
+           TooN::Matrix<4> M11 = M.template slice<0,0,4,4>();
+           TooN::Matrix<4,5> M12 = M.template slice<0,4,4,5>();
+           TooN::Matrix<5> M22 = M.template slice<4,4,5,5>();
+           TooN::Cholesky<5> chol(M22);
+           if (chol.get_rank() < 5) {
+               if (chol.get_rank() < 3)
+                   return false;
+               TooN::Matrix<3> R;
+               // Translation is zero (choose t = [0,0,1])
+               essential_matrix::getProjectiveHomography(begin, end, R);
+               TooN::SO3::coerce(R);
+               E[0] = -R[1];
+               E[1] = R[0];
+               E[2][0] = E[2][1] = E[2][2] = 0;
+               return true;
+           } 
+           TooN::Matrix<5,4> K = chol.inverse_times(M12.T());
+           TooN::Matrix<4> Q = M11 - M12*K;
+           TooN::SymEigen<4> eigen(Q);
+           TooN::Vector<4> e1 = eigen.get_evectors()[0];
+           TooN::Vector<5> e2 = -(K * e1);
+           E[0][0] = e1[0];
+           E[0][1] = e1[1];
+           E[1][0] = e1[2];
+           E[1][1] = e1[3];
+           E[0][2] = e2[0];
+           E[1][2] = e2[1];
+           E[2] = e2.template slice<2,3>();
+           
+           TooN::SVD<3> svdE(E);
+           E = svdE.get_U()*TooN::diagmult(makeVector(1,1,0),svdE.get_VT());
+           return true;            
+       }
+       
+       template <class Match> inline bool isInlier(const Match& m, double r) 
const {
+           TooN::Vector<3> line = E.template slice<0,0,3,2>()*first_point(m) + 
E.T()[2];
+           double dot = line.template slice<0,2>() * second_point(m) + line[2];
+           return (dot*dot <= (line[0]*line[0] + line[1]*line[1]) * r*r * 
noise(m));
+       }
+       
+       /// Decompose the essential matrix into four possible SE3s
+       /// @param[in] begin beginning iterator for observations
+       /// @param[in] end ending iterator for observations
+       /// @param[out] group where to store the membership info (0,1,2, or 3) 
for each observation
+       /// @return four pairs of the form {number of votes, SE3}
+       template <class It> std::vector<std::pair<size_t, TooN::SE3> > 
decompose(It begin, It end, std::vector<int>& group)
+       {
+           static const double vals[9]={0,-1,0,1,0,0,0,0,1};
+           static const TooN::Matrix<3> Rz(vals);
+
+           const size_t N = std::distance(begin,end);
+           assert(group.size() >= N);
+           
+           TooN::SVD<3> svdE(E);
+           TooN::Matrix<3> R1 = svdE.get_U()*Rz.T()*svdE.get_VT();
+           TooN::Matrix<3> R2 = svdE.get_U()*Rz*svdE.get_VT();
+           if (essential_matrix::determinant(R1) < 0) {
+               R1 = -1*R1;
+               R2 = -1*R2;
+           }
+           TooN::Vector<3> t1 = svdE.get_U().T()[2];
+           TooN::Vector<3> t2 = -t1;
+           TooN::Vector<2> epipole = project(t1); // which is the same as 
project(t2)
+           std::vector<std::pair<size_t, TooN::SE3> > 
result(4,std::make_pair(0,TooN::SE3()));
+           int i=0;
+           for (It it = begin; it!=end; ++it, ++i) {
+               int index = getValidPair(R1, R2, epipole, t1[2], *it);
+               result[index].first++;  
+               group[i] = index;
+           }
+           result[0].second.get_rotation() = result[1].second.get_rotation() = 
R1;
+           result[2].second.get_rotation() = result[3].second.get_rotation() = 
R2;
+           result[0].second.get_translation() = 
result[2].second.get_translation() = t1;
+           result[1].second.get_translation() = 
result[3].second.get_translation() = t2;
+           return result;
     }
-};
 
-/// RANSAC estimator to compute the fundamental matrix between two views based 
on 2D-2D correspondences.
-/// The correspondences are coordinates in camera frame (after undistortion 
etc.)
-/// The Correspondence datatype has to implement the following interface:
-/// @code
-/// struct Correspondence {
-///   TooN::Vector<2> a;
-///   TooN::Vector<2> b;
-/// };
-/// @endcode
-/// @ingroup ransac
-template <class Correspondence>
-struct FundamentalMatrix {
-    TooN::Matrix<3> F;
-    TooN::Matrix<3> T;
-    TooN::Vector<9> diag;
-
-    void setScale(double scale, bool translate=true) {
-        T[0][0] = T[1][1] = 1/scale;
-        T[0][1] = T[1][0] = T[2][0] = T[2][1] = 0;
-        T[0][2] = translate ? -0.5 : 0;
-        T[1][2] = translate ? -0.5 : 0;
-        T[2][2] = 1;
-    }
-
-    FundamentalMatrix() {}
-
-    void estimate(const std::vector<Correspondence>& matches) {
-        TooN::Matrix<> A(std::max(matches.size(),(unsigned)9),9);
-        for (unsigned int i=0; i<matches.size(); i++) {
-            TooN::Vector<3> a,b;
-            a.template slice<0,2>() = matches[i].a;  a[2] = 1;
-            b.template slice<0,2>() = matches[i].b;  b[2] = 1;
-            a = T*a;
-            b = T*b;
-            A[i][0] = a[0]*b[0];
-            A[i][1] = a[0]*b[1];
-            A[i][2] = a[0];
-            A[i][3] = a[1]*b[0];
-            A[i][4] = a[1]*b[1];
-            A[i][5] = a[1];
-            A[i][6] = b[0];
-            A[i][7] = b[1];
-            A[i][8] = -1;
-        }
-        for (unsigned int i=matches.size(); i<9; i++) {
-            for (int j=0; j<9; j++)
-                A[i][j] = 0;
-        }
-        TooN::SVD<> svdA(A);
-        diag = svdA.get_diagonal();
-        TooN::Vector<9> f = svdA.get_VT()[8];
-        for (int i=0; i<9; i++)
-        F[i/3][i%3] = f[i];
-        TooN::SVD<3> svdF(F);
-        TooN::Vector<3> fdiag = svdF.get_diagonal();
-        TooN::Matrix<3> DVT = svdF.get_VT();
-        fdiag[2] = 0;
-        for (int i=0; i<3; i++)
-        DVT[i] *= fdiag[i]/fdiag[0];
-        F = T.T() * svdF.get_U()*DVT * T;
-    }
-
-    inline double getSqError(const Correspondence& m) const {
-        TooN::Vector<3> u;
-        u[0] = m.b[0];
-        u[1] = m.b[1];
-        u[2] = 1;
-        TooN::Vector<3> line = F*u;
-        double dot = line[0]*m.a[0] + line[1]*m.a[1] + line[2];
-        double a2 = line[0]*line[0];
-        double b2 = line[1]*line[1];
-        return (dot*dot)*1/(a2+b2);
-    }
-};
+    };
+} // close namespace essential_matrix
+
+ using essential_matrix::EssentialMatrix;
 
-/// RANSAC estimator to compute ???
-/// The Correspondence datatype has to implement the following interface:
+/// RANSAC estimator to compute an affine homography from a set of 2D-2D 
correspondences
+/// The observations passed (via iterators) to the estimate method must allow:
 /// @code
-/// struct Correspondence {
-///   TooN::Vector<2> a;
-///   TooN::Vector<2> b;
-/// };
+/// TooN::Vector<2> a = first_point(*it); // default value is "(*it).first"
+/// TooN::Vector<2> b = second_point(*it); // default value is "(*it).second"
+/// double R = noise(*it); // default value is "1.0"
 /// @endcode
+/// The resulting transformation will map from a -> b.
 /// @ingroup ransac
-template <class Correspondence>
-struct DifferentialEssentialMatrix {
-    TooN::Matrix<3> s;
-    TooN::Vector<3> v,w;
-    TooN::Vector<9> diag;
-
-    DifferentialEssentialMatrix() {}
-
-    void estimate(const std::vector<Correspondence>& matches) {
-        TooN::Matrix<> A(std::max(matches.size(),(unsigned)9),9);
-        for (unsigned int i=0; i<matches.size(); i++) {
-            TooN::Vector<2> q=matches[i].a, u=matches[i].b-matches[i].a;
-            A[i][0] = -u[1];
-            A[i][1] = u[0];
-            A[i][2] = u[1]*q[0]-u[0]*q[1];
-            A[i][3] = q[0]*q[0];
-            A[i][4] = 2*q[0]*q[1];
-            A[i][5] = 2*q[0];
-            A[i][6] = q[1]*q[1];
-            A[i][7] = 2*q[1];
-            A[i][8] = 1;
-        }
-        for (unsigned int i=matches.size(); i<9; i++) {
-            for (int j=0; j<9; j++)
-                A[i][j] = 0;
-        }
-        TooN::SVD<> svdA(A);
-        diag = svdA.get_diagonal();
-        TooN::Vector<9> e = svdA.get_VT()[8];
-        // renormalize ?
-        e *= 1.0/sqrt(e.template slice<0,3>() * e.template slice<0,3>());
-        // Velocity
-        v = e.template slice<0,3>();
-        // Symmetric TooN::Matrix s
-        s[0][0]=e[3];
-        s[0][1]=s[1][0]=e[4];
-        s[0][2]=s[2][0]=e[5];
-        s[1][1]=e[6];
-        s[1][2]=s[2][1]=e[7];
-        s[2][2]=e[8];
-        TooN::SymEigen<3> eigen(s);
-        // We want e1 >= e2 >= e3, e1 >= 0, e3 <= 0
-        TooN::Vector<3> lambda = eigen.get_evalues();
-        lambda[0] = std::max(0.0,lambda[0]);
-        lambda[2] = std::min(0.0,lambda[2]);
-        // Project to special symmetric TooN::Matrix
-        TooN::Vector<3> sigma = (TooN::make_Vector, 2*lambda[0] + lambda[1] - 
lambda[2],
-                        lambda[0] + 2*lambda[1] + lambda[2],
-                        -lambda[0] + lambda[1] + 2*lambda[2]);
-        sigma /= 3;
-        s = eigen.get_evectors().T() * diagmult(sigma,eigen.get_evectors());
-        // recover w
-        double lam = sigma[0] - sigma[2];
-        double theta = acos(-sigma[1]/lam);
-        TooN::Matrix<3> Ry1, Ry2, Rz;
-        rotationY(theta/2 - M_PI/2, Ry1);
-        rotationY(theta, Ry2);
-        zero(Rz);
-        Rz[0][1] = -1;
-        Rz[1][0] = 1;
-        Rz[2][2] = 1;
-        TooN::Vector<3> diagLambda = (TooN::make_Vector,lam, lam,0);
-        TooN::Vector<3> diag1 = (TooN::make_Vector,1,1,0);
-        TooN::Matrix<3> V = eigen.get_evectors().T() * Ry1.T();
-        TooN::Matrix<3> U = -1* V * Ry2;
-        TooN::Vector<3> vhat[4] = { uncross(V*Rz*diagmult(diag1,V.T())),
-                    uncross(V*Rz.T()*diagmult(diag1,V.T())),
-                    uncross(U*Rz*diagmult(diag1,U.T())),
-                    uncross(U*Rz.T()*diagmult(diag1,U.T())) };
-        double dots[4];
-        for (int k=0; k<4; k++)
-        dots[k] = vhat[k]*v;
-        int maxdot = std::max_element(dots, dots+4)-dots;
-        switch (maxdot) {
-        case 0: w = uncross(U*Rz*diagmult(diagLambda, U.T())); break;
-        case 1: w = uncross(U*Rz.T()*diagmult(diagLambda, U.T())); break;
-        case 2: w = uncross(V*Rz*diagmult(diagLambda, V.T())); break;
-        case 3: w = uncross(V*Rz.T()*diagmult(diagLambda, V.T())); break;
-        }
-    }
-
-    inline double getSqError(const Correspondence& m) const {
-        TooN::Vector<3> q = (TooN::make_Vector, m.a[0], m.a[1], 1);
-        TooN::Vector<3> u = (TooN::make_Vector, m.b[0]-m.a[0], m.b[1]-m.a[1], 
0);
-        TooN::Vector<3> top = v^q;
-        TooN::Vector<3> bottom = s*q;
-        return u*top + q*bottom;
-    }
-
-    TooN::Vector<3> recoverAngularVelocity() {
-        TooN::SymEigen<3> eigen(s);
-        TooN::Vector<3> sigma = eigen.get_evalues();
-        double lambda = sigma[0] - sigma[2];
-        double theta = acos(-sigma[1]/lambda);
-        TooN::Matrix<3> Ry1, Ry2, Rz;
-        rotationY(theta/2 - M_PI/2, Ry1);
-        rotationY(theta, Ry2);
-        zero(Rz);
-        Rz[0][1] = -1;
-        Rz[1][0] = 1;
-        Rz[2][2] = 1;
-        TooN::Vector<3> diagLambda = (TooN::make_Vector, lambda, lambda,0);
-        TooN::Vector<3> diag1 = (TooN::make_Vector,1,1,0);
-        TooN::Matrix<3> V = eigen.get_evectors().T() * Ry1.T();
-        TooN::Matrix<3> U = -1* V * Ry2;
-        TooN::Vector<3> vhat[4] = { uncross(V*Rz*diagmult(diag1,V.T())),
-                    uncross(V*Rz.T()*diagmult(diag1,V.T())),
-                    uncross(U*Rz*diagmult(diag1,U.T())),
-                    uncross(U*Rz.T()*diagmult(diag1,U.T())) };
-        double dots[4];
-        for (int k=0; k<4; k++)
-        dots[k] = vhat[k]*v;
-        int maxdot = std::max_element(dots, dots+4)-dots;
-        switch (maxdot) {
-        case 0: w = uncross(U*Rz*diagmult(diagLambda, U.T())); break;
-        case 1: w = uncross(U*Rz.T()*diagmult(diagLambda, U.T())); break;
-        case 2: w = uncross(V*Rz*diagmult(diagLambda, V.T())); break;
-        case 3: w = uncross(V*Rz.T()*diagmult(diagLambda, V.T())); break;
-        }
-        return w;
+
+struct AffineHomography {
+    /// the linear part of the affine transformation
+    TooN::Matrix<2> A;
+    /// the translation part of the affine transformation
+    TooN::Vector<2> t;
+
+    AffineHomography() : A(TooN::zeros<2,2>()), t(TooN::zeros<2>()) {}
+
+    template <class It> void estimate(It begin, It end) {
+       TooN::WLS<3> wls_x, wls_y;
+       wls_x.clear();
+       wls_y.clear();
+       size_t i=0;
+        for (It it = begin; it!= end; ++it, ++i) {         
+           const TooN::Vector<2>& a = first_point(matches[i]);
+           const TooN::Vector<2>& b = second_point(matches[i]);
+           const double weight = 1.0 / noise(matches[i]);
+           wls_x.add_df(b[0], TooN::unproject(a), weight);
+           wls_y.add_df(b[1], TooN::unproject(a), weight);
+        }
+       wls_x.compute();
+       wls_y.compute();
+       TooN::Vector<3> Atx = wls_x.get_mu();
+       TooN::Vector<3> Aty = wls_y.get_mu();
+       A[0] = Atx.template slice<0,2>();
+       A[1] = Aty.template slice<0,2>();
+       t[0] = Atx[2];
+       t[1] = Aty[2];
+    }
+
+    template <class M> inline bool isInlier(const M& m, double r) const {
+       const TooN::Vector<2>& a = first_point(m);
+       const TooN::Vector<2>& b = second_point(m);
+       const TooN::Vector<2> disp = A*a + t - b;
+       return (disp*disp) <= r*r * noise(m);
     }
 };
 
+
 } // namespace tag
 
 #endif




reply via email to

[Prev in Thread] Current Thread [Next in Thread]