Gauss filter -> Normalisierung?



  • Hallo,
    sorry für das etwas seltsame Thema aber weiss net wohin ichs sonst posten soll.

    Ein gauss filter glättet Bilder. Dazu wird eine Gauss Matrix erstellt die man auch auf den eindimensionalen Fall zurückführen kann was dann einem Vektor gleich kommt. Die Einträge sind dabei immer zwischen 0 und 1 .
    Jetzt habe ich aber rgb werte die immer zwischen 0 und 255 liegen. Eine blosse Multiplikation kann es damit nicht sein weil damit die werte nur noch sehr kleine würden.

    Wenn jemand genau bescheid weiß wie der filter korrekt funktioniert wäre ich sehr dankbar. Ich benutze als parameter den radius der die breite des kernel angibt.

    Danke für Hilfe



  • Normalisiere halt den RGB Vektor z.B. Rote Farbe (255,0,0) ist normalisiert (1,0,0)
    dann Wert ausrechnen und Skalierung rückgängig machchen - also einmal mit 1/255 multiplizieren und danach wieder mit 255





  • Vielen dank für eure Hilfe...
    leider komme ich immer noch nicht auf meinen Fehler:
    Hier ein wenig code:

    //Mein Gauss kernel sieht z.B so aus:
    kernel = {0.00443; 0.05399; 0.2419; 0.3989; 0.2419; 0.05399; 0.00443}
    

    Ich laufe zuerst die Spalten durch und dann die Zeilen:
    Hier mal nur ein ausschnitt aus dem wichtige Teil:

    //mid ist die mitte des kernels
    
    for(int col = -mid; col <= mid; col++)
                {
                    float f = kernel[mid + col];
                    if((x - col < 0) || ( x + col >= width))
                        continue;
    
                    rgb = vec_img[y*width + x];
    
                    r = ((rgb >> 16) & 0xff) / 255;
                    g = ((rgb >> 8) & 0xff) / 255;
                    b = (rgb & 0xff) / 255;
    
                    sumr += r * f;
                    sumg += g * f;
                    sumb += b * f;
                    gauss_sum += f;                
                }
                int ir = static_cast<int>(sumr / gauss_sum * 255);
                int ig = static_cast<int>(sumg / gauss_sum * 255);
                int ib = static_cast<int>(sumb / gauss_sum * 255);
                blurred[y*width + x] =  (ir << 16) | (ig << 8) | ib;
    
                sumr = 0.0;
                sumg = 0.0;
                sumb = 0.0;
                gauss_sum = 0.0;
    

    Leider funktioniert es so Nicht. Sieht jemand meinen Fehler ??? 😞



  • aaaaaaaaaaargh...
    sehe gerade dass ich immer denselben Pixel hernehme zum rgb value auslesen 🙄

    *gleich mal verändern tut



  • also bei mir kann man für den kernel das sigma ebenfalls einstellen lassen. Macht das sinn?
    Wenn nicht welches ist denn der default wert. Mit was arbeitet z.B Gimp als default wert für das sigma?



  • http://de.wikipedia.org/wiki/Gaußsche_Glockenkurve

    mit Sigama machst du die Glockenkurve größer bzw. kleiner - damit bestimmt man wieviele Pixel in der Umgebung zur Normalisierung beitragen sollen



  • Mati schrieb:

    Mit was arbeitet z.B Gimp als default wert für das sigma?

    Vermutlich arbeitet GIMP mit einem 3x3 Filter - aber du kannst dir ja die Impulsantwort liefern lassen



  • hmm...bei gimp kann man den radius einstellen. Und je grösser dieser ist desto grosser ist auch das blurring. Oder ist in dem fall der radius das sigma?
    Ich habe es mir so vorgestellt:

    Wenn ich radius 2 einstelle dann ist es ein 5 x 5 Filter. weil vom zentralen pixel dann 2 nach oben und 2 nach unten miteinberechnet wird. Stimmt das so?
    Wenn man sich aber die Funktion anschaut (im 1D) dann kann man doch für x auch einen Pixel einsetzen der viel weiter entfernt ist als der radius(sigma) erlaubt.

    Oder verstehe ich da was falsch?



  • Schon richtig, die Gewichtsfunktion wird nie Null (e-Funktion wird nie Null).

    Wenn ich ein 512x512 Bild Gaussfiltern möchte, müsste ich dann aber um das Pixel bei (0,0) im Ergebnisbild zu berechnen auch das Pixel bei (511,511) vom Eingabebild mitbetrachten ...

    Sinvollerweise begrenzt man also die Größe der Filtermaske so, dass Pixel deren Gewicht vernachlässigbar klein wird auch nicht betrachtet werden. z.B. 2*sigma

    Damit würde sich bei sigma=2 etwa eine 9x9 Filtermaske ergeben (2*2=4 in jede Richtung).

    (Das man bei GIMP ein Radius einstellt ist dann irgendwie ein bischen falschrum.)

    Allgemein zum Filtern: Um Pixel (x,y) für die Ausgabe zu bestimmen legst du um (x,y) die Filtermaske (Gewichtsfunktion). An jeder Stelle in der Maske:

    1. Pixelwert der Eingabe * Gewicht aufsummieren
    2. Gewichte einzeln aufsummieren

    dann ist in der Ausgabe (x,y) = Summe aus 1) geteilt durch Summe aus 2)

    Die Filtermaske mit den Gewichten für Gaussfilter: exp(- (d*d) / (2*s*s))
    d = Abstand zum Ursprung, s = sigma

    Gaussfilter ist noch ein bischen besser implementierbar, da er separabel ist.

    Du kannst also statt kannst, wenn du sagen wir eine 9x9 Filtermaske hast zuerst 9x1 filtern (also nur in x-Richtung), danach dann das Ergebnis mit 1x9 (y-Richtung).

    Zusatzinfo: Den genau selben effekt erreicht man auch, wenn man die fouriertransformierte mit einer darübergelegten Gaussglocke multipliziert.
    Da man diese Multiplikation durch eine Division logischerweise rückgängig machen kann, kann man diesen "Verschmiereffekt" den ein Gaussfilter zufolge hat auch tatsächlich zurückrechnen 🙂



  • danke vielen dank für die genaue beschreibung koch....
    mir ist einiges klarer aber noch nicht alles 🙂

    1. müsste die funktion zur bestimmung der gewichte so lauten:
      1 / (2 * PI * s * s) * exp(- d * d / 2 * s * s)
      Also noch durch 2 mal pi mal sigma hoch 2 teilen ?

    2. Macht es also doch nur sinn jetzt nur das sigma einstellbar zu machen? Und je nach sigma dann die filterbreite? Habe ich das richtg verstanden dass die filterbreite dann einfach 2* sigma +1 wäre?

    3. Aber schließlich könnte es doch sein dass man z.B ein Bild zwar mit 2 sigma gauss-filtern möchte aber NUR einen filter der grösse 3 z.B dann haben halt die randgewichte einfach einen grösseren wert. Z.B man hat einen Punkt und will dann zum rand hin immer weniger blurren also praktisch abrupt aufhören - verständlich?
      Hier ein beispiel: Die rautenanzahl gibt die stärke des pixels an(jeder pixel hat eine anzahl von rauten die durch leerzeichen separiert sind:
      a) wenn die filterbreite wirklich immer angepasst wird ans sigma:
      1.Pix 2.Px 3.P 4.P
      #### ### ## #

    b) Wenn man dann z.B den filter kleiner als die sigmabreite macht:
    #### ### ##

    verstehst du das mit dem abrubt ?

    Was macht denn mehr sinn?
    Danke für Antwort 🙂



  • ich habe nämlich vor dem user eine möglichkeit zu geben einzustellen wie stark er gauss-filtern kann. was macht denn da sinn ihn einstellen zu lassen?

    Ich habe bisher gedacht:

    1. Horizontaler radius:
    2. Vertikaler radius:
      //damit kann man ja dann unterschiedlich in die richtungen blurren
    3. Standardabweichung der Gausskurve: sigma

    Verbesserungsvorschläge ?



  • zu 1)
    Das 1 / (2*Pi*s*s) ist ja ein fester Faktor.
    Bei den beiden Summen 1) und 2) aus meinem Post vorher würde der Faktor in jedem Summanden stehen. Den kannst du rausziehen und dann kürzt er sich raus, wenn du beide Summen durcheinander teilst. Er dürfte also nichts ändern.

    zu 2 und 3)
    Das mit dem aprupt habe ich, glaube ich, so verstanden wie du es gemeint hast.
    Aber richtig antworten kann ich darauf nicht. Es ist die Frage was für ein Programm du für welche Art von User schreiben willst.
    Wenn da 2 Regler sind, einer für Sigma, einer für Filterbreite (für das aprupt), die beide irgendwie das Ergebnis zu verändern scheinen wird der Standarduser vielleicht einfach nicht verstehen was das alles soll ...

    Ich würde spontan nur den Sigma-Wert einstellbar machen und mir dann überlegen wo ich für ein bestimmtes Sigma die Filtermaske "abbreche", eben bei dem besagten Abstand=2*sigma zum Beispiel.

    Wenn du für x und y einzelne Sigmas hast sieht die Gewichtsfunktion übrigens so aus: (mal aus einem uralten c programm rauskopiert)

    exp( -(((dx*dx)/(2*sigma_x*sigma_x))+((dy*dy)/(2*sigma_y*sigma_y))))

    Wobei dx und dy die Äbstände zum Ursprung, jeweils für x- und y-Koordinatenwert.
    Die Filtermaske muss dann logischerweise auch nicht immer ein Quadrat sein, sondern eher ein Rechteck, wenn x- und y-Sigma unterschiedlich sind.

    Bei der Implementation aber nicht die Separierbarkeit (x und y Richtung einzeln filtern, siehe vorheriges Post) nict vergessen 😉
    (Statt Quadratischer laufzeit nur Linear, in Abhängigkeit von sigma.)



  • vielen dank für die Hilfe...

    ich habe ein Problem bei der sigma übergabe. Es soll ein float zugelassen sein.
    Aber ich kann ja nur int-plätze für den kernel vergeben also muss ich auf int casten.
    das bedeutet aber dass ich für werte 1.3 sowie 1.6 oder 1.99 immer die gleiche anzahl an kernel-plätzen.
    Am ergebnis ändert sich bei mir leider nichts. das bild ist für sigmawerte 1.3 , 1.6 und 1.99 gleich.

    ich bräuchte also etwas hilfe bei der schleife:

    ...
    simga = static_cast<int>(ceil(übergebenes_sigma));
    ...
    for (int row = - (2 * sigma); row <= (2 * sigma); row++)
        {
            distance = row * row;
    
            if(distance > (simga * sigma))
                kernel.push_back(0.0);
            else 
                kernel.push_back(static_cast<float>(exp(-distance/two_sigma_square)));
    
            sum += kernel[idx];  
            idx++;
        }
    

    würde das so passen?



  • hmm ich bin immern och nicht dahinter gekommen....weiß jemand wie es mit sigma werten ist die nicht ganzzahlig sind? Also z.B 1,6 ?


Anmelden zum Antworten