contrib/rpl/rrel/rrel_orthogonal_regression.cxx
Go to the documentation of this file.
00001 #include "rrel_orthogonal_regression.h"
00002 
00003 #include <vnl/vnl_matrix.h>
00004 #include <vnl/vnl_math.h>
00005 #include <vnl/vnl_vector.h>
00006 #include <vnl/algo/vnl_svd.h>
00007 
00008 #include <vcl_cassert.h>
00009 #include <vcl_iostream.h>
00010 #include <vcl_vector.h>
00011 
00012 rrel_orthogonal_regression::rrel_orthogonal_regression( const vnl_matrix<double>& pts )
00013   : vars_( pts )
00014 {
00015   unsigned int num_pts = pts.rows ();
00016   set_param_dof( pts.cols() );
00017   if ( param_dof() > num_pts )
00018     vcl_cerr << "\nrrel_orthogonal_regression::rrel_orthogonal_regression  WARNING:\n"
00019              << "DoF ("<<param_dof()<<") is greater than the number of data points ("
00020              << num_pts << ").\nAn infinite set of equally valid solutions exists.\n";
00021   set_num_samples_for_fit( param_dof() );
00022 }
00023 
00024 
00025 rrel_orthogonal_regression::rrel_orthogonal_regression( const vcl_vector<vnl_vector<double> >& pts )
00026   : vars_( pts.size(),pts[0].size() )
00027 {
00028   unsigned int num_pts = vars_.rows();
00029 
00030   for (unsigned int i=0;i<num_pts;i++)
00031     vars_.set_row(i, pts[i]);
00032 
00033   set_param_dof( vars_.cols() ); // up to a scale
00034   if ( param_dof() > num_pts )
00035     vcl_cerr << "\nrrel_orthogonal_regression::rrel_orthogonal_regression  WARNING:\n"
00036              << "DoF ("<<param_dof()<<") is greater than the number of data points ("
00037              << num_pts << ").\nAn infinite set of equally valid solutions exists.\n";
00038   set_num_samples_for_fit( param_dof() );
00039 }
00040 
00041 
00042 rrel_orthogonal_regression::~rrel_orthogonal_regression()
00043 {
00044 }
00045 
00046 unsigned int
00047 rrel_orthogonal_regression::num_samples( ) const
00048 {
00049   return vars_.rows();
00050 }
00051 
00052 bool
00053 rrel_orthogonal_regression::fit_from_minimal_set( const vcl_vector<int>& point_indices,
00054                                                   vnl_vector<double>& params ) const
00055 {
00056   if ( point_indices.size() != param_dof() ) {
00057     vcl_cerr << "rrel_orthogonal_regression::fit_from_minimal_sample  The number of point "
00058              << "indices must agree with the fit degrees of freedom.\n";
00059     return false;
00060   }
00061 
00062   // The equation to be solved is Ap = 0, where A is a dof_ x (dof_+1)
00063   // because the solution is up to a scale.
00064   vnl_matrix<double> A(param_dof(), param_dof()+1, 1);
00065   for (unsigned int i=0; i<param_dof(); ++i) {
00066     int index = point_indices[i];
00067     for ( unsigned int j=0; j<param_dof(); ++j) {
00068       A(i,j) = vars_(index, j);
00069     }
00070   }
00071   vnl_svd<double> svd( A, 1.0e-8 );
00072   if ( (unsigned int)svd.rank() < param_dof() ) {
00073     vcl_cerr << "rrel_orthogonal_regression:: singular fit!\n";
00074     return false;    // singular fit
00075   }
00076   else {
00077     params = svd.nullvector();
00078     params /= vcl_sqrt( 1 - vnl_math_sqr( params[ params.size()-1 ] ) );
00079   }
00080   return true;
00081 }
00082 
00083 
00084 void
00085 rrel_orthogonal_regression::compute_residuals( const vnl_vector<double>& params,
00086                                                vcl_vector<double>& residuals ) const
00087 {
00088   // The residual is the algebraic distance, which is simply A * p.
00089   // Assumes the parameter vector has a unit normal.
00090   assert( residuals.size() == vars_.rows() );
00091 
00092   vnl_vector<double> norm(params.size()-1);
00093   for (unsigned int i=0; i<params.size()-1; i++)
00094     norm[i] = params[i];
00095   assert(norm.two_norm() > 0.99 && norm.two_norm() < 1.01);
00096   for ( unsigned int i=0; i<vars_.rows(); ++i ) {
00097     residuals[i] = dot_product(norm, vars_.get_row(i) ) + params[params.size()-1];
00098   }
00099 }
00100 
00101 
00102 // Compute a least-squares fit, using the weights if they are provided.
00103 // The cofact matrix is not used or set.
00104 bool
00105 rrel_orthogonal_regression::weighted_least_squares_fit( vnl_vector<double>& params,
00106                                                         vnl_matrix<double>& /*cofact*/,
00107                                                         const vcl_vector<double> *weights ) const
00108 {
00109   // If params and cofact are NULL pointers and the fit is successful,
00110   // this function will allocate a new vector and a new
00111   // matrix. Otherwise, it assumes that *params is a param_dof() x 1 vector
00112   // and that cofact is param_dof() x param_dof() matrix.
00113 
00114   assert( !weights || weights->size() == vars_.rows() );
00115 
00116   vnl_vector<double> sum_vect(vars_.cols(), 0.0);
00117   vnl_matrix<double> A(vars_.cols(), vars_.cols());
00118   vnl_vector<double> avg;
00119   vnl_matrix<double> shift_vars( vars_.rows(), vars_.cols() );
00120 
00121   if (weights) {
00122     double sum_weight=0;
00123     for (unsigned int i=0; i<vars_.rows(); ++i) {
00124       sum_vect += vars_.get_row(i) * (*weights)[i];
00125       sum_weight += (*weights)[i];
00126     }
00127     avg = sum_vect / sum_weight;
00128     for (unsigned int i=0; i<vars_.rows(); ++i)
00129       shift_vars.set_row(i, (vars_.get_row(i)-avg) * vcl_sqrt((*weights)[i]));
00130   }
00131   else {
00132     for (unsigned int i=0; i<vars_.rows(); ++i)
00133       sum_vect += vars_.get_row(i);
00134     avg = sum_vect / vars_.rows();
00135     for (unsigned int i=0; i<vars_.rows(); ++i)
00136       shift_vars.set_row(i, vars_.get_row(i) -avg);
00137   }
00138 
00139   A = shift_vars.transpose() * shift_vars;
00140 
00141   vnl_svd<double> svd( A, 1.0e-8 );
00142   // Rank of (param_dof() -1) is an exact fit
00143   if ( (unsigned int)svd.rank() < param_dof() - 1 ) {
00144     vcl_cerr << "rrel_orthogonal_regression::WeightedLeastSquaresFit --- singularity!\n";
00145     return false;
00146   }
00147   else {
00148     vnl_vector<double> norm = svd.nullvector();
00149     params.set_size(norm.size()+1);
00150     for (unsigned int i=0; i<norm.size(); ++i) {
00151       params[i] = norm[i];
00152     }
00153     params[norm.size()] = -1*dot_product(norm, avg);
00154     return true;
00155   }
00156 }
00157 
00158 
00159 void
00160 rrel_orthogonal_regression::print_points() const
00161 {
00162   vcl_cout << "\nrrel_orthogonal_regression::print_points:\n"
00163            << "  param_dof() = " << param_dof() << '\n'
00164            << "  num_pts = " << vars_.rows() << "\n\n"
00165            << " i   vars_\n"
00166            << " =   =========\n";
00167   for ( unsigned int i=0; i<vars_.rows(); ++i ) {
00168     vcl_cout << ' ' << i << "   " << vars_.get_row (i) << '\n';
00169   }
00170 }