vgl_homg_operators_2d.hxx
Go to the documentation of this file.
1 // This is core/vgl/algo/vgl_homg_operators_2d.hxx
2 #ifndef vgl_homg_operators_2d_hxx_
3 #define vgl_homg_operators_2d_hxx_
4 //:
5 // \file
6 
7 #include <iostream>
8 #include <limits>
9 #include <cmath>
10 #include "vgl_homg_operators_2d.h"
11 
12 #ifdef _MSC_VER
13 # include <vcl_msvc_warnings.h>
14 #endif
15 #include <cassert>
16 #include <vgl/vgl_homg_line_2d.h>
17 #include <vgl/vgl_homg_point_2d.h>
18 #include <vgl/vgl_point_2d.h>
19 #include <vgl/vgl_conic.h>
20 #include <vgl/vgl_box_2d.h>
21 #include <vgl/vgl_homg.h>
22 
23 #include <vnl/vnl_vector_fixed.h>
24 #include <vnl/vnl_matrix.h>
25 #include <vnl/vnl_matrix_fixed.h>
26 #include <vnl/vnl_math.h>
27 #include <vnl/algo/vnl_scatter_3x3.h> // used in most_orthogonal_vector()
28 #include <vnl/algo/vnl_real_eigensystem.h> // used for conic intersection
29 #include <vnl/vnl_diag_matrix.h> // used for conic intersection
30 
31 //-----------------------------------------------------------------------------
32 
33 template <class T>
34 inline static vgl_homg_line_2d<T> cross(const vgl_homg_point_2d<T>& p1,
35  const vgl_homg_point_2d<T>& p2)
36 {
37  return vgl_homg_line_2d<T> (p1.y() * p2.w() - p1.w() * p2.y(),
38  p1.w() * p2.x() - p1.x() * p2.w(),
39  p1.x() * p2.y() - p1.y() * p2.x());
40 }
41 
42 template <class T>
43 inline static vgl_homg_point_2d<T> cross(const vgl_homg_line_2d<T>& l1,
44  const vgl_homg_line_2d<T>& l2)
45 {
46  return vgl_homg_point_2d<T> (l1.b() * l2.c() - l1.c() * l2.b(),
47  l1.c() * l2.a() - l1.a() * l2.c(),
48  l1.a() * l2.b() - l1.b() * l2.a());
49 }
50 
51 template <class T>
52 inline static T dot(vgl_homg_line_2d<T> const& l,
53  vgl_homg_point_2d<T> const& p)
54 {
55  return l.a()*p.x() + l.b()*p.y() + l.c()*p.w();
56 }
57 
58 //-----------------------------------------------------------------------------
59 
60 template <class T>
62 {
63  return vnl_vector_fixed<T,3>(p.x(),p.y(),p.w());
64 }
65 
66 template <class T>
68 {
69  return vnl_vector_fixed<T,3>(l.a(),l.b(),l.c());
70 }
71 
72 template <class T>
73 vnl_vector_fixed<T,6> vgl_homg_operators_2d<T>::get_vector(vgl_conic<T> const& c)
74 {
75  vnl_vector_fixed<T,6> v;
76  v.put(0,c.a());
77  v.put(1,c.b());
78  v.put(2,c.c());
79  v.put(3,c.d());
80  v.put(4,c.e());
81  v.put(5,c.f());
82 
83  return v;
84 }
85 
86 //-----------------------------------------------------------------------------
87 //: Normalize vgl_homg_point_2d<T> to unit magnitude
88 template <class T>
90 {
91  double norm = std::sqrt (a.x()*a.x() + a.y()*a.y() + a.w()*a.w());
92 
93  if (norm == 0.0) {
94  std::cerr << "vgl_homg_operators_2d<T>::unitize() -- Zero length vector\n";
95  return;
96  }
97  norm = 1.0/norm;
98  a.set(T(a.x()*norm), T(a.y()*norm), T(a.w()*norm));
99 }
100 
101 // DISTANCE MEASUREMENTS IN EUCLIDEAN COORDINATES
102 
103 template <class T>
104 T
106  const vgl_homg_point_2d<T>& point2)
107 {
108  return std::sqrt(vgl_homg_operators_2d<T>::distance_squared(point1,point2));
109 }
110 
111 template <class T>
112 T
114  const vgl_homg_point_2d<T>& p2)
115 {
116  if (p1 == p2) return T(0); // quick return if possible
117 
118  if (p1.w() == 0 || p2.w() == 0) {
119  std::cerr << "vgl_homg_operators_2d<T>::distance_squared() -- point at infinity\n";
120  return std::numeric_limits<T>::infinity();
121  }
122 
123  return vnl_math::sqr (p1.x() / p1.w() - p2.x() / p2.w()) +
124  vnl_math::sqr (p1.y() / p1.w() - p2.y() / p2.w());
125 }
126 
127 //: Get the square of the perpendicular distance to a line.
128 // This is just the homogeneous form of the familiar
129 // \f$ \frac{a x + b y + c}{\sqrt{a^2+b^2}} \f$ :
130 // \f[ d = \frac{(l^\top p)}{p_z\sqrt{l_x^2 + l_y^2}} \f]
131 // If either the point or the line are at infinity an error message is
132 // printed and vgl_homg::infinity is returned.
133 template <class T>
134 T
136  const vgl_homg_line_2d<T>& line)
137 {
138  if ((line.a()==0 && line.b()== 0) || point.w()==0) {
139  std::cerr << "vgl_homg_operators_2d<T>::perp_dist_squared() -- line or point at infinity\n";
140  return vgl_homg<T>::infinity;
141  }
142 
143  T numerator = vnl_math::sqr (dot(line,point) / point.w());
144  if (numerator == 0) return T(0); // efficiency
145  T denominator = line.a()*line.a() + line.b()*line.b();
146 
147  return numerator / denominator;
148 }
149 
150 // ANGLES
151 
152 //-----------------------------------------------------------------------------
153 //: Get the anticlockwise angle between a line and the \a x axis.
154 template <class T>
155 double
157 {
158  return std::atan2 (line.b(), line.a());
159 }
160 
161 //-----------------------------------------------------------------------------
162 //: Get the 0 to pi/2 angle between two lines
163 template <class T>
164 double
166  const vgl_homg_line_2d<T>& line2)
167 {
168  double angle1 = line_angle(line1);
169  double angle2 = line_angle(line2);
170 
171  double diff = angle2 - angle1;
172  if (diff > vnl_math::pi_over_2) diff -= vnl_math::pi;
173  if (diff < -vnl_math::pi_over_2) diff += vnl_math::pi;
174 
175  return std::fabs(diff);
176 }
177 
178 //-----------------------------------------------------------------------------
179 //
180 //: Get the angle between two lines, a number between -PI and PI.
181 // Although homogeneous coordinates are
182 // only defined up to scale, here it is assumed that a line with homogeneous
183 // coordinates (m) is at 180 degrees to a line (-m), and this is why the term
184 // "oriented_line" is used. However, the overall scale (apart from sign) is
185 // not significant.
186 template <class T>
187 double
189  const vgl_homg_line_2d<T>& line2)
190 {
191  return std::fmod(line_angle(line2)-line_angle(line1), vnl_math::twopi);
192 }
193 
194 // JOINS/INTERSECTIONS
195 
196 //-----------------------------------------------------------------------------
197 //
198 //: Get the line through two points (the cross-product).
199 //
200 
201 template <class T>
204  const vgl_homg_point_2d<T>& p2)
205 {
206  return cross(p1,p2);
207 }
208 
209 //-----------------------------------------------------------------------------
210 //
211 //: Get the line through two points (the cross-product).
212 // In this case, we assume that the points are oriented, and ensure the
213 // cross is computed with positive point omegas.
214 template <class T>
217  const vgl_homg_point_2d<T>&p2)
218 {
219  int s1 = p1.w() < 0;
220  int s2 = p2.w() < 0;
221 
222  if (s1 ^ s2)
223  return cross(p2,p1);
224  else
225  return cross(p1,p2);
226 }
227 
228 //-----------------------------------------------------------------------------
229 //
230 //: Get the intersection point of two lines (the cross-product).
231 template <class T>
234  const vgl_homg_line_2d<T>& l2)
235 {
236  return cross(l1,l2);
237 }
238 
239 //-----------------------------------------------------------------------------
240 //
241 //: Get the perpendicular line to line which passes through point.
242 // Params are line \f$(a,b,c)\f$ and point \f$(x,y,1)\f$.
243 // Then the cross product of \f$(x,y,1)\f$ and the line's direction \f$(a,b,0)\f$,
244 // called \f$(p,q,r)\f$ satisfies
245 // - \f$ap+bq=0\f$ (perpendicular condition) and
246 // - \f$px+qy+r=0\f$ (incidence condition).
247 template <class T>
250  const vgl_homg_point_2d<T>& p)
251 {
252  vgl_homg_point_2d<T> d(l.a(), l.b(), 0);
253  return cross(d, p);
254 }
255 
256 //-----------------------------------------------------------------------------
257 //
258 //: Get the perpendicular projection of point onto line.
259 template <class T>
262  const vgl_homg_point_2d<T>& point)
263 {
264  vgl_homg_line_2d<T> perpline = perp_line_through_point (line, point);
265  vgl_homg_point_2d<T> answer = cross(line, perpline);
266  return answer;
267 }
268 
269 //: Return the midpoint of the line joining two homogeneous points
270 // When one of the points is at infinity, that point is returned.
271 // When both points are at infinity, the invalid point (0,0,0) is returned.
272 template <class T>
275  const vgl_homg_point_2d<T>& p2)
276 {
277  T x = p1.x() * p2.w() + p2.x() * p1.w();
278  T y = p1.y() * p2.w() + p2.y() * p1.w();
279  T w = p1.w() * p2.w() + p2.w() * p1.w();
280 
281  return vgl_homg_point_2d<T>(x,y,w);
282 }
283 
284 // FITTING
285 
286 // - Kanatani sect 2.2.2.
287 template <class T>
288 vnl_vector_fixed<T,3>
290 {
291  vnl_scatter_3x3<T> scatter_matrix;
292 
293  for (typename std::vector<vgl_homg_line_2d<T> >::const_iterator i = inpoints.begin();
294  i != inpoints.end(); ++i)
295  scatter_matrix.add_outer_product(get_vector(*i));
296 
297  return scatter_matrix.minimum_eigenvector();
298 }
299 
300 #include <vnl/algo/vnl_svd.h>
301 
302 template <class T>
303 vnl_vector_fixed<T,3>
305 {
306  vnl_matrix<T> D(lines.size(), 3);
307 
308  typename std::vector<vgl_homg_line_2d<T> >::const_iterator i = lines.begin();
309  for (unsigned j = 0; i != lines.end(); ++i,++j)
310  D.set_row(j, get_vector(*i).as_ref());
311 
312  vnl_svd<T> svd(D);
313 #ifdef DEBUG
314  std::cout << "[movrank " << svd.W() << ']';
315 #endif
316 
317  return svd.nullvector();
318 }
319 
320 //: Intersect a set of 2D lines to find the least-square point of intersection.
321 // This finds the point $\bf x$ that minimizes $\|\tt L \bf x\|$, where $\tt L$
322 // is the matrix whose rows are the lines. The implementation uses either
323 // vnl_scatter_3x3 (default) or vnl_svd (when at compile time
324 // VGL_HOMG_OPERATORS_2D_LINES_TO_POINT_USE_SVD has been set) to accumulate and
325 // compute the nullspace of $\tt L^\top \tt L$.
326 template <class T>
329 {
330  // ho_triveccam_aspect_lines_to_point
331  assert(lines.size() >= 2);
332 
333 #ifdef VGL_HOMG_OPERATORS_2D_LINES_TO_POINT_USE_SVD
334  vnl_vector_fixed<T,3> mov = most_orthogonal_vector_svd(lines);
335 #else
336  vnl_vector_fixed<T,3> mov = most_orthogonal_vector(lines);
337 #endif
338  return vgl_homg_point_2d<T>(mov[0], mov[1], mov[2]);
339 }
340 
341 // MISCELLANEOUS
342 
343 //-----------------------------------------------------------------------------
344 //: Calculates the crossratio of four collinear points p1, p2, p3 and p4.
345 // This number is projectively invariant, and it is the coordinate of p4
346 // in the reference frame where p2 is the origin (coordinate 0), p3 is
347 // the unity (coordinate 1) and p1 is the point at infinity.
348 // This cross ratio is often denoted as ((p1, p2; p3, p4)) (which also
349 // equals ((p3, p4; p1, p2)) or ((p2, p1; p4, p3)) or ((p4, p3; p2, p1)) )
350 // and is calculated as
351 // \verbatim
352 // p1 - p3 p2 - p3 (p1-p3)(p2-p4)
353 // ------- : -------- = --------------
354 // p1 - p4 p2 - p4 (p1-p4)(p2-p3)
355 // \endverbatim
356 //
357 // In principle, any single nonhomogeneous coordinate from the four points
358 // can be used as parameters for cross_ratio (but of course the same for all
359 // points). The most reliable answer will be obtained when the coordinate with
360 // the largest spacing is used, i.e., the one with smallest slope.
361 template <class T>
363  const vgl_homg_point_2d<T>& b,
364  const vgl_homg_point_2d<T>& c,
365  const vgl_homg_point_2d<T>& d)
366 {
367  double x1 = a.x(), y1 = a.y(), w1 = a.w();
368  double x2 = b.x(), y2 = b.y(), w2 = b.w();
369  double x3 = c.x(), y3 = c.y(), w3 = c.w();
370  double x4 = d.x(), y4 = d.y(), w4 = d.w();
371  double x = x1 - x2; if (x<0) x = -x; // assuming a != b ;-)
372  double y = y1 - y2; if (y<0) y = -y;
373  double n = (x>y) ? (x1*w3-x3*w1)*(x2*w4-x4*w2) : (y1*w3-y3*w1)*(y2*w4-y4*w2);
374  double m = (x>y) ? (x1*w4-x4*w1)*(x2*w3-x3*w2) : (y1*w4-y4*w1)*(y2*w3-y3*w2);
375  if (n == 0 && m == 0)
376  std::cerr << "cross ratio not defined: three of the given points coincide\n";
377  return n/m;
378 }
379 
380 //: Conjugate point of three given collinear points.
381 // If cross ratio cr is given (default: -1), the generalized conjugate point
382 // returned is such that ((x1,x2;x3,answer)) = cr.
383 template <class T>
386  const vgl_homg_point_2d<T>& b,
387  const vgl_homg_point_2d<T>& c,
388  double cr)
389 // Default for cr is -1.
390 {
391  T x1 = a.x(), y1 = a.y(), w1 = a.w();
392  T x2 = b.x(), y2 = b.y(), w2 = b.w();
393  T x3 = c.x(), y3 = c.y(), w3 = c.w();
394  T kx = x1*w3 - x3*w1, mx = x2*w3 - x3*w2, nx = T(kx*w2-cr*mx*w1);
395  T ky = y1*w3 - y3*w1, my = y2*w3 - y3*w2, ny = T(ky*w2-cr*my*w1);
396  return vgl_homg_point_2d<T>(T(x2*kx-cr*x1*mx)*ny,T(y2*ky-cr*y1*my)*nx,nx*ny);
397 }
398 
399 //: returns the vgl_conic which has the given matrix as its matrix
400 // \verbatim
401 // [A,B,C,D,E,F] <- [ A B/2 D/2 ]
402 // [ B/2 C E/2 ]
403 // [ D/2 E/2 F ]
404 // \endverbatim
405 template <class T>
407 vgl_homg_operators_2d<T>::vgl_conic_from_matrix(vnl_matrix_fixed<T,3,3> const& mat)
408 {
409  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]);
410 }
411 
412 //: returns 3x3 matrix containing conic coefficients.
413 // \verbatim
414 // [A,B,C,D,E,F] -> [ A B/2 D/2 ]
415 // [ B/2 C E/2 ]
416 // [ D/2 E/2 F ]
417 // \endverbatim
418 //
419 template <class T>
420 vnl_matrix_fixed<T,3,3>
422 {
423  vnl_matrix_fixed<T,3,3> mat;
424  T A = c.a(), B = c.b()/2, C = c.c(), D = c.d()/2, E = c.e()/2, F = c.f();
425 
426  mat[0][0] = A; mat[0][1] = B; mat[0][2] = D;
427  mat[1][0] = B; mat[1][1] = C; mat[1][2] = E;
428  mat[2][0] = D; mat[2][1] = E; mat[2][2] = F;
429 
430  return mat;
431 }
432 
433 //-------------------------------------------------------------------------
434 //: returns 3x3 matrix containing conic coefficients of dual conic.
435 // I.e., the inverse matrix (up to a scale factor) of the conic matrix.
436 
437 template <class T>
438 vnl_matrix_fixed<T,3,3>
440 {
441  vnl_matrix_fixed<T,3,3> mat;
442  T A = c.a(), B = c.b()/2, C = c.c(), D = c.d()/2, E = c.e()/2, F = c.f();
443 
444  mat[0][0] = C*F-E*E; mat[0][1] = E*D-B*F; mat[0][2] = B*E-C*D;
445  mat[1][0] = E*D-B*F; mat[1][1] = A*F-D*D; mat[1][2] = B*D-A*E;
446  mat[2][0] = B*E-C*D; mat[2][1] = B*D-A*E; mat[2][2] = A*C-B*B;
447 
448  return mat;
449 }
450 
451 //:
452 // This function is called from within intersection(vgl_conic<T>,vgl_conic<T>).
453 // The two conics passed to this function MUST NOT be degenerate!
454 template <class T>
455 std::list<vgl_homg_point_2d<T> >
457  vgl_conic<T> const& c2)
458 {
459  if (c1==c2)
460  {
461  std::cerr << __FILE__
462  << "Warning: the intersection of two identical conics is not a finite set of points.\n"
463  << "Returning an empty list.\n";
464  return std::list<vgl_homg_point_2d<T> >();
465  }
466  T A=c1.a(),B=c1.b(),C=c1.c(),D=c1.d(),E=c1.e(),F=c1.f();
467  T a=c2.a(),b=c2.b(),c=c2.c(),d=c2.d(),e=c2.e(),f=c2.f();
468  // Idea: eliminate the coefficient in x^2, solve for x in terms of y, resubstitute in the other equation.
469  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;
470 
471  // If the quadratic parts of the two conics are identical (up to scale factor),
472  // the intersection is the same as with a degenerate conic where one part is the line at infinity,
473  // viz. the conic with as equation the (weighted) difference of the two equations;
474  if ((ab==0 && ac==0)
475  // If the parts without x of the two conics are identical (up to scale factor),
476  // the intersection is the same as with a degenerate conic consisting of two vertical lines;
477  || (ab==0 && ad==0)
478  // And of course similarly for y:
479  || (ab==0 && ae==0)
480  // The following fix by Peter Vanroose, 28 december 2010:
481  // The general idea *does not* work (at least: not easily) if two intersection points have the same y coordinate.
482  // So make that a special case. It is easily detected, since in that case there is a scale factor lambda
483  // such that c1-lamda*c2 decomposes into (y-y0)(x+my+n)=0. Which means that lambda=A/a, and the equation satisfies:
484  || (ac*ad*ad+af*ab*ab == ab*ad*ae))
485  return intersection(vgl_conic<T>(0,ab,ac,ad,ae,af),c1);
486 
487  // Back to the "general" case:
488  vnl_vector_fixed<T,5> coef;
489  coef(0) = ac*ac-ab*(b*C-B*c);
490  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;
491  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;
492  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;
493  coef(4) = af*af-ad*(d*F-D*f);
494 
495  // Solutions of the fourth order equation
496  // 4 3 2
497  // y + py + qy + ry + s = 0
498  // are the eigenvalues of the matrix
499  // [ -p -q -r -s ]
500  // [ 1 0 0 0 ]
501  // [ 0 1 0 0 ]
502  // [ 0 0 1 0 ]
503 
504  if (coef(0) == 0 && coef(1) == 0) { // The equation is actually of 2nd degree
505  if (coef(2) == 0 && coef(3) == 0) return std::list<vgl_homg_point_2d<T> >(); // no real solutions.
506  T dis = coef(3)*coef(3)-4*coef(2)*coef(4); // discriminant
507  if (dis < 0) return std::list<vgl_homg_point_2d<T> >(); // no real solutions.
508  T y;
509  if (coef(2) == 0) dis=0, y = - coef(4) / coef(3);
510  else y = - coef(3) / coef(2) / 2;
511  T w = y*ab+ad;
512  T x = -(y*y*ac+y*ae+af);
513  if (x == 0 && w == 0) x = w = 1;
514  if (dis == 0) {return std::list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(x,y*w,w));}
515  dis = std::sqrt(dis) / coef(2) / 2;
516  std::list<vgl_homg_point_2d<T> > solutions;
517  y -= dis; w = y*ab+ad; x = -(y*y*ac+y*ae+af);
518  if (x == 0 && w == 0) x = w = 1;
519  solutions.push_back(vgl_homg_point_2d<T>(x,y*w,w));
520  y += 2*dis; w = y*ab+ad; x = -(y*y*ac+y*ae+af);
521  if (x == 0 && w == 0) x = w = 1;
522  solutions.push_back(vgl_homg_point_2d<T>(x,y*w,w));
523  return solutions;
524  }
525  if (coef(0) == 0) { // The equation is actually of 3rd degree
526  coef /= -coef(1);
527  double data[]={coef(2),coef(3),coef(4), 1,0,0, 0,1,0};
528  vnl_matrix<double> M(data,3,3);
529  vnl_real_eigensystem eig(M);
530  vnl_diag_matrix<std::complex<double> > polysolutions = eig.D;
531  std::list<vgl_homg_point_2d<T> > solutions;
532  for (int i=0;i<3;++i)
533  if (std::abs(std::imag(polysolutions(i))) < 1e-3) {// only want the real solutions
534  T y = (T)std::real(polysolutions(i));
535  T w = y*ab+ad;
536  T x = -(y*y*ac+y*ae+af);
537  if (x == 0 && w == 0) x = w = 1;
538  solutions.push_back(vgl_homg_point_2d<T>(x,y*w,w));
539  }
540  return solutions;
541  }
542 
543  coef /= -coef(0);
544  double data[]={coef(1),coef(2),coef(3),coef(4), 1,0,0,0, 0,1,0,0, 0,0,1,0};
545  vnl_matrix<double> M(data,4,4);
546  vnl_real_eigensystem eig(M);
547 
548  vnl_diag_matrix<std::complex<double> > polysolutions = eig.D;
549 
550  // Ignore imaginary solutions: place just the real solutions in yvals:
551  std::list<T> yvals;
552  for (int i=0;i<4;++i)
553  if (std::abs(std::imag(polysolutions(i))) < 1e-7)
554  yvals.push_back((T)std::real(polysolutions(i)));
555 
556  // These are only the solutions of the fourth order equation.
557  // The solutions of the intersection of the two conics are:
558 
559  std::list<vgl_homg_point_2d<T> > solutions;
560 
561  // Special case: two or more y values of intersection points are identical:
562  typename std::list<T>::const_iterator it = yvals.begin();
563 #if 0
564  if (yvals(0) == yvals(1) || yvals(1) == yvals(2) || yvals(2) == yvals(3))
565  {
566  }
567 #endif
568 
569  for (it = yvals.begin(); it != yvals.end(); ++it) {
570  T y = *it;
571  T w = y*ab+ad;
572  T x = -(y*y*ac+y*ae+af);
573  if (x == 0 && w == 0) x = w = 1;
574  solutions.push_back(vgl_homg_point_2d<T>(x,y*w,w));
575  }
576  return solutions;
577 }
578 
579 //:
580 // This function is called from within intersection(vgl_conic<T>,vgl_homg_line_2d<T>).
581 // The conic passed to this function MUST NOT be degenerate!
582 template <class T>
583 std::list<vgl_homg_point_2d<T> >
585  vgl_homg_line_2d<T> const& l)
586 {
587  T A=q.a(), B=q.b(), C=q.c(), D=q.d(), E=q.e(), F=q.f();
588  T a=l.a(), b=l.b(), c=l.c();
589 
590  if (a==0 && b==0) { // line at infinity
591  if (A==0)
592  return std::list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(1,0,0));
593  T d = B*B-4*A*C; // discriminant
594  if (d < 0) return std::list<vgl_homg_point_2d<T> >(); // no solutions
595  if (d == 0)
596  return std::list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(-B,2*A,0));
597  d = std::sqrt(d);
598  std::list<vgl_homg_point_2d<T> > v(1, vgl_homg_point_2d<T>(-B+d, 2*A, 0));
599  v.push_back(vgl_homg_point_2d<T>(-B-d, 2*A, 0));
600  return v;
601  }
602  if (a==0) { // write y in terms of w and solve for (x,w)
603  T y = -c/b; B = B*y+D;
604  T d = B*B-4*A*(C*y*y+E*y+F); // discriminant
605  if (d < 0) return std::list<vgl_homg_point_2d<T> >(); // no solutions
606  if (d == 0 && A == 0)
607  return std::list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(1,0,0));
608  if (d == 0)
609  return std::list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(-B,y*2*A,2*A));
610  if (A == 0) {
611  std::list<vgl_homg_point_2d<T> > v(1, vgl_homg_point_2d<T>(1,0,0));
612  v.push_back(vgl_homg_point_2d<T>(C*y*y+E*y+F, -y*B, -B));
613  return v;
614  }
615  d = std::sqrt(d);
616  std::list<vgl_homg_point_2d<T> > v(1, vgl_homg_point_2d<T>(-B+d, y*2*A, 2*A));
617  v.push_back(vgl_homg_point_2d<T>(-B-d, y*2*A, 2*A));
618  return v;
619  }
620  b /= -a; c /= -a; // now x = b*y+c*w.
621  T AA = A*b*b+B*b+C;
622  B = 2*A*b*c+B*c+D*b+E;
623  T d = B*B-4*AA*(A*c*c+D*c+F); // discriminant
624  if (d < 0) return std::list<vgl_homg_point_2d<T> >(); // no solutions
625  if (d == 0 && AA == 0)
626  return std::list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(b,1,0));
627  if (d == 0)
628  return std::list<vgl_homg_point_2d<T> >(2,vgl_homg_point_2d<T>(c*2*AA-b*B,-B,2*AA));
629  if (AA == 0) {
630  std::list<vgl_homg_point_2d<T> > v(1, vgl_homg_point_2d<T>(b,1,0));
631  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));
632  return v;
633  }
634  d = std::sqrt(d);
635  std::list<vgl_homg_point_2d<T> > v(1, vgl_homg_point_2d<T>(c*2*AA-b*B+b*d, -B+d, 2*AA));
636  v.push_back(vgl_homg_point_2d<T>(c*2*AA-b*B-b*d, -B-d, 2*AA));
637  return v;
638 }
639 
640 //: Return the (real) intersection points of a conic and a line.
641 template <class T>
642 std::list<vgl_homg_point_2d<T> >
644  vgl_homg_line_2d<T> const& l)
645 {
646  if (c.type()==vgl_conic<T>::no_type ||
651  return std::list<vgl_homg_point_2d<T> >(); // empty list
652  // let's hope the intersection point of the two complex lines is not on the line..
653 
655  vgl_homg_point_2d<T> p = intersection(l, c.components().front());
656  std::list<vgl_homg_point_2d<T> > list(2, p); // intersection is *two* coincident points
657  return list;
658  }
659 
661  std::list<vgl_homg_point_2d<T> > list;
662  list.push_back(intersection(l, c.components().front()));
663  list.push_back(intersection(l, c.components().back()));
664  return list;
665  }
666  return do_intersect(c, l);
667 }
668 
669 template <class T>
670 std::list<vgl_homg_point_2d<T> >
672 {
675  && c2.contains(c1.centre()))
676  return std::list<vgl_homg_point_2d<T> >(2, c1.centre()); // double intersection point
679  && c1.contains(c2.centre()))
680  return std::list<vgl_homg_point_2d<T> >(2, c2.centre()); // double intersection point
681  if (c1.type() == vgl_conic<T>::no_type || c2.type() == vgl_conic<T>::no_type ||
686  return std::list<vgl_homg_point_2d<T> >(); // empty list
687 
688  if (c1.type() == vgl_conic<T>::coincident_lines ||
691  std::list<vgl_homg_point_2d<T> > l1=intersection(c2,c1.components().front());
692  std::list<vgl_homg_point_2d<T> > l2=intersection(c2,c1.components().back());
693  l1.insert(l1.end(), l2.begin(), l2.end()); // append l2 to l1
694  return l1;
695  }
696 
697  if (c2.type() == vgl_conic<T>::coincident_lines ||
700  std::list<vgl_homg_point_2d<T> > l1=intersection(c1,c2.components().front());
701  std::list<vgl_homg_point_2d<T> > l2=intersection(c1,c2.components().back());
702  l1.insert(l1.end(), l2.begin(), l2.end()); // append l2 to l1
703  return l1;
704  }
705 
706  return do_intersect(c1, c2);
707 }
708 
709 //: Return the (at most) two tangent lines that pass through p and are tangent to the conic.
710 // For points on the conic, exactly 1 line is returned: the tangent at that point.
711 // For points inside the conic, an empty list is returned.
712 // For points outside the conic, there are always two tangents returned.
713 // Found by intersecting the dual conic with the dual line.
714 // If the conic is degenerate, an empty list is returned, unless the point is
715 // on the conic (in which case the component is returned to which it belongs,
716 // or even both components in the exclusive case that the point is the centre).
717 template <class T>
718 std::list<vgl_homg_line_2d<T> >
720  vgl_homg_point_2d<T> const& p)
721 {
722  if (c.is_degenerate()) {
723  if (!c.contains(p)) return std::list<vgl_homg_line_2d<T> >();
724  std::list<vgl_homg_line_2d<T> > v = c.components();
725  if (c.type() == vgl_conic<T>::coincident_lines || p == c.centre())
726  return v;
727  if (v.size() > 0 && dot(v.front(),p) == 0)
728  return std::list<vgl_homg_line_2d<T> >(1,v.front());
729  else if (v.size() > 1 && dot(v.back(),p) == 0)
730  return std::list<vgl_homg_line_2d<T> >(1,v.back());
731  else
732  return std::list<vgl_homg_line_2d<T> >();
733  }
734 
735  vgl_conic<T> C = c.dual_conic();
736  vgl_homg_line_2d<T> l(p.x(),p.y(),p.w()); // dual line
737  std::list<vgl_homg_point_2d<T> > dualpts = intersection(C,l);
738  std::list<vgl_homg_line_2d<T> > v;
739  typename std::list<vgl_homg_point_2d<T> >::iterator it = dualpts.begin();
740  for (; !(it == dualpts.end()); ++it)
741  v.push_back(vgl_homg_line_2d<T>((*it).x(), (*it).y(), (*it).w()));
742  return v;
743 }
744 
745 //: Return the list of common tangent lines of two conics.
746 // This is done by finding the intersection points of the two dual conics,
747 // which are the duals of the common tangent lines.
748 // If one of the conics is degenerate, an empty list is returned.
749 template <class T>
750 std::list<vgl_homg_line_2d<T> >
752  vgl_conic<T> const& c2)
753 {
754  if ((c1.type() != vgl_conic<T>::parabola && ! c1.is_central()) ||
755  (c2.type() != vgl_conic<T>::parabola && ! c2.is_central()))
756  return std::list<vgl_homg_line_2d<T> >();//empty list: degenerate conic has no dual
757 
758  vgl_conic<T> C1 = c1.dual_conic();
759  vgl_conic<T> C2 = c2.dual_conic();
760  std::list<vgl_homg_point_2d<T> > dualpts = intersection(C1,C2);
761  std::list<vgl_homg_line_2d<T> > v;
762  typename std::list<vgl_homg_point_2d<T> >::iterator it = dualpts.begin();
763  for (; !(it == dualpts.end()); ++it)
764  v.push_back(vgl_homg_line_2d<T>((*it).x(), (*it).y(), (*it).w()));
765  return v;
766 }
767 
768 //: Return the point on the conic closest to the given point
769 template <class T>
772  vgl_homg_point_2d<T> const& pt)
773 {
774  // First check if the point is on the curve, since this simplifies things:
775  if (c.contains(pt)) return pt;
776 
777  // The nearest point must have a polar line which is orthogonal to its
778  // connection line with the given point; all points with this property form
779  // a certain conic (actually an orthogonal hyperbola) :
780  std::list<vgl_homg_point_2d<T> > candidates; // all intersection points
781  if (pt.w() == 0) // given point is at infinity
782  {
783  // ==> degenerate hyperbola: line + line at infinity
784  vgl_homg_line_2d<T> l(c.a()*pt.y()*2-c.b()*pt.x(),
785  -c.c()*pt.x()*2+c.b()*pt.y(),
786  c.d()*pt.y() -c.e()*pt.x());
787  candidates = intersection(c, l);
788  if (candidates.size() == 0)
789  return vgl_homg_point_2d<T>(0,0,0); // this cannot happen
790  // just return any of the candidates, since distance makes no sense at infinity:
791  else return candidates.front();
792  }
793  else if (c.b()==0 && c.a()==c.c()) // the given conic is a circle
794  {
795  // ==> degenerate hyperbola: line thru centre & point + line at infinity
797  if (centre == pt) // in this case, any point of the circle is all right
798  centre = vgl_homg_point_2d<T>(1,0,0); // take a horizontal line thru pt
799  candidates = intersection(c, vgl_homg_line_2d<T>(centre,pt));
800  }
801  else // general case:
802  {
803  vgl_conic<T> conic(pt.w()*c.b(),
804  pt.w()*(c.c()-c.a())*2,
805  -pt.w()*c.b(),
806  pt.y()*c.a()*2-pt.x()*c.b()+pt.w()*c.e(),
807  -pt.x()*c.c()*2+pt.y()*c.b()-pt.w()*c.d(),
808  pt.y()*c.d() -pt.x()*c.e());
809  // Now it suffices to intersect the hyperbola with the given conic:
810  candidates = intersection(c, conic);
811  }
812  if (candidates.size() == 0)
813  {
814  std::cerr << "Warning: vgl_homg_operators_2d<T>::closest_point: no intersection\n";
815  return vgl_homg_point_2d<T>(0,0,0);
816  }
817 
818  // And find the intersection point closest to the given location:
819  typename std::list<vgl_homg_point_2d<T> >::iterator it = candidates.begin();
820  vgl_homg_point_2d<T> p = (*it);
822  for (++it; it != candidates.end(); ++it) {
823  if ((*it).w() == 0) continue;
825  if (d < dist) { p = (*it); dist = d; }
826  }
827  return p;
828 }
829 
830 //: Return the point on the conic closest to the given point.
831 // Still return a homogeneous point, even if the argument is non-homogeneous.
832 template <class T>
835  vgl_point_2d<T> const& pt)
836 {
837  return closest_point(c,vgl_homg_point_2d<T>(pt));
838 }
839 
840 //: Compute the bounding box of an ellipse
841 // This is done by finding the tangent lines to the ellipse from the two points
842 // at infinity (1,0,0) and (0,1,0).
843 template <class T>
846 {
847  // Only ellipses have a finite bounding box:
848 
849  if (c.real_type() == "complex intersecting lines") { // a single point:
850  vgl_homg_point_2d<T> pt = c.centre();
852  }
853 
854  if (c.real_type() == "invalid conic" ||
855  c.real_type() == "imaginary ellipse" ||
856  c.real_type() == "imaginary circle" ||
857  c.real_type() == "complex parallel lines")
858  return vgl_box_2d<T>(); // empty box
859 
860  if (c.real_type() == "real parallel lines" ||
861  c.real_type() == "coincident lines")
862  {
863  // find out if these lines happen to be horizontal or vertical
864  std::list<vgl_homg_line_2d<T> > l = c.components();
865  if (l.front().a() == 0) // horizontal lines
866  return vgl_box_2d<T>(vgl_point_2d<T>(T(1e33),-l.front().c()/l.front().b()),
867  vgl_point_2d<T>(T(-1e33),-l.back().c()/l.back().b()));
868  if (l.front().b() == 0) // vertical lines
869  return vgl_box_2d<T>(vgl_point_2d<T>(-l.front().c()/l.front().b(),T(1e33)),
870  vgl_point_2d<T>(-l.back().c()/l.back().b(),T(-1e33)));
871  // if not, go to the general case, i.e., return "everything".
872  }
873 
874  if (c.real_type() != "real ellipse" && c.real_type() != "real circle")
875  return vgl_box_2d<T>(T(-1e33), T(1e33), T(-1e33), T(1e33)); // everything
876 
877  // Now for the ellipses:
878 
879  vgl_homg_point_2d<T> px (1,0,0); // point at infinity of the X axis
880  vgl_homg_point_2d<T> py (0,1,0); // point at infinity of the Y axis
881 
882  std::list<vgl_homg_line_2d<T> > lx = vgl_homg_operators_2d<T>::tangent_from(c, px);
883  std::list<vgl_homg_line_2d<T> > ly = vgl_homg_operators_2d<T>::tangent_from(c, py);
884 
885  T y1 = - lx.front().c() / lx.front().b(); // lx are two horizontal lines
886  T y2 = - lx.back().c() / lx.back().b();
887  if (y1 > y2) { T t = y1; y1 = y2; y2 = t; }
888  T x1 = - ly.front().c() / ly.front().a(); // ly are two vertical lines
889  T x2 = - ly.back().c() / ly.back().a();
890  if (x1 > x2) { T t = x1; x1 = x2; x2 = t; }
891 
892  return vgl_box_2d<T>(x1, x2, y1, y2);
893 }
894 
895 //: Transform a point through a 3x3 projective transformation matrix
896 // \relatesalso vgl_homg_point_2d
897 template <class T>
898 vgl_homg_point_2d<T> operator*(vnl_matrix_fixed<T,3,3> const& m,
899  vgl_homg_point_2d<T> const& p)
900 {
901  return vgl_homg_point_2d<T>(m(0,0)*p.x()+m(0,1)*p.y()+m(0,2)*p.w(),
902  m(1,0)*p.x()+m(1,1)*p.y()+m(1,2)*p.w(),
903  m(2,0)*p.x()+m(2,1)*p.y()+m(2,2)*p.w());
904 }
905 
906 //: Transform a line through a 3x3 projective transformation matrix
907 // \relatesalso vgl_homg_line_2d
908 template <class T>
909 vgl_homg_line_2d<T> operator*(vnl_matrix_fixed<T,3,3> const& m,
910  vgl_homg_line_2d<T> const& l)
911 {
912  return vgl_homg_line_2d<T>(m(0,0)*l.a()+m(0,1)*l.b()+m(0,2)*l.c(),
913  m(1,0)*l.a()+m(1,1)*l.b()+m(1,2)*l.c(),
914  m(2,0)*l.a()+m(2,1)*l.b()+m(2,2)*l.c());
915 }
916 
917 //: Return the point on the line closest to the given point
918 template <class T>
920  vgl_homg_point_2d<T> const& p)
921 {
922  // Return p itself, if p lies on l:
923  if (l.a()*p.x()+l.b()*p.y()+l.c()*p.w() == 0) return p;
924  // Otherwise, make sure that both l and p are not at infinity:
925  assert(!l.ideal()); // should not be the line at infinity
926  // Line othogonal to l and through p is bx-ay+d=0, with d = (a*py-b*px)/pw.
927  vgl_homg_line_2d<T> o(l.b()*p.w(), -l.a()*p.w(), l.a()*p.y()-l.b()*p.x());
928  // Finally return the intersection point of l with this orthogonal line:
930 }
931 
932 #undef VGL_HOMG_OPERATORS_2D_INSTANTIATE
933 #define VGL_HOMG_OPERATORS_2D_INSTANTIATE(T) \
934 template class vgl_homg_operators_2d<T >; \
935 template vgl_homg_point_2d<T > operator*(vnl_matrix_fixed<T,3,3> const& m, vgl_homg_point_2d<T > const& p); \
936 template vgl_homg_line_2d<T > operator*(vnl_matrix_fixed<T,3,3> const& m, vgl_homg_line_2d<T > const& p)
937 
938 #endif // vgl_homg_operators_2d_hxx_
static vgl_conic< T > vgl_conic_from_matrix(vnl_matrix_fixed< T, 3, 3 > const &mat)
returns the vgl_conic which has the given matrix as its matrix.
A quadratic plane curve.
vgl_conic_type type() const
Definition: vgl_conic.h:101
vgl_homg_point_1d< T > centre(vgl_homg_point_1d< T > const &p1, vgl_homg_point_1d< T > const &p2)
Return the point at the centre of gravity of two given points.
2D homogeneous operations
static vgl_homg_point_2d< T > conjugate(const vgl_homg_point_2d< T > &a, const vgl_homg_point_2d< T > &b, const vgl_homg_point_2d< T > &c, double cr=-1.0)
Conjugate point of three given collinear points.
a point in 2D nonhomogeneous space
T a() const
Parameter a of line a*x + b*y + c*w = 0.
static vnl_matrix_fixed< T, 3, 3 > matrix_from_conic(vgl_conic< T > const &)
returns 3x3 matrix containing conic coefficients.
Represents a homogeneous 2D line.
Definition: vgl_fwd.h:14
static vgl_homg_point_2d< T > intersection(const vgl_homg_line_2d< T > &line1, const vgl_homg_line_2d< T > &line2)
Get the intersection point of two lines (the cross-product).
T b() const
Parameter b of line a*x + b*y + c*w = 0.
bool contains(vgl_homg_point_2d< T > const &pt) const
Returns true if the point pt belongs to the conic.
Definition: vgl_conic.hxx:185
static vgl_homg_line_2d< T > perp_line_through_point(const vgl_homg_line_2d< T > &line, const vgl_homg_point_2d< T > &point)
Get the perpendicular line to line which passes through point.
T f() const
Returns the coefficient of .
Definition: vgl_conic.h:135
line in projective 2D space
static std::list< vgl_homg_point_2d< T > > do_intersect(vgl_conic< T > const &q, vgl_homg_line_2d< T > const &l)
This function is called from within intersection(vgl_conic<T>,vgl_homg_line_2d<T>).
T a() const
Returns the coefficient of .
Definition: vgl_conic.h:120
static vgl_homg_point_2d< T > midpoint(const vgl_homg_point_2d< T > &p1, const vgl_homg_point_2d< T > &p2)
Return the midpoint of the line joining two homogeneous points.
#define v
Definition: vgl_vector_2d.h:74
static vgl_homg_point_2d< T > closest_point(vgl_homg_line_2d< T > const &l, vgl_homg_point_2d< T > const &p)
Return the point on the line closest to the given point.
static vnl_vector_fixed< T, 3 > most_orthogonal_vector(const std::vector< vgl_homg_line_2d< T > > &lines)
compute most orthogonal vector with vnl_symmetric_eigensystem.
T c() const
Returns the coefficient of .
Definition: vgl_conic.h:126
#define dot(p, q)
vgl_homg_point_2d< T > centre() const
Returns the centre of the conic, or its point at infinity if a parabola.
Definition: vgl_conic.h:245
static vnl_vector_fixed< T, 3 > get_vector(vgl_homg_point_2d< T > const &p)
get a vnl_vector_fixed representation of a homogeneous object.
2D homogeneous operations.
Definition: vgl_algo_fwd.h:28
std::string real_type() const
Returns the type of the conic as a string.
Definition: vgl_conic.hxx:36
bool is_central() const
Returns true if a central conic, i.e., an ellipse, circle, or hyperbola.
Definition: vgl_conic.hxx:306
static vgl_homg_line_2d< T > join(const vgl_homg_point_2d< T > &point1, const vgl_homg_point_2d< T > &point2)
Get the line through two points (the cross-product).
General purpose support class for vgl_homg_ classes.
static void unitize(vgl_homg_point_2d< T > &a)
Normalize vgl_homg_point_2d<T> to unit magnitude.
T e() const
Returns the coefficient of .
Definition: vgl_conic.h:132
vgl_conic dual_conic() const
Returns the dual or tangential representation of this conic.
Definition: vgl_conic.hxx:324
static vgl_homg_point_2d< T > lines_to_point(const std::vector< vgl_homg_line_2d< T > > &lines)
Intersect a set of 2D lines to find the least-square point of intersection.
A quadratic plane curve.
Definition: vgl_conic.h:70
static double cross_ratio(const vgl_homg_point_2d< T > &p1, const vgl_homg_point_2d< T > &p2, const vgl_homg_point_2d< T > &p3, const vgl_homg_point_2d< T > &p4)
cross ratio of four collinear points.
point in projective 2D space
void set(T px, T py, T pw=(T) 1)
Set x,y,w.
T b() const
Returns the coefficient of .
Definition: vgl_conic.h:123
Contains class to represent a cartesian 2D bounding box.
static vgl_homg_line_2d< T > join_oriented(const vgl_homg_point_2d< T > &point1, const vgl_homg_point_2d< T > &point2)
Get the line through two points (the cross-product).
static vnl_matrix_fixed< T, 3, 3 > matrix_from_dual_conic(vgl_conic< T > const &)
returns 3x3 matrix containing conic coefficients of dual conic.
static vgl_homg_point_2d< T > perp_projection(const vgl_homg_line_2d< T > &line, const vgl_homg_point_2d< T > &point)
Get the perpendicular projection of point onto line.
T d() const
Returns the coefficient of .
Definition: vgl_conic.h:129
static vnl_vector_fixed< T, 3 > most_orthogonal_vector_svd(const std::vector< vgl_homg_line_2d< T > > &lines)
compute most orthogonal vector with SVD.
static T distance_squared(const vgl_homg_point_2d< T > &point1, const vgl_homg_point_2d< T > &point2)
Get the square of the 2D distance between the two points.
#define l
vgl_homg_point_3d< Type > intersection(l const &l1, l const &l2)
Return the intersection point of two concurrent lines.
vgl_homg_point_1d< T > operator *(vnl_matrix_fixed< T, 2, 2 > const &m, vgl_homg_point_1d< T > const &p)
Transform a point through a 2x2 projective transformation matrix.
static vgl_box_2d< T > compute_bounding_box(vgl_conic< T > const &c)
Compute the bounding box of an ellipse.
T c() const
Parameter c of line a*x + b*y + c*w = 0.
General purpose support class for vgl_homg_ classes.
Definition: vgl_fwd.h:6
static double angle_between_oriented_lines(const vgl_homg_line_2d< T > &line1, const vgl_homg_line_2d< T > &line2)
Get the angle between two lines, a number between -PI and PI.
static T distance(const vgl_homg_point_2d< T > &point1, const vgl_homg_point_2d< T > &point2)
Get the 2D distance between the two points.
std::list< vgl_homg_line_2d< T > > components() const
Returns the list of component lines, when degenerate and real components.
Definition: vgl_conic.hxx:238
static double line_angle(const vgl_homg_line_2d< T > &line)
Get the anticlockwise angle between a line and the x axis.
static double abs_angle(const vgl_homg_line_2d< T > &line1, const vgl_homg_line_2d< T > &line2)
Get the 0 to pi/2 angle between two lines.
Represents a homogeneous 2D point.
Definition: vgl_fwd.h:8
static std::list< vgl_homg_line_2d< T > > common_tangents(vgl_conic< T > const &c1, vgl_conic< T > const &c2)
Return the list of common tangent lines of two conics.
static T perp_dist_squared(const vgl_homg_point_2d< T > &point, const vgl_homg_line_2d< T > &line)
Get the square of the perpendicular distance to a line.
bool is_degenerate() const
Returns true if this conic is degenerate, i.e., if it consists of 2 lines.
Definition: vgl_conic.hxx:191
static std::list< vgl_homg_line_2d< T > > tangent_from(vgl_conic< T > const &c, vgl_homg_point_2d< T > const &p)
Return the (at most) two tangent lines that pass through p and are tangent to the conic.