Laplace-UnschĂ€rfe - Ist es möglich, Laplace anstelle von Gauß zu blubbern, wie oft ist es schneller und ist es den Verlust von 1/32 Genauigkeit wert?

Bild

"UnschĂ€rfe" bei gewöhnlichen Menschen ist ein UnschĂ€rfeeffekt in der digitalen Bildverarbeitung. Es kann an sich und als Bestandteil von Schnittstellenanimationen oder komplexeren abgeleiteten Effekten (Bloom / FocusBlur / MotionBlur) sehr effektiv sein. Bei alledem ist ehrlicher Blues in der Stirn eher langsam. Und oft lassen die in die Zielplattform integrierten Implementierungen zu wĂŒnschen ĂŒbrig. Entweder ist die Geschwindigkeit traurig, die Artefakte verletzen die Augen. Die Situation fĂŒhrt zu vielen Kompromissimplementierungen, die fĂŒr bestimmte Bedingungen besser oder schlechter geeignet sind. Eine originelle Implementierung mit guter ZuverlĂ€ssigkeitsqualitĂ€t und höchster Geschwindigkeit, wĂ€hrend die geringste AbhĂ€ngigkeit von Hardware unter dem Strich auf Sie wartet. Guten Appetit!

(Laplace Blur - Vorgeschlagener ursprĂŒnglicher Algorithmusname)

Heute hat mich meine interne Demoszene getreten und mich gezwungen, einen Artikel zu schreiben, der vor sechs Monaten geschrieben werden musste. Als Amateur möchte ich der Öffentlichkeit in aller Ruhe einen „fast gausischen Blurah“ -Algorithmus anbieten, der durch die Verwendung außergewöhnlich schneller Prozessoranweisungen (Verschiebungen und Masken) gekennzeichnet ist und daher fĂŒr die Implementierung bis zu Mikrocontrollern zugĂ€nglich ist (extrem schnell in einer begrenzten Umgebung).

GemĂ€ĂŸ meiner Tradition, Artikel ĂŒber Habr zu schreiben, werde ich Beispiele in JS als beliebteste Sprache nennen und ob Sie es glauben oder nicht, es ist sehr praktisch fĂŒr den Zweck des Rapid Prototyping von Algorithmen. DarĂŒber hinaus war die Möglichkeit, dies effektiv in JS zu implementieren, mit typisierten Arrays verbunden. Auf meinem nicht sehr leistungsstarken Laptop wird das Vollbild mit einer Geschwindigkeit von 30 fps verarbeitet (Multithreading von Arbeitern war nicht beteiligt).

Haftungsausschluss fĂŒr Cool Maths
Ich werde sofort sagen, dass ich meinen Hut abnehme, weil ich mich in der Grundmathematik als nicht versiert genug betrachte. Ich lasse mich jedoch immer vom allgemeinen Geist eines grundlegenden Ansatzes leiten. Bevor Sie meinen etwas „beobachtenden“ Ansatz zur Approximation betrĂŒgen, mĂŒssen Sie daher die BitkomplexitĂ€t des Algorithmus berechnen, die, wie Sie denken, mit klassischen polynomiellen Approximationsmethoden erhalten werden kann. Ich habe richtig geraten? Sie wollten sie schnell approximieren? Da sie eine schwebende Arithmetik erfordern, sind sie erheblich langsamer als eine einzelne Bitverschiebung, die ich am Ende erlĂ€utern werde. Mit einem Wort, beeilen Sie sich nicht zum theoretischen Fundamentalismus und vergessen Sie nicht den Kontext, in dem ich das Problem löse.

Diese Beschreibung ist hier eher vorhanden, um den Verlauf meiner Gedanken und Vermutungen zu erklĂ€ren, die mich zum Ergebnis gefĂŒhrt haben. FĂŒr diejenigen, die interessiert sein werden:

UrsprĂŒngliche Gauß-Funktion:

Bild

