Skip to content

Commit 809f02c

Browse files
committed
Add hawku/TabletDriver TabletFilterNoiseReduction
1 parent fe0ef42 commit 809f02c

1 file changed

Lines changed: 189 additions & 0 deletions

File tree

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using TabletDriverPlugin.Attributes;
4+
using TabletDriverPlugin.Tablet;
5+
6+
namespace TabletDriverPlugin
7+
{
8+
[PluginName("TabletDriver Noise Reduction")]
9+
public class TabletDriverNoiseReduction : IFilter
10+
{
11+
private LinkedList<Point> _buffer = new LinkedList<Point>();
12+
private float _distThreshold, _distMax;
13+
private const int _iterations = 10;
14+
private int _samples = 10;
15+
private Point _lastPoint;
16+
17+
public Point Filter(Point point)
18+
{
19+
SetTarget(point);
20+
21+
if (_buffer.Count <= 1)
22+
{
23+
return SetOutput(point);
24+
}
25+
26+
// Calculate geometric median from the buffer positions
27+
GetGeometricMedianVector(ref _lastPoint);
28+
29+
// Distance between latest position and ring buffer
30+
var distance = point.DistanceFrom(_lastPoint);
31+
32+
// Distance larger than threshold -> modify the ring buffer
33+
if (distance > DistThreshold)
34+
{
35+
// Ratio between current distance and maximum distance
36+
double distanceRatio;
37+
38+
// Distance ratio should be between 0.0 and 1.0
39+
// 0.0 -> distance == distanceThreshold
40+
// 1.0 -> distance == distanceMaximum
41+
distanceRatio = (distance - DistThreshold) / (_distMax - DistThreshold);
42+
43+
if (distanceRatio >= 1f)
44+
{
45+
// Distance larger than maximum -> fill buffer with the latest target position
46+
var bufCount = _buffer.Count;
47+
_buffer.Clear();
48+
for (int i = 0; i < bufCount; i++)
49+
_buffer.AddLast(point);
50+
return SetOutput(point);
51+
}
52+
else
53+
{
54+
// Move buffer positions and current position towards the latest target using linear interpolation
55+
// Amount of movement is the distance ratio between threshold and maximum
56+
var bufEnum = _buffer.GetEnumerator();
57+
58+
// buffer.LerpAdd()
59+
while (bufEnum.MoveNext())
60+
{
61+
bufEnum.Current.X += (float)((point.X - bufEnum.Current.X) * distanceRatio);
62+
bufEnum.Current.Y += (float)((point.Y - bufEnum.Current.Y) * distanceRatio);
63+
}
64+
65+
// outputPosition.LerpAdd()
66+
_lastPoint.X += (float)((point.X - _lastPoint.X) * distanceRatio);
67+
_lastPoint.Y += (float)((point.Y - _lastPoint.Y) * distanceRatio);
68+
69+
// New logging feature available only in TDPlugin 0.3.2
70+
// Log.Write("NoiseReduction", "Noise Reduced! " + _lastPoint, LogLevel.Debug);
71+
return _lastPoint;
72+
}
73+
}
74+
return SetOutput(point);
75+
}
76+
77+
private void SetTarget(Point point)
78+
{
79+
_buffer.AddLast(point);
80+
while (_buffer.Count > Samples)
81+
_buffer.RemoveFirst();
82+
}
83+
84+
private Point SetOutput(Point point)
85+
{
86+
_lastPoint = point;
87+
return point;
88+
}
89+
90+
private Point GetGeometricMedianVector(ref Point point)
91+
{
92+
var candidate = new Point();
93+
var next = new Point();
94+
var minimumDistance = 0.001;
95+
96+
double denominator, weight, distance;
97+
98+
// Calculate the starting position
99+
if (!GetAverageVector(ref candidate))
100+
return _lastPoint;
101+
102+
// Iterate
103+
for (int iteration = 0; iteration < _iterations; iteration++)
104+
{
105+
denominator = 0;
106+
107+
// Loop through the buffer and calculate a denominator.
108+
foreach (var bufferPoint in _buffer)
109+
{
110+
distance = candidate.DistanceFrom(bufferPoint);
111+
112+
if (distance > minimumDistance)
113+
denominator += 1.0 / distance;
114+
else
115+
denominator += 1.0 / minimumDistance;
116+
}
117+
118+
// Reset the next vector
119+
next.X = 0;
120+
next.Y = 0;
121+
122+
// Loop through the buffer and calculate a weighted average
123+
foreach (var bufferPoint in _buffer)
124+
{
125+
distance = candidate.DistanceFrom(bufferPoint);
126+
127+
if (distance > minimumDistance)
128+
weight = 1.0 / distance;
129+
else
130+
weight = 1.0 / minimumDistance;
131+
132+
next.X += (float)(bufferPoint.X * weight / denominator);
133+
next.Y += (float)(bufferPoint.Y * weight / denominator);
134+
}
135+
136+
// Set the new candidate vector
137+
candidate.X = next.X;
138+
candidate.Y = next.Y;
139+
}
140+
141+
// Set output
142+
point.X = candidate.X;
143+
point.Y = candidate.Y;
144+
return point;
145+
}
146+
147+
private bool GetAverageVector(ref Point point)
148+
{
149+
if (_buffer.Count == 0)
150+
return false;
151+
152+
point.X = 0;
153+
point.Y = 0;
154+
155+
foreach (var bufferPoint in _buffer)
156+
{
157+
point.X += bufferPoint.X;
158+
point.Y += bufferPoint.Y;
159+
}
160+
161+
point.X /= _buffer.Count;
162+
point.Y /= _buffer.Count;
163+
return true;
164+
}
165+
166+
[Property("Buffer")]
167+
public int Samples
168+
{
169+
set
170+
{
171+
_samples = Math.Clamp(value, 0, 20);
172+
}
173+
get => _samples;
174+
}
175+
176+
[UnitProperty("Distance Threshold", "px")]
177+
public float DistThreshold
178+
{
179+
set
180+
{
181+
_distThreshold = Math.Clamp(value, 0, 10);
182+
_distMax = value * 2;
183+
}
184+
get => _distThreshold;
185+
}
186+
187+
public FilterStage FilterStage => FilterStage.PostTranspose;
188+
}
189+
}

0 commit comments

Comments
 (0)