15using namespace detail;
17std::vector<std::vector<double>>
filterTime(
const std::vector<std::vector<double>>& data,
18 double freq_filt,
double freq_acq) {
19 if (data.empty() || data[0].empty()) {
23 size_t nPoints = data.size();
24 size_t nFrames = data[0].size();
27 double wn = freq_filt / (freq_acq / 2.0);
28 if (wn >= 1.0) wn = 0.99;
29 if (wn <= 0.0) wn = 0.01;
34 std::vector<std::vector<double>> filtered(nPoints, std::vector<double>(nFrames, NAN));
39 for (
size_t ipt = 0; ipt < nPoints; ++ipt) {
42 for (
size_t iframe = 0; iframe < nFrames; ++iframe) {
43 if (std::isnan(data[ipt][iframe])) {
57 std::vector<double> padded;
58 padded.reserve(nFrames + 2 * npad);
61 for (
int i = 0; i < npad; ++i) {
62 padded.push_back(data[ipt][0]);
66 for (
size_t iframe = 0; iframe < nFrames; ++iframe) {
67 padded.push_back(data[ipt][iframe]);
71 for (
int i = 0; i < npad; ++i) {
72 padded.push_back(data[ipt][nFrames - 1]);
76 std::vector<double> result =
filtfilt(b, a, padded);
79 for (
size_t iframe = 0; iframe < nFrames; ++iframe) {
80 filtered[ipt][iframe] = result[npad + iframe];
87std::tuple<std::vector<std::vector<double>>, std::vector<std::vector<double>>,
88 std::vector<std::vector<double>>>
90 const std::vector<std::vector<double>>& disp_y,
91 const std::vector<std::vector<double>>& disp_z,
double freq_filt,
double freq_acq) {
92 auto filt_x =
filterTime(disp_x, freq_filt, freq_acq);
93 auto filt_y =
filterTime(disp_y, freq_filt, freq_acq);
94 auto filt_z =
filterTime(disp_z, freq_filt, freq_acq);
96 return {filt_x, filt_y, filt_z};
107 std::vector<std::complex<double>> poles;
108 for (
int k = 0; k <
order; ++k) {
109 double theta = M_PI * (2.0 * k +
order + 1) / (2.0 *
order);
110 poles.emplace_back(std::cos(theta), std::sin(theta));
114 double wp = 2.0 * std::tan(M_PI * wn / 2.0);
117 for (
auto& p : poles) {
122 std::vector<std::complex<double>> z_poles;
123 for (
const auto& p : poles) {
124 z_poles.push_back((2.0 + p) / (2.0 - p));
131 std::vector<double> b, a;
136 for (
const auto& zp : z_poles) {
137 k *= std::abs(1.0 - zp);
139 k = k / std::pow(2.0,
order);
142 b = {k, 4 * k, 6 * k, 4 * k, k};
150 std::vector<std::complex<double>> poly(5, 0.0);
153 for (
const auto& zp : z_poles) {
154 for (
int i = 4; i >= 1; --i) {
155 poly[i] = poly[i - 1] - zp * poly[i];
157 poly[0] = -zp * poly[0];
166 for (
int i = 0; i < 5; ++i) {
167 a[i] = poly[4 - i].real();
171 double alpha = std::sin(M_PI * wn) / (2.0 * 0.707);
172 double cos_w = std::cos(M_PI * wn);
174 b = {alpha, 2 * alpha, alpha};
175 a = {1.0 + alpha, -2.0 * cos_w, 1.0 - alpha};
188std::vector<double> filterDF2T(
const std::vector<double>& b,
const std::vector<double>& a,
189 const std::vector<double>& x, std::vector<double> z) {
190 const size_t n = x.size();
191 const size_t order = std::max(b.size(), a.size()) - 1;
194 const double a0 = a[0];
195 std::vector<double> bn(
order + 1, 0.0), an(
order + 1, 0.0);
196 for (
size_t i = 0; i < b.size(); ++i) bn[i] = b[i] / a0;
197 for (
size_t i = 0; i < a.size(); ++i) an[i] = a[i] / a0;
199 z.resize(
order, 0.0);
201 std::vector<double> y(n, 0.0);
202 for (
size_t i = 0; i < n; ++i) {
203 const double xi = x[i];
204 const double yi = bn[0] * xi + z[0];
205 for (
size_t j = 0; j + 1 <
order; ++j) {
206 z[j] = bn[j + 1] * xi + z[j + 1] - an[j + 1] * yi;
219std::vector<double> lfilterZi(
const std::vector<double>& b_in,
const std::vector<double>& a_in) {
220 const size_t order = std::max(b_in.size(), a_in.size()) - 1;
221 const double a0 = a_in[0];
222 std::vector<double> b(
order + 1, 0.0), a(
order + 1, 0.0);
223 for (
size_t i = 0; i < b_in.size(); ++i) b[i] = b_in[i] / a0;
224 for (
size_t i = 0; i < a_in.size(); ++i) a[i] = a_in[i] / a0;
226 std::vector<double> zi(
order, 0.0);
227 if (
order == 0)
return zi;
229 double a_sum = 0.0, b_minus = 0.0;
230 for (
size_t k = 0; k <=
order; ++k) a_sum += a[k];
231 for (
size_t k = 1; k <=
order; ++k) b_minus += b[k] - a[k] * b[0];
232 zi[0] = b_minus / a_sum;
234 double asum = 1.0, csum = 0.0;
235 for (
size_t k = 1; k <
order; ++k) {
237 csum += b[k] - a[k] * b[0];
238 zi[k] = asum * zi[0] - csum;
245std::vector<double>
filter(
const std::vector<double>& b,
const std::vector<double>& a,
246 const std::vector<double>& x) {
248 return filterDF2T(b, a, x, {});
251std::vector<double>
filtfilt(
const std::vector<double>& b,
const std::vector<double>& a,
252 const std::vector<double>& x) {
253 if (x.empty())
return x;
259 const std::vector<double> zi = lfilterZi(b, a);
262 std::vector<double> zi_fwd = zi;
263 for (
auto& v : zi_fwd) v *= x.front();
264 std::vector<double> y_forward = filterDF2T(b, a, x, zi_fwd);
267 std::vector<double> y_reversed(y_forward.rbegin(), y_forward.rend());
268 std::vector<double> zi_bwd = zi;
269 for (
auto& v : zi_bwd) v *= y_reversed.front();
270 std::vector<double> y_backward = filterDF2T(b, a, y_reversed, zi_bwd);
273 std::vector<double> y(y_backward.rbegin(), y_backward.rend());
std::vector< double > filter(const std::vector< double > &b, const std::vector< double > &a, const std::vector< double > &x)
Forward IIR filter.
std::pair< std::vector< double >, std::vector< double > > butterLowPass(int order, double wn)
Design Butterworth filter coefficients.
std::vector< double > filtfilt(const std::vector< double > &b, const std::vector< double > &a, const std::vector< double > &x)
Zero-phase digital filtering (MATLAB filtfilt equivalent)
DataSerializer - Abstract base class for format-agnostic DIC data I/O.
std::tuple< std::vector< std::vector< double > >, std::vector< std::vector< double > >, std::vector< std::vector< double > > > filterTime3D(const std::vector< std::vector< double > > &disp_x, const std::vector< std::vector< double > > &disp_y, const std::vector< std::vector< double > > &disp_z, double freq_filt=10.0, double freq_acq=50.0)
Apply temporal filtering to 3D displacement vectors.
std::vector< std::vector< double > > filterTime(const std::vector< std::vector< double > > &data, double freq_filt=10.0, double freq_acq=50.0)
Apply temporal Butterworth low-pass filter to displacement data.