Effizienter Gleitender Durchschnitt
Ich habe vor kurzem über Fortschritte in der Antwort auf diesen Beitrag gelernt. Und frage mich, wie ich sie verwenden, um einen gleitenden Durchschnitt Filter effizienter als das, was ich in diesem Beitrag vorgeschlagen (mit Faltungs-Filter) zu berechnen. Dies ist, was ich bisher habe. Es nimmt eine Ansicht des ursprünglichen Arrays dann rollt es um die notwendige Menge und summiert die Kernel-Werte, um den Durchschnitt zu berechnen. Ich bin mir bewusst, dass die Kanten nicht richtig gehandhabt werden, aber danach kann ich mich darum kümmern. Gibt es einen besseren und schnelleren Weg Das Ziel ist es, große Gleitkomma-Arrays bis zu 5000x5000 x 16 Schichten in der Größe zu filtern, eine Aufgabe, die scipy. ndimage. filters. convolve ist ziemlich langsam auf. Beachten Sie, dass ich nach 8-Nachbar-Konnektivität suche, dh ein 3x3-Filter nimmt den Durchschnitt von 9 Pixeln (8 um das Brennpixel) und weist diesen Wert dem Pixel im neuen Bild zu. EDIT Klarstellung, wie ich diese Arbeit sehen: Verwenden Sie stridetricks, um ein Array wie 0,1,2,1,2,3,2,3,4 generieren. Die der oberen Zeile des Filterkerns entspricht. Rollen Sie entlang der vertikalen Achse, um die mittlere Reihe des Kerns 10,11,12,11,12,13,13,14,15 zu erhalten. Und fügen Sie es auf das Array ich bekam in 1) Wiederholen, um die untere Zeile des Kernels 20,21,22,21,22,23,22,23,24 erhalten. . An diesem Punkt nehme ich die Summe jeder Zeile und dividiere sie durch die Anzahl der Elemente im Filter, wobei mir der Durchschnitt für jedes Pixel, (um 1 Zeile und 1 col verschoben, und mit einigen Merkwürdigkeiten um die Kanten, aber ich kann Kümmere dich darum später). Was ich erhoffte, ist eine bessere Verwendung von stridetricks, um die 9 Werte oder die Summe der Kernel-Elemente direkt, für das gesamte Array zu erhalten, oder dass jemand mich von einer anderen effizienteren Methode überzeugen kann. Fragte am 8. Februar um 18:05 für was sein Wert, heres, wie youd es mit fancy schreitenden Tricks tun. Ich wollte diese Post gestern, sondern wurde von der tatsächlichen Arbeit abgelenkt. ) Paul amp essen beide haben schöne Implementierungen mit verschiedenen anderen Möglichkeiten, dies zu tun. Nur um die Dinge aus der früheren Frage fortzusetzen, dachte ich Id post das N-dimensionale Äquivalent. Sie werden nicht in der Lage, scipy. ndimage Funktionen für 1D-Arrays, aber deutlich zu schlagen. (Scipy. ndimage. uniformfilter sollte schlagen scipy. ndimage. convolve. though) Darüber hinaus, wenn youre versuchen, ein multidimensionales bewegliches Fenster zu erhalten, riskieren Sie, dass Speicherverbrauch sprengen, wenn Sie versehentlich eine Kopie des Arrays zu machen. Während das anfängliche rollende Array gerade eine Ansicht in den Gedächtnis Ihres ursprünglichen Arrays ist, bilden alle Zwischenschritte, die das Array kopieren, eine Kopie, die Größenordnungen größer als Ihr ursprüngliches Array ist (dh Lets sagen, dass youre, das mit einem 100x100 ursprünglichen Array arbeitet (Für eine Filtergröße von (3,3)) wird 98x98x3x3, aber verwenden Sie den gleichen Speicher wie das Original. Allerdings werden alle Kopien die Menge an Speicher verwenden, dass eine vollständige 98x98x3x3 Array würde) Grundsätzlich mit verrückt Schreitenden Tricks ist ideal, wenn Sie verschieben Fenster Operationen auf einer einzigen Achse eines ndarray vektorisieren möchten. Es macht es wirklich einfach, Dinge wie eine bewegte Standardabweichung, etc. mit sehr wenig Overhead zu berechnen. Wenn Sie anfangen, dies zu tun entlang mehrerer Achsen, seine mögliche, aber youre in der Regel besser mit mehr spezialisierte Funktionen. (Wie scipy. ndimage etc.) Auf jeden Fall ist heres, wie Sie es tun: Also, was wir bekommen, wenn wir b Rollingwindow (a, filtsize) ist ein 8x8x3x3 Array, das ist eigentlich eine Ansicht in den gleichen Speicher wie das Original 10x10 Array. Wir könnten genauso leicht verschiedene Filtergrößen entlang verschiedener Achsen verwenden oder nur entlang ausgewählter Achsen eines N-dimensionalen Arrays betrieben werden (dh Filzgröße (0,3,0,3) auf einem 4-dimensionalen Array würde uns eine 6-dimensionale Ansicht geben ). Wir können dann eine beliebige Funktion auf die letzte Achse anwenden, um die Dinge in einem sich bewegenden Fenster effektiv zu berechnen. Jedoch, weil waren die Speicherung von temporären Arrays, die viel größer als unsere ursprüngliche Array auf jedem Schritt der Mittelwert (oder std oder was auch immer), das ist überhaupt nicht Speicher effizient Sein auch nicht zu sein, furchtbar schnell, entweder. Das Äquivalent für ndimage ist nur: Dies wird eine Vielzahl von Randbedingungen behandeln, das Unschärfen an Ort und Stelle durchführen, ohne eine temporäre Kopie des Arrays zu benötigen, und sehr schnell sein. Schiebe-Tricks sind eine gute Möglichkeit, eine Funktion auf ein bewegtes Fenster entlang einer Achse anzuwenden, aber theyre nicht ein guter Weg, um es entlang mehrerer Achsen zu tun, in der Regel. Nur meine 0.02, jedenfalls. Sehr gut: Schiebe-Tricks sind eine gute Möglichkeit, eine Funktion auf ein bewegtes Fenster entlang einer Achse anzuwenden, aber sie sind nicht ein guter Weg, um es entlang mehrerer Achsen zu tun, in der Regel. Und natürlich ist Ihre Erklärung der Erinnerung 39blow up39 wichtig. Art der Zusammenfassung aus Ihrer Antwort (zumindest für mich) ist: 39don39t gehen zu weit Angeln, ist der Quarterteed Fang allready in scipy39. Thanks ndash eat Feb 9 11 at 16:37 Vielen Dank, Joe, für diese Antwort. In rollendem Fenster sollte das, wenn nicht hasattr (.): Rollingwindowlastaxis (.) Statt Rolling zurückgeben. Ndash unutbu Ich bin nicht vertraut genug mit Python, um Code für das zu schreiben, aber die beiden besten Möglichkeiten zur Beschleunigung von Windungen ist entweder trennen Sie den Filter oder die Fourier-Transformation verwenden. Getrennter Filter. Faltung ist O (MN), wobei M und N die Anzahl der Pixel in dem Bild bzw. dem Filter sind. Da die durchschnittliche Filterung mit einem 3-mal-3-Kernel dem Filtern zuerst mit einem 3-mal-1-Kernel und dann einem 1-mal-3-Kernel gleichkommt, können Sie (33) 30-Geschwindigkeitsverbesserung durch konsekutive Faltung mit erhalten Zwei 1-d-Kerne (dies wird offensichtlich besser, da der Kernel größer wird). Sie können immer noch in der Lage, Schritt-Tricks hier zu verwenden, natürlich. Fourier-Transformation . Conv (A, B) ist äquivalent zu ifft (fft (A) fft (B)). D. h. eine Faltung im direkten Raum wird eine Multiplikation im Fourierraum, wobei A dein Bild und B dein Filter ist. Da die (elementweise) Multiplikation der Fourier-Transformationen A und B gleich groß ist, ist B ein Array der Größe (A) mit dem Kernel im Zentrum des Bildes und Nullen überall. Um einen 3-mal-3-Kernel in der Mitte eines Arrays zu platzieren, müssen Sie A bis ungerade Größe auffüllen. Abhängig von der Implementierung der Fourier-Transformation kann dies viel schneller sein als die Faltung (und wenn Sie denselben Filter mehrmals anwenden, können Sie fft (B) vorberechnen und weitere 30 Rechenzeit sparen). Antwortete am 9. Februar um 15:27 Für was es wert ist, in python, sind diese in scipy. ndimage. uniformfilter und scipy. signal. fftconvolve implementiert. beziehungsweise. Ndash Joe Kington Jonas: Cool Der seperate Filter Ansatz funktioniert gut, wie Sie sagen, es spart mehr Zeit als die Kernel-Größe erhöht. Für ein 5000x5000-Array, bei einer 11x11-Kernelgröße, bekomme ich 7.7s für 2d Convolution mit ndimage. convolve und 2.0s für zwei 1d-Convolutions mit ndimage. convolve1d. Für Ihre zweite Lösung, was ist B ndash Benjamin Eine Sache, die ich bin zuversichtlich, muss behoben werden, ist Ihr View-Array b. Es hat ein paar Elemente aus nicht zugeordneten Speicher, so youll erhalten Abstürze. Angesichts Ihrer neuen Beschreibung Ihres Algorithmus, ist die erste Sache, die Fixierung ist die Tatsache, dass Sie außerhalb der Zuweisung von a: Weil Im noch nicht ganz erfassen die Methode und es scheint einfacher Wege, um das Problem zu lösen, Im nur gehen Um das hier zu sagen: das scheint nur der einfache Ansatz zu sein. Der einzige äußere Vorgang ist, dass er B nur einmal zugewiesen und bevölkert hat. Der Zusatz, die Teilung und die Indexierung müssen unbedingt durchgeführt werden. Wenn Sie 16 Bands machen, müssen Sie B nur noch einmal zuweisen, wenn Sie die Absicht haben, ein Bild zu speichern. Auch wenn dies keine Hilfe ist, könnte es klären, warum ich nicht verstehen, das Problem, oder zumindest dienen als ein Maßstab für die Zeit der Beschleunigungen von anderen Methoden. Dies läuft in 2,6 Sekunden auf meinem Laptop auf einem 5k x 5k-Array von float64s, 0,5 von denen die Schaffung von B beantwortet ist nicht so klar, Ihre Frage, aber Im, dass jetzt, dass youll verbessern möchten Deutlich diese Art der Mittelung. Nun, welche Art von Leistungsverbesserungen würden Sie eigentlich erwarten Update: Zunächst einmal eine Warnung: der Code in seinem aktuellen Zustand passt sich nicht richtig an die Kernform an. Aber das ist nicht meine primäre Sorge jetzt (sowieso ist die Idee gibt es bereits, wie man richtig anpassen). Ich habe gerade die neue Form eines 4D A intuitiv gewählt, für mich ist es wirklich sinnvoll, darüber nachzudenken, ein 2D-Kernel zentriert zu jedem Raster Position des ursprünglichen 2D A. Aber dass 4D-Gestaltung kann nicht wirklich die beste sein. Ich denke, das eigentliche Problem hier ist die Leistung der Summierung. Man sollte in der Lage, die beste Ordnung (der 4D A) zu finden, um voll zu nutzen Ihre Maschinen Cache-Architektur. Allerdings kann diese Reihenfolge nicht die gleichen für kleine Arrays, die Art der Zusammenarbeit mit Ihrem Maschinen-Cache und die größeren, die nicht (zumindest nicht so einfach). Update 2: Hier ist eine leicht modifizierte Version von mf. Offensichtlich ist es besser, wieder zu einem 3D-Array zuerst und dann anstatt summieren einfach tun Punktprodukt (dies hat den Vorteil, alle so, dass Kernel willkürlich sein kann). Jedoch seine noch etwas 3x langsamer (auf meiner Maschine) als Pauls aktualisiert Funktion. Ich habe vor kurzem über die Fortschritte in der Antwort auf diesen Beitrag gelernt. Und frage mich, wie ich sie verwenden, um einen gleitenden Durchschnitt Filter effizienter als das, was ich in diesem Beitrag vorgeschlagen (mit Faltungs-Filter) zu berechnen. Dies ist, was ich bisher habe. Es nimmt eine Ansicht des ursprünglichen Arrays dann rollt es um die notwendige Menge und summiert die Kernel-Werte, um den Durchschnitt zu berechnen. Ich bin mir bewusst, dass die Kanten nicht richtig gehandhabt werden, aber danach kann ich mich darum kümmern. Gibt es einen besseren und schnelleren Weg Das Ziel ist es, große Gleitkomma-Arrays bis zu 5000x5000 x 16 Schichten in der Größe zu filtern, eine Aufgabe, die scipy. ndimage. filters. convolve ist ziemlich langsam auf. Beachten Sie, dass ich nach 8-Nachbar-Konnektivität suche, dh ein 3x3-Filter nimmt den Durchschnitt von 9 Pixeln (8 um das Brennpixel) und weist diesen Wert dem Pixel im neuen Bild zu. EDIT Klarstellung, wie ich diese Arbeit sehen: Verwenden Sie stridetricks, um ein Array wie 0,1,2,1,2,3,2,3,4 generieren. Die der oberen Zeile des Filterkerns entspricht. Rollen Sie entlang der vertikalen Achse, um die mittlere Reihe des Kerns 10,11,12,11,12,13,13,14,15 zu erhalten. Und fügen Sie es auf das Array ich bekam in 1) Wiederholen, um die untere Zeile des Kernels 20,21,22,21,22,23,22,23,24 erhalten. . An diesem Punkt nehme ich die Summe jeder Zeile und dividiere sie durch die Anzahl der Elemente im Filter, wobei mir der Durchschnitt für jedes Pixel, (um 1 Zeile und 1 col verschoben, und mit einigen Merkwürdigkeiten um die Kanten, aber ich kann Kümmere dich darum später). Was ich erhoffte, ist eine bessere Verwendung von stridetricks, um die 9 Werte oder die Summe der Kernel-Elemente direkt, für das gesamte Array zu erhalten, oder dass jemand mich von einer anderen effizienteren Methode überzeugen kann. Fragte am 8. Februar um 18:05 für was sein Wert, heres, wie youd es mit fancy schreitenden Tricks tun. Ich wollte diese Post gestern, sondern wurde von der tatsächlichen Arbeit abgelenkt. ) Paul amp essen beide haben schöne Implementierungen mit verschiedenen anderen Möglichkeiten, dies zu tun. Nur um die Dinge aus der früheren Frage fortzusetzen, dachte ich Id post das N-dimensionale Äquivalent. Sie werden nicht in der Lage, scipy. ndimage Funktionen für 1D-Arrays, aber deutlich zu schlagen. (Scipy. ndimage. uniformfilter sollte schlagen scipy. ndimage. convolve. though) Darüber hinaus, wenn youre versuchen, ein multidimensionales bewegliches Fenster zu erhalten, riskieren Sie, dass Speicherverbrauch sprengen, wenn Sie versehentlich eine Kopie des Arrays zu machen. Während das anfängliche rollende Array gerade eine Ansicht in den Gedächtnis Ihres ursprünglichen Arrays ist, bilden alle Zwischenschritte, die das Array kopieren, eine Kopie, die Größenordnungen größer als Ihr ursprüngliches Array ist (dh Lets sagen, dass youre, das mit einem 100x100 ursprünglichen Array arbeitet (Für eine Filtergröße von (3,3)) wird 98x98x3x3, aber verwenden Sie den gleichen Speicher wie das Original. Allerdings werden alle Kopien die Menge an Speicher verwenden, dass eine vollständige 98x98x3x3 Array würde) Grundsätzlich mit verrückt Schreitenden Tricks ist ideal, wenn Sie verschieben Fenster Operationen auf einer einzigen Achse eines ndarray vektorisieren möchten. Es macht es wirklich einfach, Dinge wie eine bewegte Standardabweichung, etc. mit sehr wenig Overhead zu berechnen. Wenn Sie anfangen, dies zu tun entlang mehrerer Achsen, seine mögliche, aber youre in der Regel besser mit mehr spezialisierte Funktionen. (Wie scipy. ndimage etc.) Auf jeden Fall ist heres, wie Sie es tun: Also, was wir bekommen, wenn wir b Rollingwindow (a, filtsize) ist ein 8x8x3x3 Array, das ist eigentlich eine Ansicht in den gleichen Speicher wie das Original 10x10 Array. Wir könnten genauso leicht verschiedene Filtergrößen entlang verschiedener Achsen verwenden oder nur entlang ausgewählter Achsen eines N-dimensionalen Arrays betrieben werden (dh Filzgröße (0,3,0,3) auf einem 4-dimensionalen Array würde uns eine 6-dimensionale Ansicht geben ). Wir können dann eine beliebige Funktion auf die letzte Achse anwenden, um die Dinge in einem sich bewegenden Fenster effektiv zu berechnen. Jedoch, weil waren die Speicherung von temporären Arrays, die viel größer als unsere ursprüngliche Array auf jedem Schritt der Mittelwert (oder std oder was auch immer), das ist überhaupt nicht Speicher effizient Sein auch nicht zu sein, furchtbar schnell, entweder. Das Äquivalent für ndimage ist nur: Dies wird eine Vielzahl von Randbedingungen behandeln, das Unschärfen an Ort und Stelle durchführen, ohne eine temporäre Kopie des Arrays zu benötigen, und sehr schnell sein. Schiebe-Tricks sind eine gute Möglichkeit, eine Funktion auf ein bewegtes Fenster entlang einer Achse anzuwenden, aber theyre nicht ein guter Weg, um es entlang mehrerer Achsen zu tun, in der Regel. Nur meine 0.02, jedenfalls. Sehr gut: Schiebe-Tricks sind eine gute Möglichkeit, eine Funktion auf ein bewegtes Fenster entlang einer Achse anzuwenden, aber sie sind nicht ein guter Weg, um es entlang mehrerer Achsen zu tun, in der Regel. Und natürlich ist Ihre Erklärung der Erinnerung 39blow up39 wichtig. Art der Zusammenfassung aus Ihrer Antwort (zumindest für mich) ist: 39don39t gehen zu weit Angeln, ist der Quarterteed Fang allready in scipy39. Thanks ndash eat Feb 9 11 at 16:37 Vielen Dank, Joe, für diese Antwort. In rollendem Fenster sollte das, wenn nicht hasattr (.): Rollingwindowlastaxis (.) Statt Rolling zurückgeben. Ndash unutbu Ich bin nicht vertraut genug mit Python, um Code für das zu schreiben, aber die beiden besten Möglichkeiten zur Beschleunigung von Windungen ist entweder trennen Sie den Filter oder die Fourier-Transformation verwenden. Getrennter Filter. Faltung ist O (MN), wobei M und N die Anzahl der Pixel in dem Bild bzw. dem Filter sind. Da die durchschnittliche Filterung mit einem 3-mal-3-Kernel dem Filtern zuerst mit einem 3-mal-1-Kernel und dann einem 1-mal-3-Kernel gleichkommt, können Sie (33) 30-Geschwindigkeitsverbesserung durch konsekutive Faltung mit erhalten Zwei 1-d-Kerne (dies wird offensichtlich besser, da der Kernel größer wird). Sie können immer noch in der Lage, Schritt-Tricks hier zu verwenden, natürlich. Fourier-Transformation . Conv (A, B) ist äquivalent zu ifft (fft (A) fft (B)). D. h. eine Faltung im direkten Raum wird eine Multiplikation im Fourierraum, wobei A dein Bild und B dein Filter ist. Da die (elementweise) Multiplikation der Fourier-Transformierten erfordert, dass A und B die gleiche Größe haben, ist B ein Array der Größe (A), wobei Ihr Kernel im Zentrum des Bildes und überall Nullen sind. Um einen 3-mal-3-Kernel in der Mitte eines Arrays zu platzieren, müssen Sie A bis ungerade Größe auffüllen. Abhängig von der Implementierung der Fourier-Transformation kann dies viel schneller sein als die Faltung (und wenn Sie denselben Filter mehrmals anwenden, können Sie fft (B) vorberechnen und weitere 30 Rechenzeit sparen). Antwortete am 9. Februar um 15:27 Für was es wert ist, in python, sind diese in scipy. ndimage. uniformfilter und scipy. signal. fftconvolve implementiert. beziehungsweise. Ndash Joe Kington Jonas: Cool Der seperate Filter Ansatz funktioniert gut, wie Sie sagen, es spart mehr Zeit als die Kernel-Größe erhöht. Für ein 5000x5000-Array, bei einer 11x11-Kernelgröße, bekomme ich 7.7s für 2d Convolution mit ndimage. convolve und 2.0s für zwei 1d-Convolutions mit ndimage. convolve1d. Für Ihre zweite Lösung, was ist B ndash Benjamin Eine Sache, die ich bin zuversichtlich, muss behoben werden, ist Ihr View-Array b. Es hat ein paar Elemente aus nicht zugeordneten Speicher, so youll erhalten Abstürze. Angesichts Ihrer neuen Beschreibung Ihres Algorithmus, ist die erste Sache, die Fixierung ist die Tatsache, dass Sie außerhalb der Zuweisung von a: Weil Im noch nicht ganz erfassen die Methode und es scheint einfacher Wege, um das Problem zu lösen, Im nur gehen Um das hier zu sagen: das scheint nur der einfache Ansatz zu sein. Der einzige äußere Vorgang ist, dass er B nur einmal zugewiesen und bevölkert hat. Der Zusatz, die Teilung und die Indexierung müssen unbedingt durchgeführt werden. Wenn Sie 16 Bands machen, müssen Sie B nur einmal zuweisen, wenn Sie die Absicht haben, ein Bild zu speichern. Auch wenn dies keine Hilfe ist, könnte es klären, warum ich nicht verstehen, das Problem, oder zumindest dienen als ein Maßstab für die Zeit der Beschleunigungen von anderen Methoden. Dies läuft in 2,6 Sekunden auf meinem Laptop auf einem 5k x 5k-Array von float64s, 0,5 von denen die Schaffung von B beantwortet ist nicht so klar, Ihre Frage, aber Im, dass jetzt, dass youll wie zu verbessern Deutlich diese Art der Mittelung. Nun, welche Art von Leistungsverbesserungen würden Sie eigentlich erwarten Update: Zunächst einmal eine Warnung: der Code in seinem aktuellen Zustand passt sich nicht richtig an die Kernform an. Aber das ist nicht meine primäre Sorge jetzt (sowieso ist die Idee gibt es schon, wie man richtig anpassen). Ich habe gerade die neue Form eines 4D A intuitiv gewählt, für mich ist es wirklich sinnvoll, darüber nachzudenken, ein 2D-Kernel zentriert zu jedem Raster Position des ursprünglichen 2D A. Aber dass 4D-Gestaltung kann nicht wirklich die beste sein. Ich denke, das eigentliche Problem hier ist die Leistung der Summierung. Man sollte in der Lage, die beste Ordnung (der 4D A) zu finden, um voll zu nutzen Ihre Maschinen Cache-Architektur. Allerdings kann diese Reihenfolge nicht die gleiche für kleine Arrays, die Art der Zusammenarbeit mit Ihrem Maschinen-Cache und die größeren, die nicht (zumindest nicht so einfach). Update 2: Hier ist eine leicht modifizierte Version von mf. Offensichtlich ist es besser, wieder zu einem 3D-Array zuerst und dann anstelle von Summierung einfach tun Punktprodukt (dies hat den Vorteil, alle so, dass Kernel willkürlich sein kann). Jedoch seine noch etwas 3x langsamer (auf meiner Maschine) als Pauls aktualisiert Funktion. Wie andere erwähnt haben, sollten Sie einen IIR (Endlosimpulsantwort) - Filter anstatt der FIR (Finite Impulse Response) Filter, die Sie jetzt verwenden. Es gibt mehr dazu, aber auf den ersten Blick werden FIR-Filter als explizite Windungen und IIR-Filter mit Gleichungen implementiert. Das besondere IIR-Filter, das ich viel in Mikrocontrollern verwende, ist ein einpoliges Tiefpaßfilter. Dies ist das digitale Äquivalent eines einfachen R-C-Analogfilters. Für die meisten Anwendungen haben diese bessere Eigenschaften als der Kastenfilter, den Sie verwenden. Die meisten Verwendungen eines Box-Filter, die ich begegnet bin, sind ein Ergebnis von jemand nicht Aufmerksamkeit in der digitalen Signalverarbeitung Klasse, nicht als Ergebnis der Notwendigkeit ihrer besonderen Eigenschaften. Wenn Sie nur wollen, um hohe Frequenzen zu dämpfen, dass Sie wissen, Rauschen sind, ist ein einpoliges Tiefpassfilter besser. Der beste Weg, um ein digitales in einem Mikrocontroller zu implementieren, ist in der Regel: FILT lt - FILT FF (NEW - FILT) FILT ist ein Stück persistenten Zustand. Dies ist die einzige persistente Variable, die Sie benötigen, um diesen Filter zu berechnen. NEU ist der neue Wert, den der Filter mit dieser Iteration aktualisiert. FF ist die Filterfraktion. Die die Schwere des Filters einstellt. Betrachten Sie diesen Algorithmus und sehen Sie, dass für FF 0 der Filter unendlich schwer ist, da sich der Ausgang nie ändert. Für FF 1 ist das eigentlich gar kein Filter, da der Ausgang nur dem Eingang folgt. Nützliche Werte sind dazwischen. Auf kleinen Systemen wählen Sie FF auf 12 N, so dass die Multiplikation mit FF als Rechtsverschiebung um N Bits erreicht werden kann. Beispielsweise könnte FF 116 sein und das Multiplizieren mit FF daher eine Rechtsverschiebung von 4 Bits. Andernfalls benötigt dieses Filter nur eine Subtraktion und eine Addition, obwohl die Zahlen in der Regel größer als der Eingangswert sein müssen (mehr über die numerische Genauigkeit in einem separaten Abschnitt weiter unten). Ich nehme in der Regel AD-Messwerte deutlich schneller als sie benötigt werden und wenden Sie zwei dieser Filter kaskadiert. Dies ist das digitale Äquivalent von zwei R-C-Filtern in Serie und dämpft um 12 dBoktave über der Rolloff-Frequenz. Allerdings für AD-Lesungen seine in der Regel mehr relevant, um das Filter im Zeitbereich zu betrachten, indem man seine Schrittantwort. Dies zeigt Ihnen, wie schnell Ihr System eine Änderung sehen wird, wenn die Sache, die Sie messen, ändert. Zur Erleichterung der Gestaltung dieser Filter (was nur bedeutet Kommissionierung FF und entscheiden, wie viele von ihnen zu kaskadieren), benutze ich mein Programm FILTBITS. Sie legen die Anzahl der Schaltbits für jede FF in der kaskadierten Filterreihe fest und berechnen die Schrittantwort und andere Werte. Eigentlich habe ich in der Regel laufen diese über mein Wrapper-Skript PLOTFILT. Dies führt FILTBITS, die eine CSV-Datei macht, dann die CSV-Datei. Beispielsweise ist hier das Ergebnis von PLOTFILT 4 4: Die beiden Parameter zu PLOTFILT bedeuten, dass es zwei Filter gibt, die von dem oben beschriebenen Typ kaskadiert sind. Die Werte von 4 geben die Anzahl der Schaltbits an, um die Multiplikation mit FF zu realisieren. Die beiden FF-Werte sind daher in diesem Fall 116. Die rote Spur ist die Einheit Schritt Antwort, und ist die Hauptsache zu betrachten. Dies bedeutet beispielsweise, dass sich der Ausgang des kombinierten Filters auf 90 des neuen Wertes in 60 Iterationen niederschlägt, falls sich der Eingang sofort ändert. Wenn Sie ca. 95 Einschwingzeit kümmern, dann müssen Sie ca. 73 Iterationen warten, und für 50 Einschwingzeit nur 26 Iterationen. Die grüne Kurve zeigt Ihnen den Ausgang einer einzelnen Amplitude. Dies gibt Ihnen eine Vorstellung von der zufälligen Rauschunterdrückung. Es sieht aus wie keine einzelne Probe wird mehr als eine 2,5 Änderung in der Ausgabe verursachen. Die blaue Spur soll ein subjektives Gefühl geben, was dieser Filter mit weißem Rauschen macht. Dies ist kein strenger Test, da es keine Garantie gibt, was genau der Inhalt der Zufallszahlen war, die als der weiße Rauscheneingang für diesen Durchlauf von PLOTFILT ausgewählt wurden. Seine nur, um Ihnen ein grobes Gefühl, wie viel es gequetscht werden und wie glatt es ist. PLOTFILT, vielleicht FILTBITS, und viele andere nützliche Dinge, vor allem für PIC-Firmware-Entwicklung ist verfügbar in der PIC Development Tools-Software-Release auf meiner Software-Downloads-Seite. Hinzugefügt über numerische Genauigkeit Ich sehe aus den Kommentaren und nun eine neue Antwort, dass es Interesse an der Diskussion der Anzahl der Bits benötigt, um diesen Filter zu implementieren. Beachten Sie, dass das Multiplizieren mit FF Log 2 (FF) neue Bits unterhalb des Binärpunkts erzeugt. Auf kleinen Systemen wird FF gewöhnlich mit 12 N gewählt, so daß diese Multiplikation tatsächlich durch eine Rechtsverschiebung von N Bits realisiert wird. FILT ist daher meist eine feste Ganzzahl. Beachten Sie, dass dies ändert keine der Mathematik aus dem Prozessor Sicht. Wenn Sie z. B. 10-Bit-AD-Lesungen und N 4 (FF 116) filtern, benötigen Sie 4 Fraktionsbits unter den 10-Bit-Integer-AD-Messwerten. Einer der meisten Prozessoren, youd tun 16-Bit-Integer-Operationen aufgrund der 10-Bit-AD-Lesungen. In diesem Fall können Sie immer noch genau die gleichen 16-Bit-Integer-Opertions, aber beginnen mit der AD-Lesungen um 4 Bits verschoben verschoben. Der Prozessor kennt den Unterschied nicht und muss nicht. Das Durchführen der Mathematik auf ganzen 16-Bit-Ganzzahlen funktioniert, ob Sie sie als 12,4 feste oder wahre 16-Bit-Ganzzahlen (16,0 Fixpunkt) betrachten. Im Allgemeinen müssen Sie jedem Filterpole N Bits hinzufügen, wenn Sie aufgrund der numerischen Darstellung kein Rauschen hinzufügen möchten. Im obigen Beispiel müsste das zweite Filter von zwei 1044 18 Bits haben, um keine Informationen zu verlieren. In der Praxis auf einer 8-Bit-Maschine bedeutet, dass youd 24-Bit-Werte verwenden. Technisch nur den zweiten Pol von zwei würde den größeren Wert benötigen, aber für Firmware Einfachheit ich in der Regel die gleiche Darstellung, und damit der gleiche Code, für alle Pole eines Filters. Normalerweise schreibe ich eine Unterroutine oder Makro, um eine Filterpol-Operation durchzuführen, dann gelten, dass für jeden Pol. Ob eine Unterroutine oder ein Makro davon abhängt, ob Zyklen oder Programmspeicher in diesem Projekt wichtiger sind. So oder so, ich benutze einige Scratch-Zustand, um NEU in die subroutinemacro, die FILT Updates, sondern auch lädt, dass in den gleichen Kratzer NEU war in. Dies macht es einfach, mehrere Pole anzuwenden, da die aktualisierte FILT von einem Pole ist die NEUE Der nächsten. Wenn ein Unterprogramm, ist es sinnvoll, einen Zeiger auf FILT auf dem Weg in, die auf nur nach FILT auf dem Weg nach draußen aktualisiert wird. Auf diese Weise arbeitet das Unterprogramm automatisch auf aufeinanderfolgenden Filtern im Speicher, wenn es mehrmals aufgerufen wird. Mit einem Makro benötigen Sie nicht einen Zeiger, da Sie in der Adresse passieren, um auf jeder Iteration zu arbeiten. Code-Beispiele Hier ist ein Beispiel für ein Makro wie oben für eine PIC 18 beschrieben: Und hier ist ein ähnliches Makro für eine PIC 24 oder dsPIC 30 oder 33: Beide Beispiele sind als Makros mit meinem PIC-Assembler-Präprozessor implementiert. Die mehr fähig ist als eine der eingebauten Makroanlagen. Clabacchio: Ein weiteres Thema, das ich erwähnen sollte, ist die Firmware-Implementierung. Sie können eine einpolige Tiefpassfilter-Subroutine einmal schreiben, dann mehrfach anwenden. Tatsächlich schreibe ich normalerweise solch ein Unterprogramm, um einen Zeiger im Gedächtnis in den Filterzustand zu nehmen, dann ihn voranbringen den Zeiger, so daß er nacheinander leicht aufgerufen werden kann, um mehrpolige Filter zu verwirklichen. Ndash Olin Lathrop Apr 20 12 at 15:03 1. Dank sehr viel für Ihre Antworten - alle von ihnen. Ich beschloss, dieses IIR-Filter zu verwenden, aber dieser Filter wird nicht als Standard-Tiefpaßfilter verwendet, da ich die Zählerwerte berechnen und sie vergleichen muss, um Änderungen in einem bestimmten Bereich zu erkennen. Da diese Werte von sehr unterschiedlichen Dimensionen abhängig von Hardware Ich wollte einen Durchschnitt nehmen, um in der Lage sein, auf diese Hardware spezifischen Änderungen automatisch reagieren. Wenn Sie mit der Beschränkung einer Macht von zwei Anzahl von Elementen zu durchschnittlich leben können (dh 2,4,8,16,32 etc), dann kann die Teilung einfach und effizient auf einem getan werden Low-Performance-Mikro ohne dedizierte Division, weil es als Bit-Shift durchgeführt werden kann. Jede Schicht rechts ist eine Macht von zwei zB: Der OP dachte, er hatte zwei Probleme, die Teilung in einem PIC16 und Speicher für seinen Ringpuffer. Diese Antwort zeigt, dass die Teilung nicht schwierig ist. Zwar adressiert es nicht das Gedächtnisproblem, aber das SE-System erlaubt Teilantworten, und Benutzer können etwas von jeder Antwort für selbst nehmen, oder sogar redigieren und kombinieren andere39s Antworten. Da einige der anderen Antworten eine Divisionsoperation erfordern, sind sie ähnlich unvollständig, da sie nicht zeigen, wie dies auf einem PIC16 effizient erreicht werden kann. Ndash Martin Apr 20 12 at 13:01 Es gibt eine Antwort für einen echten gleitenden Durchschnitt Filter (auch bekannt als Boxcar-Filter) mit weniger Speicher Anforderungen, wenn Sie dont mind Downsampling. Es heißt ein kaskadiertes Integrator-Kamm-Filter (CIC). Die Idee ist, dass Sie einen Integrator, die Sie nehmen Differenzen über einen Zeitraum, und die wichtigsten Speicher-sparende Gerät ist, dass durch Downsampling, müssen Sie nicht jeden Wert des Integrators zu speichern. Es kann mit dem folgenden Pseudocode implementiert werden: Ihre effektive gleitende durchschnittliche Länge ist decimationFactorstatesize, aber Sie müssen nur um Stateize Proben zu halten. Offensichtlich können Sie bessere Leistung erzielen, wenn Ihr stateize und decimationFactor Potenzen von 2 sind, so dass die Divisions - und Restoperatoren durch Shifts und Masken ersetzt werden. Postscript: Ich stimme mit Olin, dass Sie immer sollten einfache IIR-Filter vor einem gleitenden durchschnittlichen Filter. Wenn Sie die Frequenz-Nullen eines Boxcar-Filters nicht benötigen, wird ein 1-poliger oder 2-poliger Tiefpassfilter wahrscheinlich gut funktionieren. Auf der anderen Seite, wenn Sie für die Zwecke der Dezimierung filtern (mit einer hohen Sample-Rate-Eingang und Mittelung es für die Verwendung durch einen Low-Rate-Prozess), dann kann ein CIC-Filter genau das, was Sie suchen. (Vor allem, wenn Sie stateize1 verwenden und den Ringbuffer insgesamt mit nur einem einzigen vorherigen Integrator-Wert zu vermeiden) Theres einige eingehende Analyse der Mathematik hinter der Verwendung der ersten Ordnung IIR-Filter, Olin Lathrop bereits beschrieben hat auf der Digital Signal Processing Stack-Austausch (Enthält viele schöne Bilder.) Die Gleichung für diese IIR-Filter ist: Dies kann mit nur Ganzzahlen und keine Division mit dem folgenden Code implementiert werden (möglicherweise benötigen einige Debugging, wie ich aus dem Speicher wurde.) Dieser Filter approximiert einen gleitenden Durchschnitt von Die letzten K Proben durch Setzen des Wertes von alpha auf 1K. Führen Sie dies im vorherigen Code durch die Definition von BITS auf LOG2 (K), dh für K 16 gesetzt BITS auf 4, für K 4 gesetzt BITS auf 2, etc. (Ill Überprüfung der Code hier aufgelistet, sobald ich eine Änderung und Bearbeiten Sie diese Antwort, wenn nötig.) Antwort # 1 am: Juni 23, 2010, um 4:04 Uhr Heres ein einpoliges Tiefpassfilter (gleitender Durchschnitt, mit Cutoff-Frequenz CutoffFrequency). Sehr einfach, sehr schnell, funktioniert super, und fast kein Speicher Overhead. Hinweis: Alle Variablen haben einen Bereich über die Filterfunktion hinaus, mit Ausnahme des übergebenen newInput Hinweis: Dies ist ein einstufiger Filter. Mehrere Stufen können zusammen kaskadiert werden, um die Schärfe des Filters zu erhöhen. Wenn Sie mehr als eine Stufe verwenden, müssen Sie DecayFactor anpassen (was die Cutoff-Frequenz betrifft), um sie zu kompensieren. Und natürlich alles, was Sie brauchen, ist die beiden Zeilen überall platziert, brauchen sie nicht ihre eigene Funktion. Dieser Filter hat eine Rampenzeit, bevor der gleitende Durchschnitt diejenige des Eingangssignals darstellt. Wenn Sie diese Rampenzeit umgehen müssen, können Sie MovingAverage einfach auf den ersten Wert von newInput anstelle von 0 initialisieren und hoffen, dass der erste newInput kein Ausreißer ist. (CutoffFrequencySampleRate) einen Bereich zwischen 0 und 0,5 hat. DecayFactor ist ein Wert zwischen 0 und 1, in der Regel in der Nähe von 1. Single-precision Schwimmer sind gut genug für die meisten Dinge, ich bevorzuge nur Doppel. Wenn Sie mit ganzen Zahlen bleiben müssen, können Sie DecayFactor und Amplitude Factor in Fractional Integers umwandeln, in denen der Zähler als Integer gespeichert wird und der Nenner eine Ganzzahl von 2 ist (so können Sie Bit-Shift nach rechts als die Nenner, anstatt sich während der Filterschleife teilen zu müssen). Zum Beispiel, wenn DecayFactor 0.99, und Sie Ganzzahlen verwenden möchten, können Sie DecayFactor 0.99 65536 64881. Und dann immer wenn Sie multiplizieren mit DecayFactor in Ihrer Filterschleife, nur verschieben Sie das Ergebnis 16. Für weitere Informationen über dieses, ein ausgezeichnetes Buch thats Online, Kapitel 19 auf rekursive Filter: dspguidech19.htm PS Für das Moving Average-Paradigma, einen anderen Ansatz für die Einstellung DecayFactor und AmplitudeFactor, die möglicherweise mehr relevant für Ihre Bedürfnisse, können Sie sagen, dass Sie wollen, dass die vorherigen, etwa 6 Artikeln gemittelt, es diskret tun, fügen Sie 6 Elemente und teilen durch 6, so Können Sie den AmplitudeFactor auf 16 und DecayFactor auf (1.0 - AmplitudeFactor) einstellen. Antwortete May 14 12 at 22:55 Jeder andere hat kommentiert gründlich über den Nutzen der IIR vs FIR, und auf Power-of-two-Division. Id nur, um einige Implementierungsdetails zu geben. Das unten genannte funktioniert gut auf kleinen Mikrocontrollern ohne FPU. Es gibt keine Multiplikation, und wenn Sie N eine Potenz von zwei halten, ist die gesamte Division ein-Zyklus-Bit-Verschiebung. Basic FIR-Ringpuffer: Halten Sie einen laufenden Puffer der letzten N-Werte und einen laufenden SUM aller Werte im Puffer. Each time a new sample comes in, subtract the oldest value in the buffer from SUM, replace it with the new sample, add the new sample to SUM, and output SUMN. Modified IIR ring buffer: keep a running SUM of the last N values. Each time a new sample comes in, SUM - SUMN, add in the new sample, and output SUMN. answered Aug 28 13 at 13:45 If I39m reading you right, you39re describing a first-order IIR filter the value you39re subtracting isn39t the oldest value which is falling out, but is instead the average of the previous values. First-order IIR filters can certainly be useful, but I39m not sure what you mean when you suggest that the output is the same for all periodic signals. At a 10KHz sample rate, feeding a 100Hz square wave into a 20-stage box filter will yield a signal that rises uniformly for 20 samples, sits high for 30, drops uniformly for 20 samples, and sits low for 30. A first-order IIR filter. ndash supercat Aug 28 13 at 15:31 will yield a wave which sharply starts rising and gradually levels off near (but not at) the input maximum, then sharply starts falling and gradually levels off near (but not at) the input minimum. Very different behavior. ndash supercat Aug 28 13 at 15:32 One issue is that a simple moving average may or may not be useful. With an IIR filter, you can get a nice filter with relatively few calcs. The FIR you describe can only give you a rectangle in time -- a sinc in freq -- and you can39t manage the side lobes. It may be well worth it to throw in a few integer multiplies to make it a nice symmetric tunable FIR if you can spare the clock ticks. ndash Scott Seidman Aug 29 13 at 13:50 ScottSeidman: No need for multiplies if one simply has each stage of the FIR either output the average of the input to that stage and its previous stored value, and then store the input (if one has the numeric range, one could use the sum rather than average). Whether that39s better than a box filter depends on the application (the step response of a box filter with a total delay of 1ms, for example, will have a nasty d2dt spike when the input change, and again 1ms later, but will have the minimum possible ddt for a filter with a total 1ms delay). ndash supercat Aug 29 13 at 15:25 As mikeselectricstuff said, if you really need to reduce your memory needs, and you dont mind your impulse response being an exponential (instead of a rectangular pulse), I would go for an exponential moving average filter. I use them extensively. With that type of filter, you dont need any buffer. You dont have to store N past samples. Just one. So, your memory requirements get cut down by a factor of N. Also, you dont need any division for that. Only multiplications. If you have access to floating-point arithmetic, use floating-point multiplications. Otherwise, do integer multiplications and shifts to the right. However, we are in 2012, and I would recommend you to use compilers (and MCUs) that allow you to work with floating-point numbers. Besides being more memory efficient and faster (you dont have to update items in any circular buffer), I would say it is also more natural . because an exponential impulse response matches better the way nature behaves, in most cases. answered Apr 20 12 at 9:59 One issue with the IIR filter as almost touched by olin and supercat but apparently disregarded by others is that the rounding down introduces some imprecision (and potentially biastruncation). assuming that N is a power of two, and only integer arithmetic is used, the shift right does systematically eliminate the LSBs of the new sample. That means that how long the series could ever be, the average will never take those into account. For example, suppose a slowly decreasing series (8,8,8. 8,7,7,7. 7,6,6,) and assume the average is indeed 8 at the beginning. The fist 7 sample will bring the average to 7, whatever the filter strength. Just for one sample. Same story for 6, etc. Now think of the opposite. the serie goes up. THe average will stay on 7 forever, until the sample is big enough to make it change. Of course, you can correct for the bias by adding 12N2, but that wont really solve the precision problem. in that case the decreasing series will stay forever at 8 until the sample is 8-12(N2). For N4 for example, any sample above zero will keep the average unchanged. I believe a solution for that would imply to hold an accumulator of the lost LSBs. But I didnt make it far enough to have code ready, and Im not sure it would not harm the IIR power in some other cases of series (for example whether 7,9,7,9 would average to 8 then). Olin, your two-stage cascade also would need some explanation. Do you mean holding two average values with the result of the first fed into the second in each iteration. Whats the benefit of this
Comments
Post a Comment