Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi Gaußsches Weichzeichen (https://www.delphipraxis.net/151122-gausssches-weichzeichen.html)

Neutral General 6. Mai 2010 15:16


Gaußsches Weichzeichen
 
Hallo,

Hab jetzt gestern mal den Gaußschen Algorithmus implementiert. Das funktioniert soweit auch schon ganz gut. Ich bin nur etwas verwirrt, bzw. habe evtl. noch ein kleines Verständnisproblem.

Habe eine 5x5 Matrix genutzt (Sigma = 1). Das Ergebnis ist jetzt das gleiche wie in Photoshop wenn ich "Gaussian Blur" verwende mit dem Radius von 1 Pixel. Aber warum 1 Pixel? Bei einer 5x5 Matrix ist jeder Pixel doch von 2 "Reihen" Pixel umgeben (=> Radius = 2 ?!)

xxxxx
xxxxx
xxoxx
xxxxx
xxxxx

Oder verstehe ich da was falsch?

Dann die andere Sache... Gibt das Sigma die (ich nenne es mal) "Intensität" der Verteilung an?

Gruß
Neutral General

Medium 6. Mai 2010 15:39

Re: Gaußsches Weichzeichen
 
Da müssen wir ein klein wenig in die Statistik reisen, denn dort ist die Guaß'sche Glockenkurve beheimatet. Das Dingen mit der Gauß-Kurve ist, dass sie bei 0 ihr Maximum hat, und nach -/+ in X-Richtung abfällt - sie wird aber nie Y=0. Daher müsste der Kernel genau genommen unendlich groß sein! Da das aber nicht wirklich praktikabel ist, und man sich gerade bei diskreten Werten den sehr kleinen Beitrag weit entfernter Werte schenken kann, ohne dass man es wirklich sieht, legt man den Kernelradius oft auf Sigma*3, womit 99,73% des Gesamtbeitrages abgedeckt sind. Das ist für fast alle Anwendungen genau genug.

Jetzt das Sigma. Das ist schon schwieriger, da es den statistischen Begriff der Standardabweichung beschreibt, und mit "Radius" im Hinterkopf schwer erklärbar ist. Der Wikipedia Artikel schafft da evtl. etwas Klarheit.

Dein Kernelradius von 2 könnte daher kommen, dass bei r=Sigma*2 auch schon >95% abgedeckt sind, was idR auch schon gut genug ist.

Und noch eine Kleinigkeit: Hast du in deinem Kernel alle Werte gleichen Pixelabstandes identisch (was deine Färbung suggeriert)? Wenn ja, ist das auch wieder nur eine grobe Näherung. Der Gauß-Kernel ist eigentlich radial, und entspricht der Zweidimensionalen Normalverteilung.

Neutral General 6. Mai 2010 16:07

Re: Gaußsches Weichzeichen
 
Hi,

Danke schonmal für die Antwort :)

Ich werde hier mal mein aktualisiertes Verständnis von der Sache niederschreiben und es wäre nett, wenn du mir dann sagen könntest, ob ich der Sache schon näher gekommen bin^^. Nachdem ich ein bisschen mit einem 3D-Funktionsplotter rumexperimentiert habe bin ich zu folgender (hoffentlich richtigen) Erkenntnis gekommen:

Das Sigma gibt, wie du sagtest, die Standardabweichung an. Praktisch also die "Streuung" der Werte. Streuung ist ein guter Begriff glaube ich. Ich dachte gerade an Egoshooter... :mrgreen: Wenn man dort mit verschiedenen Waffen auf 1 Punkt zielt, dann streuen die auch unterschiedlich stark. D.h. sogesehen hätte eine Pistole eine geringe Standardabweichung (ein kleines Sigma) und ein großes Maschienengewehr hat demnach eine sehr große Streuung, also eine große Standardabweichung (ein großes Sigma).
Der grad der Streuung (also die Standardabweichung) ist in diesem konkreten Fall quasi der Blur-Radius.

Die Kernelgröße hat also erstmal nichts mit dem Radius selbst zu tun, sondern ist quasi nur eine Art "Fenster", was vergrößert werden muss, wenn die Standardabweichung steigt, damit die größere Streuung noch erfasst werden kann (also in die Berechnung einfließen kann).

