Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
HMatrixEstimation.cpp
1 #include "HMatrixEstimation.hh"
2 
3 #include <MathAlgo/SVD.hh>
4 #include <MathAlgo/Lapack.hh>
5 #include <Base/Geometry/KMatrix.hh>
6 #include "Parametrization.hh"
7 #include "MathAlgo/Minpack.hh"
8 
9 using namespace BIAS;
10 using namespace std;
11 
13 HErrorFunction_(void *p, int m, int n, const double *x, double *fvec, int iflag)
14 {
15  HMatrixEstimation *pCurrentObjectH = (HMatrixEstimation *)p;
16  vector<double> params;
17  params.reserve(8);
18  for (unsigned int i = 0; i < 8; i++) params.push_back(x[i]);
19  BIAS::HMatrix H;
20  Parametrization *ParaH = new Parametrization;
21  ParaH->ParamsToHMatrix(H, params);
22 
23  HMatrix Hinv;
24  int res = H.GetInverse(Hinv);
25  if (res != 0) {
26  iflag = -42;
27  return -1;
28  }
29  BIASASSERT(res == 0);
30 
31  const unsigned int uiDataSize = 4*pCurrentObjectH->pointsFrom_.size();
32  const vector<BIAS::HomgPoint2D> &p1 = pCurrentObjectH->pointsFrom_;
33  const vector<BIAS::HomgPoint2D> &p2 = pCurrentObjectH->pointsTo_;
34 
35  double errorsum = 0;
36  for (unsigned int j = 0; j < uiDataSize; j+=4) {
37  HomgPoint2D ep1 = Hinv*p2[j/4];
38  ep1.Homogenize();
39  fvec[j] = ep1[0]-p1[j/4][0];
40  fvec[j+1] = ep1[1]-p1[j/4][1];
41  HomgPoint2D ep2 = H*p1[j/4];
42  ep2.Homogenize();
43  fvec[j+2] = ep2[0]-p2[j/4][0];
44  fvec[j+3] = ep2[1]-p2[j/4][1];
45  errorsum += (ep1-p1[j/4]).NormL2() +(ep2-p2[j/4]).NormL2();
46  }
47  delete ParaH;
48  return 0;
49 }
50 
52 Optimize(BIAS::HMatrix& H, const std::vector<BIAS::HomgPoint2D> &p1,
53  const std::vector<BIAS::HomgPoint2D> &p2)
54 {
55  BIASASSERT(p1.size() == p2.size());
56  if (p1.size() < 5) {
57  BIASERR("At least 5 points correspondences are needed for homography optimization!");
58  return HE_NOT_ENOUGH_POINTS;
59  }
60 
61  // We need to call an external function from Levenberg-Marquardt,
62  // prepare data structures here. Warning: This section is not thread-safe!
63  pointsFrom_ = p1;
64  pointsTo_ = p2;
65 
66  vector<double> vecHParamsStart, vecHParamsResult;
67  Vector<double> HParamsStart(8), HParamsResult(8);
68  Parametrization *ParaH = new Parametrization;
69  ParaH->HMatrixToParams(H, vecHParamsStart);
70  for (unsigned int i = 0; i < 8; i++)
71  HParamsStart[i] = vecHParamsStart[i];
72  long int res = LevenbergMarquardt(&HErrorFunction_, this,p1.size()*4,8,
73  HParamsStart, HParamsResult,
74  MINPACK_DEF_TOLERANCE);
75  if (res != 0) {
76  BIASERR("Error in Levenberg-Marquardt optimization of homography!");
77  return HE_LM_ERROR;
78  }
79 
80  vecHParamsResult.resize(8);
81  for (unsigned int i=0; i<8; i++) vecHParamsResult[i] = HParamsResult[i];
82  ParaH->ParamsToHMatrix(H, vecHParamsResult);
83  delete ParaH;
84  ParaH = NULL;
85 
86  H_ = H;
87 
88  return HE_OK;
89 }
90 
92 Compute(const vector<BIAS::HomgPoint2D>& points1,
93  const vector<BIAS::HomgPoint2D>& points2, HMatrix& H)
94 {
95  int result = 0;
96  BIASASSERT(points1.size() == points2.size());
97  if (points1.size() < 4) {
98  BIASERR("At least 4 points correspondences are needed for homography estimation!");
99  return HE_NOT_ENOUGH_POINTS;
100  }
101  pointsFrom_ = points1;
102  pointsTo_ = points2;
103 
104  const std::vector<BIAS::HomgPoint2D> *pp1 = &points1;
105  const std::vector<BIAS::HomgPoint2D> *pp2 = &points2;
106  std::vector<BIAS::HomgPoint2D> p1norm;
107  std::vector<BIAS::HomgPoint2D> p2norm;
108  KMatrix K1, K2;
109  if (normalizeHartley_) {
110  normalization_.Hartley(points1, p1norm, K1);
111  normalization_.Hartley(points2, p2norm, K2);
112  pp1 = &p1norm;
113  pp2 = &p2norm;
114  }
115 
116  Matrix<double> A(2*points1.size(), 9);
117  Vector<double> S;
118  Matrix<double> U, VT;
119 
120  register unsigned int p=0, p2=0;
121  for (p=0, p2=0; p<2*(*pp1).size(); p+=2, p2++){
122 
123  A[p][0] = A[p][1] = A[p][2] = 0.0;
124  A[p+1][3] = A[p+1][4] = A[p+1][5] = 0.0;
125 
126  A[p+1][0] = (*pp2)[p2][2] * (*pp1)[p2][0];
127  A[p+1][1] = (*pp2)[p2][2] * (*pp1)[p2][1];
128  A[p+1][2] = (*pp2)[p2][2] * (*pp1)[p2][2];
129 
130  A[p][3] = - A[p+1][0];
131  A[p][4] = - A[p+1][1];
132  A[p][5] = - A[p+1][2];
133 
134  A[p][6] = (*pp2)[p2][1] * (*pp1)[p2][0];
135  A[p][7] = (*pp2)[p2][1] * (*pp1)[p2][1];
136  A[p][8] = (*pp2)[p2][1] * (*pp1)[p2][2];
137 
138  A[p+1][6] = - (*pp2)[p2][0] * (*pp1)[p2][0];
139  A[p+1][7] = - (*pp2)[p2][0] * (*pp1)[p2][1];
140  A[p+1][8] = - (*pp2)[p2][0] * (*pp1)[p2][2];
141  }
142 
143  result = General_singular_value_decomposition(A, S, U, VT);
144 
145  if (result == 0){
146  for (p=0; p<9; p++){
147  H.GetData()[p] = VT[8][p];
148  }
149  } else {
150  H.SetZero();
151  }
152 
153  // undo Hartley normalization
154  if (normalizeHartley_) H = K2.Invert() * H * K1;
155 
156  H_ = H;
157 
158  return result;
159 }
160 
162 ComputeAffine(const vector<BIAS::HomgPoint2D>& points1,
163  const vector<BIAS::HomgPoint2D>& points2, HMatrix& H)
164 {
165  // Use direct linear transformation on point pairs (p, p')
166  // solving p x (H * p') = 0 which leads to A * h = 0
167  // where A is a matrix computed from p and p' and h is the column-wise
168  // vector representation for matrix H
169 
170  BIASASSERT(points1.size() == points2.size());
171 
172  pointsFrom_ = points1;
173  pointsTo_ = points2;
174 
175  // perform Hartley normalization
176  const std::vector<BIAS::HomgPoint2D> *pp1 = &points1;
177  const std::vector<BIAS::HomgPoint2D> *pp2 = &points2;
178  std::vector<BIAS::HomgPoint2D> p1norm;
179  std::vector<BIAS::HomgPoint2D> p2norm;
180  KMatrix K1, K2;
181  if (normalizeHartley_) {
182  normalization_.Hartley(points1, p1norm, K1);
183  normalization_.Hartley(points2, p2norm, K2);
184  pp1 = &p1norm;
185  pp2 = &p2norm;
186  }
187 
188  // initialize model matrix with zero
189  Matrix<double> A(2*points1.size(), 7, MatrixZero);
190  H.SetIdentity();
191 
192  // fill rest of model matrix, same as for general H case, but leaving
193  // out last row of H (only 7 parameters of which one is redundant)
194  const unsigned int nummeas = 2*(*pp1).size();
195  for (register unsigned int p=0, p2=0; p<nummeas; p+=2, p2++){
196  A[p][3] = -(A[p+1][0] = (*pp2)[p2][2] * (*pp1)[p2][0]);
197  A[p][4] = -(A[p+1][1] = (*pp2)[p2][2] * (*pp1)[p2][1]);
198  A[p][5] = -(A[p+1][2] = (*pp2)[p2][2] * (*pp1)[p2][2]);
199 
200  A[p][6] = (*pp2)[p2][1] * (*pp1)[p2][2];
201  A[p+1][6] = - (*pp2)[p2][0] * (*pp1)[p2][2];
202  }
203 
204  // solve using SVD (at least 1d solution space)
205  Vector<double> S;
206  Matrix<double> U, VT;
207  if (General_singular_value_decomposition(A, S, U, VT) == 0){
208  // normalize to H[2][2] = 1
209  const HMATRIX_TYPE normalization = 1.0 / VT[6][6];
210  HMATRIX_TYPE* pHData = H.GetData();
211  for (int p = 0; p < 6; p++){
212  *pHData++ = VT[6][p] * normalization;
213  }
214  } else {
215  // SVD failed, no solution
216  return HE_SVD_ERROR;
217  }
218 
219  // undo Hartley normalization
220  if (normalizeHartley_) H = K2.Invert() * H * K1;
221 
222  H_ = H;
223 
224  return HE_OK;
225 }
226 
228 Compute(const std::vector<Vector2<double> >& fromPoint,
229  const std::vector<Vector2<double> >& toPoints, HMatrix &H)
230 {
231  BIASASSERT(fromPoint.size() == toPoints.size());
232  vector<BIAS::HomgPoint2D> fromPointHomg(fromPoint.size());
233  vector<BIAS::HomgPoint2D> toPointsHomg(toPoints.size());
234  for (unsigned int i = 0; i < fromPoint.size(); i++) {
235  fromPointHomg[i][0]= fromPoint[i][0];
236  fromPointHomg[i][1]= fromPoint[i][1];
237  fromPointHomg[i][2]= 1.0;
238  toPointsHomg[i][0]= toPoints[i][0];
239  toPointsHomg[i][1]= toPoints[i][1];
240  toPointsHomg[i][2]= 1.0;
241  }
242  return Compute(fromPointHomg, toPointsHomg, H);
243 }
244 
246 {
247  double err = 0;
248  BIAS::HomgPoint2D mapped;
249  for (unsigned int c = 0; c < pointsFrom_.size(); c++) {
250  mapped = H_ * pointsFrom_[c];
251  mapped.Homogenize();
252  mapped -= pointsTo_[c];
253  err += mapped[0]*mapped[0] + mapped[1]*mapped[1];
254  }
255  err /= (double)pointsFrom_.size();
256  return err;
257 }
int ComputeAffine(const std::vector< BIAS::HomgPoint2D > &fromPoints, const std::vector< BIAS::HomgPoint2D > &toPoints, HMatrix &H)
Compute affine transformation between 2d points, i.e.
class HomgPoint2D describes a point with 2 degrees of freedom in projective coordinates.
Definition: HomgPoint2D.hh:67
a 3x3 Matrix describing projective transformations between planes
Definition: HMatrix.hh:39
double GetError() const
Returns the average squared error of the estimation applying the estimated homography matrix to all c...
This class is used for parametrizing F- and H-matrices, generally for optimization purposes...
HomgPoint2D & Homogenize()
homogenize class data member elements to W==1 by divison by W
Definition: HomgPoint2D.hh:215
std::vector< BIAS::HomgPoint2D > pointsFrom_
Stores the points in image 1 (used in Optimize and GetError)
int Optimize(BIAS::HMatrix &H, const std::vector< BIAS::HomgPoint2D > &p1, const std::vector< BIAS::HomgPoint2D > &p2)
Optimize a given homography matrix from 2d point correspondences.
static int HErrorFunction_(void *p, int m, int n, const double *x, double *fvec, int iflag)
Objective function for Levenberg-Marquardt optimization.
int GetInverse(Matrix3x3< T > &inv) const
Matrix inversion: inverts this and stores resulty in argument inv.
Definition: Matrix3x3.cpp:373
bool HMatrixToParams(const BIAS::HMatrix &Mat, std::vector< double > &Params)
parametizes an H-matrix to eight parameters
int Compute(const std::vector< BIAS::HomgPoint2D > &fromPoints, const std::vector< BIAS::HomgPoint2D > &toPoints, HMatrix &H)
Calculate homography between 2d points using the DLT.
long int LevenbergMarquardt(minpack_funcder_mn fun, void *p, long int m, long int n, Vector< double > &InitialGuess, Vector< double > &Result, double Tolerance)
Solves an overdetermined system f(x) = 0 of m non-linear equations with n parameters using the Levenb...
Definition: Minpack.cpp:97
std::vector< BIAS::HomgPoint2D > pointsTo_
Stores the points in image 2 (used in Optimize and GetError)
int General_singular_value_decomposition(char jobu, char jobvt, const BIAS::Matrix< double > &A, BIAS::Vector< double > &ret_S, BIAS::Matrix< double > &ret_U, BIAS::Matrix< double > &ret_VT)
solve general eigenproblem.
Definition: Lapack.cpp:688
Estimate 3x3 matrix relating image coordinates with each other, i.e.
K describes the mapping from world coordinates (wcs) to pixel coordinates (pcs).
Definition: KMatrix.hh:48
KMatrix Invert() const
returns analyticaly inverted matrix
Definition: KMatrix.cpp:31
const unsigned int size() const
Definition: Vector3.hh:185
void SetIdentity()
set the elements of this matrix to the identity matrix (possibly overriding the inherited method) ...
Definition: Matrix3x3.hh:429
bool ParamsToHMatrix(BIAS::HMatrix &Mat, const std::vector< double > &Params)
composes an H-matrix from the seven given parameters