g (x) = a * e ** (- ((xb) ** 2) / c), wobei
a ist die Amplitude (wenn wir acht Farbbits pro Kanal haben, dann ist es = 256)
e ist die Eulerkonstante ~ 2.7
b - Graphverschiebung in x (wir brauchen nicht = 0)
c - Parameter, der die Breite des damit verbundenen Diagramms beeinflusst, als ~ w / 2.35

Unsere private Funktion (minus vom Exponenten, der durch Ersetzen der Multiplikation durch Division entfernt wurde):

Bild

g (x) = 256 / e ** (x * x / c)

Lassen Sie die schmutzige Approximationsaktion beginnen:
Beachten Sie, dass Parameter c sehr nahe an der halben Breite liegt und 8 eingestellt ist (dies liegt daran, wie viele Schritte Sie jeweils um einen 8-Bit-Kanal verschieben können).

Wir ersetzen e auch grob durch 2, wobei wir jedoch feststellen, dass dies die KrĂŒmmung der „Glocke“ stĂ€rker beeinflusst als ihre Grenzen. Eigentlich betrifft es 2 / e-mal, aber die Überraschung ist, dass dieser Fehler den Parameter c kompensiert, so dass die Randbedingungen noch in Ordnung sind und der Fehler nur in einer leicht falschen „Normalverteilung“ fĂŒr die Grafik erscheint Algorithmen, dies wird die Dynamik von FarbverlĂ€ufen mit FarbverlĂ€ufen beeinflussen, aber es ist fast unmöglich, mit dem Auge zu bemerken.

Unsere Funktion lautet nun wie folgt:
gg (x) = 256/2 ** (x * x / 8) oder gg (x) = 2 ** (8 - x * x / 8)
Es ist zu beachten, dass der Exponent (x * x / 8) den gleichen Wertebereich [0-8] wie die Funktion eines Abs (x) niedrigerer Ordnung hat, daher ist letzterer ein Kandidat fĂŒr eine Ersetzung. Wir werden die Vermutung schnell ĂŒberprĂŒfen, indem wir uns ansehen, wie sich der Graph damit Ă€ndert. Gg (x) = 256 / (2 ** abs (x)):

GaussBlur gegen LaplasBlur:

Bild

Abweichungen scheinen zu groß zu sein, außerdem hat die Funktion, die ihre GlĂ€tte verloren hat, jetzt einen Höhepunkt. Aber hey.

Vergessen wir zunĂ€chst nicht, dass die GlĂ€tte der durch UnschĂ€rfe erhaltenen Gradienten nicht von der Wahrscheinlichkeitsdichtefunktion (der Gauß-Funktion) abhĂ€ngt, sondern von ihrem Integral - der Verteilungsfunktion. Zu dieser Zeit kannte ich diese Tatsache nicht, aber nachdem ich eine „destruktive“ NĂ€herung in Bezug auf die Wahrscheinlichkeitsdichtefunktion (Gauß) durchgefĂŒhrt hatte, blieb die Verteilungsfunktion ziemlich Ă€hnlich.

Es war:

Bild

Es wurde:

Bild

Der Beweis, der dem vorgefertigten Algorithmus entnommen wurde, stimmt ĂŒberein:

Bild

(Mit Blick auf die Zukunft werde ich sagen, dass der UnschÀrfefehler meines Algorithmus in Bezug auf Gausian x5 nur 3% betrug.)

Wir sind also der Laplace-Verteilungsfunktion viel nÀher gekommen. Wer hÀtte das gedacht, aber sie können die Bilder zu 97% nicht schlechter waschen.

Beweis, Unterschiede Gausian Blura x5 und "Laplace Blura" x7:

Bild

(Dies ist kein schwarzes Bild! Sie können im Editor studieren)

Die Annahme dieser Transformation ermöglichte es uns, zu der Idee ĂŒberzugehen, den Wert durch iterative Filterung zu erhalten, auf die ich zunĂ€chst reduzieren wollte.

