vpgl_fm_compute_7_point.cxx
Go to the documentation of this file.
1 // This is core/vpgl/algo/vpgl_fm_compute_7_point.cxx
2 #ifndef vpgl_fm_compute_7_point_cxx_
3 #define vpgl_fm_compute_7_point_cxx_
4 //:
5 // \file
6 
7 #include <iostream>
9 //
10 #include <vnl/vnl_fwd.h>
11 #include <vnl/vnl_math.h> // for twopi
13 #ifdef _MSC_VER
14 # include <vcl_msvc_warnings.h>
15 #endif
16 
17 //-------------------------------------------
18 bool
20  const std::vector< vgl_homg_point_2d<double> >& pr,
21  const std::vector< vgl_homg_point_2d<double> >& pl,
22  std::vector< vpgl_fundamental_matrix<double>* >& fm )
23 {
24  // Check that there are at least 7 points.
25  if ( pr.size() < 7 || pl.size() < 7 ) {
26  std::cerr << "vpgl_fm_compute_7_point: Need at least 7 point pairs.\n"
27  << "Number in each set: " << pr.size() << ", " << pl.size() << std::endl;
28  return false;
29  }
30 
31  // Check that the correspondence lists are the same size.
32  if ( pr.size() != pl.size() ) {
33  std::cerr << "vpgl_fm_compute_7_point: Need correspondence lists of same size.\n";
34  return false;
35  }
36 
37  // Condition if necessary.
38  std::vector< vgl_homg_point_2d<double> > pr_norm, pl_norm;
39  vgl_norm_trans_2d<double> prnt, plnt;
40  if ( precondition_ ) {
41  prnt.compute_from_points(pr);
42  plnt.compute_from_points(pl);
43  for ( unsigned i = 0; i < pl.size(); i++ ) {
44  pr_norm.push_back( prnt*pr[i] );
45  pl_norm.push_back( plnt*pl[i] );
46  }
47  }
48  else {
49  for ( unsigned i = 0; i < pl.size(); i++ ) {
50  pr_norm.push_back( pr[i] );
51  pl_norm.push_back( pl[i] );
52  }
53  }
54 
55  // Construct the design matrix from the point correspondences.
56  vnl_matrix<double> design_matrix(static_cast<unsigned int>(pr_norm.size()), 9);
57  for ( unsigned r = 0; r < pr_norm.size(); r++ ) {
58  design_matrix(r,0) = pr_norm[r].x()*pl_norm[r].x();
59  design_matrix(r,1) = pr_norm[r].y()*pl_norm[r].x();
60  design_matrix(r,2) = pr_norm[r].w()*pl_norm[r].x();
61  design_matrix(r,3) = pr_norm[r].x()*pl_norm[r].y();
62  design_matrix(r,4) = pr_norm[r].y()*pl_norm[r].y();
63  design_matrix(r,5) = pr_norm[r].w()*pl_norm[r].y();
64  design_matrix(r,6) = pr_norm[r].x()*pl_norm[r].w();
65  design_matrix(r,7) = pr_norm[r].y()*pl_norm[r].w();
66  design_matrix(r,8) = pr_norm[r].w()*pl_norm[r].w();
67  }
68 
69  design_matrix.normalize_rows();
70  vnl_svd<double> design_svd( design_matrix );
71  vnl_matrix<double> W = design_svd.nullspace();
72 
73  // Take the first and second nullvectors from the nullspace
74  // Since rank 2 these should be the only associated with non-zero
75  // root (probably need conditioning first to be actually rank 2)
76 
79 
80  // Using the fact that Det(alpha*F1 +(1 - alpha)*F2) == 0
81  // find the real roots of the cubic equation that satisfy
82  std::vector<double> a = get_coeffs(F1, F2);
83  std::vector<double> roots = solve_cubic(a);
84  for (unsigned int i = 0; i < roots.size(); i++) {
85  vpgl_fundamental_matrix<double> F_temp( F1.as_ref()*roots[0] + F2*(1 - roots[i]) );
86  if ( precondition_ )
87  F_temp.set_matrix( plnt.get_matrix().transpose()*F_temp.get_matrix()*prnt.get_matrix() );
88  fm.push_back( new vpgl_fundamental_matrix<double>(F_temp) );
89  }
90  return true;
91 };
92 
93 
94 //------------------------------------------------
95 std::vector<double>
97  vnl_double_3x3 const& F2 )
98 {
99  // All the following computed with Mapl_norme for oxl/mvl/FMatrixCompute7Point.
100  double a=F1(0,0), j=F2(0,0), aa=a-j,
101  b=F1(0,1), k=F2(0,1), bb=b-k,
102  c=F1(0,2), l=F2(0,2), cc=c-l,
103  d=F1(1,0), m=F2(1,0), dd=d-m,
104  e=F1(1,1), n=F2(1,1), ee=e-n,
105  f=F1(1,2), o=F2(1,2), ff=f-o,
106  g=F1(2,0), p=F2(2,0), gg=g-p,
107  h=F1(2,1), q=F2(2,1), hh=h-q,
108  i=F1(2,2), r=F2(2,2), ii=i-r;
109 
110  double a1=ee*ii-ff*hh, b1=ee*r+ii*n-ff*q-hh*o, c1=r*n-o*q;
111  double d1=bb*ii-cc*hh, e1=bb*r+ii*k-cc*q-hh*l, f1=r*k-l*q;
112  double g1=bb*ff-cc*ee, h1=bb*o+ff*k-cc*n-ee*l, i1=o*k-l*n;
113 
114  std::vector<double> v;
115  v.push_back(aa*a1-dd*d1+gg*g1);
116  v.push_back(aa*b1+a1*j-dd*e1-d1*m+gg*h1+g1*p);
117  v.push_back(aa*c1+b1*j-dd*f1-e1*m+gg*i1+h1*p);
118  v.push_back(c1*j-f1*m+i1*p);
119 
120  return v;
121 };
122 
123 
124 //------------------------------------------------
125 std::vector<double>
127 {
128  double a = v[1], b = v[2], c = v[3];
129  double s = (b > 0.0) ? 1.0 : -1.0;
130  double d = b * b - 4 * a * c;
131 
132  // round off error
133  if ( d > -1e-5 && d < 0)
134  d = 0.0;
135 
136  if (d < 0.0) // doesn't work for compl_normex roots
137  return std::vector<double>(); // empty list
138 
139  double q = -0.5 * ( b + s * std::sqrt(d));
140  std::vector<double> l; l.push_back(q/a); l.push_back(c/q);
141  return l;
142 };
143 
144 
145 //------------------------------------------------
146 std::vector<double>
147 vpgl_fm_compute_7_point::solve_cubic( std::vector<double> v )
148 {
149  double a = v[0], b = v[1], c = v[2], d = v[3];
150 
151  // firstly check to see if we have appr_normoximately a quadratic
152  double len = a*a + b*b + c*c + d*d;
153  if (std::abs(a*a/len) < 1e-6 )
154  return solve_quadratic(v);
155 
156  b /= a; c /= a; d /= a; b /= 3;
157  // With the substitution x = y-b, the equation becomes y^3-3qy+2r = 0:
158  double q = b*b - c/3;
159  double r = b*(b*b-c/2) + d/2;
160  // At this point, a, c and d are no longer needed (c and d will be reused).
161 
162  if (q == 0) {
163  std::vector<double> w;
164  double cbrt = (r<0) ? std::exp(std::log(-2*r)/3.0) : -std::exp(std::log(2*r)/3.0);
165  w.push_back(cbrt - b);
166  return w;
167  }
168 
169  // With the Vieta substitution y = z+q/z this becomes z^6+2rz^3+q^3 = 0
170  // which is essentially a quadratic equation:
171 
172  d = r*r - q*q*q;
173  if ( d >= 0.0 ) {
174  // Compute a cube root
175  double z;
176  if ( -r + std::sqrt(d) >= 0 ) z = std::exp(std::log(-r + std::sqrt(d))/3.0);
177  else z = -std::exp(std::log(-r + std::sqrt(d))/3.0);
178 
179  // The case z=0 is excluded since this is q==0 which is handled above
180  std::vector<double> w; w.push_back(z + q/z - b); return w;
181  }
182 
183  // And finally the "irreducible case" (with 3 solutions):
184  c = std::sqrt(q);
185  double theta = std::acos( r/q/c ) / 3;
186  std::vector<double> l;
187  l.push_back(-2.0*c*std::cos(theta) - b);
188  l.push_back(-2.0*c*std::cos(theta + vnl_math::twopi/3) - b);
189  l.push_back(-2.0*c*std::cos(theta - vnl_math::twopi/3) - b);
190  return l;
191 };
192 
193 
194 #endif // vpgl_fm_compute_7_point_cxx_
vnl_matrix_fixed< T, 3, 3 > const & get_matrix() const
#define m
double const * data_block() const
static std::vector< double > get_coeffs(vnl_double_3x3 const &F1, vnl_double_3x3 const &F2)
The 7 point algorithm for computing a fundamental matrix from point correspondences.
#define v
vnl_matrix & normalize_rows()
static std::vector< double > solve_quadratic(std::vector< double > v)
vnl_matrix_fixed< T, num_cols, num_rows > transpose() const
vnl_vector< double > get_column(unsigned c) const
bool compute_from_points(std::vector< vgl_homg_point_2d< T > > const &points, bool isotropic=true)
#define l
static std::vector< double > solve_cubic(std::vector< double > v)
vnl_matrix< T > nullspace() const
bool compute(const std::vector< vgl_homg_point_2d< double > > &pr, const std::vector< vgl_homg_point_2d< double > > &pl, std::vector< vpgl_fundamental_matrix< double > * > &fm)
Compute from two sets of corresponding points.
const vnl_matrix_fixed< T, 3, 3 > & get_matrix() const
Get a copy of the FM in vnl form.
void set_matrix(const vpgl_proj_camera< T > &cr, const vpgl_proj_camera< T > &cl)