sablib
Loading...
Searching...
No Matches
goldindec.cpp
Go to the documentation of this file.
1
6
7#include <cmath>
8#include <functional>
9#include <iostream>
10
11#include "../misc/polyfit.h"
12
13#include "goldindec.h"
14
15namespace sablib {
16
17//
18// Implementation of BaselineGoldindec() function
19//
20const std::vector<double>
22 const std::vector<double> & y, const unsigned int polyorder,
23 double peak_ratio, const double alpha, const unsigned int loop, const double eps,
24 const unsigned int loop_legend, const double eps_legend, const double eps_s
25)
26{
27 if(y.size() == 0) {
28 throw std::invalid_argument("BaselineGoldindec(): the length of y is zero.");
29 }
30
31 if(polyorder == 0) {
32 throw std::invalid_argument("BaselineGoldindec(): polyorder is zero.");
33 }
34
35 if(peak_ratio <= 0 || 1 < peak_ratio) {
36 throw std::invalid_argument("BaselineGoldindec(): peak_ratio must be between 0 and 1.");
37 }
38
39 if(alpha <= 0 || 1 < alpha) {
40 throw std::invalid_argument("BaselineGoldindec(): alpha must be between 0 and 1.");
41 }
42
43 if(loop == 0) {
44 throw std::invalid_argument("BaselineGoldindec(): loop is zero.");
45 }
46
47 if(eps <= 0) {
48 throw std::invalid_argument("BaselineGoldindec(): non-positive eps value is given.");
49 }
50
51 if(loop_legend == 0) {
52 throw std::invalid_argument("BaselineGoldindec(): loop_legend is zero.");
53 }
54
55 if(eps_legend <= 0) {
56 throw std::invalid_argument("BaselineGoldindec(): non-positive eps_legend value is given.");
57 }
58
59 if(eps_s <= 0) {
60 throw std::invalid_argument("BaselineGoldindec(): non-positive eps_s value is given.");
61 }
62
63 Eigen::VectorXd yy = Eigen::VectorXd::Map(y.data(), y.size());
64 Eigen::VectorXd x = Eigen::VectorXd::LinSpaced(y.size(), 0, 1);
65 Eigen::MatrixXd V = Vandermonde(x, polyorder);
66 Eigen::LDLT<Eigen::MatrixXd> ldltV = (V.transpose() * V).ldlt();
67
68 double a = 0, b = 1;
69 double rud = 0.7679
70 + 11.2358 * peak_ratio
71 - 39.7064 * peak_ratio * peak_ratio
72 + 92.3583 * peak_ratio * peak_ratio * peak_ratio; // See Liu's paper.
73 double s = a + 0.618 * (b - a), s_old = s;
74
75 auto derivative = [&](const double xx) {
76 if(xx < s) {
77 return 2 * xx;
78 }
79 else {
80 return -s * s * s / (2 * xx * xx);
81 }
82 };
83
84 Eigen::VectorXd coeff = ldltV.solve(V.transpose() * yy);
85 Eigen::VectorXd z = PolyVal(coeff, V);
86
87 for(unsigned int i = 0; i < loop; i++) {
88 // LEGEND algorithm
89 for(unsigned int j = 0; j < loop_legend; j++) {
90 Eigen::VectorXd z_old = z;
91
92 Eigen::VectorXd e = yy - z_old;
93 Eigen::VectorXd d = -e + alpha * e.unaryExpr(derivative);
94 coeff = ldltV.solve(V.transpose() * (yy + d));
95 z = PolyVal(coeff, V);
96
97 if((z - z_old).norm() / z_old.norm() < eps_legend) {
98 break;
99 }
100 }
101
102 double up_down_ratio = (double)(yy.array() >= z.array()).count() / (double)(yy.array() < z.array()).count();
103 double diff = up_down_ratio - rud;
104
105 if(diff > eps) {
106 a = s;
107 }
108 else if(diff < -eps) {
109 b = s;
110 }
111 else {
112 break;
113 }
114
115 s = a + 0.618 * (b - a);
116 // std::cerr << "up_down_ratio = " << up_down_ratio << ", s = " << s << std::endl;
117
118 // Based on experimentation, it seems better to set a threshold for s as well.
119 if(std::fabs(s - s_old) < eps_s) {
120 break;
121 }
122
123 s_old = s;
124 }
125
126 std::vector<double> result(z.size());
127 Eigen::VectorXd::Map(result.data(), result.size()) = z;
128
129 return result;
130}
131
132}; // namespace sablib
const std::vector< double > BaselineGoldindec(const std::vector< double > &y, const unsigned int polyorder, double peak_ratio, const double alpha, const unsigned int loop, const double eps, const unsigned int loop_legend, const double eps_legend, const double eps_s)
Performs baseline estimation using the Goldindec algorithm.
Definition goldindec.cpp:21
Baseline estimation using the Goldindec algorithm.
Polynomial fitting using the least squares method (Gauss-Newton for linear models) and evaluating pol...
const Derived::PlainObject PolyVal(const Eigen::MatrixBase< Derived > &coeff, const Eigen::MatrixX< typename Derived::PlainObject::Scalar > &V)
Evaluates the polynomial at specified points using a pre-calculated Vandermonde matrix.
Definition polyfit.h:122
const Eigen::MatrixX< typename Derived::PlainObject::Scalar > Vandermonde(const Eigen::MatrixBase< Derived > &x, const unsigned int polyorder)
Generates a Vandermonde matrix for a given vector and polynomial order.
Definition polyfit.h:25