1 /*M///////////////////////////////////////////////////////////////////////////////////////
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
5 // By downloading, copying, installing or using the software you agree to this license.
6 // If you do not agree to this license, do not download, install,
7 // copy or use the software.
11 // For Open Source Computer Vision Library
13 // Copyright (C) 2009, PhaseSpace Inc., all rights reserved.
14 // Third party copyrights are property of their respective owners.
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
22 // * Redistribution's in binary form must reproduce the above copyright notice,
23 // this list of conditions and the following disclaimer in the documentation
24 // and/or other materials provided with the distribution.
26 // * The names of the copyright holders may not be used to endorse or promote products
27 // derived from this software without specific prior written permission.
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
42 #include "precomp.hpp"
43 #include "opencv2/calib3d.hpp"
44 #include "opencv2/contrib/compat.hpp"
45 #include "opencv2/calib3d/calib3d_c.h"
50 LevMarqSparse::LevMarqSparse() {
51 Vis_index = X = prevP = P = deltaP = err = JtJ_diag = S = hX = NULL;
52 U = ea = V = inv_V_star = eb = Yj = NULL;
53 num_cams = 0, num_points = 0, num_err_param = 0;
54 num_cam_param = 0, num_point_param = 0;
58 LevMarqSparse::~LevMarqSparse() {
62 LevMarqSparse::LevMarqSparse(int npoints, // number of points
63 int ncameras, // number of cameras
64 int nPointParams, // number of params per one point (3 in case of 3D points)
65 int nCameraParams, // number of parameters per one camera
66 int nErrParams, // number of parameters in measurement vector
67 // for 1 point at one camera (2 in case of 2D projections)
68 Mat& visibility, // visibility matrix. rows correspond to points, columns correspond to cameras
69 // 1 - point is visible for the camera, 0 - invisible
70 Mat& P0, // starting vector of parameters, first cameras then points
71 Mat& X_, // measurements, in order of visibility. non visible cases are skipped
72 TermCriteria _criteria, // termination criteria
74 // callback for estimation of Jacobian matrices
75 void (CV_CDECL * _fjac)(int i, int j, Mat& point_params,
76 Mat& cam_params, Mat& A, Mat& B, void* data),
77 // callback for estimation of backprojection errors
78 void (CV_CDECL * _func)(int i, int j, Mat& point_params,
79 Mat& cam_params, Mat& estim, void* data),
80 void* _data, // user-specific data passed to the callbacks
81 BundleAdjustCallback _cb, void* _user_data
83 Vis_index = X = prevP = P = deltaP = err = JtJ_diag = S = hX = NULL;
84 U = ea = V = inv_V_star = eb = Yj = NULL;
88 user_data = _user_data;
90 run(npoints, ncameras, nPointParams, nCameraParams, nErrParams, visibility,
91 P0, X_, _criteria, _fjac, _func, _data);
94 void LevMarqSparse::clear() {
95 for( int i = 0; i < num_points; i++ ) {
96 for(int j = 0; j < num_cams; j++ ) {
97 //CvMat* tmp = ((CvMat**)(A->data.ptr + i * A->step))[j];
98 CvMat* tmp = A[j+i*num_cams];
100 cvReleaseMat( &tmp );
102 //tmp = ((CvMat**)(B->data.ptr + i * B->step))[j];
103 tmp = B[j+i*num_cams];
105 cvReleaseMat( &tmp );
107 //tmp = ((CvMat**)(W->data.ptr + j * W->step))[i];
108 tmp = W[j+i*num_cams];
110 cvReleaseMat( &tmp );
113 delete A; //cvReleaseMat(&A);
114 delete B;//cvReleaseMat(&B);
115 delete W;//cvReleaseMat(&W);
116 cvReleaseMat( &Vis_index);
118 for( int j = 0; j < num_cams; j++ ) {
119 cvReleaseMat( &U[j] );
123 for( int j = 0; j < num_cams; j++ ) {
124 cvReleaseMat( &ea[j] );
128 //allocate V and inv_V_star
129 for( int i = 0; i < num_points; i++ ) {
131 cvReleaseMat(&inv_V_star[i]);
136 for( int i = 0; i < num_points; i++ ) {
137 cvReleaseMat(&eb[i]);
141 for( int i = 0; i < num_points; i++ ) {
142 cvReleaseMat(&Yj[i]);
147 cvReleaseMat(&prevP);
149 cvReleaseMat(&deltaP);
153 cvReleaseMat(&JtJ_diag);
158 //A params correspond to Cameras
159 //B params correspont to Points
161 //num_cameras - total number of cameras
162 //num_points - total number of points
164 //num_par_per_camera - number of parameters per camera
165 //num_par_per_point - number of parameters per point
167 //num_errors - number of measurements.
169 void LevMarqSparse::run( int num_points_, //number of points
170 int num_cams_, //number of cameras
171 int num_point_param_, //number of params per one point (3 in case of 3D points)
172 int num_cam_param_, //number of parameters per one camera
173 int num_err_param_, //number of parameters in measurement vector for 1 point at one camera (2 in case of 2D projections)
174 Mat& visibility, //visibility matrix . rows correspond to points, columns correspond to cameras
175 // 0 - point is visible for the camera, 0 - invisible
176 Mat& P0, //starting vector of parameters, first cameras then points
177 Mat& X_init, //measurements, in order of visibility. non visible cases are skipped
178 TermCriteria criteria_init,
179 void (*fjac_)(int i, int j, Mat& point_params, Mat& cam_params, Mat& A, Mat& B, void* data),
180 void (*func_)(int i, int j, Mat& point_params, Mat& cam_params, Mat& estim, void* data),
182 ) { //termination criteria
185 func = func_; //assign evaluation function
186 fjac = fjac_; //assign jacobian
189 num_cams = num_cams_;
190 num_points = num_points_;
191 num_err_param = num_err_param_;
192 num_cam_param = num_cam_param_;
193 num_point_param = num_point_param_;
196 int Aij_width = num_cam_param;
197 int Aij_height = num_err_param;
199 int Bij_width = num_point_param;
200 int Bij_height = num_err_param;
202 int U_size = Aij_width;
203 int V_size = Bij_width;
205 int Wij_height = Aij_width;
206 int Wij_width = Bij_width;
208 //allocate memory for all Aij, Bij, U, V, W
210 //allocate num_points*num_cams matrices A
212 //Allocate matrix A whose elements are nointers to Aij
213 //if Aij is zero (point i is not visible in camera j) then A(i,j) contains NULL
214 //A = cvCreateMat( num_points, num_cams, CV_32S /*pointer is stored here*/ );
215 //B = cvCreateMat( num_points, num_cams, CV_32S /*pointer is stored here*/ );
216 //W = cvCreateMat( num_cams, num_points, CV_32S /*pointer is stored here*/ );
218 A = new CvMat* [num_points * num_cams];
219 B = new CvMat* [num_points * num_cams];
220 W = new CvMat* [num_cams * num_points];
221 Vis_index = cvCreateMat( num_points, num_cams, CV_32S /*integer index is stored here*/ );
225 cvSet( Vis_index, cvScalar(-1) );
227 //fill matrices A and B based on visibility
228 CvMat _vis = visibility;
230 for (int i = 0; i < num_points; i++ ) {
231 for (int j = 0; j < num_cams; j++ ) {
232 if (((int*)(_vis.data.ptr+ i * _vis.step))[j] ) {
233 ((int*)(Vis_index->data.ptr + i * Vis_index->step))[j] = index;
234 index += num_err_param;
236 //create matrices Aij, Bij
237 CvMat* tmp = cvCreateMat(Aij_height, Aij_width, CV_64F );
238 //((CvMat**)(A->data.ptr + i * A->step))[j] = tmp;
239 cvSet(tmp,cvScalar(1.0,1.0,1.0,1.0));
240 A[j+i*num_cams] = tmp;
242 tmp = cvCreateMat( Bij_height, Bij_width, CV_64F );
243 //((CvMat**)(B->data.ptr + i * B->step))[j] = tmp;
244 cvSet(tmp,cvScalar(1.0,1.0,1.0,1.0));
245 B[j+i*num_cams] = tmp;
247 tmp = cvCreateMat( Wij_height, Wij_width, CV_64F );
248 //((CvMat**)(W->data.ptr + j * W->step))[i] = tmp; //note indices i and j swapped
249 cvSet(tmp,cvScalar(1.0,1.0,1.0,1.0));
250 W[j+i*num_cams] = tmp;
252 A[j+i*num_cams] = NULL;
253 B[j+i*num_cams] = NULL;
254 W[j+i*num_cams] = NULL;
260 U = new CvMat* [num_cams];
261 for (int j = 0; j < num_cams; j++ ) {
262 U[j] = cvCreateMat( U_size, U_size, CV_64F );
267 ea = new CvMat* [num_cams];
268 for (int j = 0; j < num_cams; j++ ) {
269 ea[j] = cvCreateMat( U_size, 1, CV_64F );
273 //allocate V and inv_V_star
274 V = new CvMat* [num_points];
275 inv_V_star = new CvMat* [num_points];
276 for (int i = 0; i < num_points; i++ ) {
277 V[i] = cvCreateMat( V_size, V_size, CV_64F );
278 inv_V_star[i] = cvCreateMat( V_size, V_size, CV_64F );
280 cvSetZero(inv_V_star[i]);
284 eb = new CvMat* [num_points];
285 for (int i = 0; i < num_points; i++ ) {
286 eb[i] = cvCreateMat( V_size, 1, CV_64F );
291 Yj = new CvMat* [num_points];
292 for (int i = 0; i < num_points; i++ ) {
293 Yj[i] = cvCreateMat( Wij_height, Wij_width, CV_64F ); //Yij has the same size as Wij
298 S = cvCreateMat( num_cams * num_cam_param, num_cams * num_cam_param, CV_64F);
300 JtJ_diag = cvCreateMat( num_cams * num_cam_param + num_points * num_point_param, 1, CV_64F );
303 //set starting parameters
304 CvMat _tmp_ = CvMat(P0);
305 prevP = cvCloneMat( &_tmp_ );
306 P = cvCloneMat( &_tmp_ );
307 deltaP = cvCloneMat( &_tmp_ );
310 _tmp_ = CvMat(X_init);
311 X = cvCloneMat( &_tmp_ );
312 //create vector for estimated measurements
313 hX = cvCreateMat( X->rows, X->cols, CV_64F );
315 //create error vector
316 err = cvCreateMat( X->rows, X->cols, CV_64F );
319 //compute initial error
323 assert(X->rows == hX->rows);
324 std::cerr<<"X size = "<<X->rows<<" "<<X->cols<<std::endl;
325 std::cerr<<"hX size = "<<hX->rows<<" "<<hX->cols<<std::endl;
326 for (int j=0;j<X->rows;j+=2) {
327 double Xj1 = *(double*)(X->data.ptr + j * X->step);
328 double hXj1 = *(double*)(hX->data.ptr + j * hX->step);
329 double err1 = *(double*)(err->data.ptr + j * err->step);
330 double Xj2 = *(double*)(X->data.ptr + (j+1) * X->step);
331 double hXj2 = *(double*)(hX->data.ptr + (j+1) * hX->step);
332 double err2 = *(double*)(err->data.ptr + (j+1) * err->step);
333 std::cerr<<"("<<Xj1<<","<<Xj2<<") -> ("<<hXj1<<","<<hXj2<<"). err = ("<<err1<<","<<err2<<")"<<std::endl;
337 prevErrNorm = cvNorm( err, 0, CV_L2 );
338 // std::cerr<<"prevErrNorm = "<<prevErrNorm<<std::endl;
340 criteria = criteria_init;
344 ask_for_proj(_vis,true);
346 errNorm = cvNorm( err, 0, CV_L2 );
349 void LevMarqSparse::ask_for_proj(CvMat &/*_vis*/,bool once) {
351 //given parameter P, compute measurement hX
353 for (int i = 0; i < num_points; i++ ) {
355 cvGetSubRect( P, &point_mat, cvRect( 0, num_cams * num_cam_param + num_point_param * i, 1, num_point_param ));
356 for (int j = 0; j < num_cams; j++ ) {
357 //CvMat* Aij = ((CvMat**)(A->data.ptr + A->step * i))[j];
358 CvMat* Aij = A[j+i*num_cams];
359 if (Aij ) { //visible
361 cvGetSubRect( P, &cam_mat, cvRect( 0, j * num_cam_param, 1, num_cam_param ));
363 cvGetSubRect( hX, &measur_mat, cvRect( 0, ind * num_err_param, 1, num_err_param ));
364 Mat _point_mat = cv::cvarrToMat(&point_mat), _cam_mat = cv::cvarrToMat(&cam_mat), _measur_mat = cv::cvarrToMat(&measur_mat);
365 func( i, j, _point_mat, _cam_mat, _measur_mat, data);
366 assert( ind*num_err_param == ((int*)(Vis_index->data.ptr + i * Vis_index->step))[j]);
373 //iteratively asks for Jacobians for every camera_point pair
374 void LevMarqSparse::ask_for_projac(CvMat &/*_vis*/) //should be evaluated at point prevP
376 // compute jacobians Aij and Bij
377 for (int i = 0; i < num_points; i++ )
380 cvGetSubRect( prevP, &point_mat, cvRect( 0, num_cams * num_cam_param + num_point_param * i, 1, num_point_param ));
382 //CvMat** A_line = (CvMat**)(A->data.ptr + A->step * i);
383 //CvMat** B_line = (CvMat**)(B->data.ptr + B->step * i);
384 for( int j = 0; j < num_cams; j++ )
386 //CvMat* Aij = A_line[j];
387 //if( Aij ) //Aij is not zero
388 CvMat* Aij = A[j+i*num_cams];
389 CvMat* Bij = B[j+i*num_cams];
392 //CvMat** A_line = (CvMat**)(A->data.ptr + A->step * i);
393 //CvMat** B_line = (CvMat**)(B->data.ptr + B->step * i);
395 //CvMat* Aij = A_line[j];
396 //CvMat* Aij = ((CvMat**)(A->data.ptr + A->step * i))[j];
399 cvGetSubRect( prevP, &cam_mat, cvRect( 0, j * num_cam_param, 1, num_cam_param ));
401 //CvMat* Bij = B_line[j];
402 //CvMat* Bij = ((CvMat**)(B->data.ptr + B->step * i))[j];
403 Mat _point_mat = cv::cvarrToMat(&point_mat), _cam_mat = cv::cvarrToMat(&cam_mat), _Aij = cv::cvarrToMat(Aij), _Bij = cv::cvarrToMat(Bij);
404 (*fjac)(i, j, _point_mat, _cam_mat, _Aij, _Bij, data);
410 void LevMarqSparse::optimize(CvMat &_vis) { //main function that runs minimization
413 CvMat* YWt = cvCreateMat( num_cam_param, num_cam_param, CV_64F ); //this matrix used to store Yij*Wik'
414 CvMat* E = cvCreateMat( S->height, 1 , CV_64F ); //this is right part of system with S
419 // compute jacobians Aij and Bij
420 ask_for_projac(_vis);
421 int invisible_count=0;
422 //compute U_j and ea_j
423 for (int j = 0; j < num_cams; j++ ) {
426 //summ by i (number of points)
427 for (int i = 0; i < num_points; i++ ) {
429 //CvMat* Aij = ((CvMat**)(A->data.ptr + A->step * i))[j];
430 CvMat* Aij = A[j+i*num_cams];
433 cvGEMM( Aij, Aij, 1, U[j], 1, U[j], CV_GEMM_A_T );
434 //ea_j += AijT * e_ij
437 int index = ((int*)(Vis_index->data.ptr + i * Vis_index->step))[j];
439 cvGetSubRect( err, &eij, cvRect( 0, index, 1, Aij->height ) ); //width of transposed Aij
440 cvGEMM( Aij, &eij, 1, ea[j], 1, ea[j], CV_GEMM_A_T );
445 } //U_j and ea_j computed for all j
449 int nviz = X->rows / num_err_param;
450 double e2 = prevErrNorm*prevErrNorm, e2n = e2 / nviz;
451 std::cerr<<"Iteration: "<<iters<<", normError: "<<e2<<" ("<<e2n<<")"<<std::endl;
454 cb(iters, prevErrNorm, user_data);
455 //compute V_i and eb_i
456 for (int i = 0; i < num_points; i++ ) {
460 //summ by i (number of points)
461 for( int j = 0; j < num_cams; j++ ) {
463 //CvMat* Bij = ((CvMat**)(B->data.ptr + B->step * i))[j];
464 CvMat* Bij = B[j+i*num_cams];
467 cvGEMM( Bij, Bij, 1, V[i], 1, V[i], CV_GEMM_A_T );
469 //eb_i += BijT * e_ij
470 int index = ((int*)(Vis_index->data.ptr + i * Vis_index->step))[j];
473 cvGetSubRect( err, &eij, cvRect( 0, index, 1, Bij->height ) ); //width of transposed Bij
474 cvGEMM( Bij, &eij, 1, eb[i], 1, eb[i], CV_GEMM_A_T );
477 } //V_i and eb_i computed for all i
480 for( int i = 0; i < num_points; i++ ) {
481 for( int j = 0; j < num_cams; j++ ) {
482 //CvMat* Aij = ((CvMat**)(A->data.ptr + A->step * i))[j];
483 CvMat* Aij = A[j+i*num_cams];
484 if( Aij ) { //visible
485 //CvMat* Bij = ((CvMat**)(B->data.ptr + B->step * i))[j];
486 CvMat* Bij = B[j+i*num_cams];
487 //CvMat* Wij = ((CvMat**)(W->data.ptr + W->step * j))[i];
488 CvMat* Wij = W[j+i*num_cams];
491 cvGEMM( Aij, Bij, 1, NULL, 0, Wij, CV_GEMM_A_T );
496 //backup diagonal of JtJ before we start augmenting it
500 for( int j = 0; j < num_cams; j++ ) {
501 cvGetDiag(U[j], &dia);
502 cvGetSubRect(JtJ_diag, &subr,
503 cvRect(0, j*num_cam_param, 1, num_cam_param ));
504 cvCopy( &dia, &subr );
506 for( int i = 0; i < num_points; i++ ) {
507 cvGetDiag(V[i], &dia);
508 cvGetSubRect(JtJ_diag, &subr,
509 cvRect(0, num_cams*num_cam_param + i * num_point_param, 1, num_point_param ));
510 cvCopy( &dia, &subr );
515 //initialize lambda. It is set to 1e-3 * average diagonal element in JtJ
516 double average_diag = 0;
517 for( int j = 0; j < num_cams; j++ ) {
518 average_diag += cvTrace( U[j] ).val[0];
520 for( int i = 0; i < num_points; i++ ) {
521 average_diag += cvTrace( V[i] ).val[0];
523 average_diag /= (num_cams*num_cam_param + num_points * num_point_param );
525 // lambda = 1e-3 * average_diag;
526 lambda = 1e-3 * average_diag;
530 //now we are going to find good step and make it
532 //augmentation of diagonal
533 for(int j = 0; j < num_cams; j++ ) {
535 cvGetDiag( U[j], &diag );
537 cvAddS( &diag, cvScalar( lambda ), &diag );
539 cvScale( &diag, &diag, 1 + lambda );
542 for(int i = 0; i < num_points; i++ ) {
544 cvGetDiag( V[i], &diag );
546 cvAddS( &diag, cvScalar( lambda ), &diag );
548 cvScale( &diag, &diag, 1 + lambda );
553 bool inverted_ok = true;
554 for(int i = 0; i < num_points; i++ ) {
555 double det = cvInvert( V[i], inv_V_star[i] );
557 if( fabs(det) <= FLT_EPSILON ) {
559 std::cerr<<"V["<<i<<"] failed"<<std::endl;
561 } //means we did wrong augmentation, try to choose different lambda
566 //loop through cameras, compute upper diagonal blocks of matrix S
567 for( int j = 0; j < num_cams; j++ ) {
568 //compute Yij = Wij (V*_i)^-1 for all i (if Wij exists/nonzero)
569 for( int i = 0; i < num_points; i++ ) {
571 //CvMat* Wij = ((CvMat**)(W->data.ptr + W->step * j))[i];
572 CvMat* Wij = W[j+i*num_cams];
574 cvMatMul( Wij, inv_V_star[i], Yj[i] );
578 //compute Sjk for k>=j (because Sjk = Skj)
579 for( int k = j; k < num_cams; k++ ) {
581 for( int i = 0; i < num_points; i++ ) {
582 //check that both Wij and Wik exist
583 // CvMat* Wij = ((CvMat**)(W->data.ptr + W->step * j))[i];
584 CvMat* Wij = W[j+i*num_cams];
585 //CvMat* Wik = ((CvMat**)(W->data.ptr + W->step * k))[i];
586 CvMat* Wik = W[k+i*num_cams];
589 //multiply YWt += Yj[i]*Wik'
590 cvGEMM( Yj[i], Wik, 1, YWt, 1, YWt, CV_GEMM_B_T ); ///*transpose Wik
594 //copy result to matrix S
598 cvGetSubRect( S, &Sjk, cvRect( k * num_cam_param, j * num_cam_param, num_cam_param, num_cam_param ));
601 //if j==k, add diagonal
603 //just copy with minus
604 cvScale( YWt, &Sjk, -1 ); //if we set initial S to zero then we can use cvSub( Sjk, YWt, Sjk);
608 //subtract YWt from augmented Uj
609 cvSub( U[j], YWt, &Sjk );
613 //compute right part of equation involving matrix S
614 // e_j=ea_j - \sum_i Y_ij eb_i
619 cvGetSubRect( E, &e_j, cvRect( 0, j * num_cam_param, 1, num_cam_param ) );
621 for( int i = 0; i < num_points; i++ ) {
622 //CvMat* Wij = ((CvMat**)(W->data.ptr + W->step * j))[i];
623 CvMat* Wij = W[j+i*num_cams];
625 cvMatMulAdd( Yj[i], eb[i], &e_j, &e_j );
628 cvSub( ea[j], &e_j, &e_j );
632 //fill below diagonal elements of matrix S
633 cvCompleteSymm( S, 0 ); ///*from upper to low //operation may be done by nonzero blocks or during upper diagonal computation
635 //Solve linear system S * deltaP_a = E
637 cvGetSubRect( deltaP, &dpa, cvRect(0, 0, 1, S->width ) );
638 int res = cvSolve( S, E, &dpa, CV_CHOLESKY );
640 if( res ) { //system solved ok
642 for( int i = 0; i < num_points; i++ ) {
644 cvGetSubRect( deltaP, &dbi, cvRect( 0, dpa.height + i * num_point_param, 1, num_point_param ) );
646 // compute \sum_j W_ij^T da_j
647 for( int j = 0; j < num_cams; j++ ) {
649 //CvMat* Wij = ((CvMat**)(W->data.ptr + W->step * j))[i];
650 CvMat* Wij = W[j+i*num_cams];
654 cvGetSubRect( &dpa, &daj, cvRect( 0, j * num_cam_param, 1, num_cam_param ));
655 cvGEMM( Wij, &daj, 1, &dbi, 1, &dbi, CV_GEMM_A_T ); ///* transpose Wij
659 cvSub( eb[i], &dbi, &dbi );
660 cvMatMul(inv_V_star[i], &dbi, &dbi ); //here we get final dbi
661 } //now we computed whole deltaP
663 //add deltaP to delta
664 cvAdd( prevP, deltaP, P );
666 //evaluate function with new parameters
667 ask_for_proj(_vis); // func( P, hX );
670 errNorm = cvNorm( X, hX, CV_L2 );
679 if( error || ///* singularities somewhere
680 errNorm > prevErrNorm ) { //step was not accepted
681 //increase lambda and reject change
684 int nviz = X->rows / num_err_param;
685 double e2 = errNorm*errNorm, e2_prev = prevErrNorm*prevErrNorm;
686 double e2n = e2/nviz, e2n_prev = e2_prev/nviz;
687 std::cerr<<"move failed: lambda = "<<lambda<<", e2 = "<<e2<<" ("<<e2n<<") > "<<e2_prev<<" ("<<e2n_prev<<")"<<std::endl;
690 //restore diagonal from backup
694 for( int j = 0; j < num_cams; j++ ) {
695 cvGetDiag(U[j], &dia);
696 cvGetSubRect(JtJ_diag, &subr,
697 cvRect(0, j*num_cam_param, 1, num_cam_param ));
698 cvCopy( &subr, &dia );
700 for( int i = 0; i < num_points; i++ ) {
701 cvGetDiag(V[i], &dia);
702 cvGetSubRect(JtJ_diag, &subr,
703 cvRect(0, num_cams*num_cam_param + i * num_point_param, 1, num_point_param ));
704 cvCopy( &subr, &dia );
708 //accept change and decrease lambda
710 lambda = MAX(lambda, 1e-16);
711 std::cerr<<"decreasing lambda to "<<lambda<<std::endl;
712 prevErrNorm = errNorm;
714 //compute new projection error vector
721 double param_change_norm = cvNorm(P, prevP, CV_RELATIVE_L2);
722 //check termination criteria
723 if( (criteria.type&CV_TERMCRIT_ITER && iters > criteria.max_iter ) ||
724 (criteria.type&CV_TERMCRIT_EPS && param_change_norm < criteria.epsilon) ) {
725 // std::cerr<<"relative norm change "<<param_change_norm<<" lower than eps "<<criteria.epsilon<<", stopping"<<std::endl;
729 //copy new params and continue iterations
739 static void fjac(int /*i*/, int /*j*/, CvMat *point_params, CvMat* cam_params, CvMat* A, CvMat* B, void* /*data*/) {
740 //compute jacobian per camera parameters (i.e. Aij)
741 //take i-th point 3D current coordinates
744 cvReshape(point_params, &_Mi, 3, 1 );
746 CvMat* _mp = cvCreateMat(1, 1, CV_64FC2 ); //projection of the point
748 //split camera params into different matrices
749 CvMat _ri, _ti, _k = cvMat(0, 0, CV_64F, NULL); // dummy initialization to fix warning of cl.exe
750 cvGetRows( cam_params, &_ri, 0, 3 );
751 cvGetRows( cam_params, &_ti, 3, 6 );
753 double intr_data[9] = {0, 0, 0, 0, 0, 0, 0, 0, 1};
754 intr_data[0] = cam_params->data.db[6];
755 intr_data[4] = cam_params->data.db[7];
756 intr_data[2] = cam_params->data.db[8];
757 intr_data[5] = cam_params->data.db[9];
759 CvMat _A = cvMat(3,3, CV_64F, intr_data );
761 CvMat _dpdr, _dpdt, _dpdf, _dpdc, _dpdk;
763 bool have_dk = cam_params->height - 10 ? true : false;
765 cvGetCols( A, &_dpdr, 0, 3 );
766 cvGetCols( A, &_dpdt, 3, 6 );
767 cvGetCols( A, &_dpdf, 6, 8 );
768 cvGetCols( A, &_dpdc, 8, 10 );
771 cvGetRows( cam_params, &_k, 10, cam_params->height );
772 cvGetCols( A, &_dpdk, 10, A->width );
774 cvProjectPoints2(&_Mi, &_ri, &_ti, &_A, have_dk ? &_k : NULL, _mp, &_dpdr, &_dpdt,
775 &_dpdf, &_dpdc, have_dk ? &_dpdk : NULL, 0);
777 cvReleaseMat( &_mp );
779 //compute jacobian for point params
780 //compute dMeasure/dPoint3D
782 // x = (r11 * X + r12 * Y + r13 * Z + t1)
783 // y = (r21 * X + r22 * Y + r23 * Z + t2)
784 // z = (r31 * X + r32 * Y + r33 * Z + t3)
789 //d(x') = ( dx*z - x*dz)/(z*z)
790 //d(y') = ( dy*z - y*dz)/(z*z)
792 //g = 1 + k1*r_2 + k2*r_4 + k3*r_6
793 //r_2 = x'*x' + y'*y'
795 //d(r_2) = 2*x'*dx' + 2*y'*dy'
797 //dg = k1* d(r_2) + k2*2*r_2*d(r_2) + k3*3*r_2*r_2*d(r_2)
799 //x" = x'*g + 2*p1*x'*y' + p2(r_2+2*x'_2)
800 //y" = y'*g + p1(r_2+2*y'_2) + 2*p2*x'*y'
802 //d(x") = d(x') * g + x' * d(g) + 2*p1*( d(x')*y' + x'*dy) + p2*(d(r_2) + 2*2*x'* dx')
803 //d(y") = d(y') * g + y' * d(g) + 2*p2*( d(x')*y' + x'*dy) + p1*(d(r_2) + 2*2*y'* dy')
808 // du = fx * d(x") = fx * ( dx*z - x*dz)/ (z*z)
809 // dv = fy * d(y") = fy * ( dy*z - y*dz)/ (z*z)
811 // dx/dX = r11, dx/dY = r12, dx/dZ = r13
812 // dy/dX = r21, dy/dY = r22, dy/dZ = r23
813 // dz/dX = r31, dz/dY = r32, dz/dZ = r33
815 // du/dX = fx*(r11*z-x*r31)/(z*z)
816 // du/dY = fx*(r12*z-x*r32)/(z*z)
817 // du/dZ = fx*(r13*z-x*r33)/(z*z)
819 // dv/dX = fy*(r21*z-y*r31)/(z*z)
820 // dv/dY = fy*(r22*z-y*r32)/(z*z)
821 // dv/dZ = fy*(r23*z-y*r33)/(z*z)
823 //get rotation matrix
824 double R[9], t[3], fx = intr_data[0], fy = intr_data[4];
825 CvMat _R = cvMat( 3, 3, CV_64F, R );
826 cvRodrigues2(&_ri, &_R);
829 X = point_params->data.db[0];
830 Y = point_params->data.db[1];
831 Z = point_params->data.db[2];
833 t[0] = _ti.data.db[0];
834 t[1] = _ti.data.db[1];
835 t[2] = _ti.data.db[2];
838 double x = R[0] * X + R[1] * Y + R[2] * Z + t[0];
839 double y = R[3] * X + R[4] * Y + R[5] * Z + t[1];
840 double z = R[6] * X + R[7] * Y + R[8] * Z + t[2];
844 double x_strike = x/z;
845 double y_strike = y/z;
846 //compute dx',dy' matrix
848 // dx'/dX dx'/dY dx'/dZ =
849 // dy'/dX dy'/dY dy'/dZ
851 double coeff[6] = { z, 0, -x,
853 CvMat coeffmat = cvMat( 2, 3, CV_64F, coeff );
855 CvMat* dstrike_dbig = cvCreateMat(2,3,CV_64F);
856 cvMatMul(&coeffmat, &_R, dstrike_dbig);
857 cvScale(dstrike_dbig, dstrike_dbig, 1/(z*z) );
860 double strike_[2] = {x_strike, y_strike};
861 CvMat strike = cvMat(1, 2, CV_64F, strike_);
864 double r_2 = x_strike*x_strike + y_strike*y_strike;
865 double r_4 = r_2*r_2;
866 double r_6 = r_4*r_2;
868 //compute d(r_2)/dbig
869 CvMat* dr2_dbig = cvCreateMat(1,3,CV_64F);
870 cvMatMul( &strike, dstrike_dbig, dr2_dbig);
871 cvScale( dr2_dbig, dr2_dbig, 2 );
873 double& k1 = _k.data.db[0];
874 double& k2 = _k.data.db[1];
875 double& p1 = _k.data.db[2];
876 double& p2 = _k.data.db[3];
879 if( _k.cols*_k.rows == 5 ) {
883 double dg_dr2 = k1 + k2*2*r_2 + k3*3*r_4;
884 double g = 1+k1*r_2+k2*r_4+k3*r_6;
886 CvMat* dg_dbig = cvCreateMat(1,3,CV_64F);
887 cvScale( dr2_dbig, dg_dbig, dg_dr2 );
889 CvMat* tmp = cvCreateMat( 2, 3, CV_64F );
890 CvMat* dstrike2_dbig = cvCreateMat( 2, 3, CV_64F );
892 double c[4] = { g+2*p1*y_strike+4*p2*x_strike, 2*p1*x_strike,
893 2*p2*y_strike, g+2*p2*x_strike + 4*p1*y_strike };
895 CvMat coeffmat2 = cvMat(2,2,CV_64F, c );
897 cvMatMul(&coeffmat2, dstrike_dbig, dstrike2_dbig );
899 cvGEMM( &strike, dg_dbig, 1, NULL, 0, tmp, CV_GEMM_A_T );
900 cvAdd( dstrike2_dbig, tmp, dstrike2_dbig );
902 double p[2] = { p2, p1 };
903 CvMat pmat = cvMat(2, 1, CV_64F, p );
905 cvMatMul( &pmat, dr2_dbig ,tmp);
906 cvAdd( dstrike2_dbig, tmp, dstrike2_dbig );
908 cvCopy( dstrike2_dbig, B );
910 cvReleaseMat(&dr2_dbig);
911 cvReleaseMat(&dg_dbig);
914 cvReleaseMat(&dstrike2_dbig);
917 cvCopy(dstrike_dbig, B);
921 cvGetRows( B, &row, 0, 1 );
922 cvScale( &row, &row, fx );
924 cvGetRows( B, &row, 1, 2 );
925 cvScale( &row, &row, fy );
931 cvmSet( B, 0, 0, k*(R[0]*z-x*R[6]));
932 cvmSet( B, 0, 1, k*(R[1]*z-x*R[7]));
933 cvmSet( B, 0, 2, k*(R[2]*z-x*R[8]));
937 cvmSet( B, 1, 0, k*(R[3]*z-y*R[6]));
938 cvmSet( B, 1, 1, k*(R[4]*z-y*R[7]));
939 cvmSet( B, 1, 2, k*(R[5]*z-y*R[8]));
944 static void func(int /*i*/, int /*j*/, CvMat *point_params, CvMat* cam_params, CvMat* estim, void* /*data*/) {
945 //just do projections
947 cvReshape( point_params, &_Mi, 3, 1 );
949 CvMat* _mp = cvCreateMat(1, 1, CV_64FC2 ); //projection of the point
950 CvMat* _mp2 = cvCreateMat(1, 2, CV_64F ); //projection of the point
952 //split camera params into different matrices
955 cvGetRows( cam_params, &_ri, 0, 3 );
956 cvGetRows( cam_params, &_ti, 3, 6 );
958 double intr_data[9] = {0, 0, 0, 0, 0, 0, 0, 0, 1};
959 intr_data[0] = cam_params->data.db[6];
960 intr_data[4] = cam_params->data.db[7];
961 intr_data[2] = cam_params->data.db[8];
962 intr_data[5] = cam_params->data.db[9];
964 CvMat _A = cvMat(3,3, CV_64F, intr_data );
966 //int cn = CV_MAT_CN(_Mi.type);
968 bool have_dk = cam_params->height - 10 ? true : false;
971 cvGetRows( cam_params, &_k, 10, cam_params->height );
973 cvProjectPoints2( &_Mi, &_ri, &_ti, &_A, have_dk ? &_k : NULL, _mp, NULL, NULL,
974 NULL, NULL, NULL, 0);
975 // std::cerr<<"_mp = "<<_mp->data.db[0]<<","<<_mp->data.db[1]<<std::endl;
977 _mp2->data.db[0] = _mp->data.db[0];
978 _mp2->data.db[1] = _mp->data.db[1];
979 cvTranspose( _mp2, estim );
980 cvReleaseMat( &_mp );
981 cvReleaseMat( &_mp2 );
984 static void fjac_new(int i, int j, Mat& point_params, Mat& cam_params, Mat& A, Mat& B, void* data) {
985 CvMat _point_params = point_params, _cam_params = cam_params, _Al = A, _Bl = B;
986 fjac(i,j, &_point_params, &_cam_params, &_Al, &_Bl, data);
989 static void func_new(int i, int j, Mat& point_params, Mat& cam_params, Mat& estim, void* data) {
990 CvMat _point_params = point_params, _cam_params = cam_params, _estim = estim;
991 func(i,j,&_point_params,&_cam_params,&_estim,data);
994 void LevMarqSparse::bundleAdjust( std::vector<Point3d>& points, //positions of points in global coordinate system (input and output)
995 const std::vector<std::vector<Point2d> >& imagePoints, //projections of 3d points for every camera
996 const std::vector<std::vector<int> >& visibility, //visibility of 3d points for every camera
997 std::vector<Mat>& cameraMatrix, //intrinsic matrices of all cameras (input and output)
998 std::vector<Mat>& R, //rotation matrices of all cameras (input and output)
999 std::vector<Mat>& T, //translation vector of all cameras (input and output)
1000 std::vector<Mat>& distCoeffs, //distortion coefficients of all cameras (input and output)
1001 const TermCriteria& criteria,
1002 BundleAdjustCallback cb, void* user_data) {
1003 //,enum{MOTION_AND_STRUCTURE,MOTION,STRUCTURE})
1004 int num_points = (int)points.size();
1005 int num_cameras = (int)cameraMatrix.size();
1007 CV_Assert( imagePoints.size() == (size_t)num_cameras &&
1008 visibility.size() == (size_t)num_cameras &&
1009 R.size() == (size_t)num_cameras &&
1010 T.size() == (size_t)num_cameras &&
1011 (distCoeffs.size() == (size_t)num_cameras || distCoeffs.size() == 0) );
1013 int numdist = distCoeffs.size() ? (distCoeffs[0].rows * distCoeffs[0].cols) : 0;
1015 int num_cam_param = 3 /* rotation vector */ + 3 /* translation vector */
1016 + 2 /* fx, fy */ + 2 /* cx, cy */ + numdist;
1018 int num_point_param = 3;
1020 //collect camera parameters into vector
1021 Mat params( num_cameras * num_cam_param + num_points * num_point_param, 1, CV_64F );
1023 //fill camera params
1024 for( int i = 0; i < num_cameras; i++ ) {
1026 Mat rot_vec; Rodrigues( R[i], rot_vec );
1027 Mat dst = params.rowRange(i*num_cam_param, i*num_cam_param+3);
1028 rot_vec.copyTo(dst);
1031 dst = params.rowRange(i*num_cam_param + 3, i*num_cam_param+6);
1034 //intrinsic camera matrix
1035 double* intr_data = (double*)cameraMatrix[i].data;
1036 double* intr = (double*)(params.data + params.step * (i*num_cam_param+6));
1038 intr[0] = intr_data[0]; //fx
1039 intr[1] = intr_data[4]; //fy
1040 //center of projection
1041 intr[2] = intr_data[2]; //cx
1042 intr[3] = intr_data[5]; //cy
1044 //add distortion if exists
1045 if( distCoeffs.size() ) {
1046 dst = params.rowRange(i*num_cam_param + 10, i*num_cam_param+10+numdist);
1047 distCoeffs[i].copyTo(dst);
1052 Mat ptparams(num_points, 1, CV_64FC3, params.data + num_cameras*num_cam_param*params.step);
1053 Mat _points(points);
1054 CV_Assert(_points.size() == ptparams.size() && _points.type() == ptparams.type());
1055 _points.copyTo(ptparams);
1057 //convert visibility vectors to visibility matrix
1058 Mat vismat(num_points, num_cameras, CV_32S);
1059 for( int i = 0; i < num_cameras; i++ ) {
1061 Mat col = vismat.col(i);
1062 Mat((int)visibility[i].size(), 1, vismat.type(), (void*)&visibility[i][0]).copyTo( col );
1065 int num_proj = countNonZero(vismat); //total number of points projections
1067 //collect measurements
1068 Mat X(num_proj*2,1,CV_64F); //measurement vector
1071 for(int i = 0; i < num_points; i++ ) {
1072 for(int j = 0; j < num_cameras; j++ ) {
1074 if( visibility[j][i] ) {
1075 //extract point and put tu vector
1076 Point2d p = imagePoints[j][i];
1077 ((double*)(X.data))[counter] = p.x;
1078 ((double*)(X.data))[counter+1] = p.y;
1079 assert(p.x != -1 || p.y != -1);
1085 LevMarqSparse levmar( num_points, num_cameras, num_point_param, num_cam_param, 2, vismat, params, X,
1086 TermCriteria(criteria), fjac_new, func_new, NULL,
1090 /*Mat final_points(num_points, 1, CV_64FC3,
1091 levmar.P->data.db + num_cameras*num_cam_param *levmar.P->step);
1092 CV_Assert(_points.size() == final_points.size() && _points.type() == final_points.type());
1093 final_points.copyTo(_points);*/
1096 for( int i = 0; i < num_points; i++ ) {
1098 cvGetSubRect( levmar.P, &point_mat, cvRect( 0, levmar.num_cams * levmar.num_cam_param+ levmar.num_point_param * i, 1, levmar.num_point_param ));
1099 CvScalar x = cvGet2D(&point_mat,0,0); CvScalar y = cvGet2D(&point_mat,1,0); CvScalar z = cvGet2D(&point_mat,2,0);
1100 points.push_back(Point3d(x.val[0],y.val[0],z.val[0]));
1101 //std::cerr<<"point"<<points[points.size()-1].x<<","<<points[points.size()-1].y<<","<<points[points.size()-1].z<<std::endl;
1103 //fill camera params
1104 //R.clear();T.clear();cameraMatrix.clear();
1105 Mat levmarP = cv::cvarrToMat(levmar.P);
1106 for( int i = 0; i < num_cameras; i++ ) {
1108 Mat rot_vec = levmarP.rowRange(i*num_cam_param, i*num_cam_param+3);
1109 Rodrigues( rot_vec, R[i] );
1111 levmarP.rowRange(i*num_cam_param + 3, i*num_cam_param+6).copyTo(T[i]);
1113 //intrinsic camera matrix
1114 double* intr_data = (double*)cameraMatrix[i].data;
1115 double* intr = (double*)(levmarP.data +levmarP.step * (i*num_cam_param+6));
1117 intr_data[0] = intr[0]; //fx
1118 intr_data[4] = intr[1]; //fy
1119 //center of projection
1120 intr_data[2] = intr[2]; //cx
1121 intr_data[5] = intr[3]; //cy
1123 //add distortion if exists
1124 if( distCoeffs.size() ) {
1125 levmarP.rowRange(i*num_cam_param + 10, i*num_cam_param+10+numdist).copyTo(distCoeffs[i]);