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