📄 sba_levmar.c
字号:
///////////////////////////////////////////////////////////////////////////////////// //// Expert drivers for sparse bundle adjustment based on the//// Levenberg - Marquardt minimization algorithm//// Copyright (C) 2004-2008 Manolis Lourakis (lourakis at ics forth gr)//// Institute of Computer Science, Foundation for Research & Technology - Hellas//// Heraklion, Crete, Greece.//////// This program is free software; you can redistribute it and/or modify//// it under the terms of the GNU General Public License as published by//// the Free Software Foundation; either version 2 of the License, or//// (at your option) any later version.//////// This program is distributed in the hope that it will be useful,//// but WITHOUT ANY WARRANTY; without even the implied warranty of//// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the//// GNU General Public License for more details.///////////////////////////////////////////////////////////////////////////////////////#include <stdio.h>#include <stdlib.h>#include <string.h>#include <math.h>#include <float.h>#include "compiler.h"#include "sba.h"#include "sba_chkjac.h"#define SBA_EPSILON 1E-12#define SBA_EPSILON_SQ ( (SBA_EPSILON)*(SBA_EPSILON) )#define SBA_ONE_THIRD 0.3333333334 /* 1.0/3.0 */#define emalloc(sz) emalloc_(__FILE__, __LINE__, sz)#define FABS(x) (((x)>=0)? (x) : -(x))#define ROW_MAJOR 0#define COLUMN_MAJOR 1#define MAT_STORAGE COLUMN_MAJOR/* contains information necessary for computing a finite difference approximation to a jacobian, * e.g. function to differentiate, problem dimensions and pointers to working memory buffers */struct fdj_data_x_ { void (*func)(double *p, struct sba_crsm *idxij, int *rcidxs, int *rcsubs, double *hx, void *adata); /* function to differentiate */ int cnp, pnp, mnp; /* parameter numbers */ int *func_rcidxs, *func_rcsubs; /* working memory for func invocations. * Notice that this has to be different * than the working memory used for * evaluating the jacobian! */ double *hx, *hxx; /* memory to save results in */ void *adata;};/* auxiliary memory allocation routine with error checking */inline static void *emalloc_(char *file, int line, size_t sz){void *ptr; ptr=(void *)malloc(sz); if(ptr==NULL){ fprintf(stderr, "SBA: memory allocation request for %u bytes failed in file %s, line %d, exiting", sz, file, line); exit(1); } return ptr;}/* auxiliary routine for clearing an array of doubles */inline static void _dblzero(register double *arr, register int count){ while(--count>=0) *arr++=0.0;}/* auxiliary routine for computing the mean reprojection error; used for debugging */static double sba_mean_repr_error(int n, int mnp, double *x, double *hx, struct sba_crsm *idxij, int *rcidxs, int *rcsubs){register int i, j;int nnz, nprojs;double *ptr1, *ptr2;double err; for(i=nprojs=0, err=0.0; i<n; ++i){ nnz=sba_crsm_row_elmidxs(idxij, i, rcidxs, rcsubs); /* find nonzero x_ij, j=0...m-1 */ nprojs+=nnz; for(j=0; j<nnz; ++j){ /* point i projecting on camera rcsubs[j] */ ptr1=x + idxij->val[rcidxs[j]]*mnp; ptr2=hx + idxij->val[rcidxs[j]]*mnp; err+=sqrt((ptr1[0]-ptr2[0])*(ptr1[0]-ptr2[0]) + (ptr1[1]-ptr2[1])*(ptr1[1]-ptr2[1])); } } return err/((double)(nprojs));}/* print the solution in p using sba's text format. If cnp/pnp==0 only points/cameras are printed */static void sba_print_sol(int n, int m, double *p, int cnp, int pnp, double *x, int mnp, struct sba_crsm *idxij, int *rcidxs, int *rcsubs){register int i, j, ii;int nnz;double *ptr; if(cnp){ /* print camera parameters */ for(j=0; j<m; ++j){ ptr=p+cnp*j; for(ii=0; ii<cnp; ++ii) printf("%g ", ptr[ii]); printf("\n"); } } if(pnp){ /* 3D & 2D point parameters */ printf("\n\n\n# X Y Z nframes frame0 x0 y0 frame1 x1 y1 ...\n"); for(i=0; i<n; ++i){ ptr=p+cnp*m+i*pnp; for(ii=0; ii<pnp; ++ii) // print 3D coordinates printf("%g ", ptr[ii]); nnz=sba_crsm_row_elmidxs(idxij, i, rcidxs, rcsubs); /* find nonzero x_ij, j=0...m-1 */ printf("%d ", nnz); for(j=0; j<nnz; ++j){ /* point i projecting on camera rcsubs[j] */ ptr=x + idxij->val[rcidxs[j]]*mnp; printf("%d ", rcsubs[j]); for(ii=0; ii<mnp; ++ii) // print 2D coordinates printf("%g ", ptr[ii]); } printf("\n"); } }}/* Compute e=x-y for two n-vectors x and y and return the squared L2 norm of e. * e can coincide with either x or y. * Uses loop unrolling and blocking to reduce bookkeeping overhead & pipeline * stalls and increase instruction-level parallelism; see http://www.abarnett.demon.co.uk/tutorial.html */static double nrmL2xmy(double *const e, const double *const x, const double *const y, const int n){const int blocksize=8, bpwr=3; /* 8=2^3 */register int i;int j1, j2, j3, j4, j5, j6, j7;int blockn;register double sum0=0.0, sum1=0.0, sum2=0.0, sum3=0.0; /* n may not be divisible by blocksize, * go as near as we can first, then tidy up. */ blockn = (n>>bpwr)<<bpwr; /* (n / blocksize) * blocksize; */ /* unroll the loop in blocks of `blocksize'; looping downwards gains some more speed */ for(i=blockn-1; i>0; i-=blocksize){ e[i ]=x[i ]-y[i ]; sum0+=e[i ]*e[i ]; j1=i-1; e[j1]=x[j1]-y[j1]; sum1+=e[j1]*e[j1]; j2=i-2; e[j2]=x[j2]-y[j2]; sum2+=e[j2]*e[j2]; j3=i-3; e[j3]=x[j3]-y[j3]; sum3+=e[j3]*e[j3]; j4=i-4; e[j4]=x[j4]-y[j4]; sum0+=e[j4]*e[j4]; j5=i-5; e[j5]=x[j5]-y[j5]; sum1+=e[j5]*e[j5]; j6=i-6; e[j6]=x[j6]-y[j6]; sum2+=e[j6]*e[j6]; j7=i-7; e[j7]=x[j7]-y[j7]; sum3+=e[j7]*e[j7]; } /* * There may be some left to do. * This could be done as a simple for() loop, * but a switch is faster (and more interesting) */ i=blockn; if(i<n){ /* Jump into the case at the place that will allow * us to finish off the appropriate number of items. */ switch(n - i){ case 7 : e[i]=x[i]-y[i]; sum0+=e[i]*e[i]; ++i; case 6 : e[i]=x[i]-y[i]; sum0+=e[i]*e[i]; ++i; case 5 : e[i]=x[i]-y[i]; sum0+=e[i]*e[i]; ++i; case 4 : e[i]=x[i]-y[i]; sum0+=e[i]*e[i]; ++i; case 3 : e[i]=x[i]-y[i]; sum0+=e[i]*e[i]; ++i; case 2 : e[i]=x[i]-y[i]; sum0+=e[i]*e[i]; ++i; case 1 : e[i]=x[i]-y[i]; sum0+=e[i]*e[i]; ++i; } } return sum0+sum1+sum2+sum3;}/* Compute e=W*(x-y) for two n-vectors x and y and return the squared L2 norm of e. * This norm equals the squared C norm of x-y with C=W^T*W, W being block diagonal * matrix with nvis mnp*mnp blocks: e^T*e=(x-y)^T*W^T*W*(x-y)=||x-y||_C. * Note that n=nvis*mnp; e can coincide with either x or y. * * Similarly to nrmL2xmy() above, uses loop unrolling and blocking */static double nrmCxmy(double *const e, const double *const x, const double *const y, const double *const W, const int mnp, const int nvis){const int n=nvis*mnp;const int blocksize=8, bpwr=3; /* 8=2^3 */register int i, ii, k;int j1, j2, j3, j4, j5, j6, j7;int blockn, mnpsq;register double norm, sum;register const double *Wptr, *auxptr;register double *eptr; /* n may not be divisible by blocksize, * go as near as we can first, then tidy up. */ blockn = (n>>bpwr)<<bpwr; /* (n / blocksize) * blocksize; */ /* unroll the loop in blocks of `blocksize'; looping downwards gains some more speed */ for(i=blockn-1; i>0; i-=blocksize){ e[i ]=x[i ]-y[i ]; j1=i-1; e[j1]=x[j1]-y[j1]; j2=i-2; e[j2]=x[j2]-y[j2]; j3=i-3; e[j3]=x[j3]-y[j3]; j4=i-4; e[j4]=x[j4]-y[j4]; j5=i-5; e[j5]=x[j5]-y[j5]; j6=i-6; e[j6]=x[j6]-y[j6]; j7=i-7; e[j7]=x[j7]-y[j7]; } /* * There may be some left to do. * This could be done as a simple for() loop, * but a switch is faster (and more interesting) */ i=blockn; if(i<n){ /* Jump into the case at the place that will allow * us to finish off the appropriate number of items. */ switch(n - i){ case 7 : e[i]=x[i]-y[i]; ++i; case 6 : e[i]=x[i]-y[i]; ++i; case 5 : e[i]=x[i]-y[i]; ++i; case 4 : e[i]=x[i]-y[i]; ++i; case 3 : e[i]=x[i]-y[i]; ++i; case 2 : e[i]=x[i]-y[i]; ++i; case 1 : e[i]=x[i]-y[i]; ++i; } } /* compute w_x_ij e_ij in e and its L2 norm. * Since w_x_ij is upper triangular, the products can be safely saved * directly in e_ij, without the need for intermediate storage */ mnpsq=mnp*mnp; /* Wptr, eptr point to w_x_ij, e_ij below */ for(i=0, Wptr=W, eptr=e, norm=0.0; i<nvis; ++i, Wptr+=mnpsq, eptr+=mnp){ for(ii=0, auxptr=Wptr; ii<mnp; ++ii, auxptr+=mnp){ /* auxptr=Wptr+ii*mnp */ for(k=ii, sum=0.0; k<mnp; ++k) // k>=ii since w_x_ij is upper triangular sum+=auxptr[k]*eptr[k]; //Wptr[ii*mnp+k]*eptr[k]; eptr[ii]=sum; norm+=sum*sum; } } return norm;}/* search for & print image projection components that are infinite; useful for identifying errors */static void sba_print_inf(double *hx, int nimgs, int mnp, struct sba_crsm *idxij, int *rcidxs, int *rcsubs){register int i, j, k;int nnz;double *phxij; for(j=0; j<nimgs; ++j){ nnz=sba_crsm_col_elmidxs(idxij, j, rcidxs, rcsubs); /* find nonzero hx_ij, i=0...n-1 */ for(i=0; i<nnz; ++i){ phxij=hx + idxij->val[rcidxs[i]]*mnp; for(k=0; k<mnp; ++k) if(!SBA_FINITE(phxij[k])) printf("SBA: component %d of the estimated projection of point %d on camera %d is invalid!\n", k, rcsubs[i], j); } }}/* Given a parameter vector p made up of the 3D coordinates of n points and the parameters of m cameras, compute in * jac the jacobian of the predicted measurements, i.e. the jacobian of the projections of 3D points in the m images. * The jacobian is approximated with the aid of finite differences and is returned in the order * (A_11, B_11, ..., A_1m, B_1m, ..., A_n1, B_n1, ..., A_nm, B_nm), * where A_ij=dx_ij/da_j and B_ij=dx_ij/db_i (see HZ). * Notice that depending on idxij, some of the A_ij, B_ij might be missing * * Problem-specific information is assumed to be stored in a structure pointed to by "dat". * * NOTE: The jacobian (for n=4, m=3) in matrix form has the following structure: * A_11 0 0 B_11 0 0 0 * 0 A_12 0 B_12 0 0 0 * 0 0 A_13 B_13 0 0 0 * A_21 0 0 0 B_21 0 0 * 0 A_22 0 0 B_22 0 0 * 0 0 A_23 0 B_23 0 0 * A_31 0 0 0 0 B_31 0 * 0 A_32 0 0 0 B_32 0 * 0 0 A_33 0 0 B_33 0 * A_41 0 0 0 0 0 B_41 * 0 A_42 0 0 0 0 B_42 * 0 0 A_43 0 0 0 B_43 * To reduce the total number of objective function evaluations, this structure can be * exploited as follows: A certain d is added to the j-th parameters of all cameras and * the objective function is evaluated at the resulting point. This evaluation suffices
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -