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