core/vgl/algo/vgl_homg_operators_2d.txx
Go to the documentation of this file.
00001 // This is core/vgl/algo/vgl_homg_operators_2d.txx
00002 #ifndef vgl_homg_operators_2d_txx_
00003 #define vgl_homg_operators_2d_txx_
00004 //:
00005 // \file
00006 
00007 #include "vgl_homg_operators_2d.h"
00008 
00009 #include <vcl_iostream.h>
00010 #include <vcl_limits.h> // for infinity
00011 #include <vcl_cassert.h>
00012 #include <vcl_cmath.h> // for vcl_sqrt()
00013 #include <vgl/vgl_homg_line_2d.h>
00014 #include <vgl/vgl_homg_point_2d.h>
00015 #include <vgl/vgl_point_2d.h>
00016 #include <vgl/vgl_conic.h>
00017 #include <vgl/vgl_box_2d.h>
00018 #include <vgl/vgl_homg.h>
00019 
00020 #include <vnl/vnl_vector_fixed.h>
00021 #include <vnl/vnl_matrix.h>
00022 #include <vnl/vnl_matrix_fixed.h>
00023 #include <vnl/vnl_math.h>
00024 #include <vnl/algo/vnl_scatter_3x3.h> // used in most_orthogonal_vector()
00025 #include <vnl/algo/vnl_real_eigensystem.h> // used for conic intersection
00026 #include <vnl/vnl_diag_matrix.h>  // used for conic intersection
00027 
00028 //-----------------------------------------------------------------------------
00029 
00030 template <class T>
00031 inline static vgl_homg_line_2d<T> cross(const vgl_homg_point_2d<T>& p1,
00032                                         const vgl_homg_point_2d<T>& p2)
00033 {
00034   return vgl_homg_line_2d<T> (p1.y() * p2.w() - p1.w() * p2.y(),
00035                               p1.w() * p2.x() - p1.x() * p2.w(),
00036                               p1.x() * p2.y() - p1.y() * p2.x());
00037 }
00038 
00039 template <class T>
00040 inline static vgl_homg_point_2d<T> cross(const vgl_homg_line_2d<T>& l1,
00041                                          const vgl_homg_line_2d<T>& l2)
00042 {
00043   return vgl_homg_point_2d<T> (l1.b() * l2.c() - l1.c() * l2.b(),
00044                                l1.c() * l2.a() - l1.a() * l2.c(),
00045                                l1.a() * l2.b() - l1.b() * l2.a());
00046 }
00047 
00048 template <class T>
00049 inline static T dot(vgl_homg_line_2d<T> const& l,
00050                     vgl_homg_point_2d<T> const& p)
00051 {
00052   return l.a()*p.x() + l.b()*p.y() + l.c()*p.w();
00053 }
00054 
00055 //-----------------------------------------------------------------------------
00056 
00057 template <class T>
00058 vnl_vector_fixed<T,3> vgl_homg_operators_2d<T>::get_vector(vgl_homg_point_2d<T> const& p)
00059 {
00060   return vnl_vector_fixed<T,3>(p.x(),p.y(),p.w());
00061 }
00062 
00063 template <class T>
00064 vnl_vector_fixed<T,3> vgl_homg_operators_2d<T>::get_vector(vgl_homg_line_2d<T> const& l)
00065 {
00066   return vnl_vector_fixed<T,3>(l.a(),l.b(),l.c());
00067 }
00068 
00069 template <class T>
00070 vnl_vector_fixed<T,6> vgl_homg_operators_2d<T>::get_vector(vgl_conic<T> const& c)
00071 {
00072   vnl_vector_fixed<T,6> v;
00073   v.put(0,c.a());
00074   v.put(1,c.b());
00075   v.put(2,c.c());
00076   v.put(3,c.d());
00077   v.put(4,c.e());
00078   v.put(5,c.f());
00079 
00080   return v;
00081 }
00082 
00083 //-----------------------------------------------------------------------------
00084 //: Normalize vgl_homg_point_2d<T> to unit magnitude
00085 template <class T>
00086 void vgl_homg_operators_2d<T>::unitize(vgl_homg_point_2d<T>& a)
00087 {
00088   double norm = vcl_sqrt (a.x()*a.x() + a.y()*a.y() + a.w()*a.w());
00089 
00090   if (norm == 0.0) {
00091     vcl_cerr << "vgl_homg_operators_2d<T>::unitize() -- Zero length vector\n";
00092     return;
00093   }
00094   norm = 1.0/norm;
00095   a.set(T(a.x()*norm), T(a.y()*norm), T(a.w()*norm));
00096 }
00097 
00098 //  DISTANCE MEASUREMENTS IN EUCLIDEAN COORDINATES
00099 
00100 template <class T>
00101 T
00102 vgl_homg_operators_2d<T>::distance(const vgl_homg_point_2d<T>& point1,
00103                                    const vgl_homg_point_2d<T>& point2)
00104 {
00105   return vcl_sqrt(vgl_homg_operators_2d<T>::distance_squared(point1,point2));
00106 }
00107 
00108 template <class T>
00109 T
00110 vgl_homg_operators_2d<T>::distance_squared(const vgl_homg_point_2d<T>& p1,
00111                                            const vgl_homg_point_2d<T>& p2)
00112 {
00113   if (p1 == p2) return T(0); // quick return if possible
00114 
00115   if (p1.w() == 0 || p2.w() == 0) {
00116     vcl_cerr << "vgl_homg_operators_2d<T>::distance_squared() -- point at infinity\n";
00117     return vcl_numeric_limits<T>::infinity();
00118   }
00119 
00120   return vnl_math_sqr (p1.x() / p1.w() - p2.x() / p2.w()) +
00121          vnl_math_sqr (p1.y() / p1.w() - p2.y() / p2.w());
00122 }
00123 
00124 //: Get the square of the perpendicular distance to a line.
00125 // This is just the homogeneous form of the familiar
00126 // \f$ \frac{a x + b y + c}{\sqrt{a^2+b^2}} \f$ :
00127 // \f[ d = \frac{(l^\top p)}{p_z\sqrt{l_x^2 + l_y^2}} \f]
00128 // If either the point or the line are at infinity an error message is
00129 // printed and vgl_homg::infinity is returned.
00130 template <class T>
00131 T
00132 vgl_homg_operators_2d<T>::perp_dist_squared(const vgl_homg_point_2d<T>& point,
00133                                             const vgl_homg_line_2d<T>& line)
00134 {
00135   if ((line.a()==0 && line.b()== 0) || point.w()==0) {
00136     vcl_cerr << "vgl_homg_operators_2d<T>::perp_dist_squared() -- line or point at infinity\n";
00137     return vgl_homg<T>::infinity;
00138   }
00139 
00140   T numerator = vnl_math_sqr (dot(line,point) / point.w());
00141   if (numerator == 0) return T(0); // efficiency
00142   T denominator = line.a()*line.a() + line.b()*line.b();
00143 
00144   return numerator / denominator;
00145 }
00146 
00147 
00148 //  ANGLES
00149 
00150 //-----------------------------------------------------------------------------
00151 //: Get the anticlockwise angle between a line and the \a x axis.
00152 template <class T>
00153 double
00154 vgl_homg_operators_2d<T>::line_angle(const vgl_homg_line_2d<T>& line)
00155 {
00156   return vcl_atan2 (line.b(), line.a());
00157 }
00158 
00159 //-----------------------------------------------------------------------------
00160 //: Get the 0 to pi/2 angle between two lines
00161 template <class T>
00162 double
00163 vgl_homg_operators_2d<T>::abs_angle(const vgl_homg_line_2d<T>& line1,
00164                                     const vgl_homg_line_2d<T>& line2)
00165 {
00166   double angle1 = line_angle(line1);
00167   double angle2 = line_angle(line2);
00168 
00169   double diff = angle2 - angle1;
00170   if (diff >  vnl_math::pi_over_2) diff -= vnl_math::pi;
00171   if (diff < -vnl_math::pi_over_2) diff += vnl_math::pi;
00172 
00173   return vcl_fabs(diff);
00174 }
00175 
00176 //-----------------------------------------------------------------------------
00177 //
00178 //: Get the angle between two lines, a number between -PI and PI.
00179 // Although homogeneous coordinates are
00180 // only defined up to scale, here it is assumed that a line with homogeneous
00181 // coordinates (m) is at 180 degrees to a line (-m), and this is why the term
00182 // "oriented_line" is used.  However, the overall scale (apart from sign) is
00183 // not significant.
00184 template <class T>
00185 double
00186 vgl_homg_operators_2d<T>::angle_between_oriented_lines(const vgl_homg_line_2d<T>& line1,
00187                                                        const vgl_homg_line_2d<T>& line2)
00188 {
00189   return vcl_fmod(line_angle(line2)-line_angle(line1), 2*vnl_math::pi);
00190 }
00191 
00192 //  JOINS/INTERSECTIONS
00193 
00194 //-----------------------------------------------------------------------------
00195 //
00196 //: Get the line through two points (the cross-product).
00197 //
00198 
00199 template <class T>
00200 vgl_homg_line_2d<T>
00201 vgl_homg_operators_2d<T>::join(const vgl_homg_point_2d<T>& p1,
00202                                const vgl_homg_point_2d<T>& p2)
00203 {
00204   return cross(p1,p2);
00205 }
00206 
00207 //-----------------------------------------------------------------------------
00208 //
00209 //: Get the line through two points (the cross-product).
00210 // In this case, we assume that the points are oriented, and ensure the
00211 // cross is computed with positive point omegas.
00212 template <class T>
00213 vgl_homg_line_2d<T>
00214 vgl_homg_operators_2d<T>::join_oriented(const vgl_homg_point_2d<T>&p1,
00215                                         const vgl_homg_point_2d<T>&p2)
00216 {
00217   int s1 = p1.w() < 0;
00218   int s2 = p2.w() < 0;
00219 
00220   if (s1 ^ s2)
00221     return cross(p2,p1);
00222   else
00223     return cross(p1,p2);
00224 }
00225 
00226 //-----------------------------------------------------------------------------
00227 //
00228 //: Get the intersection point of two lines (the cross-product).
00229 template <class T>
00230 vgl_homg_point_2d<T>
00231 vgl_homg_operators_2d<T>::intersection(const vgl_homg_line_2d<T>& l1,
00232                                        const vgl_homg_line_2d<T>& l2)
00233 {
00234   return cross(l1,l2);
00235 }
00236 
00237 //-----------------------------------------------------------------------------
00238 //
00239 //: Get the perpendicular line to line which passes through point.
00240 // Params are line \f$(a,b,c)\f$ and point \f$(x,y,1)\f$.
00241 // Then the cross product of \f$(x,y,1)\f$ and the line's direction \f$(a,b,0)\f$,
00242 // called \f$(p,q,r)\f$ satisfies
00243 // - \f$ap+bq=0\f$ (perpendicular condition) and
00244 // - \f$px+qy+r=0\f$ (incidence condition).
00245 template <class T>
00246 vgl_homg_line_2d<T>
00247 vgl_homg_operators_2d<T>::perp_line_through_point(const vgl_homg_line_2d<T>& l,
00248                                                   const vgl_homg_point_2d<T>& p)
00249 {
00250   vgl_homg_point_2d<T> d(l.a(), l.b(), 0);
00251   return cross(d, p);
00252 }
00253 
00254 //-----------------------------------------------------------------------------
00255 //
00256 //: Get the perpendicular projection of point onto line.
00257 template <class T>
00258 vgl_homg_point_2d<T>
00259 vgl_homg_operators_2d<T>::perp_projection(const vgl_homg_line_2d<T>& line,
00260                                           const vgl_homg_point_2d<T>& point)
00261 {
00262   vgl_homg_line_2d<T> perpline = perp_line_through_point (line, point);
00263   vgl_homg_point_2d<T> answer = cross(line, perpline);
00264   return answer;
00265 }
00266 
00267 //: Return the midpoint of the line joining two homogeneous points
00268 //  When one of the points is at infinity, that point is returned.
00269 //  When both points are at infinity, the invalid point (0,0,0) is returned.
00270 template <class T>
00271 vgl_homg_point_2d<T>
00272 vgl_homg_operators_2d<T>::midpoint(const vgl_homg_point_2d<T>& p1,
00273                                    const vgl_homg_point_2d<T>& p2)
00274 {
00275   T x = p1.x() * p2.w() + p2.x() * p1.w();
00276   T y = p1.y() * p2.w() + p2.y() * p1.w();
00277   T w = p1.w() * p2.w() + p2.w() * p1.w();
00278 
00279   return vgl_homg_point_2d<T>(x,y,w);
00280 }
00281 
00282 //  FITTING
00283 
00284 // - Kanatani sect 2.2.2.
00285 template <class T>
00286 vnl_vector_fixed<T,3>
00287 vgl_homg_operators_2d<T>::most_orthogonal_vector(const vcl_vector<vgl_homg_line_2d<T> >& inpoints)
00288 {
00289   vnl_scatter_3x3<T> scatter_matrix;
00290 
00291   for (typename vcl_vector<vgl_homg_line_2d<T> >::const_iterator i = inpoints.begin();
00292        i != inpoints.end(); ++i)
00293     scatter_matrix.add_outer_product(get_vector(*i));
00294 
00295   return scatter_matrix.minimum_eigenvector();
00296 }
00297 
00298 #include <vnl/algo/vnl_svd.h>
00299 
00300 template <class T>
00301 vnl_vector_fixed<T,3>
00302 vgl_homg_operators_2d<T>::most_orthogonal_vector_svd(const vcl_vector<vgl_homg_line_2d<T> >& lines)
00303 {
00304   vnl_matrix<T> D(lines.size(), 3);
00305 
00306   typename vcl_vector<vgl_homg_line_2d<T> >::const_iterator i = lines.begin();
00307   for (unsigned j = 0; i != lines.end(); ++i,++j)
00308     D.set_row(j, get_vector(*i).as_ref());
00309 
00310   vnl_svd<T> svd(D);
00311 #ifdef DEBUG
00312   vcl_cout << "[movrank " << svd.W() << ']';
00313 #endif
00314 
00315   return svd.nullvector();
00316 }
00317 
00318 //: Intersect a set of 2D lines to find the least-square point of intersection.
00319 // This finds the point $\bf x$ that minimizes $\|\tt L \bf x\|$, where $\tt L$
00320 // is the matrix whose rows are the lines. The implementation uses either
00321 // vnl_scatter_3x3 (default) or vnl_svd (when at compile time
00322 // VGL_HOMG_OPERATORS_2D_LINES_TO_POINT_USE_SVD has been set) to accumulate and
00323 // compute the nullspace of $\tt L^\top \tt L$.
00324 template <class T>
00325 vgl_homg_point_2d<T>
00326 vgl_homg_operators_2d<T>::lines_to_point(const vcl_vector<vgl_homg_line_2d<T> >& lines)
00327 {
00328   // ho_triveccam_aspect_lines_to_point
00329   assert(lines.size() >= 2);
00330 
00331 #ifdef VGL_HOMG_OPERATORS_2D_LINES_TO_POINT_USE_SVD
00332   vnl_vector_fixed<T,3> mov = most_orthogonal_vector_svd(lines);
00333 #else
00334   vnl_vector_fixed<T,3> mov = most_orthogonal_vector(lines);
00335 #endif
00336   return vgl_homg_point_2d<T>(mov[0], mov[1], mov[2]);
00337 }
00338 
00339 //  MISCELLANEOUS
00340 
00341 
00342 //-----------------------------------------------------------------------------
00343 //: Calculates the crossratio of four collinear points p1, p2, p3 and p4.
00344 // This number is projectively invariant, and it is the coordinate of p4
00345 // in the reference frame where p2 is the origin (coordinate 0), p3 is
00346 // the unity (coordinate 1) and p1 is the point at infinity.
00347 // This cross ratio is often denoted as ((p1, p2; p3, p4)) (which also
00348 // equals ((p3, p4; p1, p2)) or ((p2, p1; p4, p3)) or ((p4, p3; p2, p1)) )
00349 // and is calculated as
00350 // \verbatim
00351 //                      p1 - p3   p2 - p3      (p1-p3)(p2-p4)
00352 //                      ------- : --------  =  --------------
00353 //                      p1 - p4   p2 - p4      (p1-p4)(p2-p3)
00354 // \endverbatim
00355 //
00356 // In principle, any single nonhomogeneous coordinate from the four points
00357 // can be used as parameters for cross_ratio (but of course the same for all
00358 // points). The most reliable answer will be obtained when the coordinate with
00359 // the largest spacing is used, i.e., the one with smallest slope.
00360 template <class T>
00361 double vgl_homg_operators_2d<T>::cross_ratio(const vgl_homg_point_2d<T>& a,
00362                                              const vgl_homg_point_2d<T>& b,
00363                                              const vgl_homg_point_2d<T>& c,
00364                                              const vgl_homg_point_2d<T>& d)
00365 {
00366   double x1 = a.x(), y1 = a.y(), w1 = a.w();
00367   double x2 = b.x(), y2 = b.y(), w2 = b.w();
00368   double x3 = c.x(), y3 = c.y(), w3 = c.w();
00369   double x4 = d.x(), y4 = d.y(), w4 = d.w();
00370   double x = x1 - x2; if (x<0) x = -x; // assuming a != b ;-)
00371   double y = y1 - y2; if (y<0) y = -y;
00372   double n = (x>y) ? (x1*w3-x3*w1)*(x2*w4-x4*w2) : (y1*w3-y3*w1)*(y2*w4-y4*w2);
00373   double m = (x>y) ? (x1*w4-x4*w1)*(x2*w3-x3*w2) : (y1*w4-y4*w1)*(y2*w3-y3*w2);
00374   if (n == 0 && m == 0)
00375     vcl_cerr << "cross ratio not defined: three of the given points coincide\n";
00376   return n/m;
00377 }
00378 
00379 //: Conjugate point of three given collinear points.
00380 // If cross ratio cr is given (default: -1), the generalized conjugate point
00381 // returned is such that ((x1,x2;x3,answer)) = cr.
00382 template <class T>
00383 vgl_homg_point_2d<T>
00384 vgl_homg_operators_2d<T>::conjugate(const vgl_homg_point_2d<T>& a,
00385                                     const vgl_homg_point_2d<T>& b,
00386                                     const vgl_homg_point_2d<T>& c,
00387                                     double cr)
00388 // Default for cr is -1.
00389 {
00390   T x1 = a.x(), y1 = a.y(), w1 = a.w();
00391   T x2 = b.x(), y2 = b.y(), w2 = b.w();
00392   T x3 = c.x(), y3 = c.y(), w3 = c.w();
00393   T kx = x1*w3 - x3*w1, mx = x2*w3 - x3*w2, nx = T(kx*w2-cr*mx*w1);
00394   T ky = y1*w3 - y3*w1, my = y2*w3 - y3*w2, ny = T(ky*w2-cr*my*w1);
00395   return vgl_homg_point_2d<T>(T(x2*kx-cr*x1*mx)*ny,T(y2*ky-cr*y1*my)*nx,nx*ny);
00396 }
00397 
00398 //: returns the vgl_conic which has the given matrix as its matrix
00399 // \verbatim
00400 // [A,B,C,D,E,F] <-   [ A  B/2 D/2 ]
00401 //                    [ B/2 C  E/2 ]
00402 //                    [ D/2 E/2 F  ]
00403 // \endverbatim
00404 template <class T>
00405 vgl_conic<T>
00406 vgl_homg_operators_2d<T>::vgl_conic_from_matrix(vnl_matrix_fixed<T,3,3> const& mat)
00407 {
00408   return vgl_conic<T>(mat[0][0], mat[1][0]+mat[0][1], mat[1][1], mat[0][2]+mat[2][0], mat[1][2]+mat[2][1], mat[2][2]);
00409 }
00410 
00411 //: returns 3x3 matrix containing conic coefficients.
00412 // \verbatim
00413 // [A,B,C,D,E,F] ->   [ A  B/2 D/2 ]
00414 //                    [ B/2 C  E/2 ]
00415 //                    [ D/2 E/2 F  ]
00416 // \endverbatim
00417 //
00418 template <class T>
00419 vnl_matrix_fixed<T,3,3>
00420 vgl_homg_operators_2d<T>::matrix_from_conic(vgl_conic<T> const& c)
00421 {
00422   vnl_matrix_fixed<T,3,3> mat;
00423   T A = c.a(), B = c.b()/2, C = c.c(), D = c.d()/2, E = c.e()/2, F = c.f();
00424 
00425   mat[0][0] = A; mat[0][1] = B; mat[0][2] = D;
00426   mat[1][0] = B; mat[1][1] = C; mat[1][2] = E;
00427   mat[2][0] = D; mat[2][1] = E; mat[2][2] = F;
00428 
00429   return mat;
00430 }
00431 
00432 
00433 //-------------------------------------------------------------------------
00434 //: returns 3x3 matrix containing conic coefficients of dual conic.
00435 // I.e., the inverse matrix (up to a scale factor) of the conic matrix.
00436 
00437 template <class T>
00438 vnl_matrix_fixed<T,3,3>
00439 vgl_homg_operators_2d<T>::matrix_from_dual_conic(vgl_conic<T> const& c)
00440 {
00441   vnl_matrix_fixed<T,3,3> mat;
00442   T A = c.a(), B = c.b()/2, C = c.c(), D = c.d()/2, E = c.e()/2, F = c.f();
00443 
00444   mat[0][0] = C*F-E*E; mat[0][1] = E*D-B*F; mat[0][2] = B*E-C*D;
00445   mat[1][0] = E*D-B*F; mat[1][1] = A*F-D*D; mat[1][2] = B*D-A*E;
00446   mat[2][0] = B*E-C*D; mat[2][1] = B*D-A*E; mat[2][2] = A*C-B*B;
00447 
00448   return mat;
00449 }
00450 
00451 // This function is called from within intersection(vgl_conic<T>,vgl_conic<T>).
00452 // The two conics passed to this function MUST NOT be degenerate!
00453 template <class T>
00454 vcl_list<vgl_homg_point_2d<T> >
00455 vgl_homg_operators_2d<T>::do_intersect(vgl_conic<T> const& c1,
00456                                        vgl_conic<T> const& c2)
00457 {
00458   if (c1==c2)
00459   {
00460     vcl_cerr << __FILE__
00461              << "Warning: the intersection of two identical conics is not a finite set of points.\n"
00462              << "Returning an empty list.\n";
00463     return vcl_list<vgl_homg_point_2d<T> >();
00464   }
00465   T A=c1.a(),B=c1.b(),C=c1.c(),D=c1.d(),E=c1.e(),F=c1.f();
00466   T a=c2.a(),b=c2.b(),c=c2.c(),d=c2.d(),e=c2.e(),f=c2.f();
00467   // Idea: eliminate the coefficient in x^2, solve for x in terms of y, resubstitute in the other equation.
00468   T ab=a*B-A*b, ac=a*C-A*c, ad=a*D-A*d, ae=a*E-A*e, af=a*F-A*f, BD=b*D+B*d;
00469 
00470   // If the quadratic parts of the two conics are identical (up to scale factor),
00471   // the intersection is the same as with a degenerate conic where one part is the line at infinity,
00472   // viz. the conic with as equation the (weighted) difference of the two equations:
00473   if (ab==0 && ac==0)
00474     return intersection(c1,vgl_conic<T>(0,0,0,ad,ae,af));
00475 
00476   // If the parts without x of the two conics are identical (up to scale factor),
00477   // the intersection is the same as with a degenerate conic consisting of two vertical lines:
00478   if (ab==0 && ad==0)
00479     return intersection(c1,vgl_conic<T>(0,0,ac,0,ae,af));
00480 
00481   // And of course idem for y:
00482   if (ab==0 && ae==0)
00483     return intersection(c1,vgl_conic<T>(0,0,ac,ad,0,af));
00484 
00485   vnl_vector_fixed<T,5> coef;
00486   coef(0) = ac*ac-ab*(b*C-B*c);
00487   coef(1) = 2*ac*ae-ab*(b*E-B*e)-BD*(a*C+A*c)+2*A*b*C*d+2*a*B*c*D;
00488   coef(2) = ae*ae-ab*(b*F-B*f)+ad*(c*D-C*d)-BD*(a*E+A*e)+2*a*B*e*D+2*A*b*E*d+2*ac*af;
00489   coef(3) = 2*ae*af-ad*(d*E-D*e)-BD*(a*F+A*f)+2*A*b*d*F+2*a*B*D*f;
00490   coef(4) = af*af-ad*(d*F-D*f);
00491 
00492   // Solutions of the fourth order equation
00493   //   4      3      2
00494   //  y  +  py  +  qy  +  ry  +  s  =  0
00495   // are the eigenvalues of the matrix
00496   // [ -p   -q   -r   -s ]
00497   // [  1    0    0    0 ]
00498   // [  0    1    0    0 ]
00499   // [  0    0    1    0 ]
00500 
00501   if (coef(0) == 0 && coef(1) == 0) {
00502     if (coef(2) == 0 && coef(3) == 0) return vcl_list<vgl_homg_point_2d<T> >(); // no real solutions.
00503     T dis = coef(3)*coef(3)-4*coef(2)*coef(4); // discriminant
00504     if (dis < 0) return vcl_list<vgl_homg_point_2d<T> >(); // no real solutions.
00505     T y;
00506     if (coef(2) == 0) dis=0, y = - coef(4) / coef(3);
00507     else                     y = - coef(3) / coef(2) / 2;
00508     T w = y*ab+ad;
00509     T x = -(y*y*ac+y*ae+af);
00510     if (x == 0 && w == 0) x = w = 1;
00511     if (dis == 0) {return vcl_list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(x,y*w,w));}
00512     dis = vcl_sqrt(dis) / coef(2) / 2;
00513     vcl_list<vgl_homg_point_2d<T> > solutions;
00514     y -= dis; w = y*ab+ad; x = -(y*y*ac+y*ae+af);
00515     if (x == 0 && w == 0) x = w = 1;
00516     solutions.push_back(vgl_homg_point_2d<T>(x,y*w,w));
00517     y += 2*dis; w = y*ab+ad; x = -(y*y*ac+y*ae+af);
00518     if (x == 0 && w == 0) x = w = 1;
00519     solutions.push_back(vgl_homg_point_2d<T>(x,y*w,w));
00520     return solutions;
00521   }
00522   if (coef(0) == 0) {
00523     coef /= -coef(1);
00524     double data[]={coef(2),coef(3),coef(4), 1,0,0, 0,1,0};
00525     vnl_matrix<double> M(data,3,3);
00526     vnl_real_eigensystem eig(M);
00527     vnl_diag_matrix<vcl_complex<double> >  polysolutions = eig.D;
00528     vcl_list<vgl_homg_point_2d<T> > solutions;
00529     for (int i=0;i<3;i++)
00530       if (vcl_abs(vcl_imag(polysolutions(i))) < 1e-3) {// only want the real solutions
00531         T y = (T)vcl_real(polysolutions(i));
00532         T w = y*ab+ad;
00533         T x = -(y*y*ac+y*ae+af);
00534         if (x == 0 && w == 0) x = w = 1;
00535         solutions.push_back(vgl_homg_point_2d<T>(x,y*w,w));
00536       }
00537     return solutions;
00538   }
00539 
00540   coef /= -coef(0);
00541   double data[]={coef(1),coef(2),coef(3),coef(4), 1,0,0,0, 0,1,0,0, 0,0,1,0};
00542   vnl_matrix<double> M(data,4,4);
00543   vnl_real_eigensystem eig(M);
00544 
00545   vnl_diag_matrix<vcl_complex<double> >  polysolutions = eig.D;
00546 
00547   // These are only the solutions of the fourth order equation.
00548   // The solutions of the intersection of the two conics are:
00549 
00550   vcl_list<vgl_homg_point_2d<T> > solutions;
00551 
00552   for (int i=0;i<4;i++)
00553     if (vcl_abs(vcl_imag(polysolutions(i))) < 1e-7) { // only want the real solutions
00554       T y = (T)vcl_real(polysolutions(i));
00555       T w = y*ab+ad;
00556       T x = -(y*y*ac+y*ae+af);
00557       if (x == 0 && w == 0) x = w = 1;
00558       solutions.push_back(vgl_homg_point_2d<T>(x,y*w,w));
00559     }
00560   return solutions;
00561 }
00562 
00563 // This function is called from within intersection(vgl_conic<T>,vgl_homg_line_2d<T>).
00564 // The conic passed to this function MUST NOT be degenerate!
00565 template <class T>
00566 vcl_list<vgl_homg_point_2d<T> >
00567 vgl_homg_operators_2d<T>::do_intersect(vgl_conic<T> const& q,
00568                                        vgl_homg_line_2d<T> const& l)
00569 {
00570   T A=q.a(), B=q.b(), C=q.c(), D=q.d(), E=q.e(), F=q.f();
00571   T a=l.a(), b=l.b(), c=l.c();
00572 
00573   if (a==0 && b==0) { // line at infinity
00574     if (A==0)
00575       return vcl_list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(1,0,0));
00576     T d = B*B-4*A*C; // discriminant
00577     if (d < 0) return vcl_list<vgl_homg_point_2d<T> >(); // no solutions
00578     if (d == 0)
00579       return vcl_list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(-B,2*A,0));
00580     d = vcl_sqrt(d);
00581     vcl_list<vgl_homg_point_2d<T> > v(1, vgl_homg_point_2d<T>(-B+d, 2*A, 0));
00582     v.push_back(vgl_homg_point_2d<T>(-B-d, 2*A, 0));
00583     return v;
00584   }
00585   if (a==0) { // write y in terms of w and solve for (x,w)
00586     T y = -c/b; B = B*y+D;
00587     T d = B*B-4*A*(C*y*y+E*y+F); // discriminant
00588     if (d < 0) return vcl_list<vgl_homg_point_2d<T> >(); // no solutions
00589     if (d == 0 && A == 0)
00590       return vcl_list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(1,0,0));
00591     if (d == 0)
00592       return vcl_list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(-B,y*2*A,2*A));
00593     if (A == 0) {
00594       vcl_list<vgl_homg_point_2d<T> > v(1, vgl_homg_point_2d<T>(1,0,0));
00595       v.push_back(vgl_homg_point_2d<T>(C*y*y+E*y+F, -y*B, -B));
00596       return v;
00597     }
00598     d = vcl_sqrt(d);
00599     vcl_list<vgl_homg_point_2d<T> > v(1, vgl_homg_point_2d<T>(-B+d, y*2*A, 2*A));
00600     v.push_back(vgl_homg_point_2d<T>(-B-d, y*2*A, 2*A));
00601     return v;
00602   }
00603   b /= -a; c /= -a; // now x = b*y+c*w.
00604   T AA = A*b*b+B*b+C;
00605   B = 2*A*b*c+B*c+D*b+E;
00606   T d = B*B-4*AA*(A*c*c+D*c+F); // discriminant
00607   if (d < 0) return vcl_list<vgl_homg_point_2d<T> >(); // no solutions
00608   if (d == 0 && AA == 0)
00609     return vcl_list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(b,1,0));
00610   if (d == 0)
00611     return vcl_list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(c*2*AA-b*B,-B,2*AA));
00612   if (AA == 0) {
00613     vcl_list<vgl_homg_point_2d<T> > v(1, vgl_homg_point_2d<T>(b,1,0));
00614     v.push_back(vgl_homg_point_2d<T>(b*A*c*c+b*D*c+b*F-c*B, A*c*c+D*c+F, -B));
00615     return v;
00616   }
00617   d = vcl_sqrt(d);
00618   vcl_list<vgl_homg_point_2d<T> > v(1, vgl_homg_point_2d<T>(c*2*AA-b*B+b*d, -B+d, 2*AA));
00619   v.push_back(vgl_homg_point_2d<T>(c*2*AA-b*B-b*d, -B-d, 2*AA));
00620   return v;
00621 }
00622 
00623 //: Return the (real) intersection points of a conic and a line.
00624 template <class T>
00625 vcl_list<vgl_homg_point_2d<T> >
00626 vgl_homg_operators_2d<T>::intersection(vgl_conic<T> const& c,
00627                                        vgl_homg_line_2d<T> const& l)
00628 {
00629   if (c.type()==vgl_conic<T>::no_type ||
00630       c.type()==vgl_conic<T>::complex_parallel_lines ||
00631       c.type()==vgl_conic<T>::complex_intersecting_lines ||
00632       c.type()==vgl_conic<T>::imaginary_ellipse ||
00633       c.type()==vgl_conic<T>::imaginary_circle)
00634     return vcl_list<vgl_homg_point_2d<T> >(); // empty list
00635   // let's hope the intersection point of the two complex lines is not on the line..
00636 
00637   if (c.type() == vgl_conic<T>::coincident_lines) {
00638     vgl_homg_point_2d<T> p = intersection(l, c.components().front());
00639     vcl_list<vgl_homg_point_2d<T> > list(2, p); // intersection is *two* coincident points
00640     return list;
00641   }
00642 
00643   if (c.type() == vgl_conic<T>::real_intersecting_lines || c.type() == vgl_conic<T>::real_parallel_lines) {
00644     vcl_list<vgl_homg_point_2d<T> > list;
00645     list.push_back(intersection(l, c.components().front()));
00646     list.push_back(intersection(l, c.components().back()));
00647     return list;
00648   }
00649   return do_intersect(c, l);
00650 }
00651 
00652 //: Return the (real, finite) intersection points of two conics.
00653 // The returned points have protection level 0.
00654 template <class T>
00655 vcl_list<vgl_homg_point_2d<T> >
00656 vgl_homg_operators_2d<T>::intersection(vgl_conic<T> const& c1, vgl_conic<T> const& c2)
00657 {
00658   if ((c1.type()==vgl_conic<T>::complex_parallel_lines ||
00659        c1.type()==vgl_conic<T>::complex_intersecting_lines)
00660       && c2.contains(c1.centre()))
00661     return vcl_list<vgl_homg_point_2d<T> >(2, c1.centre()); // double intersection point
00662   if ((c2.type()==vgl_conic<T>::complex_parallel_lines ||
00663        c2.type()==vgl_conic<T>::complex_intersecting_lines)
00664       && c1.contains(c2.centre()))
00665     return vcl_list<vgl_homg_point_2d<T> >(2, c2.centre()); // double intersection point
00666   if (c1.type() == vgl_conic<T>::no_type   ||  c2.type() == vgl_conic<T>::no_type ||
00667       c1.type()==vgl_conic<T>::complex_parallel_lines||c2.type()==vgl_conic<T>::complex_parallel_lines ||
00668       c1.type()==vgl_conic<T>::complex_intersecting_lines||c2.type()==vgl_conic<T>::complex_intersecting_lines ||
00669       c1.type() == vgl_conic<T>::imaginary_ellipse|| c2.type() == vgl_conic<T>::imaginary_ellipse ||
00670       c1.type() == vgl_conic<T>::imaginary_circle || c2.type() == vgl_conic<T>::imaginary_circle)
00671     return vcl_list<vgl_homg_point_2d<T> >(); // empty list
00672 
00673   if (c1.type() == vgl_conic<T>::coincident_lines ||
00674       c1.type() == vgl_conic<T>::real_intersecting_lines ||
00675       c1.type() == vgl_conic<T>::real_parallel_lines) {
00676     vcl_list<vgl_homg_point_2d<T> > l1=intersection(c2,c1.components().front());
00677     vcl_list<vgl_homg_point_2d<T> > l2=intersection(c2,c1.components().back());
00678     l1.insert(l1.end(), l2.begin(), l2.end()); // append l2 to l1
00679     return l1;
00680   }
00681 
00682   if (c2.type() == vgl_conic<T>::coincident_lines ||
00683       c2.type() == vgl_conic<T>::real_intersecting_lines ||
00684       c2.type() == vgl_conic<T>::real_parallel_lines) {
00685     vcl_list<vgl_homg_point_2d<T> > l1=intersection(c1,c2.components().front());
00686     vcl_list<vgl_homg_point_2d<T> > l2=intersection(c1,c2.components().back());
00687     l1.insert(l1.end(), l2.begin(), l2.end()); // append l2 to l1
00688     return l1;
00689   }
00690 
00691   return do_intersect(c1, c2);
00692 }
00693 
00694 //: Return the (at most) two tangent lines that pass through p and are tangent to the conic.
00695 // For points on the conic, exactly 1 line is returned: the tangent at that point.
00696 // For points inside the conic, an empty list is returned.
00697 // For points outside the conic, there are always two tangents returned.
00698 // Found by intersecting the dual conic with the dual line.
00699 // If the conic is degenerate, an empty list is returned, unless the point is
00700 // on the conic (in which case the component is returned to which it belongs,
00701 // or even both components in the exclusive case that the point is the centre).
00702 template <class T>
00703 vcl_list<vgl_homg_line_2d<T> >
00704 vgl_homg_operators_2d<T>::tangent_from(vgl_conic<T> const& c,
00705                                        vgl_homg_point_2d<T> const& p)
00706 {
00707   if (c.is_degenerate()) {
00708     if (!c.contains(p)) return vcl_list<vgl_homg_line_2d<T> >();
00709     vcl_list<vgl_homg_line_2d<T> > v = c.components();
00710     if (c.type() == vgl_conic<T>::coincident_lines || p == c.centre())
00711       return v;
00712     if (v.size() > 0 && dot(v.front(),p) == 0)
00713       return vcl_list<vgl_homg_line_2d<T> >(1,v.front());
00714     else if (v.size() > 1 && dot(v.back(),p) == 0)
00715       return vcl_list<vgl_homg_line_2d<T> >(1,v.back());
00716     else
00717       return vcl_list<vgl_homg_line_2d<T> >();
00718   }
00719 
00720   vgl_conic<T> C = c.dual_conic();
00721   vgl_homg_line_2d<T>  l(p.x(),p.y(),p.w()); // dual line
00722   vcl_list<vgl_homg_point_2d<T> > dualpts = intersection(C,l);
00723   vcl_list<vgl_homg_line_2d<T> > v;
00724   typename vcl_list<vgl_homg_point_2d<T> >::iterator it = dualpts.begin();
00725   for (; !(it == dualpts.end()); ++it)
00726     v.push_back(vgl_homg_line_2d<T>((*it).x(), (*it).y(), (*it).w()));
00727   return v;
00728 }
00729 
00730 //: Return the list of common tangent lines of two conics.
00731 // This is done by finding the intersection points of the two dual conics,
00732 // which are the duals of the common tangent lines.
00733 // If one of the conics is degenerate, an empty list is returned.
00734 template <class T>
00735 vcl_list<vgl_homg_line_2d<T> >
00736 vgl_homg_operators_2d<T>::common_tangents(vgl_conic<T> const& c1,
00737                                           vgl_conic<T> const& c2)
00738 {
00739   if ((c1.type() != vgl_conic<T>::parabola && ! c1.is_central())  ||
00740       (c2.type() != vgl_conic<T>::parabola && ! c2.is_central()))
00741     return vcl_list<vgl_homg_line_2d<T> >();//empty list: degenerate conic has no dual
00742 
00743   vgl_conic<T> C1 = c1.dual_conic();
00744   vgl_conic<T> C2 = c2.dual_conic();
00745   vcl_list<vgl_homg_point_2d<T> > dualpts = intersection(C1,C2);
00746   vcl_list<vgl_homg_line_2d<T> > v;
00747   typename vcl_list<vgl_homg_point_2d<T> >::iterator it = dualpts.begin();
00748   for (; !(it == dualpts.end()); ++it)
00749     v.push_back(vgl_homg_line_2d<T>((*it).x(), (*it).y(), (*it).w()));
00750   return v;
00751 }
00752 
00753 //: Return the point on the conic closest to the given point
00754 template <class T>
00755 vgl_homg_point_2d<T>
00756 vgl_homg_operators_2d<T>::closest_point(vgl_conic<T> const& c,
00757                                         vgl_homg_point_2d<T> const& pt)
00758 {
00759   // First check if the point is on the curve, since this simplifies things:
00760   if (c.contains(pt)) return pt;
00761 
00762   // The nearest point must have a polar line which is orthogonal to its
00763   // connection line with the given point; all points with this property form
00764   // a certain conic  (actually an orthogonal hyperbola) :
00765   vcl_list<vgl_homg_point_2d<T> > candidates; // all intersection points
00766   if (pt.w() == 0) // given point is at infinity
00767   {
00768     // ==> degenerate hyperbola: line + line at infinity
00769     vgl_homg_line_2d<T> l(c.a()*pt.y()*2-c.b()*pt.x(),
00770                          -c.c()*pt.x()*2+c.b()*pt.y(),
00771                           c.d()*pt.y()  -c.e()*pt.x());
00772     candidates = intersection(c, l);
00773     if (candidates.size() == 0)
00774       return vgl_homg_point_2d<T>(0,0,0); // this cannot happen
00775     // just return any of the candidates, since distance makes no sense at infinity:
00776     else return candidates.front();
00777   }
00778   else if (c.b()==0 && c.a()==c.c()) // the given conic is a circle
00779   {
00780     // ==> degenerate hyperbola: line thru centre & point  +  line at infinity
00781     vgl_homg_point_2d<T> centre = c.centre();
00782     if (centre == pt) // in this case, any point of the circle is all right
00783       centre = vgl_homg_point_2d<T>(1,0,0); // take a horizontal line thru pt
00784     candidates = intersection(c, vgl_homg_line_2d<T>(centre,pt));
00785   }
00786   else // general case:
00787   {
00788     vgl_conic<T> conic(pt.w()*c.b(),
00789                        pt.w()*(c.c()-c.a())*2,
00790                       -pt.w()*c.b(),
00791                        pt.y()*c.a()*2-pt.x()*c.b()+pt.w()*c.e(),
00792                       -pt.x()*c.c()*2+pt.y()*c.b()-pt.w()*c.d(),
00793                        pt.y()*c.d()  -pt.x()*c.e());
00794     // Now it suffices to intersect the hyperbola with the given conic:
00795     candidates = intersection(c, conic);
00796   }
00797   if (candidates.size() == 0)
00798   {
00799     vcl_cerr << "Warning: vgl_homg_operators_2d<T>::closest_point: no intersection\n";
00800     return vgl_homg_point_2d<T>(0,0,0);
00801   }
00802 
00803   // And find the intersection point closest to the given location:
00804   typename vcl_list<vgl_homg_point_2d<T> >::iterator it = candidates.begin();
00805   vgl_homg_point_2d<T> p = (*it);
00806   T dist = vgl_homg_operators_2d<T>::distance_squared(*it,pt);
00807   for (++it; it != candidates.end(); ++it) {
00808     if ((*it).w() == 0) continue;
00809     T d = vgl_homg_operators_2d<T>::distance_squared(*it,pt);
00810     if (d < dist) { p = (*it); dist = d; }
00811   }
00812   return p;
00813 }
00814 
00815 //: Return the point on the conic closest to the given point.
00816 //  Still return a homogeneous point, even if the argument is non-homogeneous.
00817 template <class T>
00818 vgl_homg_point_2d<T>
00819 vgl_homg_operators_2d<T>::closest_point(vgl_conic<T> const& c,
00820                                         vgl_point_2d<T> const& pt)
00821 {
00822   return closest_point(c,vgl_homg_point_2d<T>(pt));
00823 }
00824 
00825 //: Compute the bounding box of an ellipse
00826 // This is done by finding the tangent lines to the ellipse from the two points
00827 // at infinity (1,0,0) and (0,1,0).
00828 template <class T>
00829 vgl_box_2d<T>
00830 vgl_homg_operators_2d<T>::compute_bounding_box(vgl_conic<T> const& c)
00831 {
00832   // Only ellipses have a finite bounding box:
00833 
00834   if (c.real_type() == "complex intersecting lines") { // a single point:
00835     vgl_homg_point_2d<T> pt = c.centre();
00836     return vgl_box_2d<T>(vgl_point_2d<T>(pt), vgl_point_2d<T>(pt));
00837   }
00838 
00839   if (c.real_type() == "invalid conic" ||
00840       c.real_type() == "imaginary ellipse" ||
00841       c.real_type() == "imaginary circle" ||
00842       c.real_type() == "complex parallel lines")
00843     return vgl_box_2d<T>(); // empty box
00844 
00845   if (c.real_type() == "real parallel lines" ||
00846       c.real_type() == "coincident lines")
00847   {
00848     // find out if these lines happen to be horizontal or vertical
00849     vcl_list<vgl_homg_line_2d<T> > l = c.components();
00850     if (l.front().a() == 0) // horizontal lines
00851       return vgl_box_2d<T>(vgl_point_2d<T>(T(1e33),-l.front().c()/l.front().b()),
00852                            vgl_point_2d<T>(T(-1e33),-l.back().c()/l.back().b()));
00853     if (l.front().b() == 0) // vertical lines
00854       return vgl_box_2d<T>(vgl_point_2d<T>(-l.front().c()/l.front().b(),T(1e33)),
00855                            vgl_point_2d<T>(-l.back().c()/l.back().b(),T(-1e33)));
00856     // if not, go to the general case, i.e., return "everything".
00857   }
00858 
00859   if (c.real_type() != "real ellipse" && c.real_type() != "real circle")
00860     return vgl_box_2d<T>(T(-1e33), T(1e33), T(-1e33), T(1e33)); // everything
00861 
00862   // Now for the ellipses:
00863 
00864   vgl_homg_point_2d<T> px (1,0,0); // point at infinity of the X axis
00865   vgl_homg_point_2d<T> py (0,1,0); // point at infinity of the Y axis
00866 
00867   vcl_list<vgl_homg_line_2d<T> > lx = vgl_homg_operators_2d<T>::tangent_from(c, px);
00868   vcl_list<vgl_homg_line_2d<T> > ly = vgl_homg_operators_2d<T>::tangent_from(c, py);
00869 
00870   T y1 = - lx.front().c() / lx.front().b(); // lx are two horizontal lines
00871   T y2 = - lx.back().c() / lx.back().b();
00872   if (y1 > y2) { T t = y1; y1 = y2; y2 = t; }
00873   T x1 = - ly.front().c() / ly.front().a(); // ly are two vertical lines
00874   T x2 = - ly.back().c() / ly.back().a();
00875   if (x1 > x2) { T t = x1; x1 = x2; x2 = t; }
00876 
00877   return vgl_box_2d<T>(x1, x2, y1, y2);
00878 }
00879 
00880 //: Transform a point through a 3x3 projective transformation matrix
00881 // \relatesalso vgl_homg_point_2d
00882 template <class T>
00883 vgl_homg_point_2d<T> operator*(vnl_matrix_fixed<T,3,3> const& m,
00884                                vgl_homg_point_2d<T> const& p)
00885 {
00886   return vgl_homg_point_2d<T>(m(0,0)*p.x()+m(0,1)*p.y()+m(0,2)*p.w(),
00887                               m(1,0)*p.x()+m(1,1)*p.y()+m(1,2)*p.w(),
00888                               m(2,0)*p.x()+m(2,1)*p.y()+m(2,2)*p.w());
00889 }
00890 
00891 //: Transform a line through a 3x3 projective transformation matrix
00892 // \relatesalso vgl_homg_line_2d
00893 template <class T>
00894 vgl_homg_line_2d<T> operator*(vnl_matrix_fixed<T,3,3> const& m,
00895                               vgl_homg_line_2d<T> const& l)
00896 {
00897   return vgl_homg_line_2d<T>(m(0,0)*l.a()+m(0,1)*l.b()+m(0,2)*l.c(),
00898                              m(1,0)*l.a()+m(1,1)*l.b()+m(1,2)*l.c(),
00899                              m(2,0)*l.a()+m(2,1)*l.b()+m(2,2)*l.c());
00900 }
00901 
00902 //: Return the point on the line closest to the given point
00903 template <class T>
00904 vgl_homg_point_2d<T> vgl_homg_operators_2d<T>::closest_point(vgl_homg_line_2d<T> const& l,
00905                                                              vgl_homg_point_2d<T> const& p)
00906 {
00907   // Return p itself, if p lies on l:
00908   if (l.a()*p.x()+l.b()*p.y()+l.c()*p.w() == 0) return p;
00909   // Otherwise, make sure that both l and p are not at infinity:
00910   assert(!l.ideal()); // should not be the line at infinity
00911   // Line othogonal to l and through p is  bx-ay+d=0, with d = (a*py-b*px)/pw.
00912   vgl_homg_line_2d<T> o(l.b()*p.w(), -l.a()*p.w(), l.a()*p.y()-l.b()*p.x());
00913   // Finally return the intersection point of l with this orthogonal line:
00914   return vgl_homg_operators_2d<T>::intersection(l,o);
00915 }
00916 
00917 #undef VGL_HOMG_OPERATORS_2D_INSTANTIATE
00918 #define VGL_HOMG_OPERATORS_2D_INSTANTIATE(T) \
00919 template class vgl_homg_operators_2d<T >; \
00920 template vgl_homg_point_2d<T > operator*(vnl_matrix_fixed<T,3,3> const& m, vgl_homg_point_2d<T > const& p); \
00921 template vgl_homg_line_2d<T > operator*(vnl_matrix_fixed<T,3,3> const& m, vgl_homg_line_2d<T > const& p)
00922 
00923 #endif // vgl_homg_operators_2d_txx_