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...
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.
Möglicherweise C oder Asm. Wäre ich aber nicht so sicher, die verfügbaren Bibs sind schon sehr gut.
Den Code selber ansehen, ob noch was optimiert werden kann, kann auch helfen, oder von mir aus auch so ein Peep-Hole-Optimizer. Je nachdem, ein wenig ausprobieren kostet nicht viel.
Man kann ja auch selbst eine Statistik machen, welcher Code besser performt.
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.
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.
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
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.
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.
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
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