D.h. der Kernel könnte theoretisch unendlich groß sein. Wenn Sigma klein ist, dann ist der Blur-Radius ebenso gering. Abgesehen davon, dass der Kernel theoretisch unendlich groß sein müsste, weil die Gaußfunktion sich zwar 0 annährt aber nie 0 wird. D.h. man hat nie ein 100%ig genaues Weichzeichnen. Was aber auch nicht tragisch ist :mrgreen:

Hab ich das so richtig verstanden?

Medium 6. Mai 2010 16:39

Re: Gaußsches Weichzeichen
 
Klingt so weit alles gut :) (Auf die Idee, das mit Waffengewalt zu erklären, wäre ich jetzt spontan nicht gekommen :mrgreen: )

Neutral General 6. Mai 2010 16:46

Re: Gaußsches Weichzeichen
 
Zitat:

Zitat von Medium
Klingt so weit alles gut :)

Super :) Hätte nicht gedacht, dass ich das so schnell verstehe als ich deinen Beitrag das 1. mal gelesen hatte.

Zitat:

Zitat von Medium
Auf die Idee, das mit Waffengewalt zu erklären, wäre ich jetzt spontan nicht gekommen :mrgreen:

Bin da auch nur irgendwie während dem Schreiben meines Beitrags drauf gekommen :mrgreen:

Neutral General 7. Mai 2010 09:42

Re: Gaußsches Weichzeichen
 
Hallo,

Habe gestern noch ein bisschen rumprogrammiert und bin an ein Problem geraten. Für Sigma > 1 werden die Werte in der Matrix sehr schnell zu klein, bzw. extrem. Ich benutze schon den Datentyp Extended, aber bei Werten in der Gegend von 10^30 bis 10^-80 ist das trotzdem sone Sache und das Ergebnis sieht dann etwas.. nunja.. es sieht nach Rundungsfehlern und Ungenauigkeit aus.

Vielleicht stehe ich grad nur aufm Schlauch, aber ich weiß grad nicht wie ich dieses Problem umgehen kann :?

Medium 7. Mai 2010 12:00

Re: Gaußsches Weichzeichen
 
Ich vermute da einen Fehler in deiner Berechnung. Bei wachsendem Sigma flacht die Glockenkurve ab, d.h. weiter von 0 entfernten X-Werten sollte ein größerer Y-Wert zugeteilt werden als bei kleineren Sigmas. Bei Sigma>1 spätestens darf niemals ein Wert > 1 für ein Element raus kommen. Der Sinn des ganzen ist ja, dass das Integral unter der gesamten Kurve 1 ist, was ja kleinere Y-Werte bedeutet, je größer die Streuung ist. (Weniger Kugeln treffen die Wand auf gleich bleibender Fläche ;))

Wenn nach aussen zu kleine Werte auftreten, ist das ein Zeichen für einen zu großen Kernel.

Üblicherweise bewegen sich die einzelnen Werte im Kernel so grob zwischen 0,05 und 0,4. Wirklich sehr grob, da ja stark vom Sigma abhängig, aber das ist in etwa die Größenordnung. Keinesfalls 10^30, und schon garnicht 10^-80 :shock:

Edit: Hier mal ein Gauss aus einem aktuellen Projekt. Ist nun zwar ein HLSL-Shader, aber die Mathematik bleibt ja gleich.
Code:
void ps_Gauss3_Y(in v2p IN, out p2f OUT)
{
   IN.tex.xy += rcpXY*0.5;
   float k = 0.39894228; // = 1 / (phi*sqrt(2*PI)); phi=1 (radius=3)
   
   OUT.color.agb = (float3)0;
   
   OUT.color.r =
      tex2D(sBase, IN.tex.xy-SY*3).r * k * exp(-4.5) +
      tex2D(sBase, IN.tex.xy-SY*2).r * k * exp(-2.0) +
      tex2D(sBase, IN.tex.xy-SY*1).r * k * exp(-0.5) +
      tex2D(sBase, IN.tex.xy    ).r * k            +
      tex2D(sBase, IN.tex.xy+SY*1).r * k * exp(-0.5) +
      tex2D(sBase, IN.tex.xy+SY*2).r * k * exp(-2.0) +
      tex2D(sBase, IN.tex.xy+SY*3).r * k * exp(-4.5);
}

