This commit is contained in:
Simon Lübeß
2026-03-16 23:34:31 +01:00
parent c17733580c
commit 2c02dbe2a2
3 changed files with 122 additions and 4 deletions

View File

@@ -191,12 +191,13 @@
}, },
"active": "82d87f6698a469e1", "active": "82d87f6698a469e1",
"lastOpenFiles": [ "lastOpenFiles": [
"Notizen/16.10.2025.md", "Prewrite/Änderungen an Szene und Code/Random Änderungen.md",
"Prewrite/Genereller Maßstab.md", "Prewrite/Genereller Maßstab.md",
"Paperung/Experimente/Experiment 2 - NN performance.md",
"Notizen/16.10.2025.md",
"Notizen/09.10.2025 - Anmeldung.md", "Notizen/09.10.2025 - Anmeldung.md",
"Modellieren in Blender.md", "Modellieren in Blender.md",
"Motivator.excalidraw.md", "Motivator.excalidraw.md",
"Paperung/Experimente/Experiment 2 - NN performance.md",
"Prewrite/2. Grundlagen.md", "Prewrite/2. Grundlagen.md",
"Paperung/Experimente/Experiment 1 - Renderzeit.md", "Paperung/Experimente/Experiment 1 - Renderzeit.md",
"Linux.md", "Linux.md",
@@ -205,7 +206,6 @@
"Paperung/Medizinischer Hintergrund/Äquator_Auge.avif", "Paperung/Medizinischer Hintergrund/Äquator_Auge.avif",
"Paperung/Größe des Augapfels.md", "Paperung/Größe des Augapfels.md",
"Untitled.base", "Untitled.base",
"Prewrite/Änderungen an Szene und Code/Random Änderungen.md",
"Prewrite/Änderungen an Szene und Code/Hair Node System für Wimpern und Augenbrauen.md", "Prewrite/Änderungen an Szene und Code/Hair Node System für Wimpern und Augenbrauen.md",
"Prewrite/Änderungen an Szene und Code", "Prewrite/Änderungen an Szene und Code",
"Prewrite/Untitled.md", "Prewrite/Untitled.md",

View File

@@ -149,14 +149,128 @@ In der Konfiguration für die Bild-Generierung kann für jeden diese vier Bereic
Es sei an dieser Stelle angemerkt, dass zwar kein direktes Leck von Test-Daten in das Training vorliegt, aber durch das wählen der Luminanz-Bereiche anhand der Test-Daten, liegt hier ein geringfügig vom Testset informiertes Training vor. Das ist für diese Arbeit akzeptabel, da ziel dieser Untersuchung nicht die allgemeine Generalisierungsfähigkeit von Neuronalen Netzen anhand der generierten synthetischen Daten ist, sondern nur der Einfluss von verschiedenen Augenformen von Interesse für dieses Experiment ist. Es sei an dieser Stelle angemerkt, dass zwar kein direktes Leck von Test-Daten in das Training vorliegt, aber durch das wählen der Luminanz-Bereiche anhand der Test-Daten, liegt hier ein geringfügig vom Testset informiertes Training vor. Das ist für diese Arbeit akzeptabel, da ziel dieser Untersuchung nicht die allgemeine Generalisierungsfähigkeit von Neuronalen Netzen anhand der generierten synthetischen Daten ist, sondern nur der Einfluss von verschiedenen Augenformen von Interesse für dieses Experiment ist.
# Beheben von Luminanzwerten für die Haut
Bei Tests ist aufgefallen, dass mit den neu extrahierten Werten die Haut sowohl in der alten als auch der neuen Pipeline visuell sehr falsch dargestellt wurde... Es schien, als wäre der Kontrast stark erhöht, was nicht passieren sollte...
## Mein gespräch mit ChatGPT dazu:
https://chatgpt.com/c/69b6c157-91bc-8393-b1f6-f55a50cc56c6
um dieses Problem zu beheben wurde der Tiefpunkt der Kurve auf 0, 0 gesetzt und der Hochpunkt auf 1, 1.
Gibbidy Formulierung:
>Da die ursprünglich verwendete Kalibrierung in der Praxis zu starken Kontrastartefakten führte, wurde eine vereinfachte Parametrisierung gewählt. Diese ist nicht als exakte photometrische Modellierung zu verstehen, erwies sich jedoch für die synthetische Datengenerierung als ausreichend.
oder
> Die ursprünglich vorgesehene Kalibrierung der Hautluminanz über eine vollständige Abbildung charakteristischer Texturwerte führte in der Praxis zu unplausiblen Kontrastverstärkungen und damit zu visuell ungeeigneten Ergebnissen.
> Aus diesem Grund wurde für die Generierung der synthetischen Daten eine vereinfachte Transformation verwendet, bei der die Extremwerte nicht zusätzlich auf den Zielbereich abgebildet wurden. Diese Vorgehensweise stellt keine vollständig physikalisch bzw. photometrisch exakte Kalibrierung dar, führte jedoch zu stabileren und für den Anwendungszweck hinreichend plausiblen Resultaten.
## Glints
Für die glints wurden auch die Größe mit dem Paramter-Picker extrahiert. Da einige Bilder mehrere Glints enthalten, wurden diese Kopiert und die Glints einzeln markiert. Als Minimalwert wurde für die Glints auch das 5%-Quantil verwendet. Als maximum allerdings 255, was dem höchst möglichen Wert entspricht. Das wurde getan, da davon ausgegangen werden kann, dass Glints, welche
https://chatgpt.com/g/g-p-693e918aca0081919b456cb68144a742-masterarbeit/c/69b6d450-89e4-8396-908a-d195d4005a49
> Für Glints wurde der obere Intensitätsgrenzwert abweichend von den übrigen Regionen auf 255 gesetzt. Der Grund ist, dass Glints spekulare Reflexionen der Lichtquelle auf der feuchten Augenoberfläche darstellen. Solche Reflexe können in realen Infrarotaufnahmen lokal sehr hohe Intensitäten erreichen und den Dynamikbereich des Sensors ausschöpfen oder in Sättigung treiben. Eine Begrenzung anhand der aus den Flächenregionen abgeleiteten 95%-Perzentile wäre daher für Glints nicht angemessen.
## Lidschluss
In der Konfiguration kann ein Skalierungsfaktor für eine Exponentialverteilung eingestellt werden. Je höher dieser Faktor, desto wahrscheinlicher wird das Auge ein wenig oder ganz geschlossen.
Manuelles Testen hat ergeben, dass ein Faktor von 0,4 meist ein geöffnetes Auge erzeugt, aber auch ab und zu teilweise oder vollständig geschlossene Augen erzeugt.
## Blur
Zur Zeit ist in der Pipeline ein konstanter Blur eingestellt. Dieser wurde aufgrund der geringen Auflösung der generierten Bilder auf 1px reduziert. Das musste über eine Code-Änderung erfolgen
# Generieren der synthetischen Daten # Generieren der synthetischen Daten
Es wurden jeweils mit der Alten und der neuen Pipeline 5000 Bilder erzeugt.
Config:
- Seed: 42
- Bilder: 5000
- Luminanz-Bereich: Siehe oben
- Eyelid-Exponent: 0.4 -> sah ganz gut aus
- Auflösung: wie bei 500k-Datensatz 192 x 144
Die Baseline Bilder wurden innerhalb von 00:54:30 generiert. Die neuen Bilder in 00:57:45. Hierbei sei angemerkt, dass diese Zeiten nicht vergleichbar sind mit denen aus Experiment 1. Abgesehen davon, dass die Auflösung geringer ist und die Optimierte Sclera-Textur-Generierung von der neuen Version in die alte übernommen wurde, wurde der Computer während der Generierung für andere Dinge verwendet. Außerdem hat Blender 5 scheinbar ein Memory-Leak, weshalb die Bilder für die neue Pipeline auch mit Blender 4 generiert werden mussten.
```sh
# Generieren mit dem Kommando: (blender4 muss auf Blender4-Exe zeigen)
> blender4 --background Objects.blend --python render.py
```
## Filtern der generierten Daten
Die Pipeline berücksichtigt nicht, ob die Pupille sichtbar ist oder nicht. Die Pupillen-Masken enthalten immer die gesamte Pupille, auch wenn sie z. B. teilweise oder ganz vom Augenlid verdeckt wird. Daher wurde das Script `prepare_training_data.py` geschrieben. Diesem Script wird bei der Ausführung übergeben, wie viel Prozent der Pupille mindestens sichtbar sein soll. Die Pupillenmasken sind Schwarz-Weiß-Bilder, wobei weiße Pixel bedeuten, dass die Pupille sichtbar ist. Die rgb_mask_covered Bilder enthalten kombinierte Segmentierungsmasken für Pupille (Rot), Iris (Grün) und die Sclera (Blau). Hier werden auch Verdeckungen berücksichtigt. Somit kann ermittelt werden, wie viel Prozent der Pupille sichtbar ist, indem die weißen Pixel in der Pupillen-Maske gezählt werden. Und dann wird gezählt, wie viele der weißen Pixel aus der Pupillen-Maske in der rgb_mask_covered Rot sind. Wenn sie nicht rot sind, sind sie nämlich verdeckt. Daraus kann der Prozentsatz errechnet werden. Für Pupillen über dem Schwellwert wird die original Pupillen-Maske ins Ausgabeverzeichnis kopiert, sonst wird eine komplett schwarze Maske kopiert. Manuelles Probieren hat gezeigt, dass bei einem Schwellwert von ca. 20%
TODO! ein Grayscale, Pupil und rgb_mask_covered Bild zeigen
__Von [Gibbidy](https://chatgpt.com/g/g-p-693e918aca0081919b456cb68144a742-masterarbeit/c/69b82bf8-dfe8-8387-962e-be2f4f90ec48):__
EllSeg Paper: _EllSeg: An Ellipse Segmentation Framework for Robust Gaze Tracking_
Für die synthetisch erzeugten Daten sind sowohl die vollständige Pupillenmaske als auch die nach Verdeckung sichtbare Pupillenmaske bekannt. Dadurch kann der Okklusionsgrad direkt pixelbasiert als Verhältnis der verdeckten zur vollständigen Pupillenfläche bestimmt werden. Formal wurde der Okklusionsgrad als $1 - \frac{|P_{\mathrm{vis}}|}{|P_{\mathrm{full}}|}$ berechnet, wobei $|P_{\mathrm{vis}}|$ die Anzahl der Pixel der sichtbaren Pupille und $|P_{\mathrm{full}}|$ die Anzahl der Pixel der vollständigen Pupille bezeichnet. Dieses Vorgehen entspricht im vorliegenden Fall dem im EllSeg-Paper verwendeten IoU-basierten Okklusionsmaß, da die sichtbare Pupillenmaske eine Teilmenge der vollständigen Pupillenmaske ist. In Anlehnung an die dort empirisch gewählten Schwellenwerte wurden Pupillen ab einem Okklusionsgrad von 0,7 als stark verdeckt behandelt. Für diese Fälle wurde beim Training eine leere Segmentierungsmaske verwendet. Bei geringerem Okklusionsgrad wurde dagegen weiterhin die vollständige Pupillengeometrie als Ground Truth hinterlegt, sodass das Modell lernen soll, die durch Lidverdeckung verdeckten Bereiche implizit zu ergänzen.
Falls du es etwas vorsichtiger-wissenschaftlich formuliert haben möchtest, mein Herr, wäre diese Variante auch gut 😊
> In Anlehnung an EllSeg wurden Pupillen mit einem Okklusionsgrad von mindestens 0,7 nicht mehr als regulär sichtbar behandelt, sondern beim Training durch eine leere Segmentierungsmaske repräsentiert. Damit orientiert sich die gewählte Schwelle an einer bestehenden empirischen Kategorisierung starker Okklusion, wird im vorliegenden Fall jedoch direkt über den pixelbasiert bestimmten Flächenanteil der Verdeckung umgesetzt.
Durch dieses Vorgehen wurden für den Baseline-Datensatz 783 Bilder als vollständig Verdeckt interpretiert. Im neuen Datensatz wurden 1394 Bilder als vollständig verdeckt interpretiert. Dabei sei angemerkt, dass mit der neuen Pipeline 890 Bilder tatsächlich vollständig verdeckt (also 100% verdeckt) erzeugt wurden, während in der alten lediglich 368 solche Bilder existierte. Das liegt daran, dass in den mit der neuen Pipeline generierten Bildern deutlich häufiger das Augenlid soweit geschlossen ist, dass es die Pupille vollständig verdeckt. Das kann damit zusammenhängen, dass in der neuen Pipeline das Auge tatsächlich vollständig geschlossen werden kann, weshalb der selbe zufällige Schlusswert (0.0 = offen 1.0 = maximal geschlossen) tendenziell das Auge etwas weiter schließt. Daher nähern sich die Verhältnisse bei steigendem Schwellwert auch an.
# Training mit Yolo
Yolo erwartet als ground truth keine Segmentierungsmasken, sodnern Polygone, die die segmentierte Region einschließen. Daher werden keine Bilder, sondern Textdateien als Labels verwendet.
Pro Bild gibt es eine Text-Datei. Jede Zeile dieser Textdatei enthält als erstes den Index der Klasse des Objekts gefolgt von den Koordinaten der Eckpunkte des Polygons welches das Objekt einschließt. Eine Zeile ist also in folgendem Format:
```
<class-index> <x1> <y1> <x2> <y2> ... <xn> <yn>
```
Für mehrere Objekte könnte die Datei z. B. so aussehen:
```
0 0.681 0.485 0.670 0.487 0.676 0.487
1 0.504 0.000 0.501 0.004 0.498 0.004 0.493 0.010 0.492 0.0104
```
Hier wären also zwei Objekte in dem Bild. Eines mit Klasse 0, eingeschlossen von einem Dreieck und eines mit Klasse 1, eingeschlossen von einem Fünfeck.
Da wir immer nur ein Objekt - nämlich die Pupille - und auch nur eine Klasse haben, enthalten diese Dateien bei uns immer nur eine Zeile.
## Umwandlung von Segmentierungsmasken zu Yolo-Format
Yolo bietet eine integrierte Funktion um für alle Segmentierungsmasken in einem Ordner Textdateien im Yolo-Format zu erzeugen und diese in einem Zielordner abzuspeichern. Dieser Funktion kann man auch angeben, wie viele Klassen es insgesamt gibt. Allerdings konnte diese Funktion hier aus zwei Gründen nicht genutzt werden:
1. Die Funktion erwartet, dass die erste Klasse den Grauwerte 1 hat. Die Masken, die von der Pipeline erzeugt werden haben aber den Grauwert 255 für die Pupille.
2. Es gibt einen Bug, sodass bestimmte CV2 Versionen unter Windows falsch funktionieren, weshalb ein Fehler in der Yolo-Konvertierungsfunktion auftritt.
Daher wurde basierend auf der von Yolo gelieferten Funktion eine neue geschrieben (siehe datei: yolotest.ipynb 😅)
Diese Funktion behebt den Bug und nutzt einen Schwellwert von 127 um nur eine einzelne Maske zu extrahieren.
## Trainings- und Validierungsset
Nun liegen die Bilder und Labels im korrekten Format vor. Allerdings erwartet Yolo eine spezielle Ordnerstruktur. Yolo erwartet eine Konfigurationsdatei im Yaml-Format, welche den Datensatz beschreibt.
```yaml
path: current # Datensatz Wurzelverzeichnis
train: images/train # Pfad zu den Trianingsdaten (relativ zu "path")
val: images/val # Pfad zu den Validierungsdaten (relativ zu "path")
test: images/test # Optionaler Pfad zu den Testdaten (relativ zu "path")
classes:
0: pupil
```
Ordnerstruktur:
```
+ current
+-+ train
| +-+ images
| | +-- XXXX.png
| | +-- ...
| +-+ labels
| | +-- XXXX.txt
| | +-- ...
+-+ val
| +-+ images
| | +-- XXXX.png
| | +-- ...
| +-+ labels
| | +-- XXXX.txt
| | +-- ...
+-+ test
| +-+ images
| | +-- XXXX.png
| | +-- ...
| +-+ labels
| | +-- XXXX.txt
| | +-- ...
```
# TODO: # TODO:
1. [x] Nochmal die Bilder extrahieren, damit wir die Statisik haben, wie viele wir nach Phase 1 hatten. (Kommando ist in Experiment2/extracted_500k_images) 1. [x] Nochmal die Bilder extrahieren, damit wir die Statisik haben, wie viele wir nach Phase 1 hatten. (Kommando ist in Experiment2/extracted_500k_images)
1. [x] Dafür im Script wieder alle Testpersonen aktivieren 1. [x] Dafür im Script wieder alle Testpersonen aktivieren
2. [x] Nochmal gucken, ob 0.5 für SSIM-Schwellwert wirklich gut ist 2. [x] Nochmal gucken, ob 0.5 für SSIM-Schwellwert wirklich gut ist
2. [x] Helligkeitswerte extrahieren 2. [x] Helligkeitswerte extrahieren
3. [ ] Mit einem kleinen Yolo-Modell testen, ob das geplotte funktioniert 3. [ ] Mit einem kleinen Yolo-Modell testen, ob das geplotte funktioniert
-> es schein einen runs-Ordner im root-Verzeichnis zu geben, da macht Yolo schon einiges
4. [ ] Optimierungen in alte Pipeline aufnehmen 4. [ ] Optimierungen in alte Pipeline aufnehmen
5. [ ] Die Trainings-Bilder generieren (5000 alt, 5000 neu) 5. [ ] Die Trainings-Bilder generieren (5000 alt, 5000 neu)
6. [ ] Trainings der Modelle 6. [ ] Trainings der Modelle

View File

@@ -9,3 +9,7 @@ Im Material für die Haut wird die Luminanz-Kurve angepasst, dafür wird erstmal
Um die Sclera-Textur zu erzeugen wird in der alten Implementierung zunächst eine weiße Textur erzeugt, dann werden die RGB-Werte aller Pixel durch einen zufällig bestimmten Grauwert ersetzt. Anschließend wird ein transparenter Kreis für die Cornea erzeugt und die Glints auf die Textur gemalt. Um die Sclera-Textur zu erzeugen wird in der alten Implementierung zunächst eine weiße Textur erzeugt, dann werden die RGB-Werte aller Pixel durch einen zufällig bestimmten Grauwert ersetzt. Anschließend wird ein transparenter Kreis für die Cornea erzeugt und die Glints auf die Textur gemalt.
Die ersten beiden Schritte lassen sich zusammenfassen. Statt erst eine weiße Textur zu erzeugen (`np.ones`) und die Pixel dann grau (`tex[::3] = grau`) zu machen, kann in einem einzigen Schritt ein Array mit den korrekten Grauwerten erzeugt werden (`np.full(size, [grau,grau,grau,1])`). Tatsächlich kommt der meiste Performance-Vorteil durch das ersetzen von `tex[::3]` Die ersten beiden Schritte lassen sich zusammenfassen. Statt erst eine weiße Textur zu erzeugen (`np.ones`) und die Pixel dann grau (`tex[::3] = grau`) zu machen, kann in einem einzigen Schritt ein Array mit den korrekten Grauwerten erzeugt werden (`np.full(size, [grau,grau,grau,1])`). Tatsächlich kommt der meiste Performance-Vorteil durch das ersetzen von `tex[::3]`
# Pupille verkürzen
Da nur noch ein Modell für alle Ansichten verwendet wird, und dieses einen langen Zylinder am Ende hat, kam es manchmal vor, dass eine Maske generiert wurde, bei der hinten dieser Pimmel sichtbar ist. In der alten Implementierung war er nur in der Hauptszene lang und die anderen hatten ein kurzen. Er wurde in der neuen verkürzt. Es ist nicht wichtig für uns, dass er lang ist.