Bevor ich einen bestimmten Algorithmus erzĂ€hle, ist es ehrlich, wenn ich vorauslaufe und sofort seinen einzigen Nachteil beschreibe (obwohl die Implementierung mit einem Geschwindigkeitsverlust behoben werden kann). Dieser Algorithmus wird jedoch unter Verwendung von Scherarithmetik implementiert, und Potenzen von 2 sind seine Begrenzung. Das Original verwischt also x7 (was in Tests am ehesten mit Gausian x5 zusammenhĂ€ngt). Diese ImplementierungsbeschrĂ€nkung ist auf die Tatsache zurĂŒckzufĂŒhren, dass bei einer 8-Bit-Farbe, bei der der Wert im Filterantrieb um ein Bit pro Schritt verschoben wird, jede Aktion vom Punkt aus in maximal 8 Schritten endet. Ich habe auch eine etwas langsamere Version durch Proportionen und zusĂ€tzliche ErgĂ€nzungen implementiert, die eine schnelle Division durch 1,5 implementiert (was zu einem Radius von x15 fĂŒhrt). Mit der weiteren Anwendung dieses Ansatzes steigt jedoch der Fehler und die Geschwindigkeit sinkt, was eine solche Verwendung nicht zulĂ€sst. Andererseits ist anzumerken, dass x15 bereits ausreicht, um den Unterschied nicht zu bemerken. Das Ergebnis wird aus dem Original oder aus dem heruntergetasteten Bild erhalten. Die Methode eignet sich daher gut, wenn Sie in einer begrenzten Umgebung eine außergewöhnliche Geschwindigkeit benötigen.

Der Kern des Algorithmus ist also einfach: Es werden vier DurchgĂ€nge desselben Typs ausgefĂŒhrt:

1. Die HĂ€lfte des Wertes des Laufwerks t (anfĂ€nglich gleich Null) wird zur HĂ€lfte des Wertes des nĂ€chsten Pixels addiert, das Ergebnis wird ihm zugewiesen. Fahren Sie auf diese Weise bis zum Ende der Bildzeile fort. FĂŒr alle Zeilen.

Nach Abschluss des ersten Durchgangs wird das Bild in eine Richtung unscharf.

2. Beim zweiten Durchgang machen wir fĂŒr alle Linien dasselbe in die entgegengesetzte Richtung.
Wir erhalten ein Bild, das horizontal vollstÀndig unscharf ist.

3-4. Machen Sie jetzt dasselbe vertikal.
Fertig!

Anfangs habe ich einen Zwei-Pass-Algorithmus mit der Implementierung von Back-Blur durch den Stack verwendet, aber es ist schwer zu verstehen, nicht anmutig, und es stellte sich heraus, dass es auf aktuellen Architekturen langsamer ist. Möglicherweise ist der One-Pass-Algorithmus auf Mikrocontrollern schneller, und die Möglichkeit, das Ergebnis schrittweise auszugeben, ist ebenfalls von Vorteil.

Bei der aktuellen Vier-Wege-Implementierungsmethode habe ich mir HabrĂ© vom vorherigen Guru ĂŒber UnschĂ€rfealgorithmen angesehen. habr.com/post/151157 Ich nutze diese Gelegenheit, um ihm meine SolidaritĂ€t und tiefe Dankbarkeit auszudrĂŒcken.

Aber die Hacks endeten nicht dort. Nun erfahren Sie, wie Sie alle drei FarbkanĂ€le in einer Prozessoranweisung berechnen! Tatsache ist, dass Sie mit der Bitverschiebung, die als Division durch zwei verwendet wird, die Position der Ergebnisbits sehr gut steuern können. Das einzige Problem besteht darin, dass die unteren Bits der KanĂ€le in benachbarte höhere Bits verschoben werden. Sie können sie jedoch einfach zurĂŒcksetzen, um das Problem mit einem gewissen Genauigkeitsverlust zu beheben. Und gemĂ€ĂŸ der beschriebenen Filterformel fĂŒhrt die Addition des halben Wertes des Laufwerks mit dem halben Wert der nĂ€chsten Zelle (vorbehaltlich des ZurĂŒcksetzens der entladenen Bits) niemals zu einem Überlauf, sodass Sie sich darĂŒber keine Sorgen machen sollten. Und die Filterformel fĂŒr die gleichzeitige Berechnung aller Ziffern lautet wie folgt:

