00001
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005
00006
00007
00008 #include "FMatrix.h"
00009
00010 #include <vcl_cassert.h>
00011 #include <vcl_iostream.h>
00012 #include <vcl_fstream.h>
00013
00014 #include <vul/vul_printf.h>
00015
00016 #include <vnl/vnl_math.h>
00017 #include <vnl/vnl_matrix.h>
00018 #include <vnl/vnl_double_3x3.h>
00019 #include <vnl/vnl_double_3.h>
00020 #include <vnl/vnl_cross_product_matrix.h>
00021 #include <vnl/algo/vnl_svd.h>
00022 #include <vnl/algo/vnl_rpoly_roots.h>
00023 #include <vgl/algo/vgl_homg_operators_2d.h>
00024
00025 #include <mvl/HomgOperator2D.h>
00026
00027
00028
00029
00030
00031
00032 FMatrix::FMatrix()
00033 {
00034 rank2_flag_ = false;
00035 }
00036
00037
00038
00039
00040
00041 FMatrix::FMatrix(vcl_istream& f)
00042 {
00043 rank2_flag_ = false;
00044 read_ascii(f);
00045 }
00046
00047
00048
00049
00050
00051 FMatrix::FMatrix(const double *f_matrix)
00052 {
00053 rank2_flag_ = false;
00054 set(f_matrix);
00055 }
00056
00057
00058
00059
00060
00061 FMatrix::FMatrix(const vnl_double_3x3& f_matrix)
00062 {
00063 rank2_flag_ = false;
00064 set(f_matrix);
00065 }
00066
00067
00068
00069
00070
00071
00072 FMatrix::FMatrix(const PMatrix& P1, const PMatrix& P2)
00073 {
00074 rank2_flag_ = false;
00075 set(P1, P2);
00076 }
00077
00078
00079
00080
00081
00082 FMatrix::FMatrix(const PMatrix& P2)
00083 {
00084 rank2_flag_ = false;
00085 set(P2);
00086 }
00087
00088
00089
00090
00091 FMatrix::~FMatrix()
00092 {
00093 }
00094
00095
00096
00097 bool FMatrix::read_ascii(vcl_istream& s)
00098 {
00099 s >> f_matrix_;
00100 if (!(s.good() || s.eof()))
00101 return false;
00102
00103 ft_matrix_ = f_matrix_.transpose();
00104 rank2_flag_ = false;
00105 set_rank2_using_svd();
00106
00107 return true;
00108 }
00109
00110 FMatrix FMatrix::read(char const* filename)
00111 {
00112 vcl_ifstream f(filename);
00113 FMatrix F;
00114 if (!F.read_ascii(f))
00115 vcl_cerr << "FMatrix: Error reading from [" << filename << "]\n";
00116 return F;
00117 }
00118
00119
00120
00121 vcl_istream& operator>>(vcl_istream& s, FMatrix& F)
00122 {
00123 F.read_ascii(s);
00124 return s;
00125 }
00126
00127
00128
00129 FMatrix FMatrix::read(vcl_istream& s)
00130 {
00131 return FMatrix(s);
00132 }
00133
00134
00135
00136
00137
00138
00139 vgl_homg_line_2d<double> FMatrix::image1_epipolar_line(const vgl_homg_point_2d<double>& x2) const
00140 {
00141 vgl_homg_point_2d<double> p = ft_matrix_ * x2;
00142 return vgl_homg_line_2d<double>(p.x(),p.y(),p.w());
00143 }
00144
00145
00146
00147
00148 HomgLine2D FMatrix::image1_epipolar_line(const HomgPoint2D& x2) const
00149 {
00150 return HomgLine2D(ft_matrix_ * x2.get_vector());
00151 }
00152
00153
00154
00155
00156
00157 vgl_homg_line_2d<double> FMatrix::image2_epipolar_line(const vgl_homg_point_2d<double>& x1) const
00158 {
00159 vgl_homg_point_2d<double> p = f_matrix_ * x1;
00160 return vgl_homg_line_2d<double>(p.x(),p.y(),p.w());
00161 }
00162
00163
00164
00165
00166
00167 HomgLine2D FMatrix::image2_epipolar_line(const HomgPoint2D& x1) const
00168 {
00169 return HomgLine2D(f_matrix_ * x1.get_vector());
00170 }
00171
00172
00173
00174
00175
00176
00177 double
00178 FMatrix::image1_epipolar_distance_squared(vgl_homg_point_2d<double> const& p1,
00179 vgl_homg_point_2d<double> const& p2) const
00180 {
00181 vgl_homg_line_2d<double> epipolar_line = image1_epipolar_line (p2);
00182 return vgl_homg_operators_2d<double>::perp_dist_squared (p1, epipolar_line);
00183 }
00184
00185
00186
00187
00188
00189
00190 double
00191 FMatrix::image1_epipolar_distance_squared(HomgPoint2D *point1_ptr,
00192 HomgPoint2D *point2_ptr) const
00193 {
00194 HomgLine2D epipolar_line = image1_epipolar_line (*point2_ptr);
00195 return HomgOperator2D::perp_dist_squared (*point1_ptr, epipolar_line);
00196 }
00197
00198
00199
00200
00201
00202
00203 double
00204 FMatrix::image2_epipolar_distance_squared(vgl_homg_point_2d<double> const& p1,
00205 vgl_homg_point_2d<double> const& p2) const
00206 {
00207 vgl_homg_line_2d<double> epipolar_line = image2_epipolar_line(p1);
00208 return vgl_homg_operators_2d<double>::perp_dist_squared(p2, epipolar_line);
00209 }
00210
00211
00212
00213
00214
00215
00216 double
00217 FMatrix::image2_epipolar_distance_squared(HomgPoint2D *point1_ptr,
00218 HomgPoint2D *point2_ptr) const
00219 {
00220 HomgLine2D epipolar_line = image2_epipolar_line (*point1_ptr);
00221 return HomgOperator2D::perp_dist_squared (*point2_ptr, epipolar_line);
00222 }
00223
00224
00225
00226 vcl_ostream& operator<<(vcl_ostream& os, const FMatrix& F)
00227 {
00228 const vnl_double_3x3& m = F.get_matrix();
00229 for (unsigned long i = 0; i < m.rows(); i++) {
00230 for (unsigned long j = 0; j < m.columns(); j++)
00231 vul_printf(os, "%24.16e ", m(i,j));
00232 os << '\n';
00233 }
00234 return os;
00235 }
00236
00237
00238
00239
00240
00241 FMatrix FMatrix::transpose() const
00242 {
00243 return FMatrix(ft_matrix_);
00244 }
00245
00246
00247
00248
00249
00250
00251 bool
00252 FMatrix::get_epipoles(vgl_homg_point_2d<double>& epipole1,
00253 vgl_homg_point_2d<double>& epipole2) const
00254 {
00255
00256 vnl_svd<double> svd(f_matrix_.as_ref());
00257 vnl_double_3 v = svd.nullvector();
00258 epipole1.set(v[0],v[1],v[2]);
00259 v = svd.left_nullvector();
00260 epipole2.set(v[0],v[1],v[2]);
00261 return svd.W(2,2) == 0;
00262 }
00263
00264
00265
00266
00267
00268
00269 bool
00270 FMatrix::get_epipoles(HomgPoint2D*epipole1_ptr, HomgPoint2D*epipole2_ptr) const
00271 {
00272
00273 vnl_svd<double> svd(f_matrix_.as_ref());
00274 epipole1_ptr->set(svd.nullvector());
00275 epipole2_ptr->set(svd.left_nullvector());
00276 return svd.W(2,2) == 0;
00277 }
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290 void
00291 FMatrix::find_nearest_perfect_match(vgl_homg_point_2d<double> const& point1,
00292 vgl_homg_point_2d<double> const& point2,
00293 vgl_homg_point_2d<double>& perfect_point1,
00294 vgl_homg_point_2d<double>& perfect_point2) const
00295 {
00296 vgl_homg_point_2d<double> epipole1, epipole2;
00297 get_epipoles(epipole1, epipole2);
00298
00299 find_nearest_perfect_match(point1, point2, epipole1, epipole2, perfect_point1, perfect_point2);
00300 }
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312 void
00313 FMatrix::find_nearest_perfect_match(const HomgPoint2D& point1,
00314 const HomgPoint2D& point2,
00315 HomgPoint2D *perfect_point1_ptr,
00316 HomgPoint2D *perfect_point2_ptr) const
00317 {
00318 HomgPoint2D epipole1, epipole2;
00319 get_epipoles(&epipole1, &epipole2);
00320
00321 find_nearest_perfect_match(point1, point2, epipole1, epipole2, perfect_point1_ptr, perfect_point2_ptr);
00322 }
00323
00324
00325 void
00326 FMatrix::find_nearest_perfect_match(vgl_homg_point_2d<double> const& point1,
00327 vgl_homg_point_2d<double> const& point2,
00328 vgl_homg_point_2d<double> const& epipole1,
00329 vgl_homg_point_2d<double> const& epipole2,
00330 vgl_homg_point_2d<double>& perfect_point1,
00331 vgl_homg_point_2d<double>& perfect_point2) const
00332 {
00333 vgl_homg_line_2d<double> line_horiz(0,1,0);
00334
00335 vgl_homg_line_2d<double> line1 = vgl_homg_operators_2d<double>::join_oriented(point1, epipole1);
00336 double angle1 = vgl_homg_operators_2d<double>::angle_between_oriented_lines (line1, line_horiz);
00337
00338 vgl_homg_line_2d<double> line2 = vgl_homg_operators_2d<double>::join_oriented(point2, epipole2);
00339 double angle2 = vgl_homg_operators_2d<double>::angle_between_oriented_lines (line2, line_horiz);
00340
00341
00342
00343
00344 double x1 = point1.x()/point1.w(), y1 = point1.y()/point1.w(),
00345 x2 = point2.x()/point2.w(), y2 = point2.y()/point2.w();
00346
00347
00348
00349
00350 vnl_double_3x3 p1_matrix;
00351 p1_matrix(0, 0) = vcl_cos (angle1);
00352 p1_matrix(0, 1) = vcl_sin (angle1);
00353 p1_matrix(0, 2) = x1;
00354 p1_matrix(1, 0)= -vcl_sin (angle1);
00355 p1_matrix(1, 1) = vcl_cos (angle1);
00356 p1_matrix(1, 2) = y1;
00357 p1_matrix(2, 0) = 0;
00358 p1_matrix(2, 1) = 0;
00359 p1_matrix(2, 2) = 1;
00360
00361 vnl_double_3x3 p2_matrix;
00362 p2_matrix(0, 0) = vcl_cos (angle2);
00363 p2_matrix(0, 1) = vcl_sin (angle2);
00364 p2_matrix(0, 2) = x2;
00365 p2_matrix(1, 0)= -vcl_sin (angle2);
00366 p2_matrix(1, 1) = vcl_cos (angle2);
00367 p2_matrix(1, 2) = y2;
00368 p2_matrix(2, 0) = 0;
00369 p2_matrix(2, 1) = 0;
00370 p2_matrix(2, 2) = 1;
00371
00372 vnl_double_3x3 special_f_matrix= p2_matrix.transpose() *f_matrix_ *p1_matrix;
00373
00374 double f = -special_f_matrix(1, 0) / special_f_matrix(1, 2);
00375 double f2 = -special_f_matrix(2, 0) / special_f_matrix(2, 2);
00376 double g = -special_f_matrix(0, 1) / special_f_matrix(2, 1);
00377 double g2 = -special_f_matrix(0, 2) / special_f_matrix(2, 2);
00378 if (vcl_fabs ((f-f2) / f) > 0.05 || vcl_fabs ((g-g2) / g) > 0.05)
00379 vcl_cerr << "F matrix isn't rank 2.\n";
00380
00381
00382
00383 double a = special_f_matrix(1, 1);
00384 double b = special_f_matrix(1, 2);
00385 double c = special_f_matrix(2, 1);
00386 double d = special_f_matrix(2, 2);
00387
00388
00389 vnl_vector<double> coeffs(7);
00390 coeffs[0] = b*b*c*d - a*b*d*d;
00391 coeffs[1] = b*b*b*b + b*b*c*c - a*a*d*d + 2.0*b*b*d*d*g*g + d*d*d*d*g*g*g*g;
00392 coeffs[2] = 4.0*a*b*b*b + a*b*c*c - a*a*c*d + 2.0*b*b*c*d*f*f - 2.0*a*b*d*d*f*f
00393 + 4.0*b*b*c*d*g*g + 4.0*a*b*d*d*g*g + 4.0*c*d*d*d*g*g*g*g;
00394 coeffs[3] = 6.0*a*a*b*b + 2.0*b*b*c*c*f*f - 2.0*a*a*d*d*f*f + 2.0*b*b*c*c*g*g
00395 + 8*a*b*c*d*g*g + 2.0*a*a*d*d*g*g + 6.0*c*c*d*d*g*g*g*g;
00396 coeffs[4] = 4.0*a*a*a*b + 2.0*a*b*c*c*f*f - 2.0*a*a*c*d*f*f + b*b*c*d*f*f*f*f
00397 - a*b*d*d*f*f*f*f + 4.0*a*b*c*c*g*g + 4.0*a*a*c*d*g*g + 4.0*c*c*c*d*g*g*g*g;
00398 coeffs[5] = a*a*a*a + b*b*c*c*f*f*f*f - a*a*d*d*f*f*f*f + 2.0*a*a*c*c*g*g + c*c*c*c*g*g*g*g;
00399 coeffs[6] = a*b*c*c*f*f*f*f - a*a*c*d*f*f*f*f;
00400
00401 vnl_rpoly_roots roots(coeffs);
00402
00403
00404
00405
00406 bool real_root_flag = false;
00407 double s_min = 1e20;
00408 double t_min = 0;
00409 for (int root_index = 0; root_index < 6; root_index++)
00410 if (roots.imag(root_index) == 0) {
00411
00412 double t = roots.real(root_index);
00413 double s = t * t / vnl_math_sqr(1.0 + f * f * t * t) +
00414 vnl_math_sqr(c * t + d) /
00415 (vnl_math_sqr(a * t + b) + g * g * vnl_math_sqr(c * t + d));
00416 if (s < s_min) {
00417 real_root_flag = true;
00418 t_min = t;
00419 s_min = s;
00420 }
00421 }
00422
00423 if (!real_root_flag) {
00424 vcl_cerr << "FMatrix::find_nearest_perfect_match -- no real root\n";
00425 return;
00426 }
00427
00428
00429
00430
00431 vgl_homg_line_2d<double> epipolar_line1(t_min * f, 1, -t_min);
00432 vgl_homg_line_2d<double> epipolar_line2(-g * (c*t_min + d), a*t_min + b, c*t_min + d);
00433 vgl_homg_point_2d<double> origin(0,0,1);
00434
00435 perfect_point1 = p1_matrix * vgl_homg_operators_2d<double>::perp_projection(epipolar_line1, origin);
00436 perfect_point2 = p2_matrix * vgl_homg_operators_2d<double>::perp_projection(epipolar_line2, origin);
00437 }
00438
00439
00440 void
00441 FMatrix::find_nearest_perfect_match(const HomgPoint2D& point1,
00442 const HomgPoint2D& point2,
00443 const HomgPoint2D& epipole1,
00444 const HomgPoint2D& epipole2,
00445 HomgPoint2D *perfect_point1_ptr,
00446 HomgPoint2D *perfect_point2_ptr) const
00447 {
00448 HomgLine2D line_horiz(0,1,0);
00449
00450 HomgLine2D line1 = HomgOperator2D::join_oriented(point1, epipole1);
00451 double angle1 = HomgOperator2D::angle_between_oriented_lines (line1, line_horiz);
00452
00453 HomgLine2D line2 = HomgOperator2D::join_oriented(point2, epipole2);
00454 double angle2 = HomgOperator2D::angle_between_oriented_lines (line2, line_horiz);
00455
00456
00457
00458
00459 double x1, y1;
00460 point1.get_nonhomogeneous(x1, y1);
00461
00462 double x2, y2;
00463 point2.get_nonhomogeneous(x2, y2);
00464
00465
00466
00467
00468 vnl_double_3x3 p1_matrix;
00469 p1_matrix(0, 0) = vcl_cos (angle1);
00470 p1_matrix(0, 1) = vcl_sin (angle1);
00471 p1_matrix(0, 2) = x1;
00472 p1_matrix(1, 0)= -vcl_sin (angle1);
00473 p1_matrix(1, 1) = vcl_cos (angle1);
00474 p1_matrix(1, 2) = y1;
00475 p1_matrix(2, 0) = 0;
00476 p1_matrix(2, 1) = 0;
00477 p1_matrix(2, 2) = 1;
00478
00479 vnl_double_3x3 p2_matrix;
00480 p2_matrix(0, 0) = vcl_cos (angle2);
00481 p2_matrix(0, 1) = vcl_sin (angle2);
00482 p2_matrix(0, 2) = x2;
00483 p2_matrix(1, 0)= -vcl_sin (angle2);
00484 p2_matrix(1, 1) = vcl_cos (angle2);
00485 p2_matrix(1, 2) = y2;
00486 p2_matrix(2, 0) = 0;
00487 p2_matrix(2, 1) = 0;
00488 p2_matrix(2, 2) = 1;
00489
00490 vnl_double_3x3 special_f_matrix= p2_matrix.transpose() *f_matrix_ *p1_matrix;
00491
00492 double f = -special_f_matrix(1, 0) / special_f_matrix(1, 2);
00493 double f2 = -special_f_matrix(2, 0) / special_f_matrix(2, 2);
00494 double g = -special_f_matrix(0, 1) / special_f_matrix(2, 1);
00495 double g2 = -special_f_matrix(0, 2) / special_f_matrix(2, 2);
00496 if (vcl_fabs ((f-f2) / f) > 0.05 || vcl_fabs ((g-g2) / g) > 0.05)
00497 vcl_cerr << "F matrix isn't rank 2.\n";
00498
00499
00500
00501 double a = special_f_matrix(1, 1);
00502 double b = special_f_matrix(1, 2);
00503 double c = special_f_matrix(2, 1);
00504 double d = special_f_matrix(2, 2);
00505
00506
00507 vnl_vector<double> coeffs(7);
00508 coeffs[0] = b*b*c*d - a*b*d*d;
00509 coeffs[1] = b*b*b*b + b*b*c*c - a*a*d*d + 2.0*b*b*d*d*g*g + d*d*d*d*g*g*g*g;
00510 coeffs[2] = 4.0*a*b*b*b + a*b*c*c - a*a*c*d + 2.0*b*b*c*d*f*f - 2.0*a*b*d*d*f*f
00511 + 4.0*b*b*c*d*g*g + 4.0*a*b*d*d*g*g + 4.0*c*d*d*d*g*g*g*g;
00512 coeffs[3] = 6.0*a*a*b*b + 2.0*b*b*c*c*f*f - 2.0*a*a*d*d*f*f + 2.0*b*b*c*c*g*g
00513 + 8*a*b*c*d*g*g + 2.0*a*a*d*d*g*g + 6.0*c*c*d*d*g*g*g*g;
00514 coeffs[4] = 4.0*a*a*a*b + 2.0*a*b*c*c*f*f - 2.0*a*a*c*d*f*f + b*b*c*d*f*f*f*f
00515 - a*b*d*d*f*f*f*f + 4.0*a*b*c*c*g*g + 4.0*a*a*c*d*g*g + 4.0*c*c*c*d*g*g*g*g;
00516 coeffs[5] = a*a*a*a + b*b*c*c*f*f*f*f - a*a*d*d*f*f*f*f + 2.0*a*a*c*c*g*g + c*c*c*c*g*g*g*g;
00517 coeffs[6] = a*b*c*c*f*f*f*f - a*a*c*d*f*f*f*f;
00518
00519 vnl_rpoly_roots roots(coeffs);
00520
00521
00522
00523
00524 bool real_root_flag = false;
00525 double s_min = 1e20;
00526 double t_min = 0;
00527 for (int root_index = 0; root_index < 6; root_index++)
00528 if (roots.imag(root_index) == 0) {
00529
00530 double t = roots.real(root_index);
00531 double s = t * t / vnl_math_sqr(1.0 + f * f * t * t) +
00532 vnl_math_sqr(c * t + d) /
00533 (vnl_math_sqr(a * t + b) + g * g * vnl_math_sqr(c * t + d));
00534 if (s < s_min) {
00535 real_root_flag = true;
00536 t_min = t;
00537 s_min = s;
00538 }
00539 }
00540
00541 if (!real_root_flag) {
00542 vcl_cerr << "FMatrix::find_nearest_perfect_match -- no real root\n";
00543 return;
00544 }
00545
00546
00547
00548 if (perfect_point1_ptr) {
00549
00550 HomgLine2D epipolar_line1(t_min * f, 1, -t_min);
00551 HomgLine2D epipolar_line2(-g * (c*t_min + d), a*t_min + b, c*t_min + d);
00552 HomgPoint2D origin(0,0,1);
00553
00554 *perfect_point1_ptr = HomgPoint2D(p1_matrix * HomgOperator2D::perp_projection(epipolar_line1, origin).get_vector());
00555 *perfect_point2_ptr = HomgPoint2D(p2_matrix * HomgOperator2D::perp_projection(epipolar_line2, origin).get_vector());
00556 }
00557 }
00558
00559
00560
00561 void FMatrix::compute_P_matrix(vnl_matrix<double> &P2) const
00562 {
00563 HomgPoint2D e1, e2;
00564 get_epipoles(&e1, &e2);
00565
00566 vnl_double_3x3 A;
00567 vnl_double_3 a = e2.get_vector();
00568
00569 vnl_cross_product_matrix e2x(a);
00570 A = e2x * f_matrix_;
00571
00572 P2.set_columns(0, A.as_ref());
00573 P2.set_column(3, a.as_ref());
00574 }
00575
00576
00577
00578
00579
00580
00581
00582 void FMatrix::set_rank2_using_svd(void)
00583 {
00584
00585 vnl_svd<double> svd(f_matrix_.as_ref());
00586 svd.W(2) = 0;
00587 f_matrix_ = svd.recompose();
00588 ft_matrix_ = f_matrix_.transpose();
00589 rank2_flag_ = true;
00590 }
00591
00592
00593
00594
00595
00596 void
00597 FMatrix::decompose_to_skew_rank3(vnl_matrix<double>*, vnl_matrix<double>*) const
00598 {
00599 assert(!"Not implemented\n");
00600 }
00601
00602
00603
00604
00605
00606
00607 double FMatrix::get (unsigned int row_index, unsigned int col_index) const
00608 {
00609 return f_matrix_(row_index, col_index);
00610 }
00611
00612
00613
00614
00615
00616 void FMatrix::get (double *c) const
00617 {
00618 for (int row_index = 0; row_index < 3; row_index++)
00619 for (int col_index = 0; col_index < 3; col_index++)
00620 *c++ = f_matrix_(row_index, col_index);
00621 }
00622
00623
00624
00625
00626 void FMatrix::get (vnl_matrix<double>* f_matrix) const
00627 {
00628 *f_matrix = f_matrix_.as_ref();
00629 }
00630
00631
00632
00633
00634
00635 bool FMatrix::get_rank2_flag (void) const
00636 {
00637 return rank2_flag_;
00638 }
00639
00640
00641
00642
00643 void FMatrix::set_rank2_flag (bool rank2_flag)
00644 {
00645 rank2_flag_ = rank2_flag;
00646 }
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656 bool FMatrix::set (const double *c_matrix)
00657 {
00658 for (int row_index = 0; row_index < 3; row_index++)
00659 for (int col_index = 0; col_index < 3; col_index++) {
00660 double v = *c_matrix++;
00661 f_matrix_(row_index, col_index) = v;
00662 ft_matrix_(col_index, row_index) = v;
00663 }
00664
00665 return true;
00666 }
00667
00668
00669
00670
00671
00672
00673
00674
00675 bool FMatrix::set (const vnl_double_3x3& f_matrix)
00676 {
00677 f_matrix_ = f_matrix;
00678 ft_matrix_ = f_matrix.transpose();
00679
00680 return true;
00681 }
00682
00683
00684 void FMatrix::set (const PMatrix& P1, const PMatrix& P2)
00685 {
00686 vnl_svd<double>* svd = P1.svd();
00687
00688 vnl_cross_product_matrix e2x(P2.get_matrix() * svd->nullvector());
00689
00690 set(vnl_double_3x3((e2x * P2.get_matrix() * svd->inverse()).data_block()));
00691 }
00692
00693
00694 void FMatrix::set (const PMatrix& P2)
00695 {
00696 vnl_double_3x3 A;
00697 vnl_double_3 a;
00698 P2.get(&A.as_ref().non_const(), &a.as_ref().non_const());
00699
00700 vnl_cross_product_matrix e2x(a);
00701
00702 set(e2x * A);
00703 }
00704
00705
00706 void FMatrix::set (const FMatrix& F)
00707 {
00708 *this = F;
00709 }