00001
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005
00006
00007
00008 #include "FMPlanarNonLinFun.h"
00009
00010 #include <vcl_cstdlib.h>
00011 #include <vcl_iostream.h>
00012
00013 #include <vnl/vnl_math.h>
00014 #include <vnl/vnl_matrix.h>
00015 #include <vnl/vnl_transpose.h>
00016 #include <vnl/vnl_matops.h>
00017
00018 #include <vnl/vnl_cross_product_matrix.h>
00019 #include <vnl/algo/vnl_symmetric_eigensystem.h>
00020 #include <vnl/algo/vnl_levenberg_marquardt.h>
00021
00022 #include <vgl/vgl_homg_line_2d.h>
00023 #include <mvl/ImageMetric.h>
00024 #include <mvl/HomgOperator2D.h>
00025 #include <mvl/FMatrixPlanar.h>
00026
00027 const int FMPlanarNonLinFun_nparams = 9;
00028
00029
00030
00031
00032 FMPlanarNonLinFun::FMPlanarNonLinFun(const ImageMetric* image_metric1,
00033 const ImageMetric* image_metric2,
00034 double ,
00035 vcl_vector<vgl_homg_point_2d<double> >& points1,
00036 vcl_vector<vgl_homg_point_2d<double> >& points2)
00037 : vnl_least_squares_function(FMPlanarNonLinFun_nparams, points1.size(), no_gradient)
00038 , data_size_(points1.size())
00039 , points1_(points1)
00040 , points2_(points2)
00041 , normalized_(2*data_size_)
00042 , image_metric1_(image_metric1)
00043 , image_metric2_(image_metric2)
00044 {
00045
00046 vcl_vector<vgl_homg_point_2d<double> > points(points1);
00047 for (unsigned i = 0; i < points2.size(); ++i)
00048 points.push_back(points2[i]);
00049
00050
00051 normalized_.normalize(points);
00052
00053
00054 denorm_matrix_ = normalized_.get_C();
00055 denorm_matrix_inv_ = normalized_.get_C_inverse();
00056 }
00057
00058 FMPlanarNonLinFun::FMPlanarNonLinFun(const ImageMetric* image_metric1,
00059 const ImageMetric* image_metric2,
00060 double ,
00061 vcl_vector<HomgPoint2D>& points1,
00062 vcl_vector<HomgPoint2D>& points2):
00063 vnl_least_squares_function(FMPlanarNonLinFun_nparams, points1.size(), no_gradient),
00064 data_size_(points1.size()),
00065 normalized_(2*data_size_),
00066 image_metric1_(image_metric1),
00067 image_metric2_(image_metric2)
00068 {
00069 for (unsigned i = 0; i < points1.size(); ++i)
00070 points1_.push_back(vgl_homg_point_2d<double>(points1[i].x(),points1[i].y(),points1[i].w()));
00071 for (unsigned i = 0; i < points2.size(); ++i)
00072 points2_.push_back(vgl_homg_point_2d<double>(points2[i].x(),points2[i].y(),points2[i].w()));
00073
00074 vcl_vector<HomgPoint2D> points(points1);
00075 for (unsigned i = 0; i < points2.size(); ++i)
00076 points.push_back(points2[i]);
00077
00078
00079 normalized_.normalize(points);
00080
00081
00082 denorm_matrix_ = normalized_.get_C();
00083 denorm_matrix_inv_ = normalized_.get_C_inverse();
00084 }
00085
00086
00087
00088
00089 bool FMPlanarNonLinFun::compute(FMatrixPlanar* F)
00090 {
00091
00092 vcl_cerr << "FMPlanarNonLinFun: matches = "<<data_size_<<", using "<<FMPlanarNonLinFun_nparams<<" parameters\n";
00093
00094
00095 const vnl_matrix<double>& post = denorm_matrix_inv_.as_ref();
00096 const vnl_matrix<double>& pre = denorm_matrix_inv_.transpose().as_ref();
00097 FMatrixPlanar norm_F(pre * F->get_matrix() * post);
00098
00099
00100 vnl_vector<double> f_params(FMPlanarNonLinFun_nparams);
00101 fmatrix_to_params (norm_F, f_params);
00102
00103 vnl_levenberg_marquardt lm(*this);
00104 if (!lm.minimize(f_params))
00105 return false;
00106
00107 vcl_cerr<<"FMPlanarNonLinFun: minimisation start error "
00108 << lm.get_start_error() / vcl_sqrt(double(data_size_))
00109 <<" end error "
00110 << lm.get_end_error() / vcl_sqrt(double(data_size_))
00111 <<vcl_endl;
00112
00113 norm_F = params_to_fmatrix (f_params);
00114
00115 F->set(vnl_transpose(denorm_matrix_) * norm_F.get_matrix() * denorm_matrix_);
00116
00117 vcl_cerr << "fm_fmatrix_nagmin: accepted " << data_size_ << '/' << data_size_
00118 << " rms point-epipolar error " << lm.get_end_error() / vcl_sqrt(double(data_size_))
00119 << vcl_endl;
00120
00121 return true;
00122 }
00123
00124
00125
00126
00127 void FMPlanarNonLinFun::f(vnl_vector<double> const& f_params, vnl_vector<double>& fx)
00128 {
00129 FMatrixPlanar norm_F = params_to_fmatrix(f_params);
00130
00131 FMatrixPlanar F(vnl_transpose(denorm_matrix_) * norm_F.get_matrix() * denorm_matrix_);
00132
00133 for (int i = 0; i < data_size_; ++i) {
00134 const vgl_homg_point_2d<double>& p1 = points1_[i];
00135 const vgl_homg_point_2d<double>& p2 = points2_[i];
00136
00137 vgl_homg_line_2d<double> l12 = F.image2_epipolar_line(p1);
00138 double r1 = image_metric2_.perp_dist_squared(p2, l12);
00139
00140 vgl_homg_line_2d<double> l21 = F.image1_epipolar_line(p2);
00141 double r2 = image_metric1_.perp_dist_squared(p1, l21);
00142
00143 fx[i] = vcl_sqrt((r1 + r2) / 2.0);
00144 }
00145
00146
00147
00148 }
00149
00150 void FMPlanarNonLinFun::fmatrix_to_params(const FMatrixPlanar& F,
00151 vnl_vector<double>& params)
00152 {
00153 if (FMPlanarNonLinFun_nparams == 9)
00154 fmatrix_to_params_awf(F, params);
00155 else
00156 fmatrix_to_params_mna(F, params);
00157 }
00158
00159 FMatrixPlanar FMPlanarNonLinFun::params_to_fmatrix(const vnl_vector<double>& params)
00160 {
00161 if (FMPlanarNonLinFun_nparams == 9)
00162 return params_to_fmatrix_awf(params);
00163 else
00164 return params_to_fmatrix_mna(params);
00165 }
00166
00167
00168
00169
00170
00171
00172 void FMPlanarNonLinFun::fmatrix_to_params_mna(const FMatrixPlanar& F,
00173 vnl_vector<double>& params)
00174 {
00175
00176 HomgPoint2D e1,e2;
00177 F.get_epipoles(&e1,&e2);
00178
00179 vnl_symmetric_eigensystem<double> symm_eig(F.get_matrix()+F.get_matrix().transpose());
00180
00181 double eig0 = symm_eig.D(0,0);
00182 double eig1 = symm_eig.D(2,2);
00183
00184 if (eig0 > 0 || eig1 < 0) {
00185 vcl_cerr << "ERROR in FMPlanarNonLinFun: vnl_symmetric_eigensystem<double> is unsorted: " << symm_eig.D << vcl_endl;
00186 vcl_abort();
00187 }
00188
00189 if (vcl_fabs(symm_eig.D(1,1)) > 1e-12)
00190 vcl_cerr << "FMPlanarNonLinFun: WARNING: middle eigenvalue not 0: " << symm_eig.D << vcl_endl;
00191
00192 vnl_vector<double> v0(symm_eig.get_eigenvector(0));
00193 vnl_vector<double> v1(symm_eig.get_eigenvector(2));
00194
00195 vnl_double_3 f1 = vcl_sqrt(eig1)*v1 + vcl_sqrt(-eig0)*v0;
00196 vnl_double_3 f2 = vcl_sqrt(eig1)*v1 - vcl_sqrt(-eig0)*v0;
00197
00198 vnl_double_3 ls;
00199 if (vcl_fabs(HomgOperator2D::dot(e1,f1)/e1.w()) + vcl_fabs(HomgOperator2D::dot(e2,f1)/e2.w()) >
00200 vcl_fabs(HomgOperator2D::dot(e1,f2)/e1.w()) + vcl_fabs(HomgOperator2D::dot(e2,f2)/e2.w()) )
00201 ls = f1;
00202 else
00203 ls = f2;
00204
00205 ls /= ls.magnitude();
00206
00207 double ls_thi = vcl_acos(ls[2]);
00208 if (ls_thi < 0) ls_thi += vnl_math::pi;
00209
00210 double ls_theta;
00211 if (ls[1] >= 0)
00212 ls_theta = vcl_acos(ls[0]/vcl_sin(ls_thi));
00213 else
00214 ls_theta = -vcl_acos(ls[0]/vcl_sin(ls_thi));
00215
00216 params[0] = ls_theta;
00217 params[1] = ls_thi;
00218 params[2] = e1.x()/e1.w();
00219 params[3] = e1.y()/e1.w();
00220 params[4] = e2.x()/e2.w();
00221 params[5] = e2.y()/e2.w();
00222
00223 #ifdef PARANOID
00224
00225 {
00226 FMatrixPlanar back = params_to_fmatrix_mna(params);
00227 double norm = vnl_matops::homg_diff(back.get_matrix(), F.get_matrix());
00228 if (norm > 1e-12) {
00229 vcl_cerr << "FMPlanarNonLinFun: WARNING! deparameterization diff = " << norm << vcl_endl
00230 << "b = [" << back << "];\n"
00231 << "n = [" << F << "];\n";
00232 }
00233 }
00234 #endif
00235 }
00236
00237
00238
00239
00240
00241 FMatrixPlanar FMPlanarNonLinFun::params_to_fmatrix_mna(const vnl_vector<double>& params)
00242 {
00243 double ls1 = vcl_cos(params[0])*vcl_sin(params[1]);
00244 double ls2 = vcl_sin(params[0])*vcl_sin(params[1]);
00245 double ls3 = vcl_cos(params[1]);
00246
00247 double list1[9] = {0, -1.0, params[5],
00248 1, 0, -params[4],
00249 -params[5], params[4], 0};
00250 double list2[9] = {0,-ls3,ls2,ls3,0,-ls1,-ls2,ls1,0};
00251 double list3[9] = {0,-1.0,params[3],1,0,-params[2],-params[3],params[2],0};
00252
00253 vnl_matrix<double> mat1(3,3,9,list1),mat2(3,3,9,list2),mat3(3,3,9,list3);
00254
00255 vnl_matrix<double> fmat = mat1*mat2*mat3;
00256
00257 fmat /= fmat.fro_norm();
00258
00259 return FMatrixPlanar(fmat);
00260 }
00261
00262 void FMPlanarNonLinFun::fmatrix_to_params_awf(const FMatrixPlanar& F, vnl_vector<double>& params)
00263 {
00264
00265 HomgPoint2D e1,e2;
00266 F.get_epipoles(&e1,&e2);
00267
00268 vnl_symmetric_eigensystem<double> symm_eig(F.get_matrix()+F.get_matrix().transpose());
00269
00270 double eig0 = symm_eig.D(0,0);
00271 double eig1 = symm_eig.D(2,2);
00272
00273 if (eig0 > 0 || eig1 < 0) {
00274 vcl_cerr << "ERROR in FMPlanarNonLinFun: vnl_symmetric_eigensystem<double> is unsorted: " << symm_eig.D << vcl_endl;
00275 vcl_abort();
00276 }
00277
00278 if (vcl_fabs(symm_eig.D(1,1)) > 1e-12)
00279 vcl_cerr << "FMPlanarNonLinFun: WARNING: middle eigenvalue not 0: " << symm_eig.D << vcl_endl;
00280
00281 vnl_vector<double> v0(symm_eig.get_eigenvector(0));
00282 vnl_vector<double> v1(symm_eig.get_eigenvector(2));
00283
00284 vnl_double_3 f1 = vcl_sqrt(eig1)*v1 + vcl_sqrt(-eig0)*v0;
00285 vnl_double_3 f2 = vcl_sqrt(eig1)*v1 - vcl_sqrt(-eig0)*v0;
00286
00287 vnl_double_3 ls;
00288 if (vcl_fabs(HomgOperator2D::dot(e1,f1)/e1.w()) + vcl_fabs(HomgOperator2D::dot(e2,f1)/e2.w()) >
00289 vcl_fabs(HomgOperator2D::dot(e1,f2)/e1.w()) + vcl_fabs(HomgOperator2D::dot(e2,f2)/e2.w()) )
00290 ls = f1;
00291 else
00292 ls = f2;
00293
00294 ls /= ls.magnitude();
00295 double mag1 = e1.get_vector().magnitude();
00296 double mag2 = e2.get_vector().magnitude();
00297
00298 params[0] = ls[0];
00299 params[1] = ls[1];
00300 params[2] = ls[2];
00301 params[3] = e1.x()/mag1;
00302 params[4] = e1.y()/mag1;
00303 params[5] = e1.w()/mag1;
00304 params[6] = e2.x()/mag2;
00305 params[7] = e2.y()/mag2;
00306 params[8] = e2.w()/mag2;
00307
00308
00309 {
00310 FMatrixPlanar back = params_to_fmatrix_awf(params);
00311 double norm = vnl_matops::homg_diff(back.get_matrix().as_ref(), F.get_matrix().as_ref());
00312 if (norm > 1e-12) {
00313 vcl_cerr << "FMPlanarNonLinFun: WARNING! deparameterization diff = " << norm << vcl_endl
00314 << "b = [" << back << "];\n"
00315 << "n = [" << F << "];\n";
00316 }
00317 }
00318 }
00319
00320
00321
00322
00323
00324 FMatrixPlanar FMPlanarNonLinFun::params_to_fmatrix_awf(const vnl_vector<double>& params)
00325 {
00326 const double* v = params.data_block();
00327 vnl_cross_product_matrix L(v);
00328 vnl_cross_product_matrix E1(v+3);
00329 vnl_cross_product_matrix E2(v+6);
00330
00331 vnl_matrix<double> fmat = E2.as_ref() * L * E1;
00332
00333 fmat /= fmat.fro_norm();
00334
00335 return FMatrixPlanar(fmat);
00336 }