Ein Medianfilter ist gut geeignet, um Ausreißer wegzufiltern.
Ich bin auf der Suche nach einer Effizienten Routine in C++.
Es gibt scheinbar haufenweise Implementierungen, aber welche Art ist die
effizienteste?
Im Moment scheint mir die Verwendung einer "linked List" am besten:
https://community.infineon.com/gfawx74859/attachments/gfawx74859/PSoC6MCU/8068/2/MedianFilter_v0_0_A.pdf
Gibt es schnellere Möglichkeiten?
Die Ausreißer sind eher selten und der Filter soll eine Länge zwischen
3,5,7,9 Samples haben.
Für den Rechenzeitbedarf würde ich kleiner gleich 20us auf einem
Atmega328 für die Länge 3 und int16_t erwarten.
Christoph M. schrieb:> Für den Rechenzeitbedarf würde ich kleiner gleich 20us auf einem> Atmega328 für die Länge 3 und int16_t erwarten
Das wären mehr als reichliche 320 Taktzyklen bei 16MHz.
Da kann man's ja fast in COBOL schreiben.
Christoph M. schrieb:> Gibt es schnellere Möglichkeiten?
It depends...
Bei kleinen Filterlängen, 3,5,7, ist binäre Suche in einem Array dessen
Elemente verschoben werden oft schneller. Bei grossen Filterlängen ein
B-Baum.
Michael B. schrieb:> It depends...
Absolut.
> Bei kleinen Filterlängen, 3,5,7, ist binäre Suche in einem Array dessen> Elemente verschoben werden oft schneller. Bei grossen Filterlängen ein> B-Baum.
Die genaue Grenze hängt von der Architektur ab, vom Compiler und vor
allem auch vom Signal selber. Es ist also sehr schwierig bis nahezu
unmöglich, hier eine genaue genaue Grenze für die Entscheidung Array vs.
B-Tree anzugeben. Da hilft nur: beides Implementieren und dann
Ausprobieren, was im konketen Fall besser performt.
Und nicht mal dann ist man auf der sicheren Seite: die
Signaleigenschaften können sich schließlich auch ändern...
Michael B. schrieb:> Bei kleinen Filterlängen, 3,5,7,
Was heißt denn hier Filterlänge? Zahl der zu untersuchenden letzten
Samples? Wieviele sollen behalten werden? Und wie wird der Median
bestimmt? Warum nicht gleich addieren und den kleinsten / größten
mitnehmen und jeweils entscheiden? Das wäre das Einfachste, wenn immer
nur ein Ausreisser gefiltert werden muss.
Und dann braucht es noch eine Definition für Ausreisser, weil das ja
auch ein sich ändernder Messwert sein kann.
Kati schrieb:> Und wie wird der Median bestimmt?
Das ist definiert und bereits millionenfach beschrieben.
> Warum nicht gleich addieren und den kleinsten / größten> mitnehmen und jeweils entscheiden?
Das ist mit beinahe chirurgischer Präzision das exakte Gegenteil.
Hi,
meines Kenntnisstands nach heißt 9 sample median: 9 samples der Größe
nach sortieren und den Fünftgrößten nehmen. Wenn ein neuer kommt, den
ältesten aus der Liste rausnehmen und den neuen einsortieren. Sortieren
ist O(n*log(n)), Einsortieren in eine vorsortierte Liste ist vielleicht
sogar nur O(log(n)). Das geht schnell.
Interessantes Problem, wenns morgen regnet probier ich das vllt. mal
aus.
Cheers
Detlef
Peter M. schrieb:> Hallo Detlef_.,>> ob das denn wohl so sinnvoll ist, mit der O()-Notation für n=9 zu> argumentieren? :)
Ja ebent, das geht immer schnell.
Hier hab ich einen mittelmäßig effizienten medianfilter gebaut, Laufzeit
ist O(n). Es wird eine sortierte Liste mitgeführt. Es wird aber noch so
einiges hin und hergeschoben, das ist unschön und kostet Zeit. Mit mehr
Überlegen und mehr Listen könnte man die Laufzeit vllt. auf O(log(n))
drücken, aber, wie Peter ja richtig sagte, n ist sowieso klein.
Cheers
Detlef
>Hier hab ich einen mittelmäßig effizienten medianfilter gebaut, Laufzeit>ist O(n).
Danke für den Code. Wenn ich darauf schaue, müsste die Laufzeit gegen
gehen.
Das scheint mir eher eine Debug-Version zu sein, im Code steht
haufenweise:
Christoph M. schrieb:> Das scheint mir eher eine Debug-Version zu sein, im Code steht> haufenweise:> while(1)
Vielleicht machst du dich mal mit ›break‹ vertraut. ;-)
Detlef _. schrieb:> // Wo musser denn hin?> k=0;> while(1) {> if(alt[gross[k+1]]>neu) break;> k++;> if(k==NN-1) {break;};> }
Süß. Meine erstes Bubblesort auf dem C64 vor genau 40 Jahren war ähnlich
gut lesbar.
>Vielleicht machst du dich mal mit ›break‹ vertraut. ;-)
Upps, ich bin damit vertraut, aber trotz angestrengtem Hinschauen hat es
mein Hirn ignoriert.
Die Detlev'sche Version ist etwas schneller als die Tillaart'sche :-)
Ob der Median richtig sortiert, habe ich nicht geprüft, nur wie schnell
der Algo läuft ..
1
// Algo from Detlef _. (detlef_a) 19.05.2024 20:53
Hab's gerade mal in – Obacht – Python programmiert und auf dem
Mikrocontroller laufen lassen.
FILTERSIZE = 9
same values: 10.21 µs
linear up: 24.76 µs
linear down: 23.29 µs
random values: 24.09 µs
Gar nicht mal so übel. Kommt zwar nicht an die Thumb Assembler Version
heran, aber immerhin gut für ~40.000 Werte pro Sekunde…
Klaus schrieb:> Norbert schrieb:>> FILTERSIZE = 9>> Christoph M. schrieb:>> #define NN (3)>> Äpfel und Birnen?
Nein Klaus, viel einfacher. Einmal Drei und einmal Neun. Kann man auch
mit etwas Eigeninitiative ablesen.
Hint: Wenn man's mit ›3‹ macht, dann wird's schneller.
Wenn man's mit ›15‹ macht, dann wird's langsamer.
Hint2: Es ging nicht um einen direkten Vergleich.
Hint3: Bei einer Filterlänge von ›3‹ wäre der Aufwand des beschriebenen
Algorithmus um Größenordnungen zu hoch!
Das könnte man mit einigen wenigen simplen Vergleichen viel schneller
haben.
Christoph M. schrieb:> Upps, ich bin damit vertraut, aber trotz angestrengtem Hinschauen hat es> mein Hirn ignoriert.
Ist auch ein bisschen ungewöhnlich bis pervers, die Abbruchbedingung in
ein if zu packen, statt direkt in das while....
>Hab's gerade mal in – Obacht – Python programmiert und auf dem> Mikrocontroller laufen lassen.
Könntest du den Python-Code posten? Ich würde es gerne "nacheruieren".
Welchen Mikrocontroller hast du verwendet? Da gibt es große Unterschiede
in der Geschwindigkeit.
Hier mal die Laufzeit auf dem Atmega328 mit int16-Werten und Indexlänge
uint8.
Zieht man die Laufzeit der leeren Funktion von oben (~4us) ab, kommt man
auf eine durchschnittliche Laufzeit von 6us.
Nicht schlecht, würde ich sagen.
Christoph M. schrieb:> Jetzt besser?:
Genau.
Vielleicht kannst Die Laufzeit mal mit einer einfachen straight-forward
Implementierung vergleichen?
Ich hätte das so implementiert:
Klaus (feelfree)
>Vielleicht kannst Die Laufzeit mal mit einer einfachen straight-forward>Implementierung vergleichen?
Deine Implementierung ist auf jeden Fall schön übersichtlich. Ich habe
ein wenig gezögert, weil ich vermutete, dass die in der in der Arduino
IDE verwendete Compiler keine STD-Lib und damit kein QSort kennt. Aber
siehe da, es kompiliert out of the Box.
Die Performance ist allerdings nicht so gut wie bei der Detlev'schen
Version.
Hiermit möchte ich Detlev für seinen Beitrag ausdrücklich loben.
Hallo, vielen Dank. Ich bin noch nicht zufrieden. Dieses Umschaufeln ist
nicht cool. In eine sortierte Liste ein Element neu einsortieren geht
mit 'linear merge' in O(log log n), mein hochbubbeln ist O(n).Das
älteste Element fliegt raus. Ich überlege an einer Datenstruktur die
einerseits immer direkten Zugriff auf das älteste Element gibt aber
andererseits schnelles Einsortieren des Neuen erlaubt. Wenn der Neue
kommt werden alle eins älter, alle Altersangaben eins hochzählen ist
aber O(n) .
Hm
Cheers
Detlef
Detlef _. schrieb:> Wenn der Neue> kommt werden alle eins älter, alle Altersangaben eins hochzählen ist> aber O(n)
Das Alter durch stures inkrementieren darstellen? Ein neues Element
bekommt eine höhere Zahl. der älteste hat dann die kleinste Zahl.
Vielleicht könnte eine "Linked List" helfen, auf die zusätzlich ein
Pointer mit dem Alter zeigt. Ich vermute aber, dass bei kurzen
Medianfiltern dann eher der Verwaltungsaufwand zu groß ist.
Hätte nicht gedacht, dass qsort() bei kurzen Listen soo schlecht
performt....
Hier mal eine Version ohne qsort.
Eine binäre Suche nach der einzusortierenden Wert lohnt sich glaube ich
bei so kurzen Listen kaum.
Bleibt immer noch die unschöne lineare Suche nach dem ältesten Wert.
1
median_tmedian3(median_tnew_value)
2
{
3
staticuint8_tinitialized=0;
4
staticint32_tact_age=0;
5
6
structlistelement
7
{
8
median_tvalue;
9
uint8_tage;
10
};
11
staticstructlistelementlist[NN];
12
13
uint8_ti;
14
uint8_tindex_new;
15
uint8_tindex_to_delete;
16
17
if(!initialized)
18
{
19
for(i=0;i<NN;i++)
20
list[i].age=i;
21
initialized=1;
22
}
23
24
for(i=0;i<NN;i++)
25
{
26
if(new_value<list[i].value)break;
27
}
28
index_new=i;
29
30
for(i=0;i<NN;i++)
31
{
32
if(list[i].age==act_age)break;
33
}
34
index_to_delete=i;
35
36
if(index_to_delete<index_new)
37
{
38
index_new--;
39
// move elements [index_to_delete+1 .. index_new] to the left