PICCANTE  0.4
The hottest HDR imaging library!
fundamental_matrix.hpp
Go to the documentation of this file.
1 /*
2 
3 PICCANTE
4 The hottest HDR imaging library!
5 http://vcg.isti.cnr.it/piccante
6 
7 Copyright (C) 2014
8 Visual Computing Laboratory - ISTI CNR
9 http://vcg.isti.cnr.it
10 First author: Francesco Banterle
11 
12 This Source Code Form is subject to the terms of the Mozilla Public
13 License, v. 2.0. If a copy of the MPL was not distributed with this
14 file, You can obtain one at http://mozilla.org/MPL/2.0/.
15 
16 */
17 
18 #ifndef PIC_COMPUTER_VISION_FUNDAMENTAL_HPP
19 #define PIC_COMPUTER_VISION_FUNDAMENTAL_HPP
20 
21 #include <vector>
22 #include <random>
23 #include <stdlib.h>
24 
25 #include "../base.hpp"
26 
27 #include "../image.hpp"
28 
29 #include "../filtering/filter_luminance.hpp"
30 #include "../filtering/filter_gaussian_2d.hpp"
31 
32 #include "../util/math.hpp"
33 #include "../util/eigen_util.hpp"
34 
35 #include "../features_matching/orb_descriptor.hpp"
36 #include "../features_matching/feature_matcher.hpp"
37 #include "../features_matching/binary_feature_lsh_matcher.hpp"
38 #include "../computer_vision/nelder_mead_opt_fundamental.hpp"
39 
40 #ifndef PIC_DISABLE_EIGEN
41 
42 #ifndef PIC_EIGEN_NOT_BUNDLED
43  #include "../externals/Eigen/Dense"
44  #include "../externals/Eigen/SVD"
45  #include "../externals/Eigen/Geometry"
46 #else
47  #include <Eigen/Dense>
48  #include <Eigen/SVD>
49  #include <Eigen/Geometry>
50 #endif
51 
52 #endif
53 
54 namespace pic {
55 
56 #ifndef PIC_DISABLE_EIGEN
57 
64 PIC_INLINE Eigen::Matrix3d estimateFundamental(std::vector< Eigen::Vector2f > &points0,
65  std::vector< Eigen::Vector2f > &points1)
66 {
67  Eigen::Matrix3d F;
68 
69  if((points0.size() != points1.size()) || (points0.size() < 8)) {
70  F.setZero();
71  return F;
72  }
73 
74  //shift and scale points for numerical stability
75  Eigen::Vector3f transform_0 = ComputeNormalizationTransform(points0);
76  Eigen::Vector3f transform_1 = ComputeNormalizationTransform(points1);
77 
78  Eigen::Matrix3d mat_0 = getShiftScaleMatrix(transform_0);
79  Eigen::Matrix3d mat_1 = getShiftScaleMatrix(transform_1);
80 
81  Eigen::MatrixXd A(points0.size(), 9);
82 
83  //set up the linear system
84  for(unsigned int i = 0; i < points0.size(); i++) {
85 
86  //transform coordinates for increasing stability of the system
87  Eigen::Vector2f p0 = points0[i];
88  Eigen::Vector2f p1 = points1[i];
89 
90  p0[0] = (p0[0] - transform_0[0]) / transform_0[2];
91  p0[1] = (p0[1] - transform_0[1]) / transform_0[2];
92 
93  p1[0] = (p1[0] - transform_1[0]) / transform_1[2];
94  p1[1] = (p1[1] - transform_1[1]) / transform_1[2];
95 
96  A(i, 0) = p0[0] * p1[0];
97  A(i, 1) = p0[0] * p1[1];
98  A(i, 2) = p0[0];
99  A(i, 3) = p0[1] * p1[0];
100  A(i, 4) = p0[1] * p1[1];
101  A(i, 5) = p0[1];
102  A(i, 6) = p1[0];
103  A(i, 7) = p1[1];
104  A(i, 8) = 1.0;
105  }
106 
107  //solve the linear system
108  Eigen::JacobiSVD< Eigen::MatrixXd > svd(A, Eigen::ComputeFullV);
109  Eigen::MatrixXd V = svd.matrixV();
110 
111  int n = int(V.cols()) - 1;
112 
113  F(0, 0) = V(0, n);
114  F(1, 0) = V(1, n);
115  F(2, 0) = V(2, n);
116 
117  F(0, 1) = V(3, n);
118  F(1, 1) = V(4, n);
119  F(2, 1) = V(5, n);
120 
121  F(0, 2) = V(6, n);
122  F(1, 2) = V(7, n);
123  F(2, 2) = V(8, n);
124 
125  //compute the final F matrix
126  Eigen::Matrix3d mat_1_t = Eigen::Transpose< Eigen::Matrix3d >(mat_1);
127  F = mat_1_t * F * mat_0;
128 
129  //enforce singularity
130  Eigen::JacobiSVD< Eigen::MatrixXd > svdF(F, Eigen::ComputeThinU | Eigen::ComputeThinV);
131  Eigen::Matrix3d Uf = svdF.matrixU();
132  Eigen::Matrix3d Vf = svdF.matrixV();
133  Eigen::Vector3d Df = svdF.singularValues();
134  Df[2] = 0.0;
135 
136  Eigen::Matrix3d F_new = Uf * DiagonalMatrix(Df) * Eigen::Transpose< Eigen::Matrix3d >(Vf);
137 
138  double norm = MAX(Df[0], Df[1]);
139  return F_new / norm;
140 }
141 
150 PIC_INLINE Eigen::Matrix3d estimateFundamentalRansac(std::vector< Eigen::Vector2f > &points0,
151  std::vector< Eigen::Vector2f > &points1,
152  std::vector< unsigned int > &inliers,
153  unsigned int maxIterations = 100,
154  double threshold = 0.01,
155  unsigned int seed = 1)
156 {
157  if(points0.size() < 9) {
158  return estimateFundamental(points0, points1);
159  }
160 
161  Eigen::Matrix3d F;
162  int nSubSet = 8;
163 
164  std::mt19937 m(seed);
165 
166  unsigned int n = int(points0.size());
167 
168  unsigned int *subSet = new unsigned int [nSubSet];
169 
170  inliers.clear();
171 
172  for(unsigned int i = 0; i < maxIterations; i++) {
173  getRandomPermutation(m, subSet, nSubSet, n);
174 
175  std::vector< Eigen::Vector2f > sub_points0;
176  std::vector< Eigen::Vector2f > sub_points1;
177 
178  for(int j = 0; j < nSubSet; j++) {
179  unsigned int k = subSet[j];
180  sub_points0.push_back(points0[k]);
181  sub_points1.push_back(points1[k]);
182  }
183 
184  Eigen::Matrix3d tmpF = estimateFundamental(sub_points0, sub_points1);
185 
186  //is it a good one?
187  std::vector< unsigned int > tmp_inliers;
188 
189  for(unsigned int j = 0; j < n; j++) {
190  Eigen::Vector3d p0 = Eigen::Vector3d(points0[j][0], points0[j][1], 1.0);
191  Eigen::Vector3d p1 = Eigen::Vector3d(points1[j][0], points1[j][1], 1.0);
192 
193  Eigen::Vector3d tmpF_p0 = tmpF * p0;
194  double n0 = sqrt(tmpF_p0[0] * tmpF_p0[0] + tmpF_p0[1] * tmpF_p0[1]);
195  if(n0 > 0.0) {
196  tmpF_p0 /= n0;
197  }
198 
199  double err = fabs(tmpF_p0.dot(p1));
200 
201  if(err < threshold){
202  tmp_inliers.push_back(j);
203  }
204  }
205 
206  //get the inliers
207  if(tmp_inliers.size() > inliers.size()) {
208  F = tmpF;
209  inliers.clear();
210  inliers.assign(tmp_inliers.begin(), tmp_inliers.end());
211  }
212  }
213 
214  //improve estimate with inliers only
215  if(inliers.size() > 7) {
216 
217  #ifdef PIC_DEBUG
218  printf("Better estimate using inliers only.\n");
219  #endif
220 
221  std::vector< Eigen::Vector2f > sub_points0;
222  std::vector< Eigen::Vector2f > sub_points1;
223 
224  for(unsigned int i = 0; i < inliers.size(); i++) {
225  sub_points0.push_back(points0[inliers[i]]);
226  sub_points1.push_back(points1[inliers[i]]);
227  }
228 
229  F = estimateFundamental(sub_points0, sub_points1);
230  }
231 
232  return F;
233 }
234 
240 PIC_INLINE Eigen::Matrix3d estimateFundamentalWithNonLinearRefinement(std::vector< Eigen::Vector2f > &points0,
241  std::vector< Eigen::Vector2f > &points1,
242  std::vector< unsigned int > &inliers,
243  unsigned int maxIterationsRansac = 100,
244  double thresholdRansac = 0.01,
245  unsigned int seed = 1,
246  unsigned int maxIterationsNonLinear = 10000,
247  float thresholdNonLinear = 1e-4f
248  )
249 {
250  Eigen::Matrix3d F = estimateFundamentalRansac(points0, points1, inliers, maxIterationsRansac, thresholdRansac, seed);
251 
252  //non-linear refinement using Nelder-Mead
253  NelderMeadOptFundamental nmf(points0, points1, inliers);
254 
255  float F_data_opt[9];
256  nmf.run(getLinearArrayFromMatrix(F), 9, thresholdNonLinear, maxIterationsNonLinear, &F_data_opt[0]);
257  F = getMatrixdFromLinearArray(F_data_opt, 3, 3);
258 
259  return F;
260 }
261 
267 PIC_INLINE Eigen::Matrix3d noramalizeFundamentalMatrix(Eigen::Matrix3d F)
268 {
269  Eigen::JacobiSVD< Eigen::Matrix3d > svdF(F, Eigen::ComputeThinU | Eigen::ComputeThinV);
270  Eigen::Matrix3d Uf = svdF.matrixU();
271  Eigen::Matrix3d Vf = svdF.matrixV();
272  Eigen::Vector3d Df = svdF.singularValues();
273  Df[2] = 0.0;
274 
275  Eigen::Matrix3d F_new = Uf * DiagonalMatrix(Df) * Eigen::Transpose< Eigen::Matrix3d >(Vf);
276 
277  double norm = MAX(Df[0], Df[1]);
278  return F_new / norm;
279 }
280 
289 PIC_INLINE Eigen::Matrix3d extractFundamentalMatrix(Eigen::Matrix34d &M0, Eigen::Matrix34d &M1, Eigen::VectorXd &e0, Eigen::VectorXd &e1) {
290 
291  Eigen::Matrix3d M0_3 = getSquareMatrix(M0);
292  Eigen::Matrix3d M1_3 = getSquareMatrix(M1);
293 
294 
295  Eigen::Matrix3d M0_inv = M0_3.inverse();
296  Eigen::Vector3d c0 = - M0_inv * getLastColumn(M0);
297  e1 = M1 * addOne(c0);
298 
299  Eigen::Matrix3d M1_inv = M1_3.inverse();
300  Eigen::Vector3d c1 = - M1_inv * getLastColumn(M1);
301  e0 = M0 * addOne(c1);
302 
303  Eigen::Matrix3d F;
304 
305  F(0, 0) = 0.0;
306  F(0, 1) = -e1(2);
307  F(0, 2) = e1(1);
308 
309  F(1, 0) = e1(2);
310  F(1, 1) = 0.0;
311  F(1, 2) = -e1(0);
312 
313  F(2, 0) = -e1(1);
314  F(2, 1) = e1(0);
315  F(2, 2) = 0.0;
316 
317  F = F * M1_3 * M0_inv;
318 
319  Eigen::JacobiSVD< Eigen::Matrix3d > svdF(F, Eigen::ComputeThinU | Eigen::ComputeThinV);
320  Eigen::Vector3d Df = svdF.singularValues();
321 
322  double norm = MAX(Df[0], MAX(Df[1], Df[2]));
323  return F / norm;
324 }
325 
333  Image *img1,
334  std::vector< Eigen::Vector2f > &m0,
335  std::vector< Eigen::Vector2f > &m1,
336  std::vector< unsigned int > &inliers)
337 {
338  Eigen::Matrix3d F;
339  if(img0 == NULL || img1 == NULL) {
340  return F;
341  }
342 
343  m0.clear();
344  m1.clear();
345  inliers.clear();
346 
347  //corners
348  std::vector< Eigen::Vector2f > corners_from_img0;
349  std::vector< Eigen::Vector2f > corners_from_img1;
350 
351  //compute the luminance images
354 
355  //extract corners
356  HarrisCornerDetector hcd(2.5f, 5);
357  hcd.execute(L0, &corners_from_img0);
358  hcd.execute(L1, &corners_from_img1);
359 
360  //compute ORB descriptors for each corner and image
361 
362  //apply a gaussian filter to luminance images
363  Image *L0_flt = FilterGaussian2D::execute(L0, NULL, 2.5f);
364  Image *L1_flt = FilterGaussian2D::execute(L1, NULL, 2.5f);
365 
366  //compute ORB descriptor
367  ORBDescriptor b_desc(31, 512);
368 
369  std::vector< unsigned int *> descs0;
370  b_desc.getAll(L0_flt, corners_from_img0, descs0);
371 
372  std::vector< unsigned int *> descs1;
373  b_desc.getAll(L1_flt, corners_from_img1, descs1);
374 
375  //match ORB descriptors
376  std::vector< Eigen::Vector3i > matches;
377  int n = b_desc.getDescriptorSize();
378 
379  //BinaryFeatureBruteForceMatcher bffm_bin(&descs1, n);
380  BinaryFeatureLSHMatcher bffm_bin(&descs1, n, 64);
381  bffm_bin.getAllMatches(descs0, matches);
382 
383  //get matches
384  FeatureMatcher<unsigned int>::filterMatches(corners_from_img0, corners_from_img1, matches, m0, m1);
385 
386  //estimate the fundamental matrix
387  F = estimateFundamentalWithNonLinearRefinement(m0, m1, inliers, 1000, 0.5, 1, 1000, 1e-4f);
388 
389  delete L0;
390  delete L1;
391  delete L0_flt;
392  delete L1_flt;
393 
394  return F;
395 }
396 
397 #endif // PIC_DISABLE_EIGEN
398 
399 } // end namespace pic
400 
401 #endif // PIC_COMPUTER_VISION_FUNDAMENTAL_HPP
Definition: filter_luminance.hpp:28
PIC_INLINE Eigen::Matrix3d getShiftScaleMatrix(Eigen::Vector3f &info)
getShiftScaleMatrix computes a shifting and scaling matrix
Definition: eigen_util.hpp:297
PIC_INLINE float * getLinearArrayFromMatrix(Eigen::Matrix3d &mat)
getLinearArray
Definition: eigen_util.hpp:404
Definition: nelder_mead_opt_fundamental.hpp:38
PIC_INLINE Eigen::Matrix3d estimateFundamentalRansac(std::vector< Eigen::Vector2f > &points0, std::vector< Eigen::Vector2f > &points1, std::vector< unsigned int > &inliers, unsigned int maxIterations=100, double threshold=0.01, unsigned int seed=1)
estimateFundamentalRansac
Definition: fundamental_matrix.hpp:150
void execute(Image *img, std::vector< Eigen::Vector2f > *corners)
execute
Definition: harris_corner_detector.hpp:137
PIC_INLINE Eigen::Matrix3d estimateFundamental(std::vector< Eigen::Vector2f > &points0, std::vector< Eigen::Vector2f > &points1)
estimateFundamental estimates the foundamental matrix between image 1 to image 2
Definition: fundamental_matrix.hpp:64
PIC_INLINE Eigen::Matrix3d estimateFundamentalFromImages(Image *img0, Image *img1, std::vector< Eigen::Vector2f > &m0, std::vector< Eigen::Vector2f > &m1, std::vector< unsigned int > &inliers)
estimateFundamentalFromImages
Definition: fundamental_matrix.hpp:332
PIC_INLINE Eigen::Matrix3d estimateFundamentalWithNonLinearRefinement(std::vector< Eigen::Vector2f > &points0, std::vector< Eigen::Vector2f > &points1, std::vector< unsigned int > &inliers, unsigned int maxIterationsRansac=100, double thresholdRansac=0.01, unsigned int seed=1, unsigned int maxIterationsNonLinear=10000, float thresholdNonLinear=1e-4f)
estimateFundamentalWithNonLinearRefinement
Definition: fundamental_matrix.hpp:240
PIC_INLINE Eigen::Vector3d getLastColumn(Eigen::Matrix34d &mat)
getLastColumn
Definition: eigen_util.hpp:169
PIC_INLINE Eigen::Matrix3d extractFundamentalMatrix(Eigen::Matrix34d &M0, Eigen::Matrix34d &M1, Eigen::VectorXd &e0, Eigen::VectorXd &e1)
extractFundamentalMatrix
Definition: fundamental_matrix.hpp:289
PIC_INLINE Eigen::Vector3f addOne(Eigen::Vector2f &x)
addOne
Definition: eigen_util.hpp:185
The LSH class.
Definition: binary_feature_lsh_matcher.hpp:34
The HarrisCornerDetector class.
Definition: harris_corner_detector.hpp:54
static Image * execute(Image *imgIn, Image *imgOut, LUMINANCE_TYPE type=LT_CIE_LUMINANCE)
execute
Definition: filter_luminance.hpp:166
virtual Scalar * run(Scalar *x_start, unsigned int n, Scalar epsilon=1e-4f, int max_iterations=1000, Scalar *x=NULL)
Definition: nelder_mead_opt_base.hpp:333
#define PIC_INLINE
Definition: base.hpp:33
PIC_INLINE Eigen::Matrix3d getSquareMatrix(Eigen::Matrix34d &mat)
getSquareMatrix
Definition: eigen_util.hpp:146
void getAllMatches(std::vector< unsigned int *> &descs0, std::vector< Eigen::Vector3i > &matches)
Definition: feature_matcher.hpp:81
The Image class stores an image as buffer of float.
Definition: image.hpp:60
void getAll(Image *img, std::vector< Eigen::Vector2f > &corners, std::vector< uint * > &descs)
getAll
Definition: brief_descriptor.hpp:226
PIC_INLINE Eigen::Matrix3d DiagonalMatrix(Eigen::Vector3d D)
DiagonalMatrix creates a diagonal matrix.
Definition: eigen_util.hpp:113
The ORBDescriptor class.
Definition: orb_descriptor.hpp:34
PIC_INLINE Eigen::Matrix3d noramalizeFundamentalMatrix(Eigen::Matrix3d F)
noramalizeFundamentalMatrix
Definition: fundamental_matrix.hpp:267
static void filterMatches(std::vector< Eigen::Vector2f > &c0, std::vector< Eigen::Vector2f > &c1, std::vector< Eigen::Vector3i > &matches, std::vector< Eigen::Vector2f > &p0, std::vector< Eigen::Vector2f > &p1)
filterMatches
Definition: feature_matcher.hpp:103
Definition: bilateral_separation.hpp:25
PIC_INLINE Eigen::Vector3f ComputeNormalizationTransform(std::vector< Eigen::Vector2f > &points)
ComputeNormalizationTransform.
Definition: eigen_util.hpp:530
#define MAX(a, b)
Definition: math.hpp:73
int getDescriptorSize()
getDescriptorSize returns the descriptor size.
Definition: brief_descriptor.hpp:244
static Image * execute(Image *imgIn, Image *imgOut, float sigma)
execute
Definition: filter_gaussian_2d.hpp:84
PIC_INLINE Eigen::MatrixXd getMatrixdFromLinearArray(float *array, int rows, int cols)
getMatrixFromLinearArray
Definition: eigen_util.hpp:469
PIC_INLINE void getRandomPermutation(std::mt19937 &m, unsigned int *perm, unsigned int nPerm, unsigned int n)
getRandomPermutation computes a random permutation.
Definition: math.hpp:434