Galaxis Codebase  1.0.0
Team Galaxis software stack for the Carolo Cup 2023 - ROS2 based software
polyfit.h
Go to the documentation of this file.
1 /*
2  * Copyright 2012 Roland Kwitt
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * of the License at
7  *
8  * http:www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 
17 #ifndef POLYFIT_H
18 #define POLYFIT_H
19 
20 #include "logging.h"
21 
22 #include <Eigen/Dense>
23 
24 #include <set>
25 #include <vector>
26 #include <iostream>
27 
28 // Random number generation
29 #include <boost/random/mersenne_twister.hpp>
30 
31 #if BOOST_VERSION >= 104700
32 #include <boost/random/uniform_int_distribution.hpp>
33 #else
34 #include <boost/random/uniform_int.hpp>
35 #endif
36 
37 template <typename T>
38 class PolyFit {
39 public:
40  enum SolverType {
44  };
45 
46  struct Options {
47  Options() :
49  polyDeg(2),
50  maxTrial(100),
51  maxMPts(5),
52  tolerance(0.1) {}
53 
54  SolverType solver; // Default: SVD solver
55  int polyDeg; // Default: 2-nd deg. poly.
56  int maxTrial; // Max. RANAC iterations
57  int maxMPts; // Max. #pts to fit polynomial
58  double tolerance; // Pt. to model thr. for inliers
59  };
60 
61 private:
62 #if BOOST_VERSION >= 104700
63  // RNG
64  boost::random::mt19937 gen;
65  typedef boost::random::uniform_int_distribution<> DistributionType;
66 #else
67  // RNG
68  boost::mt19937 gen;
69  typedef boost::uniform_int<int> DistributionType;
70 #endif
71 
72  // Define own matrix type, based on template parameter T
73  typedef Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> MatType;
74 
75  // Define own vector type, based on template parameter T
76  typedef Eigen::Matrix<T, Eigen::Dynamic, 1> VecType;
77 
78  // Polynomial coefficients
80 
81  std::vector<T>& x; // Sample points
82  std::vector<T>& y; // Signal values
83 
84  // Sample + Signal in Eigen format
87 
88  // Vandermonde matrix
90 
91  // Fitting options
93 
94 public:
95  // Public accessor methods
96  int getDegree() const {
97  return options.polyDeg;
98  }
99 
101  return options.solver;
102  }
103 
104  const VecType getCoefficients() const {
105  return pCoef;
106  }
107 
108  // Constructor
109  PolyFit(std::vector<T>& x, std::vector<T>& y, Options& options) :
110  x(x), y(y), options(options) {
111  if(!x.size() || !y.size() || (x.size() != y.size())) {
112  throw std::exception();
113  }
114 
115  if(options.polyDeg < 1) {
116  throw std::exception();
117  }
118  dataToEigen();
119  }
120 
121  // Evaluate polynomial at positions in 'at'
122  std::vector<T> evalPoly(const std::vector<T>& at) {
123  VecType x;
124  VecType y;
125 
126  x.resize(at.size());
127  y.resize(at.size());
128 
129  for(int i = 0; i < at.size(); ++i)
130  x(i, 0) = at[i];
131 
132  // Horner method
133  if(pCoef.rows() > 0)
134  y.setConstant(pCoef(0, 0));
135 
136  int nc = pCoef.rows();
137  for(int i = 1; i < nc; ++i)
138  y = (x.cwiseProduct(y)).array() + pCoef(i, 0);
139 
140  // Copy data over to output vector
141  std::vector<T> out(y.rows());
142  for(int i = 0; i < y.rows(); ++i)
143  out[i] = y(i, 0);
144 
145  return out;
146  }
147 
148  // Points where signal is sampled
149  void setSamplePoints(std::vector<T>& v) {
150  if(!v.size()) {
151  throw std::exception();
152  }
153 
154  if(x.size()) {
155  }
156  x = v;
157  dataToEigen();
158  }
159 
160  // Signal value at sample points
161  void setSignalPoints(std::vector<T>& v) {
162  if(!v.size()) {
163  throw std::exception();
164  }
165 
166  if(y.size()) {
167  }
168  y = v;
169  dataToEigen();
170  }
171 
172  // Least-Squares (LS)
173  T solveLS(void) {
174 
175  // Size check
176  if(xM.rows() != yM.rows()) {
177  throw std::exception();
178  }
179 
180  buildV();
181 
182  switch(options.solver) {
184  pCoef = V.colPivHouseholderQr().solve(yM.col(0));
185  break;
187  pCoef = V.fullPivHouseholderQr().solve(yM.col(0));
188  break;
189  case EIGEN_JACOBI_SVD:
190  pCoef = V.jacobiSvd(Eigen::ComputeThinU |
191  Eigen::ComputeThinV)
192  .solve(yM.col(0));
193  break;
194  }
195  return getMSE();
196  }
197 
198  // Mean-Square Error (MSE)
199  T getMSE(void) {
200  if(V.cols() != pCoef.rows() && yM.rows() != V.rows()) {
201  throw std::logic_error("Assert Polyfit l220");
202  }
203 
204  T mse = 0.0;
205  Eigen::VectorXd diff = ((V * pCoef).array() - yM.col(0).array());
206  for(int i = 0; i < diff.rows(); ++i)
207  mse += pow(diff(i), 2);
208  return mse;
209  }
210 
211  // Sample s points from [1...N]
212  void randsample(int N, int s, std::set<int>& ind) {
213  if(N < 0 && s < 0 && (N <= s)) {
214  throw std::logic_error("Assert Polyfit l230");
215  }
216 
217  DistributionType dist(1, N);
218  while(static_cast<int>(ind.size()) < s) {
219  ind.insert(dist(gen));
220  }
221  }
222 
223  // RANSAC-based solution
224  void solveRLS(void) {
225  std::vector<int> bestSubset;
226  Eigen::Matrix<T, Eigen::Dynamic, 1> bestCoef;
227 
228  // Probability that we can at least choose
229  // one sample with options.maxMPts free of
230  // outliers (sugg. by Peter Kovesi's code)
231  double p = 0.99;
232 
233  int trialCount = 0;
234  int N = 1;
235  while(N > trialCount) {
236 
237  std::set<int> indexSet;
238  randsample(static_cast<int>(y.size() - 1),
239  options.maxMPts, indexSet);
240 
241  if(static_cast<int>(indexSet.size()) != options.maxMPts) {
242  throw std::logic_error("Assert Polyfit l265");
243  }
244 
245  // Build (sample,signal) for current subset
246  dataToEigenSubset(indexSet);
247  buildV();
248 
249  // Solve the LS-fitting problem
250  double mse = solveLS();
251 
252  // Compute inlier indices
253  std::vector<int> inliers;
254  int nInliers = getInliers(inliers);
255 
256  if(inliers.size() > bestSubset.size()) {
257  // Store settings
258  bestSubset = inliers;
259  bestCoef = pCoef;
260 
261  // Refine estimate of N (based on Peter Kovesi's MATLAB impl. of RANSAC)
262  double fracInliers = static_cast<double>(inliers.size()) / double(yM.rows());
263  double pNoOutliers = 1 - pow(fracInliers, options.maxMPts);
264  pNoOutliers = std::max(1e-9, pNoOutliers);
265  pNoOutliers = std::min(1 - 1e-9, pNoOutliers);
266  N = log(1 - p) / log(pNoOutliers);
267  }
268 
269  ++trialCount;
270  if(trialCount > options.maxTrial) {
271  break;
272  }
273  }
274  pCoef = bestCoef;
275  }
276 
277 private:
278  void dataToEigenSubset(std::set<int>& indexSet) {
279  int nVal = static_cast<int>(indexSet.size());
280  xM.resize(nVal, 1);
281  yM.resize(nVal, 1);
282 
283  int cnt = 0;
284  std::set<int>::iterator it = indexSet.begin();
285  while(it != indexSet.end()) {
286  xM(cnt, 0) = x[*it];
287  yM(cnt, 0) = y[*it];
288  ++cnt;
289  ++it;
290  }
291  }
292 
293  // Determine model inliers, based on max. allowed
294  // distance to fitted polynomial
295  int getInliers(std::vector<int>& inliers) {
296  // Full data to Eigen
297  dataToEigen();
298  buildV();
299 
300  Eigen::VectorXd diff = ((V * pCoef).array() - yM.col(0).array());
301  for(int i = 0; i < diff.rows(); ++i) {
302  if(pow(diff(i), 2) < options.tolerance)
303  inliers.push_back(i);
304  }
305  return static_cast<int>(inliers.size());
306  }
307 
308  // All (sample,signal) data to Eigen format
309  void dataToEigen(void) {
310  xM.resize(static_cast<int>(x.size()), 1);
311  yM.resize(static_cast<int>(y.size()), 1);
312 
313  for(int i = 0; i < static_cast<int>(x.size()); ++i)
314  xM(i, 0) = x[i];
315 
316  for(int i = 0; i < static_cast<int>(y.size()); ++i)
317  yM(i, 0) = y[i];
318  }
319 
320  // Vandermonde matrix
321  void buildV(void) {
322  V.resize(xM.rows(), options.polyDeg + 1);
323 
324  for(int i = 0; i < xM.rows(); ++i)
325  V(i, options.polyDeg) = 1;
326 
327  for(int d = 0; d < options.polyDeg; ++d)
328  for(int i = 0; i < xM.rows(); ++i)
329  V(i, d) = pow(xM(i, 0), options.polyDeg - d);
330  }
331 };
332 
333 // For convenience
334 template <typename T>
335 std::ostream& operator<<(std::ostream& out, PolyFit<T>& f) {
336  out << "Polynomial coefficients for " << f.getDegree() << "-nd order:" << std::endl;
337  out << f.getCoefficients() << std::endl;
338  out << "Solver type: " << f.getSolverType();
339  return out;
340 }
341 
342 #endif
PolyFit::setSamplePoints
void setSamplePoints(std::vector< T > &v)
Definition: polyfit.h:149
PolyFit::pCoef
VecType pCoef
Definition: polyfit.h:79
PolyFit::gen
boost::mt19937 gen
Definition: polyfit.h:68
PolyFit::randsample
void randsample(int N, int s, std::set< int > &ind)
Definition: polyfit.h:212
PolyFit
Definition: polyfit.h:38
PolyFit::MatType
Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > MatType
Definition: polyfit.h:73
PolyFit::VecType
Eigen::Matrix< T, Eigen::Dynamic, 1 > VecType
Definition: polyfit.h:76
PolyFit::setSignalPoints
void setSignalPoints(std::vector< T > &v)
Definition: polyfit.h:161
PolyFit::yM
VecType yM
Definition: polyfit.h:86
PolyFit::y
std::vector< T > & y
Definition: polyfit.h:82
PolyFit::dataToEigen
void dataToEigen(void)
Definition: polyfit.h:309
PolyFit::Options::maxMPts
int maxMPts
Definition: polyfit.h:57
PolyFit::buildV
void buildV(void)
Definition: polyfit.h:321
PolyFit::getDegree
int getDegree() const
Definition: polyfit.h:96
PolyFit::Options::tolerance
double tolerance
Definition: polyfit.h:58
PolyFit::Options::solver
SolverType solver
Definition: polyfit.h:54
PolyFit::getMSE
T getMSE(void)
Definition: polyfit.h:199
PolyFit::xM
VecType xM
Definition: polyfit.h:85
PolyFit::solveLS
T solveLS(void)
Definition: polyfit.h:173
PolyFit::solveRLS
void solveRLS(void)
Definition: polyfit.h:224
PolyFit::options
Options & options
Definition: polyfit.h:92
PolyFit::dataToEigenSubset
void dataToEigenSubset(std::set< int > &indexSet)
Definition: polyfit.h:278
PolyFit::getInliers
int getInliers(std::vector< int > &inliers)
Definition: polyfit.h:295
PolyFit::EIGEN_FULL_PIV_HOUSEHOLDER
@ EIGEN_FULL_PIV_HOUSEHOLDER
Definition: polyfit.h:42
PolyFit::Options::polyDeg
int polyDeg
Definition: polyfit.h:55
PolyFit::EIGEN_COL_PIV_HOUSEHOLDER
@ EIGEN_COL_PIV_HOUSEHOLDER
Definition: polyfit.h:41
PolyFit::getSolverType
SolverType getSolverType() const
Definition: polyfit.h:100
PolyFit::V
MatType V
Definition: polyfit.h:89
logging.h
PolyFit::PolyFit
PolyFit(std::vector< T > &x, std::vector< T > &y, Options &options)
Definition: polyfit.h:109
PolyFit::SolverType
SolverType
Definition: polyfit.h:40
PolyFit::getCoefficients
const VecType getCoefficients() const
Definition: polyfit.h:104
PolyFit::Options::maxTrial
int maxTrial
Definition: polyfit.h:56
galaxis::utils::log
void log(const Args &... args)
Definition: logger.hpp:33
PolyFit::Options::Options
Options()
Definition: polyfit.h:47
PolyFit::x
std::vector< T > & x
Definition: polyfit.h:81
PolyFit::Options
Definition: polyfit.h:46
PolyFit::evalPoly
std::vector< T > evalPoly(const std::vector< T > &at)
Definition: polyfit.h:122
PolyFit::DistributionType
boost::uniform_int< int > DistributionType
Definition: polyfit.h:69
PolyFit::EIGEN_JACOBI_SVD
@ EIGEN_JACOBI_SVD
Definition: polyfit.h:43
operator<<
std::ostream & operator<<(std::ostream &out, PolyFit< T > &f)
Definition: polyfit.h:335