Skip to content

Commit f238bd8

Browse files
authored
[pose-detection]Improve one euro filter and low pass filter. (#632)
FEATURE
1 parent 69c1723 commit f238bd8

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

pose-detection/src/calculators/interfaces/config_interfaces.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,11 @@ export interface OneEuroFilterConfig {
5353
number; // Cutoff frequency for derivate. It is set to 1Hz in the
5454
// original algorithm, but can be turned to further smooth the
5555
// speed (i.e. derivate) on the object.
56+
thresholdCutOff?:
57+
number; // The outlier threshold offset, will lead to a generally more
58+
// reactive filter that will be less likely to discount outliers.
59+
thresholdBeta?: number; // The outlier threshold slope, will lead to a filter
60+
// that will more aggressively react whenever the
61+
// keypoint speed increases by being less likely to
62+
// consider that an observation is an outlier.
5663
}

pose-detection/src/calculators/low_pass_filter.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,26 @@ export class LowPassFilter {
3131

3232
constructor(private alpha: number) {}
3333

34-
apply(value: number): number {
34+
apply(value: number, threshold?: number): number {
3535
let result;
3636
if (this.initialized) {
37-
result = this.alpha * value + (1 - this.alpha) * this.storedValue;
37+
if (threshold == null) {
38+
// Regular lowpass filter.
39+
// result = this.alpha * value + (1 - this.alpha) * this.storedValue;
40+
result = this.storedValue + this.alpha * (value - this.storedValue);
41+
} else {
42+
// We need to reformat the formula to be able to conveniently apply
43+
// another optional non-linear function to the
44+
// (value - this.storedValue) part.
45+
// Add additional non-linearity to cap extreme value.
46+
// More specifically, assume x = (value - this.storedValue), when x is
47+
// close zero, the derived x is close to x, when x is several magnitudes
48+
// larger, the drived x grows much slower then x. It behaves like
49+
// sign(x)log(abs(x)).
50+
result = this.storedValue +
51+
this.alpha * threshold *
52+
Math.asinh((value - this.storedValue) / threshold);
53+
}
3854
} else {
3955
result = value;
4056
this.initialized = true;
@@ -45,9 +61,9 @@ export class LowPassFilter {
4561
return result;
4662
}
4763

48-
applyWithAlpha(value: number, alpha: number): number {
64+
applyWithAlpha(value: number, alpha: number, threshold?: number): number {
4965
this.alpha = alpha;
50-
return this.apply(value);
66+
return this.apply(value, threshold);
5167
}
5268

5369
hasLastRawValue(): boolean {

pose-detection/src/calculators/one_euro_filter.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import {LowPassFilter} from './low_pass_filter';
2222
*/
2323
// ref:
2424
// https://github.com/google/mediapipe/blob/master/mediapipe/util/filtering/one_euro_filter.cc
25+
// Also ref original paper:
26+
// https://cristal.univ-lille.fr/~casiez/1euro/
2527
export class OneEuroFilter {
2628
private frequency: number;
2729
private minCutOff: number;
@@ -30,6 +32,8 @@ export class OneEuroFilter {
3032
private x: LowPassFilter;
3133
private dx: LowPassFilter;
3234
private lastTimestamp: number;
35+
private thresholdCutOff: number;
36+
private thresholdBeta: number;
3337
/**
3438
* Constructor of `OneEuroFilter` class.
3539
* @param config See documentation of `OneEuroFilterConfig`.
@@ -38,6 +42,8 @@ export class OneEuroFilter {
3842
this.frequency = config.frequency;
3943
this.minCutOff = config.minCutOff;
4044
this.beta = config.beta;
45+
this.thresholdCutOff = config.thresholdCutOff;
46+
this.thresholdBeta = config.thresholdBeta;
4147
this.derivateCutOff = config.derivateCutOff;
4248
this.x = new LowPassFilter(this.getAlpha(this.minCutOff));
4349
this.dx = new LowPassFilter(this.getAlpha(this.derivateCutOff));
@@ -77,8 +83,12 @@ export class OneEuroFilter {
7783
this.dx.applyWithAlpha(dValue, this.getAlpha(this.derivateCutOff));
7884
const cutOff = this.minCutOff + this.beta * Math.abs(edValue);
7985

86+
const threshold = this.thresholdCutOff != null ?
87+
this.thresholdCutOff + this.thresholdBeta * Math.abs(edValue) :
88+
null;
89+
8090
// filter the given value.
81-
return this.x.applyWithAlpha(value, this.getAlpha(cutOff));
91+
return this.x.applyWithAlpha(value, this.getAlpha(cutOff), threshold);
8292
}
8393

8494
private getAlpha(cutoff: number): number {

0 commit comments

Comments
 (0)