void ps_Gauss3_X(in v2p IN, out p2f OUT)
{
   IN.tex.xy += rcpXY*0.5;
   float k = 0.39894228; // = 1 / (phi*sqrt(2*PI)); phi=1 (radius=3)
   
   OUT.color.agb = (float3)0;
   
   OUT.color.r =
      tex2D(sBase, IN.tex.xy-SX*3).r * k * exp(-4.5) +
      tex2D(sBase, IN.tex.xy-SX*2).r * k * exp(-2.0) +
      tex2D(sBase, IN.tex.xy-SX*1).r * k * exp(-0.5) +
      tex2D(sBase, IN.tex.xy    ).r * k            +
      tex2D(sBase, IN.tex.xy+SX*1).r * k * exp(-0.5) +
      tex2D(sBase, IN.tex.xy+SX*2).r * k * exp(-2.0) +
      tex2D(sBase, IN.tex.xy+SX*3).r * k * exp(-4.5);
}
Das ist nun auch noch etwas optimiert, da es in zwei Passes läuft. Erst wird das Bild in Y-Richtung "gegausst", dann in X-Richtung. Spart nen Haufen Zeit. Zudem ist hier Sigma=1 fix (und heisst komischerweise phi, oops). Die Formel für einen Pass wäre hier also:
Pixel[x, y] = Summe(n=-3; n<4; Pixel[x, y+n] * 1/(phi * sqrt(2*PI)) * exp(-(0.5*n²/phi)))
Das ganze läuft dann über komplett x und y (das ganze Bild), und dann der ganze Spaß auf dem Ergebnis davon noch ein mal, aber diesmal mit:
Pixel[x, y] = Summe(n=-3; n<4; Pixel[x+n, y] * 1/(phi * sqrt(2*PI)) * exp(-(0.5*n²/phi)))

Neutral General 7. Mai 2010 13:40

Re: Gaußsches Weichzeichen
 
Ich bin gerade nicht zu Hause und kann mich jetzt nicht so sehr damit beschäftigen aber kann es sein, dass du eine etwas andere Formel hast?

Ich habe die aus Wikipedia: http://upload.wikimedia.org/math/5/b...08057e1cac.png

Deine Formel sieht da ja etwas anders aus (Mal abgesehn davon, dass du 2x 1 Dimension berechnest und nicht 1x 2D, aber das Prinzip ist ja das gleiche):

Zitat:

1/(phi * sqrt(2*PI)) * exp(-(0.5*n²/phi)))
(du quadrierst Sigma (phi) nie und die Wurzel im Nenner des Faktors von e ist anders gesetzt)

Medium 7. Mai 2010 14:30

Re: Gaußsches Weichzeichen
 
Die Quadrierung kann sehr gut aus der Umwandlung in eine 2D Funktion stammen, die Wurzel mutet da schon etwas anders an, kann aber auch sein - grad nicht den Drive das genau nachzustellen ;).

Aber ich würde so oder so empfehlen die 2-Pass Variante zu nehmen, da du dann nicht w*h*n², sondern nur noch 2*(w*h*n) Berechnungen anstellen musst (n=Kantenlänge des Kernels). Bei einem Bild der Größe 800x600 und einem 5x5 Kernel wären das 12.000.000 zu 4.800.000 (-60%), bei 7x7 schon 23.520.000 zu 6.720.000 (-71%) an Rechenaufwand.

Und ich weiss, dass mein Ergebnis richtig ist mit der 1D-Formel, auch bei anderen Sigmas ;) (Ich meine mich dunkel dran erinnern zu können, mit der 2D-Formel ausser Wikipedia auch schon mal Probleme gehabt zu haben, was aber durch die Splittung in 2 Pässe gegessen war, und keiner Lösung mehr bedurfte.)


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:40 Uhr.

Powered by vBulletin® Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf