Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
PMatrixLinear.cpp
1 /*
2 This file is part of the BIAS library (Basic ImageAlgorithmS).
3 
4 Copyright (C) 2003-2009 (see file CONTACT for details)
5  Multimediale Systeme der Informationsverarbeitung
6  Institut fuer Informatik
7  Christian-Albrechts-Universitaet Kiel
8 
9 
10 BIAS is free software; you can redistribute it and/or modify
11 it under the terms of the GNU Lesser General Public License as published by
12 the Free Software Foundation; either version 2.1 of the License, or
13 (at your option) any later version.
14 
15 BIAS is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Lesser General Public License for more details.
19 
20 You should have received a copy of the GNU Lesser General Public License
21 along with BIAS; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24 
25 
26 #include "./PMatrixLinear.hh"
27 #include "./RMatrix.hh"
28 
29 using namespace BIAS;
30 using namespace std;
31 
32 
34 {
35  NewDebugLevel("PMATRIXLINEAR");
36  //AddDebugLevel("PMATRIXLINEAR");
37 }
38 
39 
40 bool PMatrixLinear::Compute(const std::vector<HomgPoint3D> &points3D,
41  const std::vector<HomgPoint2D> &points2D,
42  PMatrix& P)
43 {
44  std::vector<HomgPoint3D *> ptrPoints3D;
45  std::vector<HomgPoint2D *> ptrPoints2D;
46  ptrPoints3D.resize(points3D.size());
47  ptrPoints2D.resize(points2D.size());
48 
49  for (unsigned int i=0;i<points3D.size();i++)
50  ptrPoints3D[i]= const_cast<HomgPoint3D *>(&(points3D[i]));
51  for (unsigned int i=0;i<points2D.size();i++)
52  ptrPoints2D[i]= const_cast<HomgPoint2D *>(&(points2D[i]));
53 
54  return Compute(ptrPoints3D,ptrPoints2D,P);
55 }
56 
57 bool PMatrixLinear::Compute(const std::vector<HomgPoint3D*> &points3D,
58  const std::vector<HomgPoint2D*> &points2D,
59  PMatrix& P){
60 #ifdef BIAS_DEBUG
61  if (points2D.size()<points3D.size()) {
62  BIASERR("Correspondence lists must have same size.");
63  BIASABORT;
64  }
65  if (points3D.size() < 6)
66  BIASERR("For P-Matrix computation at least 6 points are needed. "<<
67  "Points3D has only "<< points3D.size() << " points.");
68 #endif
69 
70 
71  // set up "over-determined" equation system
73 
74  // check whether we can set up an equation system A from the 2d-3d corrs
75  if (!GetPEstSystemHom(A, points3D, points2D)) {
76  BIASERR("no eq. system!");
77  return false;
78  }
79  BCDOUT(PMATRIXLINEAR,"A = " << A);
80 
81  // make A well-conditioned
82  A.NormalizeRows();
83  BCDOUT(PMATRIXLINEAR,"A normalized rows = " << A);
84 
85  // get an SVD of A, if second smallest singvalue is too small we have no
86  // unique solution, therefore set a relative threshold !
87  SVD svd(A, 1e-6);
88 
89  // since P has 11 degrees of freedom, we need 5.5 independent 2d/3d
90  // correspondences in order to get a 1-dimensional nullspace, i.e. a
91  // P != 0 as a solution of AP=0
92 
93  // For six or more correspondences we need a least squares solution
94  // with the additional constraint |P|=1
95  // That is, we pick the last column of V, which refers to the smallest
96  // singular value of S:
97  BIAS::Matrix<double> VT = svd.GetVT();
98  P.SetFromVector( VT.GetRow(11) );
99 
100 #ifdef BIAS_DEBUG
101  Vector<double> S;
102  S = svd.GetS();
103  BCDOUT(PMATRIXLINEAR,"Singular values in P-Estimation are "<<S
104  <<" Rel nullspace dim is "<<svd.RelNullspaceDim()
105  <<" zero thresh is "<<svd.GetZeroThreshold());
106 #endif
107  if (svd.RelNullspaceDim()<=1) {
108  return true;
109  } else {
110  // BIASERR("Detected degenerate case in linear P estimation");
111  return false;
112  }
113 }
114 
115 
117  const std::vector<HomgPoint3D*>& points3D,
118  const std::vector<HomgPoint2D*>& points2D) {
119 
120  if (points3D.size() != points2D.size()){
121 
122  BIASERR("pointlist2D has different length (=" << points2D.size()
123  << ") than pointlist3D (=" << points3D.size() <<")");
124  return false;
125  }
126 
127  A.newsize(points3D.size()* 2, 12);
128 
129  for (unsigned i = 0; i < points3D.size(); i++) {
130 
131  HomgPoint2D const& u = *points2D[i];
132  HomgPoint3D const& X = *points3D[i];
133 
134  unsigned row_index = i * 2;
135  A[row_index][0] = X[0] * u[2];
136  A[row_index][1] = X[1] * u[2];
137  A[row_index][2] = X[2] * u[2];
138  A[row_index][3] = X[3] * u[2];
139  A[row_index][4] = 0;
140  A[row_index][5] = 0;
141  A[row_index][6] = 0;
142  A[row_index][7] = 0;
143  A[row_index][8] = -X[0] * u[0];
144  A[row_index][9] = -X[1] * u[0];
145  A[row_index][10] = -X[2] * u[0];
146  A[row_index][11] = -X[3] * u[0];
147 
148  row_index = i * 2 + 1;
149  A[row_index][0] = 0;
150  A[row_index][1] = 0;
151  A[row_index][2] = 0;
152  A[row_index][3] = 0;
153  A[row_index][4] = X[0] * u[2];
154  A[row_index][5] = X[1] * u[2];
155  A[row_index][6] = X[2] * u[2];
156  A[row_index][7] = X[3] * u[2];
157  A[row_index][8] = -X[0] * u[1];
158  A[row_index][9] = -X[1] * u[1];
159  A[row_index][10] = -X[2] * u[1];
160  A[row_index][11] = -X[3] * u[1];
161  }
162 
163  return true;
164 
165 }
166 
167 
169  const std::vector<HomgPoint3D*>& points3D,
170  const std::vector<HomgPoint2D*>& points2D) {
171 
172  if (points3D.size() != points2D.size()){
173 
174  BIASERR("pointlist2D has different length (=" << points2D.size()
175  << ") than pointlist3D (=" << points3D.size() <<")");
176  return false;
177  }
178 
179  A.newsize(points3D.size()*2, 11);
180  b.newsize(points3D.size()*2);
181 
182  for (unsigned i = 0; i < points3D.size(); i++) {
183 
184  HomgPoint2D const& u = *points2D[i];
185  HomgPoint3D const& X = *points3D[i];
186 
187  unsigned row_index = i * 2;
188  A[row_index][0] = X[0] * u[2];
189  A[row_index][1] = X[1] * u[2];
190  A[row_index][2] = X[2] * u[2];
191  A[row_index][3] = X[3] * u[2];
192  A[row_index][4] = 0;
193  A[row_index][5] = 0;
194  A[row_index][6] = 0;
195  A[row_index][7] = 0;
196  A[row_index][8] = -X[0] * u[0];
197  A[row_index][9] = -X[1] * u[0];
198  A[row_index][10] = -X[2] * u[0];
199  b[row_index] = X[3] * u[0];
200 
201  row_index = i * 2 + 1;
202  A[row_index][0] = 0;
203  A[row_index][1] = 0;
204  A[row_index][2] = 0;
205  A[row_index][3] = 0;
206  A[row_index][4] = X[0] * u[2];
207  A[row_index][5] = X[1] * u[2];
208  A[row_index][6] = X[2] * u[2];
209  A[row_index][7] = X[3] * u[2];
210  A[row_index][8] = -X[0] * u[1];
211  A[row_index][9] = -X[1] * u[1];
212  A[row_index][10] = -X[2] * u[1];
213  b[row_index] = X[3] * u[1];
214  }
215 
216  return true;
217 
218 }
219 
220 bool PMatrixLinear::ComputeCalibrated(const vector<HomgPoint3D*> &points3D,
221  const vector<HomgPoint2D*> &points2D,
222  PMatrix& Pose) {
223 
224  BIASASSERT(points2D.size()>5);
225  BIASASSERT(points3D.size()==points2D.size());
226 #ifdef BIAS_DEBUG
227  for (unsigned int i=0;i<points3D.size(); i++) {
228  if (!points3D[i]->IsHomogenized()) {
229  BIASERR("Unhomogenized point !"<< *points3D[i]);
230  BIASABORT;
231  }
232  if (!points2D[i]->IsHomogenized()) {
233  BIASERR("Unhomogenized point !"<< *points2D[i]);
234  BIASABORT;
235  }
236  }
237 #endif
238 
239  // set up model model matrix, 9 colums to estimate the 9 entries of R
240  // for each pair of the n points contruct a line, and get one
241  // linear constraint on R. with 5 points, we have 10 constraints
242  Matrix<double> ModelMatrix(points2D.size()*(points2D.size()-1)/2,9);
243 
244  unsigned int countrow=0;
245  Vector3<double> B;
247  for (unsigned int i=0; i<points2D.size(); i++) {
248  Matrix3x3<double> CrossProd(points2D[i]->GetSkewSymmetricMatrix());
249  for (unsigned int k=i+1; k<points2D.size(); k++) {
250  // pick two points (k and i), construct 3D line B:
251  B.Set((*points3D[k])[0]- (*points3D[i])[0],
252  (*points3D[k])[1]- (*points3D[i])[1],
253  (*points3D[k])[2]- (*points3D[i])[2]);
254  // compute outer product matrix C of 3D line and image line
255  B.OuterProduct(CrossProd * *points2D[k], C);
256 
257  // the image line through the 2D points, viewed in 3D space and rotated
258  // by the desired rotation matrix must be coplanar to the plane
259  // defined by the two points and the camera center
260 
261  // copy from cross product matrix into model matrix:
262  register double *pMM = ModelMatrix.GetDataArray()[countrow++];
263  register double *pC = C.GetData();
264  for (register unsigned int d=0; d<9; d++) *pMM++ = *pC++;
265  }
266  }
267  SVD svd(ModelMatrix);
268 
269  // we estimated a projective entity lambda*R, however, the nullspace
270  // must not have dimension>1, because this means an ambiguity
271  if (svd.RelNullspaceDim()>1) {
272  Vector<double> S=svd.GetS();
273  BCDOUT(PMATRIXLINEAR,
274  "Detected degenerate case in linear Rotation estimation."
275  <<"Dim of Nullspace is "<<svd.RelNullspaceDim()
276  <<" with sing values: "<<S);
277  return false;
278  }
279 
280  // ignore null threshold smallest on singular value, get nullvector:
281  Vector<double> RVector(svd.GetVT().GetRow(8));
282 
283  // compose rmatrix from rvector
284  // we have to scale R, so that it comes close to a rotation
285  RMatrix R;
286 
287  // first row should have length 1:
288  //const double scale = 1.0 /
289  // sqrt(RVector[0]*RVector[0]+RVector[1]*RVector[1]+RVector[2]*RVector[2]);
290  //ROTATION_MATRIX_TYPE* pR = R.GetData();
291  //for (unsigned int j=0; j<9; j++) *pR++ = RVector[j]*scale;
292 
293  // ROTATION_MATRIX_TYPE* pR = R.GetData();
294  //for (unsigned int j=0; j<9; j++) *pR++ = RVector[j];
295  R.SetFromVector(RVector);
296 
298  R = svdR.GetU() * svdR.GetVT();
299 
300  // enforce right-handed rotation
301  if (R.GetDeterminant()<0.0) R *= -1.0;
302 
303  // BIASWARN("using ground truth rotation");
304  // R = Pose.GetR();
305 
306  // here we have something close to a rotation, trust this and compute C
307  // set up DLT equation system where only C is estimated
308  // start with R*x ~= [ I |-C]X, rearrange and solve M * CVector = 0
309  Matrix<double> M(points3D.size()*3,4);
310  register unsigned int CurRowInM = 0;
311  for (unsigned int i=0; i<points3D.size(); i++) {
312  Vector3<double> Rx = R * *(points2D[i]);
313  Matrix<double> Rx_cross = Rx.GetSkewSymmetricMatrix();
314  for (unsigned int r=0; r<3; r++) {
315  M[CurRowInM][0] = Rx_cross[r][0];
316  M[CurRowInM][1] = Rx_cross[r][1];
317  M[CurRowInM][2] = Rx_cross[r][2];
318  Vector3<double> X((*points3D[i])[0],(*points3D[i])[1],(*points3D[i])[2]);
319  X = Rx_cross * X;
320  M[CurRowInM++][3] = -X[r];
321  }
322  }
323  M.NormalizeRows();
324  SVD svdC(M);
325 
326  // check for ambiguity (multi-dim. nullspace), which we cant decide
327  if (svdC.RelNullspaceDim()>1) {
328  Vector<double> c = svdC.GetS();
329  BCDOUT(PMATRIXLINEAR,
330  "Detected degenerate case in linear center estimation."
331  <<"Dim of Nullspace is "<<svdC.RelNullspaceDim()
332  <<" with sing values: "<<c);
333  return false;
334  }
335  // pick null vector of smallest sing value (ignore threshold)
336  HomgPoint3D CVector(svdC.GetVT().GetRow(3));
337 
338  // compute center by homogenizing
339  Vector3<double> Center(CVector[0]/CVector[3], CVector[1]/CVector[3],
340  CVector[2]/CVector[3]);
341 
342  KMatrix K;
343  K.SetIdentity();
344  Pose.Compose(K, R, Center);
345  return true;
346 }
void Set(const T *pv)
copy the array of vectorsize beginning at *T to this-&gt;data_
Definition: Vector3.hh:532
class HomgPoint2D describes a point with 2 degrees of freedom in projective coordinates.
Definition: HomgPoint2D.hh:67
computes and holds the singular value decomposition of a rectangular (not necessarily quadratic) Matr...
Definition: SVD.hh:92
bool Compute(const std::vector< HomgPoint3D * > &points3D, const std::vector< HomgPoint2D * > &points2D, PMatrix &P)
computes a least squares solution P via SVD, if at least 6 2d-3d correspondences (pointers) are provi...
Matrix< T > & newsize(Subscript M, Subscript N)
Definition: cmat.h:269
void OuterProduct(const Vector3< T > &v, Matrix3x3< T > &res) const
outer product, constructs a matrix.
Definition: Vector3.cpp:151
Matrix< T > GetSkewSymmetricMatrix() const
constructs a skew symmetric 3x3 matrix from (*this), which can be used instead of the cross product ...
Definition: Vector3.cpp:257
double GetZeroThreshold() const
return zerothresh currently used
Definition: SVD.hh:160
const int RelNullspaceDim() const
compare singular values against greatest, not absolute
Definition: SVD.cpp:433
3D rotation matrix
Definition: RMatrix.hh:49
Vector< T > & newsize(Subscript N)
Definition: vec.h:220
Represents 3d pose transformations, parametrized as Euclidean translation and unit quaternion orienta...
Definition: Pose.hh:73
bool GetPEstSystemInHom(Matrix< double > &A, Vector< double > &b, const std::vector< HomgPoint3D * > &points3D, const std::vector< HomgPoint2D * > &points2D)
sets up an inhomogeneous equation system A for pmatrix computation using the 2d/3d correspondences pr...
const Matrix< double > & GetVT() const
return VT (=transposed(V))
Definition: SVD.hh:177
bool ComputeCalibrated(const std::vector< HomgPoint3D * > &points3D, const std::vector< HomgPoint2D * > &points2D, PMatrix &Pose)
given n&gt;=6 2D/3D correspondences, compute approximate R and C using linear methods (thats why we need...
Vector< T > GetRow(const int &row) const
return a copy of row &quot;row&quot; of this matrix, zero based counting
Definition: Matrix.cpp:233
const Vector< double > & GetS() const
return S which is a vector of the singular values of A in descending order.
Definition: SVD.hh:167
void SetFromVector(const Vector< PMATRIX_TYPE > &vals)
Sets P-Matrix from a vector which contains the elements of P row wise.
class HomgPoint3D describes a point with 3 degrees of freedom in projective coordinates.
Definition: HomgPoint3D.hh:61
const T ** GetDataArray() const
returns zero based arry for data access
Definition: Matrix.hh:192
const Matrix< double > & GetU() const
return U U is a m x m orthogonal matrix
Definition: SVD.hh:187
K describes the mapping from world coordinates (wcs) to pixel coordinates (pcs).
Definition: KMatrix.hh:48
describes a projective 3D -&gt; 2D mapping in homogenous coordinates
Definition: PMatrix.hh:88
void NormalizeRows()
Normalizes each row to L2 norm one.
Definition: Matrix.cpp:178
bool GetPEstSystemHom(Matrix< double > &A, const std::vector< HomgPoint3D * > &points3D, const std::vector< HomgPoint2D * > &points2D)
sets up the homogeneous equation system A for pmatrix computation using the 2d/3d correspondences pro...
void Compose(const Matrix3x3< double > &K, const Matrix3x3< double > &R, const Vector3< double > &C)
composes this from K, R and C using P = [ K R&#39; | -K R&#39; C ] with R&#39; = transpose(R) ...
Definition: PMatrix.cpp:581
void SetIdentity()
set the elements of this matrix to the identity matrix (possibly overriding the inherited method) ...
Definition: Matrix3x3.hh:429