contrib/oxl/osl/osl_ortho_regress.cxx
Go to the documentation of this file.
00001 // This is oxl/osl/osl_ortho_regress.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \author fsm
00008 
00009 #include "osl_ortho_regress.h"
00010 #include <vcl_cmath.h>
00011 #include <vcl_cassert.h>
00012 
00013 // convenience methods
00014 
00015 void osl_ortho_regress::add_points(double const *x, double const *y, unsigned n) {
00016   for (unsigned i=0; i<n; ++i)
00017     add_point(x[i], y[i]);
00018 }
00019 
00020 void osl_ortho_regress::add_points(float const *x, float const *y, unsigned n) {
00021   for (unsigned i=0; i<n; ++i)
00022     add_point(x[i], y[i]);
00023 }
00024 
00025 double osl_ortho_regress::rms_cost(double a, double b, double c) const {
00026   return vcl_sqrt( cost(a, b, c) / S1 );
00027 }
00028 
00029 //--------------------------------------------------------------------------------
00030 
00031 // this can be done without netlib
00032 #include <vnl/algo/vnl_symmetric_eigensystem.h>
00033 
00034 // f(x, y)
00035 // =
00036 // a x^2 + 2 b x y + c y^2
00037 // =
00038 // [x y] [a b] [x]
00039 //       [b c] [y]
00040 static
00041 bool symmetric_2x2_eigen_system(double a, double b, double c,
00042                                 double *xmin, double *ymin, double *lmin,
00043                                 double *xmax, double *ymax, double *lmax)
00044 {
00045   vnl_matrix<double> M(2, 2);
00046   M(0, 0) = a;
00047   M(0, 1) = M(1, 0) = b;
00048   M(1, 1) = c;
00049   vnl_symmetric_eigensystem<double> sym(M);
00050   if (xmin) *xmin = sym.V(0, 0);
00051   if (ymin) *ymin = sym.V(1, 0);
00052   if (lmin) *lmin = sym.D(0, 0);
00053 
00054   if (xmax) *xmax = sym.V(0, 1);
00055   if (ymax) *ymax = sym.V(1, 1);
00056   if (lmax) *lmax = sym.D(1, 1);
00057 
00058   return true;
00059 }
00060 
00061 //:
00062 // The problem is to minimize total squared distance to
00063 // the line u.x + d = 0, ie. :
00064 // minimize     \sum_i | u.x_i + d |^2
00065 // subject to   |u|=1
00066 //
00067 // Writing x_i = xbar + y_i, where xbar is the centroid
00068 // of the point set, the sum becomes
00069 // \sum | u.(x_i - xbar) |^2 + S1 | u.xbar + d |^2
00070 // , so that the optimal line must pass through the centroid.
00071 // To find u, we find the eigenvector of the scatter matrix
00072 // M = \sum (x_i - xbar)*(x_i - xbar)^t which has least
00073 // eigenvalue.
00074 //
00075 double osl_ortho_regress::cost(double a, double b, double c) const {
00076   return
00077     (Sxx*a*a + 2*Sxy*a*b + Syy*b*b + 2*Sx*a*c + 2*Sy*b*c + S1*c*c)/(a*a+b*b);
00078 }
00079 
00080 bool osl_ortho_regress::fit(double &a, double &b, double &c) const {
00081   assert(S1 >= 2);
00082   double xbar = Sx/S1;
00083   double ybar = Sy/S1;
00084 
00085   if (!symmetric_2x2_eigen_system(Sxx-Sx*Sx/S1, Sxy-Sx*Sy/S1, Syy-Sy*Sy/S1,
00086                                   &a, &b, 0,
00087                                   0, 0, 0))
00088     return false;
00089 
00090   c = -(a*xbar+b*ybar);
00091   return true;
00092 }
00093 
00094 //:
00095 // The problem is to minimize total squared distance from
00096 // a line passing through a given point x_0 :
00097 //
00098 // minimize     \sum_i | u.x_i + d |^2
00099 // subject to   |u|=1
00100 //              u.x_0 + d = 0
00101 //
00102 // But u.x_i + d = u.(x_i - x_0), so that we have to minimize
00103 // u^t M u , where M = \sum_i (x_i - x_0)*(x_i - x_0)^t. This
00104 // is solved by taking the eigenvector of M of least
00105 // eigenvalue.
00106 //
00107 bool osl_ortho_regress::fit_constrained(double x, double y,
00108                                         double &a, double &b, double &c) const
00109 {
00110   assert(S1 >= 1);
00111 
00112   if (!symmetric_2x2_eigen_system(Sxx-2*Sx*x+S1*x*x, Sxy-Sx*y-x*Sy+S1*x*y, Syy-2*Sy*y+S1*y*y,
00113                                   &a, &b, 0,
00114                                   0, 0, 0))
00115     return false;
00116 
00117   c = -(a*x + b*y);
00118   return true;
00119 }