contrib/oxl/mvl/HomgOperator3D.cxx
Go to the documentation of this file.
00001 #include "HomgOperator3D.h"
00002 //:
00003 //  \file
00004 
00005 #include <vcl_cmath.h>
00006 #include <vcl_iostream.h>
00007 #include <vcl_vector.h>
00008 #include <vcl_utility.h>
00009 #include <vcl_algorithm.h>
00010 
00011 #include <vnl/algo/vnl_svd.h>
00012 
00013 #include <mvl/HomgLine3D.h>
00014 #include <mvl/HomgPoint3D.h>
00015 #include <mvl/HomgPlane3D.h>
00016 
00017 // -----------------------------------------------------------------------------
00018 
00019 //: Given collinear 3D points in random order, sort them.
00020 void
00021 HomgOperator3D::sort_points(HomgPoint3D* points, int n)
00022 {
00023   if (n <= 0) return; // early return if possible
00024 
00025   // ho_quadvecstd_sort_quadvecs
00026 
00027   int num_finite = 0;
00028   HomgPoint3D finite_quadvec;
00029   for (int p_index = 0; p_index < n; p_index++) {
00030     HomgPoint3D* p = &points[p_index];
00031     if (p->w() != 0) {
00032       ++num_finite;
00033       finite_quadvec = *p;
00034     }
00035   }
00036   if (num_finite < n) {
00037     vcl_cerr << "WARNING HomgOperator3D::sort_points -- "
00038              << (n - num_finite) << " points at infinity\n";
00039   }
00040 
00041   if (num_finite == 0) {
00042     vcl_cerr << "HomgOperator3D::sort_points: all points at infinity - no action\n";
00043     return;
00044   }
00045 
00046   double distance_max = 0;
00047   HomgLine3D line;
00048   for (int p_index = 0; p_index < n; p_index++) {
00049     HomgPoint3D* p = &points[p_index];
00050     if (p->w() != 0) {
00051       double distance = HomgOperator3D::distance_squared(finite_quadvec, *p);
00052       if (distance > distance_max) {
00053         distance_max = distance;
00054         // ho_quadvecstd_points2_to_line
00055         line = HomgLine3D(finite_quadvec, *p);
00056       }
00057     }
00058   }
00059 
00060   /* now create a point which is bound to be beyond the endpoints of the set. */
00061   vnl_double_3 finite_trivec = finite_quadvec.get_double3();
00062 
00063 //vnl_double_3 start = line.get_point_finite().get_double3();
00064   vnl_double_3 dir = line.dir();
00065 
00066   vnl_double_3 faraway_trivec = finite_trivec + 2.0 * distance_max * dir;
00067   HomgPoint3D faraway(faraway_trivec[0], faraway_trivec[1], faraway_trivec[2], 1.0);
00068 
00069   typedef vcl_pair<float, int> pair_float_int;
00070   vcl_vector< pair_float_int > sort_table(n);
00071   vcl_vector< HomgPoint3D > tempoints(points, points + n);
00072 
00073   for (int p_index = 0; p_index < n; p_index++) {
00074     HomgPoint3D* p = &points[p_index];
00075     if (p->w() != 0) {
00076       sort_table[p_index].first = (float)HomgOperator3D::distance_squared(faraway, *p);
00077       sort_table[p_index].second = p_index;
00078     }
00079   }
00080 
00081   vcl_sort(sort_table.begin(), sort_table.end());
00082 
00083   for (int sort_index = 0; sort_index < n; sort_index++) {
00084     pair_float_int* sort = &sort_table[sort_index];
00085     tempoints[sort_index] = points[sort->second];
00086   }
00087 
00088   for (int i = 0; i < n; ++i)
00089     points[i] = tempoints[i];
00090 }
00091 
00092 //-----------------------------------------------------------------------------
00093 //
00094 //: Return the angle between the (oriented) lines (in radians)
00095 //
00096 double
00097 HomgOperator3D::angle_between_oriented_lines (const HomgLine3D& l1, const HomgLine3D& l2)
00098 {
00099   HomgPoint3D const& dir1 = l1.get_point_infinite();
00100   HomgPoint3D const& dir2 = l2.get_point_infinite();
00101   double n = dir1.x()*dir1.x()+dir1.y()*dir1.y()+dir1.z()*dir1.z();
00102   n       *= dir2.x()*dir2.x()+dir2.y()*dir2.y()+dir2.z()*dir2.z();
00103   // dot product of unit direction vectors:
00104   n = (dir1.x()*dir2.x()+dir1.y()*dir2.y()+dir1.z()*dir2.z())/vcl_sqrt(n);
00105   return vcl_acos(n);
00106 }
00107 
00108 
00109 //-----------------------------------------------------------------------------
00110 //
00111 //: Return the squared distance between the points
00112 //
00113 double
00114 HomgOperator3D::distance_squared (const HomgPoint3D& point1, const HomgPoint3D& point2)
00115 {
00116   return (point1.get_double3() - point2.get_double3()).magnitude();
00117 }
00118 
00119 //-----------------------------------------------------------------------------
00120 //
00121 //: Return the intersection point of the line and plane
00122 //
00123 HomgPoint3D
00124 HomgOperator3D::intersect_line_and_plane (const HomgLine3D &line, const HomgPlane3D& plane)
00125 {
00126   //
00127   /* use P.(S + lambda D) = 0 to find lambda, and hence a point on the plane. */
00128 
00129   const vnl_double_4& x1 = line.get_point_finite().get_vector();
00130   const vnl_double_4& x2 = line.get_point_infinite().get_vector();
00131   const vnl_double_4& p  = plane.get_vector();
00132 
00133   double numerator = -dot_product (x1, p);
00134   double denominator = dot_product (x2, p);
00135 
00136   // Scale for conditioning
00137   double scale = 1.0/(numerator + denominator);
00138   numerator *= scale;
00139   denominator *= scale;
00140 
00141   return denominator * x1 + numerator * x2;
00142 }
00143 
00144 //-----------------------------------------------------------------------------
00145 //
00146 // - Compute the intersection point of the lines, or the mid-point
00147 // of the common perpendicular if the lines are skew
00148 //
00149 HomgPoint3D
00150 HomgOperator3D::lines_to_point (const HomgLine3D& , const HomgLine3D& )
00151 {
00152   vcl_cerr << "Warning: HomgOperator3D::lines_to_point() not yet implemented\n";
00153   return HomgPoint3D();
00154 }
00155 
00156 
00157 //-----------------------------------------------------------------------------
00158 //
00159 // - Compute the best fit intersection point of the lines
00160 //
00161 HomgPoint3D
00162 HomgOperator3D::lines_to_point (const vcl_vector<HomgLine3D>& )
00163 {
00164   vcl_cerr << "Warning: HomgOperator3D::lines_to_point() not yet implemented\n";
00165   return HomgPoint3D();
00166 }
00167 
00168 //-----------------------------------------------------------------------------
00169 //
00170 // - Return the squared perpendicular distance between the line and point
00171 //
00172 double
00173 HomgOperator3D::perp_dist_squared (const HomgPoint3D& , const HomgLine3D& )
00174 {
00175   vcl_cerr << "Warning: HomgOperator3D::perp_dist_squared() not yet implemented\n";
00176   return 0;
00177 }
00178 
00179 //-----------------------------------------------------------------------------
00180 //
00181 // - Return the line which is perpendicular to *line and passes
00182 // through *point
00183 //
00184 HomgLine3D
00185 HomgOperator3D::perp_line_through_point (const HomgLine3D& , const HomgPoint3D& ) { return HomgLine3D(); }
00186 
00187 
00188 //-----------------------------------------------------------------------------
00189 //
00190 // - Compute the line which is the perpendicular projection of *point
00191 // onto *line
00192 //
00193 HomgPoint3D
00194 HomgOperator3D::perp_projection (const HomgLine3D& , const HomgPoint3D& )
00195 {
00196   vcl_cerr << "Warning: HomgOperator3D::perp_projection() not yet implemented\n";
00197   return HomgPoint3D();
00198 }
00199 
00200 
00201 //-----------------------------------------------------------------------------
00202 //
00203 //: Return the intersection line of the planes
00204 //
00205 HomgLine3D
00206 HomgOperator3D::planes_to_line (const HomgPlane3D& plane1, const HomgPlane3D& plane2)
00207 {
00208   vnl_matrix<double> M(2,4);
00209   M.set_row(0, plane1.get_vector());
00210   M.set_row(1, plane2.get_vector());
00211   vnl_svd<double> svd(M);
00212   M = svd.nullspace(2);
00213   HomgPoint3D p1(M.get_column(0));
00214   HomgPoint3D p2(M.get_column(1));
00215   return HomgLine3D(p1, p2);
00216 }
00217 
00218 
00219 //-----------------------------------------------------------------------------
00220 //
00221 // - Compute the best-fit intersection line of the planes
00222 //
00223 HomgLine3D
00224 HomgOperator3D::planes_to_line (const vcl_vector<HomgPlane3D>&)
00225 {
00226   vcl_cerr << "Warning: HomgOperator3D::planes_to_line() not yet implemented\n";
00227   return HomgLine3D();
00228 }
00229 
00230 
00231 //-----------------------------------------------------------------------------
00232 //
00233 // - Return the line through the points
00234 //
00235 HomgLine3D
00236 HomgOperator3D::points_to_line (const HomgPoint3D&, const HomgPoint3D&)
00237 {
00238   vcl_cerr << "Warning: HomgOperator3D::points_to_line() not yet implemented\n";
00239   return HomgLine3D();
00240 }
00241 
00242 //-----------------------------------------------------------------------------
00243 //
00244 // - Compute the best-fit line through the points
00245 //
00246 HomgLine3D
00247 HomgOperator3D::points_to_line (const vcl_vector<HomgPoint3D>&)
00248 {
00249   vcl_cerr << "Warning: HomgOperator3D::points_to_line() not yet implemented\n";
00250   return HomgLine3D();
00251 }
00252 
00253 //-----------------------------------------------------------------------------
00254 //
00255 // - Return the plane through the points
00256 //
00257 HomgPlane3D
00258 HomgOperator3D::points_to_plane (const HomgPoint3D&, const HomgPoint3D&, const HomgPoint3D&)
00259 {
00260   vcl_cerr << "Warning: HomgOperator3D::points_to_plane() not yet implemented\n";
00261   return HomgPlane3D();
00262 }
00263 
00264 
00265 //-----------------------------------------------------------------------------
00266 //
00267 // - Compute the best-fit plane through the points
00268 //
00269 HomgPlane3D
00270 HomgOperator3D::points_to_plane (const vcl_vector<HomgPoint3D>&)
00271 {
00272   vcl_cerr << "Warning: HomgOperator3D::points_to_plane() not yet implemented\n";
00273   return HomgPlane3D();
00274 }
00275 
00276 //: Compute best-fit intersection of planes in a point.
00277 
00278 HomgPoint3D
00279 HomgOperator3D::intersection_point (const HomgPlane3D& plane1, const HomgPlane3D& plane2, const HomgPlane3D& plane3)
00280 {
00281   vnl_matrix<double> A(3, 4);
00282   A(0,0) = plane1.x();
00283   A(0,1) = plane1.y();
00284   A(0,2) = plane1.z();
00285   A(0,3) = plane1.w();
00286 
00287   A(1,0) = plane2.x();
00288   A(1,1) = plane2.y();
00289   A(1,2) = plane2.z();
00290   A(1,3) = plane2.w();
00291 
00292   A(2,0) = plane3.x();
00293   A(2,1) = plane3.y();
00294   A(2,2) = plane3.z();
00295   A(2,3) = plane3.w();
00296 
00297   vnl_svd<double> svd(A);
00298   return HomgPoint3D(svd.nullvector());
00299 }
00300 
00301 HomgPoint3D
00302 HomgOperator3D::intersection_point (const vcl_vector<HomgPlane3D>& planes)
00303 {
00304   int n = planes.size();
00305   vnl_matrix<double> A(planes.size(), 4);
00306 
00307   for (int i =0; i < n; ++i) {
00308     A(i,0) = planes[i].x();
00309     A(i,1) = planes[i].y();
00310     A(i,2) = planes[i].z();
00311     A(i,3) = planes[i].w();
00312   }
00313 
00314   vnl_svd<double> svd(A);
00315   return HomgPoint3D(svd.nullvector());
00316 }
00317 
00318 //-----------------------------------------------------------------------------
00319 //: Calculates the crossratio of four collinear points p1, p2, p3 and p4.
00320 // This number is projectively invariant, and it is the coordinate of p4
00321 // in the reference frame where p2 is the origin (coordinate 0), p3 is
00322 // the unity (coordinate 1) and p1 is the point at infinity.
00323 // This cross ratio is often denoted as ((p1, p2; p3, p4)) (which also
00324 // equals ((p3, p4; p1, p2)) or ((p2, p1; p4, p3)) or ((p4, p3; p2, p1)) )
00325 // and is calculated as
00326 //                      p1 - p3   p2 - p3      (p1-p3)(p2-p4)
00327 //                      ------- : --------  =  --------------
00328 //                      p1 - p4   p2 - p4      (p1-p4)(p2-p3)
00329 //
00330 // In principle, any single nonhomogeneous coordinate from the four points
00331 // can be used as parameters for CrossRatio (but of course the same for all
00332 // points). The most reliable answer will be obtained when the coordinate with
00333 // the largest spacing is used, i.e., the one with smallest slope.
00334 //
00335 double HomgOperator3D::CrossRatio(const Homg3D& a, const Homg3D& b, const Homg3D& c, const Homg3D& d)
00336 {
00337   double x1 = a.x(), y1 = a.y(), z1 = a.z(), w1 = a.w();
00338   double x2 = b.x(), y2 = b.y(), z2 = b.z(), w2 = b.w();
00339   double x3 = c.x(), y3 = c.y(), z3 = c.z(), w3 = c.w();
00340   double x4 = d.x(), y4 = d.y(), z4 = d.z(), w4 = d.w();
00341   double x = x1 - x2; if (x<0) x = -x; // assuming a != b ;-)
00342   double y = y1 - y2; if (y<0) y = -y;
00343   double z = z1 - z2; if (z<0) z = -z;
00344   double n = (x>y && x>z) ? (x1*w3-x3*w1)*(x2*w4-x4*w2) :
00345              (y>z)        ? (y1*w3-y3*w1)*(y2*w4-y4*w2) :
00346                             (z1*w3-z3*w1)*(z2*w4-z4*w2);
00347   double m = (x>y && x>z) ? (x1*w4-x4*w1)*(x2*w3-x3*w2) :
00348              (y>z)        ? (y1*w4-y4*w1)*(y2*w3-y3*w2) :
00349                             (z1*w4-z4*w1)*(z2*w3-z3*w2);
00350   if (n == 0 && m == 0)
00351     vcl_cerr << "CrossRatio not defined: three of the given points coincide\n";
00352   return n/m;
00353 }