1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /* $Id: db_metrics.h,v 1.3 2011/06/17 14:03:31 mbansal Exp $ */ 18 19 #ifndef DB_METRICS 20 #define DB_METRICS 21 22 23 24 /***************************************************************** 25 * Lean and mean begins here * 26 *****************************************************************/ 27 28 #include "db_utilities.h" 29 /*! 30 * \defgroup LMMetrics (LM) Metrics 31 */ 32 /*\{*/ 33 34 35 36 37 /*! 38 Compute function value fp and Jacobian J of robustifier given input value f*/ 39 inline void db_CauchyDerivative(double J[4],double fp[2],const double f[2],double one_over_scale2) 40 { 41 double x2,y2,r,r2,r2s,one_over_r2,fu,r_fu,one_over_r_fu; 42 double one_plus_r2s,half_dfu_dx,half_dfu_dy,coeff,coeff2,coeff3; 43 int at_zero; 44 45 /*The robustifier takes the input (x,y) and makes a new 46 vector (xp,yp) where 47 xp=sqrt(log(1+(x^2+y^2)*one_over_scale2))*x/sqrt(x^2+y^2) 48 yp=sqrt(log(1+(x^2+y^2)*one_over_scale2))*y/sqrt(x^2+y^2) 49 The new vector has the property 50 xp^2+yp^2=log(1+(x^2+y^2)*one_over_scale2) 51 i.e. when it is square-summed it gives the robust 52 reprojection error 53 Define 54 r2=(x^2+y^2) and 55 r2s=r2*one_over_scale2 56 fu=log(1+r2s)/r2 57 then 58 xp=sqrt(fu)*x 59 yp=sqrt(fu)*y 60 and 61 d(r2)/dx=2x 62 d(r2)/dy=2y 63 and 64 dfu/dx=d(r2)/dx*(r2s/(1+r2s)-log(1+r2s))/(r2*r2) 65 dfu/dy=d(r2)/dy*(r2s/(1+r2s)-log(1+r2s))/(r2*r2) 66 and 67 d(xp)/dx=1/(2sqrt(fu))*(dfu/dx)*x+sqrt(fu) 68 d(xp)/dy=1/(2sqrt(fu))*(dfu/dy)*x 69 d(yp)/dx=1/(2sqrt(fu))*(dfu/dx)*y 70 d(yp)/dy=1/(2sqrt(fu))*(dfu/dy)*y+sqrt(fu) 71 */ 72 73 x2=db_sqr(f[0]); 74 y2=db_sqr(f[1]); 75 r2=x2+y2; 76 r=sqrt(r2); 77 78 if(r2<=0.0) at_zero=1; 79 else 80 { 81 one_over_r2=1.0/r2; 82 r2s=r2*one_over_scale2; 83 one_plus_r2s=1.0+r2s; 84 fu=log(one_plus_r2s)*one_over_r2; 85 r_fu=sqrt(fu); 86 if(r_fu<=0.0) at_zero=1; 87 else 88 { 89 one_over_r_fu=1.0/r_fu; 90 fp[0]=r_fu*f[0]; 91 fp[1]=r_fu*f[1]; 92 /*r2s is always >= 0*/ 93 coeff=(r2s/one_plus_r2s*one_over_r2-fu)*one_over_r2; 94 half_dfu_dx=f[0]*coeff; 95 half_dfu_dy=f[1]*coeff; 96 coeff2=one_over_r_fu*half_dfu_dx; 97 coeff3=one_over_r_fu*half_dfu_dy; 98 99 J[0]=coeff2*f[0]+r_fu; 100 J[1]=coeff3*f[0]; 101 J[2]=coeff2*f[1]; 102 J[3]=coeff3*f[1]+r_fu; 103 at_zero=0; 104 } 105 } 106 if(at_zero) 107 { 108 /*Close to zero the robustifying mapping 109 becomes identity*sqrt(one_over_scale2)*/ 110 fp[0]=0.0; 111 fp[1]=0.0; 112 J[0]=sqrt(one_over_scale2); 113 J[1]=0.0; 114 J[2]=0.0; 115 J[3]=J[0]; 116 } 117 } 118 119 inline double db_SquaredReprojectionErrorHomography(const double y[2],const double H[9],const double x[3]) 120 { 121 double x0,x1,x2,mult; 122 double sd; 123 124 x0=H[0]*x[0]+H[1]*x[1]+H[2]*x[2]; 125 x1=H[3]*x[0]+H[4]*x[1]+H[5]*x[2]; 126 x2=H[6]*x[0]+H[7]*x[1]+H[8]*x[2]; 127 mult=1.0/((x2!=0.0)?x2:1.0); 128 sd=db_sqr((y[0]-x0*mult))+db_sqr((y[1]-x1*mult)); 129 130 return(sd); 131 } 132 133 inline double db_SquaredInhomogenousHomographyError(const double y[2],const double H[9],const double x[2]) 134 { 135 double x0,x1,x2,mult; 136 double sd; 137 138 x0=H[0]*x[0]+H[1]*x[1]+H[2]; 139 x1=H[3]*x[0]+H[4]*x[1]+H[5]; 140 x2=H[6]*x[0]+H[7]*x[1]+H[8]; 141 mult=1.0/((x2!=0.0)?x2:1.0); 142 sd=db_sqr((y[0]-x0*mult))+db_sqr((y[1]-x1*mult)); 143 144 return(sd); 145 } 146 147 /*! 148 Return a constant divided by likelihood of a Cauchy distributed 149 reprojection error given the image point y, homography H, image point 150 point x and the squared scale coefficient one_over_scale2=1.0/(scale*scale) 151 where scale is the half width at half maximum (hWahM) of the 152 Cauchy distribution*/ 153 inline double db_ExpCauchyInhomogenousHomographyError(const double y[2],const double H[9],const double x[2], 154 double one_over_scale2) 155 { 156 double sd; 157 sd=db_SquaredInhomogenousHomographyError(y,H,x); 158 return(1.0+sd*one_over_scale2); 159 } 160 161 /*! 162 Compute residual vector f between image point y and homography Hx of 163 image point x. Also compute Jacobian of f with respect 164 to an update dx of H*/ 165 inline void db_DerivativeInhomHomographyError(double Jf_dx[18],double f[2],const double y[2],const double H[9], 166 const double x[2]) 167 { 168 double xh,yh,zh,mult,mult2,xh_mult2,yh_mult2; 169 /*The Jacobian of the inhomogenous coordinates with respect to 170 the homogenous is 171 [1/zh 0 -xh/(zh*zh)] 172 [ 0 1/zh -yh/(zh*zh)] 173 The Jacobian of the homogenous coordinates with respect to dH is 174 [x0 x1 1 0 0 0 0 0 0] 175 [ 0 0 0 x0 x1 1 0 0 0] 176 [ 0 0 0 0 0 0 x0 x1 1] 177 The output Jacobian is minus their product, i.e. 178 [-x0/zh -x1/zh -1/zh 0 0 0 x0*xh/(zh*zh) x1*xh/(zh*zh) xh/(zh*zh)] 179 [ 0 0 0 -x0/zh -x1/zh -1/zh x0*yh/(zh*zh) x1*yh/(zh*zh) yh/(zh*zh)]*/ 180 181 /*Compute warped point, which is the same as 182 homogenous coordinates of reprojection*/ 183 xh=H[0]*x[0]+H[1]*x[1]+H[2]; 184 yh=H[3]*x[0]+H[4]*x[1]+H[5]; 185 zh=H[6]*x[0]+H[7]*x[1]+H[8]; 186 mult=1.0/((zh!=0.0)?zh:1.0); 187 /*Compute inhomogenous residual*/ 188 f[0]=y[0]-xh*mult; 189 f[1]=y[1]-yh*mult; 190 /*Compute Jacobian*/ 191 mult2=mult*mult; 192 xh_mult2=xh*mult2; 193 yh_mult2=yh*mult2; 194 Jf_dx[0]= -x[0]*mult; 195 Jf_dx[1]= -x[1]*mult; 196 Jf_dx[2]= -mult; 197 Jf_dx[3]=0; 198 Jf_dx[4]=0; 199 Jf_dx[5]=0; 200 Jf_dx[6]=x[0]*xh_mult2; 201 Jf_dx[7]=x[1]*xh_mult2; 202 Jf_dx[8]=xh_mult2; 203 Jf_dx[9]=0; 204 Jf_dx[10]=0; 205 Jf_dx[11]=0; 206 Jf_dx[12]=Jf_dx[0]; 207 Jf_dx[13]=Jf_dx[1]; 208 Jf_dx[14]=Jf_dx[2]; 209 Jf_dx[15]=x[0]*yh_mult2; 210 Jf_dx[16]=x[1]*yh_mult2; 211 Jf_dx[17]=yh_mult2; 212 } 213 214 /*! 215 Compute robust residual vector f between image point y and homography Hx of 216 image point x. Also compute Jacobian of f with respect 217 to an update dH of H*/ 218 inline void db_DerivativeCauchyInhomHomographyReprojection(double Jf_dx[18],double f[2],const double y[2],const double H[9], 219 const double x[2],double one_over_scale2) 220 { 221 double Jf_dx_loc[18],f_loc[2]; 222 double J[4],J0,J1,J2,J3; 223 224 /*Compute reprojection Jacobian*/ 225 db_DerivativeInhomHomographyError(Jf_dx_loc,f_loc,y,H,x); 226 /*Compute robustifier Jacobian*/ 227 db_CauchyDerivative(J,f,f_loc,one_over_scale2); 228 229 /*Multiply the robustifier Jacobian with 230 the reprojection Jacobian*/ 231 J0=J[0];J1=J[1];J2=J[2];J3=J[3]; 232 Jf_dx[0]=J0*Jf_dx_loc[0]; 233 Jf_dx[1]=J0*Jf_dx_loc[1]; 234 Jf_dx[2]=J0*Jf_dx_loc[2]; 235 Jf_dx[3]= J1*Jf_dx_loc[12]; 236 Jf_dx[4]= J1*Jf_dx_loc[13]; 237 Jf_dx[5]= J1*Jf_dx_loc[14]; 238 Jf_dx[6]=J0*Jf_dx_loc[6]+J1*Jf_dx_loc[15]; 239 Jf_dx[7]=J0*Jf_dx_loc[7]+J1*Jf_dx_loc[16]; 240 Jf_dx[8]=J0*Jf_dx_loc[8]+J1*Jf_dx_loc[17]; 241 Jf_dx[9]= J2*Jf_dx_loc[0]; 242 Jf_dx[10]=J2*Jf_dx_loc[1]; 243 Jf_dx[11]=J2*Jf_dx_loc[2]; 244 Jf_dx[12]= J3*Jf_dx_loc[12]; 245 Jf_dx[13]= J3*Jf_dx_loc[13]; 246 Jf_dx[14]= J3*Jf_dx_loc[14]; 247 Jf_dx[15]=J2*Jf_dx_loc[6]+J3*Jf_dx_loc[15]; 248 Jf_dx[16]=J2*Jf_dx_loc[7]+J3*Jf_dx_loc[16]; 249 Jf_dx[17]=J2*Jf_dx_loc[8]+J3*Jf_dx_loc[17]; 250 } 251 /*! 252 Compute residual vector f between image point y and rotation of 253 image point x by R. Also compute Jacobian of f with respect 254 to an update dx of R*/ 255 inline void db_DerivativeInhomRotationReprojection(double Jf_dx[6],double f[2],const double y[2],const double R[9], 256 const double x[2]) 257 { 258 double xh,yh,zh,mult,mult2,xh_mult2,yh_mult2; 259 /*The Jacobian of the inhomogenous coordinates with respect to 260 the homogenous is 261 [1/zh 0 -xh/(zh*zh)] 262 [ 0 1/zh -yh/(zh*zh)] 263 The Jacobian at zero of the homogenous coordinates with respect to 264 [sin(phi) sin(ohm) sin(kap)] is 265 [-rx2 0 rx1 ] 266 [ 0 rx2 -rx0 ] 267 [ rx0 -rx1 0 ] 268 The output Jacobian is minus their product, i.e. 269 [1+xh*xh/(zh*zh) -xh*yh/(zh*zh) -yh/zh] 270 [xh*yh/(zh*zh) -1-yh*yh/(zh*zh) xh/zh]*/ 271 272 /*Compute rotated point, which is the same as 273 homogenous coordinates of reprojection*/ 274 xh=R[0]*x[0]+R[1]*x[1]+R[2]; 275 yh=R[3]*x[0]+R[4]*x[1]+R[5]; 276 zh=R[6]*x[0]+R[7]*x[1]+R[8]; 277 mult=1.0/((zh!=0.0)?zh:1.0); 278 /*Compute inhomogenous residual*/ 279 f[0]=y[0]-xh*mult; 280 f[1]=y[1]-yh*mult; 281 /*Compute Jacobian*/ 282 mult2=mult*mult; 283 xh_mult2=xh*mult2; 284 yh_mult2=yh*mult2; 285 Jf_dx[0]= 1.0+xh*xh_mult2; 286 Jf_dx[1]= -yh*xh_mult2; 287 Jf_dx[2]= -yh*mult; 288 Jf_dx[3]= -Jf_dx[1]; 289 Jf_dx[4]= -1-yh*yh_mult2; 290 Jf_dx[5]= xh*mult; 291 } 292 293 /*! 294 Compute robust residual vector f between image point y and rotation of 295 image point x by R. Also compute Jacobian of f with respect 296 to an update dx of R*/ 297 inline void db_DerivativeCauchyInhomRotationReprojection(double Jf_dx[6],double f[2],const double y[2],const double R[9], 298 const double x[2],double one_over_scale2) 299 { 300 double Jf_dx_loc[6],f_loc[2]; 301 double J[4],J0,J1,J2,J3; 302 303 /*Compute reprojection Jacobian*/ 304 db_DerivativeInhomRotationReprojection(Jf_dx_loc,f_loc,y,R,x); 305 /*Compute robustifier Jacobian*/ 306 db_CauchyDerivative(J,f,f_loc,one_over_scale2); 307 308 /*Multiply the robustifier Jacobian with 309 the reprojection Jacobian*/ 310 J0=J[0];J1=J[1];J2=J[2];J3=J[3]; 311 Jf_dx[0]=J0*Jf_dx_loc[0]+J1*Jf_dx_loc[3]; 312 Jf_dx[1]=J0*Jf_dx_loc[1]+J1*Jf_dx_loc[4]; 313 Jf_dx[2]=J0*Jf_dx_loc[2]+J1*Jf_dx_loc[5]; 314 Jf_dx[3]=J2*Jf_dx_loc[0]+J3*Jf_dx_loc[3]; 315 Jf_dx[4]=J2*Jf_dx_loc[1]+J3*Jf_dx_loc[4]; 316 Jf_dx[5]=J2*Jf_dx_loc[2]+J3*Jf_dx_loc[5]; 317 } 318 319 320 321 /*! 322 // remove the outliers whose projection error is larger than pre-defined 323 */ 324 inline int db_RemoveOutliers_Homography(const double H[9], double *x_i,double *xp_i, double *wp,double *im, double *im_p, double *im_r, double *im_raw,double *im_raw_p,int point_count,double scale, double thresh=DB_OUTLIER_THRESHOLD) 325 { 326 double temp_valueE, t2; 327 int c; 328 int k1=0; 329 int k2=0; 330 int k3=0; 331 int numinliers=0; 332 int ind1; 333 int ind2; 334 int ind3; 335 int isinlier; 336 337 // experimentally determined 338 t2=1.0/(thresh*thresh*thresh*thresh); 339 340 // count the inliers 341 for(c=0;c<point_count;c++) 342 { 343 ind1=c<<1; 344 ind2=c<<2; 345 ind3=3*c; 346 347 temp_valueE=db_SquaredInhomogenousHomographyError(im_p+ind3,H,im+ind3); 348 349 isinlier=((temp_valueE<=t2)?1:0); 350 351 // if it is inlier, then copy the 3d and 2d correspondences 352 if (isinlier) 353 { 354 numinliers++; 355 356 x_i[k1]=x_i[ind1]; 357 x_i[k1+1]=x_i[ind1+1]; 358 359 xp_i[k1]=xp_i[ind1]; 360 xp_i[k1+1]=xp_i[ind1+1]; 361 362 k1=k1+2; 363 364 // original normalized pixel coordinates 365 im[k3]=im[ind3]; 366 im[k3+1]=im[ind3+1]; 367 im[k3+2]=im[ind3+2]; 368 369 im_r[k3]=im_r[ind3]; 370 im_r[k3+1]=im_r[ind3+1]; 371 im_r[k3+2]=im_r[ind3+2]; 372 373 im_p[k3]=im_p[ind3]; 374 im_p[k3+1]=im_p[ind3+1]; 375 im_p[k3+2]=im_p[ind3+2]; 376 377 // left and right raw pixel coordinates 378 im_raw[k3] = im_raw[ind3]; 379 im_raw[k3+1] = im_raw[ind3+1]; 380 im_raw[k3+2] = im_raw[ind3+2]; // the index 381 382 im_raw_p[k3] = im_raw_p[ind3]; 383 im_raw_p[k3+1] = im_raw_p[ind3+1]; 384 im_raw_p[k3+2] = im_raw_p[ind3+2]; // the index 385 386 k3=k3+3; 387 388 // 3D coordinates 389 wp[k2]=wp[ind2]; 390 wp[k2+1]=wp[ind2+1]; 391 wp[k2+2]=wp[ind2+2]; 392 wp[k2+3]=wp[ind2+3]; 393 394 k2=k2+4; 395 396 } 397 } 398 399 return numinliers; 400 } 401 402 403 404 405 406 /*\}*/ 407 408 #endif /* DB_METRICS */ 409