buf32 [i] = t = (((t >> 1) & 0x7F7F7F) + ((buf32 [i] >> 1) & 0x7F7F7F);

Es ist jedoch noch eine weitere ErgĂ€nzung erforderlich: Es wurde experimentell festgestellt, dass der Genauigkeitsverlust in dieser Formel zu signifikant ist und die Helligkeit des Bildes visuell signifikant springt. Es wurde klar, dass das verlorene Bit auf das nĂ€chste Ganze gerundet und nicht verworfen werden muss. Eine einfache Möglichkeit, dies in Ganzzahlarithmetik zu tun, besteht darin, die HĂ€lfte des Divisors vor der Division zu addieren. Unser Divisor ist zwei, daher mĂŒssen Sie in allen Ziffern eine hinzufĂŒgen - die Konstante 0x010101. Aber bei jedem Zusatz muss man vorsichtig sein, wenn es zu einem Überlauf kommt. Daher können wir eine solche Korrektur nicht verwenden, um den halben Wert der nĂ€chsten Zelle zu berechnen. (Wenn es weiße Farbe gibt, werden wir ĂŒberlaufen, daher werden wir es nicht korrigieren). Es stellte sich jedoch heraus, dass der Hauptfehler in der mehrfachen Aufteilung des Laufwerks lag, die wir nur korrigieren können. Denn selbst bei einer solchen Korrektur steigt der Wert im Antrieb nicht ĂŒber 254. Bei Addition zu 0x010101 kann jedoch kein Überlauf garantiert werden. Und die Filterformel mit Korrektur hat folgende Form:

buf32 [i] = t = (((((0x010101 + t) >> 1) & 0x7F7F7F) + ((buf32 [i] >> 1) & 0x7F7F7F);

TatsĂ€chlich fĂŒhrt die Formel die Korrektur recht gut durch. Wenn Sie diesen Algorithmus wiederholt auf das Bild anwenden, werden Artefakte erst in den zweiten zehn DurchgĂ€ngen sichtbar. (nicht die Tatsache, dass das Wiederholen der Gausianischen Blura solche Artefakte nicht hervorbringt).

DarĂŒber hinaus gibt es ein wunderbares Anwesen mit vielen PĂ€ssen. (Dies liegt nicht an meinem Algorithmus, sondern an der "NormalitĂ€t" der Normalverteilung). Bereits beim zweiten Durchgang der Laplace Blura sieht die Wahrscheinlichkeitsdichtefunktion (wenn ich alles richtig gemacht habe) ungefĂ€hr so ​​aus:

Bild

Was, wie Sie sehen, dem Gaußschen schon sehr nahe kommt.

Empirisch fand ich, dass die Verwendung von Modifikationen mit einem großen Radius paarweise zulĂ€ssig ist, weil Die oben beschriebene Eigenschaft kompensiert Fehler, wenn der letzte Durchgang genauer ist (der genaueste ist der hier beschriebene x7-UnschĂ€rfealgorithmus).

Demo
Rap
codpen

Ein Appell an coole Mathematiker:
Was interessant wÀre zu wissen, wie richtig es ist, einen solchen Filter getrennt zu verwenden, ich bin mir nicht sicher, ob es ein symmetrisches Verteilungsbild gibt. Obwohl die HeterogenitÀt des Auges nicht sichtbar ist.

upd: Hier werde ich nĂŒtzliche Links ansprechen, die freundlicherweise von Kommentatoren prĂ€sentiert und von anderen Khabroviten gefunden wurden.
1. Wie Intel-Assistenten basierend auf der Leistung von SSE funktionieren - software.intel.com/en-us/articles/iir-gaussian-blur-filter-implementation-using-intel-advanced-vector-extensions (danke vladimirovich )
2. Theoretische Grundlage zum Thema „Schnelle Bildfaltungen“ + einige seiner benutzerdefinierten Anwendungen in Bezug auf ehrliche Gaußsche Blau - blog.ivank.net/fastest-gaussian-blur.html (danke Grox )

VorschlÀge, Kommentare, konstruktive Kritik sind willkommen!

Source: https://habr.com/ru/post/de427077/


All Articles