Hallo zusammen
Tut mir leid, wenn ich jetzt auch noch Unruhe stifte und mitmischen möchte
Kann mir einer erklären wie ich von einem bestimmten G Wert in den Wertebereich 0 - 255 komme?
Kurz: Da beide Wertebereiche als Untergrenze 0 haben, musst du nur durch die Obergrenze des bestimmten G-Wertebereich teilen und mit der Obergrenze gewünschten Wertebereichs (255) multiplizieren.
Falls das Folgende für mehr Verwirrung sorgt, als dass es hilft, dann bitte ich darum diesen Post einfach zu ignorieren
Ich fange nochmal an jeden einzelnen Schritt zu beleuchten, um ganz explizit die Wertebereiche zu nennen. Danach gehe ich auf die Normierung ein. Ich nehme vereinfachend an, dass Quell- und Zielvariablen immer 8 Bit Auflösung halten und die tatsächlichen Wertebereiche dazwischen, also im Intervall [0, 255] variieren. Auch wenn ich nur auf den 3x3 Bildkernel eingehe, so lässt sich das Prinzip auf andere (größere) Kernel anwenden.
Stichwort Bildkernel: Bei vielen Bildkerneln werden die Summe der Einträge (sprich die Faktoren mit denen die einzelnen Pixel multipliziert werden) im Bildkernel normalerweise so skaliert, dass die Zielintervalle wieder voll abgedeckt werden. (Beispiel: 3x3 Einheitsmatrix würde durch 3 geteilt, da das Maximum Spur(I) = 3*255 auf das Zielinterval [0, 255] gebracht werden sollen).
Beim (separierten) Sobelfilter fällt auf, dass die Kernel (hier nur der X-Achsenkernel)
Code:
[[ +1, 0, -1],
[ +2, 0, -2],
[ +1, 0, -1]]
so gebaut sind, dass sich die "Seiten" gegenseitig aufheben, wenn beide Spalten identisch sind (also die "übliche" Skalierung auf Eins nicht erfolgt!). Das ist bewusst so gewählt (weil das eben grade den Gradienten im Diskreten darstellt). Das impliziert nun aber Folgendes für den 3x3 Kernel in Anwendung:
Max(SobelX(P)) = 4*255 = 1020
&
Min(SobelX(P)) = -4 * 255 = -1020
, wobei SobelX(P) die Anwendung des Sobelfilters um die Pixelumgebung des Punktes P bedeutet (SobelY analog).
Würden wir auf den entstandenen Wertebereich [-1020, 1020] jetzt in ein 8 Bit Graustufenbild schreiben wollen (hypothetisch), müssten wir also auf 0 verschieben und skalieren:
Pixel[P] = round((SobelX(P) + 1020) / 2040 * 255)
. Jetzt haben wir den Wertebereich auf das Intervall [0, 255] gebracht.
Da wir die Ergebnisse von X und Y Achse aber richtungsunabhängig kombinieren und dabei quadrieren, entfallen die negativen Intervallabschnitte und es bildet sich das Zielintervall wie folgt:
Sobel(P) = sqrt(SobelX(P) * SobelX(P) + SobelY(P) * SobelY(P))
, wobei das Maximum wieder
Max(Sobel(P)) = sqrt(1020^2 + 1020^2) = sqrt(2080800) ≈ 1442.497833620557
ergibt (Genauigkeit wurde so gewählt, dass IEEE754 Double Präzision am Besten ausgereizt wird). Das Minimum ist trivialerweise immer 0. Wir befinden uns jetzt also im Interval [0, 144.497833620557].
Daraus folgt die allgemeine Skalierung für Pixel P:
Pixel[P] = round(Sobel(P) / 1442.497833620557 * 255)
. Ich runde erst jetzt für maximale Genauigkeit. Somit ist das gewünschte Zielinterval [0, 255] erreicht.
[Edit]
Michael II: Bin jetzt auch bei dir angekommen.
Hatte vergessen, dass das Bild ja den beiden Kerneln Abhängigkeiten "aufzwingt". Michael II hat also korrekt gerechnet. Meine Formel oben stimmt trotzdem von der Herleitung, allerdings kann man die Skalierung am Ende besser wählen:
Pixel[P] = round(Sobel(P) / 1140.4 * 255)
.
[/Edit]
Das war jetzt mal meine Herleitung zur normalen naiven Berechnung. Korrekturen gerne willkommen.
Jetzt kann man noch das völlig unabhängig betrachtbare Problem der Vorverarbeitung/heuristischen Betrachtung dazuziehen;
Ob du das machen kannst hängt aber komplett von deinem Einsatzzweck ab.
Wenn du weitere Schritte einfügst, die sich auf das ganze Bild beziehen, so wirst du auf jeden Fall einmal mehr durch das Bild durchgehen. Entweder einmal am Anfang, um zu Detektieren oder am Ende beim Schreiben. Ich neige bei zum Beispiel Skalierung auf Maxiumum/Minimum zu ersterem, weil ich mir die Schreibzugriffe auf den Hauptspeicher sparen kann und dann auch auf ein Array mit den Zwischenergebnissen verzichten kann: Anfangs einmal alle Pixel anschauen und Maximum und Minimum in lokaler Variable merken. Die jeweilige Skalierung kann man dann oben in die Formel integrieren (wenn dich das genauer interessiert, kann ich gerne weiterhelfen
). (alternativ würde man beim Lesen schonmal bis zum letzten Schritt berechnen und dann in ein Zwischenzielarray speichern; muss dann aber danach nochmal über alle Werte darüber. Mir fällt grade spontan kein Anwendungsfall ein, bei dem ich das mal präferiert hatte...)
Man kann das Bild auch anderweitig "vorverarbeiten": Es gibt zum Beispiel die
Histogrammnormalisierung. Mir hat für meine Anwendungszwecke bisher immer die Maximum/Minimumbetrachtung ausgereicht. Vor allem weil nicht lineares Verzerren manchmal zusätzlich Information auslöscht (wie "stark" war die Kante), wenn sie nicht geschickt angewendet wird.
Gruß, Brighty
Disclaimer: Ich musste leider grade ohne meine Codebasis das aus dem Kopf heraus rekonstruieren. Ganz besonders möchte ich anmerken, dass ich mich zugunsten des Verständnisses dafür entschieden hatte Werte innerhalb der Berechnungen implizit von Flieskomma- und Ganzzahlen zu konvertieren. Obwohl ich die Zahlen nachgerechnet habe, kann es sein, dass ich irgendwo noch etwas vergessen/nicht ganz deutlich in diesem Text herausgearbeitet habe.
Do you have the email of god??? --- I have to tell him that I'm happy to be born!