Initial commit with project files

This commit is contained in:
2025-06-27 14:34:11 +02:00
commit 7ea3207e63
310 changed files with 9331 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.idea/
venv/
*.tflite

93
0_Einführung/README.md Normal file
View File

@@ -0,0 +1,93 @@
# Einführung
In diesem Kapitel werden Sie mit den nötigen Werkzeugen ausgestattet, um diesen Kurs zu meistern.
Dafür wird ihnen gezeigt, wie Sie dieses Repository korrekt auf Ihren Rechner installieren.
Sie werden grundlegend in die Programmiersprache Python und die Programmierumgebung PyCharm eingeführt. Arbeiten Sie die
folgenden Abschnitte in der gegebenen Reihenfolge durch.
**Hinweis:** Sollten Sie bereits Erfahrung mit Github, Python und Pycharm haben, können Sie dieses Einführung überspringen.
## Installation Python
Installieren Sie den Python-Interpreter in der Version 3.5 oder höher. Die Installation ist Abhängig von Ihrem Betriebssystem.
Für das Betriebssystem
- **Windows** finden Sie Python zum Download auf [https://www.python.org/downloads/windows/](https://www.python.org/downloads/windows/)
- **Mac OS** finden Sie Python zum Download auf [https://www.python.org/downloads/mac-osx/](https://www.python.org/downloads/mac-osx/)
- **Linux** ist Python auf den meisten Distributionen vorinstalliert
Eine detailliertes Tutorial zu Python finden Sie unter [https://wiki.python.org/moin/BeginnersGuide](https://wiki.python.org/moin/BeginnersGuide).
## Installation PyCharm Community Edition
Die Programmierumgebung PyCharm ist eine für private- und Lehrzwecke frei verfügbare Software, welche die Arbeit mit
Python sehr stark vereinfacht. Die Verwendung von PyCharm ist für diesen Kurs nicht notwendig aber dringend empfohlen!
Die Installation geschieht auf jedem Betriebssystem mithilfe einer Installationsdatei, welche für
- **Windows** unter [https://www.jetbrains.com/de-de/pycharm/download/#section=windows](https://www.jetbrains.com/de-de/pycharm/download/#section=windows)
- **Mac OS** unter [https://www.jetbrains.com/de-de/pycharm/download/#section=mac](https://www.jetbrains.com/de-de/pycharm/download/#section=mac)
- **Linux** unter [https://www.jetbrains.com/de-de/pycharm/download/#section=linux](https://www.jetbrains.com/de-de/pycharm/download/#section=linux)
erhältlich ist.
**Hinweis:** Sollten Sie Interesse an zusätzlichen Funktionen haben, können Sie als Student eine zeitlich limitierte
kostenlose *PyCharm-Professional* Lizenz für Lehrzwecke erwerben. Weitere Informationen finden Sie unter
[https://www.jetbrains.com/de-de/community/education/#students](https://www.jetbrains.com/de-de/community/education/#students).
## Download der Daten
Wenn Sie diesen Text lesen, werden Sie das Github Repository vermutlich bereits gefunden haben. Der Kurs wird auf der Platform
Github gehosted, um die Aufgaben kontinuierlich weiterentwickeln zu können und Ihnen dabei stets Zugriff auf den
neusten Entwicklungsstand zu geben. Weiterhin bietet Github die Möglichkeit, dass Sie aktiv bei der Mitgestaltung dieses
Kurses beitragen können.
Der Download der Daten kann entweder über **Git** oder die einfache **Download-Funktion** geschehen. Es wird die
Methode Git empfohlen. Weitere Informationen zu Git finden Sie unter
[https://guides.github.com/introduction/git-handbook/](https://guides.github.com/introduction/git-handbook/).
Sollten Sie sich gegen Git und für die Download-Funktion entscheiden, können Sie den Download wie im folgenden Bild
gezeigt starten.
![alt text](./data/git_download.png)
Downloaden und speichern Sie die Daten an einem beliebigen Pfad, z.B. `C:\Users\Timo\Desktop\Digitale-Bildverarbeitung`.
Im folgenden wird der von Ihnen ausgesuchte Pfad als `WORKING_DIRECTORY` beschrieben. Sollten Sie die Daten erfolgreich
gespeichert haben, sollte diese README-Datei an der Stelle `WORKING_DIRECTORY\0_Einführung\README.md` liegen.
**Hinweis:** Sie sind herzlich dazu eingeladen Git *Merge Requests* zu verwenden, um eventuelle Fehler zu korrigieren oder
neue Aufgaben zu erstellen.
## Einrichtung einer Virtuellen Umgebung
Starten Sie PyCharm und erstellen Sie ein neues Projekt. Es wird ein Dialog geöffnet, welcher ähnlich der folgenden
Abbildung aussieht.
![alt text](./data/pycharm1.png)
Wählen Sie ihr `WORKIND_DIRECTORY` in dem Feld *Location* aus und wählen Sie als Projekt Interpreter
*New environment using* und *Virtualenv* aus. Die Initialisierung kann einen Moment dauern!
Nachdem die Umgebung erfolgreich erstellt wurde, sollten Sie das Programm PyCharm in etwa wie in der nächsten Abbildung
dargstellt sehen.
Die rot markierten Felder **Explorer**, **Editor**, **Run** und **Terminal** sind für Ihre Arbeit von besonderem Interesse.
Dabei ist
- **Explorer** eine Projektübersicht, um zwischen den Dateien des Projektes zu wechseln
- **Editor** ein Fenster, um Dateien zu bearbeiten
- **Run** das Ausgabefenster eines Python Programms
- **Terminal** eine interne Kommandozeile
![alt text](./data/pycharm2.png)
Wenn Sie den Reiter `File/Settings/Project: Digitale-Bildverarbeitung/Project Interpreter` (variiert leicht bei den Betriebssystemen!)
öffnen, sollte die Ausgabe ähnlich zu der folgenden Abbildung sein. Die Anzahl der Packages kann dabei deutlich geringer sein.
![alt text](./data/pycharm3.png)
Wechseln Sie nun in das Terminal und verwenden foldende Befehle:
```bash
python -m pip install --upgrade pip
python -m pip install pipenv
pipenv install --skip-lock
```
Die Ausführung kann einen Moment dauern! Verwenden Sie folgenden Befehl um die korrekte Installation zu testen:
```bash
python 0_Einführung/test_installation.py
```
Herzlichen Glückwunsch, Sie haben den Kurs erfolgreich eingerichtet!

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View File

@@ -0,0 +1,13 @@
import cv2
import matplotlib
import numpy as np
import platform
if cv2.__version__ and np.__version__ and matplotlib.__version__:
print("Digital Image Processing is fun!")
print("OpenCV version:", cv2.__version__)
print("NumPy version:", np.__version__)
print("Matplotlib version:", matplotlib.__version__)
print("Python version:", platform.python_version())
else:
print("OpenCV, NumPy and/or Matplotlib are not installed.")

View File

@@ -0,0 +1,56 @@
# Übung 1: Grundlagen Python
Das Erlernen der Programmiersprache Python gehört nicht zum Anspruch dieses Kurses. Trotzdem sollen einige grundlegende
Funktionen und Konventionen aus Python erläutert werden. Im Folgenden wird das simple Beispielprogramm erläutert:
````python
# Beispielprogramm
import numpy as np
print(np.pi)
variable1 = 10
variable2 = [variable1, variable1 + 10, variable1 + 20]
print(variable2[0])
print(variable2[1])
print(variable2[2])
for var in variable2:
if (var / 10) % 2 == 1:
print("Die erste Ziffer Zahl %s ist ungerade!" % var)
````
## Imports
Es existieren viele vorgefertigte Pakete in Python. Sie können diese importieren, indem Sie ``import PAKET `` verwenden.
Im Beispielcode wird *numpy* importiert und mit dem Namen *np* versehen. Daraufhin können Sie auf Funktionen des Pakets
zugreifen, wie z.B. in Zeile 4 ``print(np.pi)``, in welcher der Werit von Pi ausgegben wird. Nutzen Sie Imports nach
Möglichkeit immer zu Beginn ihres Skripts.
## Variablen
In Python können Sie Variablen definieren, ohne einen Typ anzugeben. Im folgenden werden einige Typen gezeigt:
````python
a = "Ein Text" # string
b = 2 # int
c = 3.4 # float
d = [2, 3, 4] # list
e = (2, 3, 4) # tuple
````
Besondere Variablentypen sind Listen oder Tupel. Sie sind Container für mehrere andere Variablen. Auf die einzelnen
Elemente der Liste/Tupel kann mit einem Index zugegriffen werden:
````python
a = [2, 3, 4] # list
b = a[0] # Entspricht dem Wert 2
````
Die Indizierung beginnt bei dem Wert 0.
## print()
Sie können Werte oder Texte ausgeben, indem Sie den Befehl ``print(IRGENDEINE_VARIABLE)`` verwenden.
Sie müssen die Variablen vorher nicht zum Datentyp *string* konvertieren.
## Schleifen
Bitte recherchieren Sie zum Thema Schleifen unter [https://www.python-kurs.eu/schleifen.php](https://www.python-kurs.eu/schleifen.php).

View File

@@ -0,0 +1,14 @@
# Übung 2: Grundlagen Matrix-Operationen mit *Numpy*
In dieser Übung werden Ihnen grundlegende Matrixoperationen mit *numpy* erläutert. Das Python Paket *numpy* ist eine der
meistverwendeten Pakete für mathematische Funktionen und Grundlage für viele andere Python Pakete. Eine detaillierte
Dokumentation finden Sie unter [https://numpy.org/](https://numpy.org/).
Einige Grundlegende Funktionen werden in dieser Übung gezeigt.
# Aufgabe a)
Schauen Sie sich das Tutorial in [https://www.programiz.com/python-programming/matrix](https://www.programiz.com/python-programming/matrix) an und verstehen Sie die
Anwendung von Matrizen in *Numpy*!
# Aufgabe b)
Öffnen Sie die Datei [b.py](b.py) und machen Sie sich mit den Teilaufgaben vertraut.

50
0_Einführung/ü2/b.py Normal file
View File

@@ -0,0 +1,50 @@
"""
ÜBUNG 1
In dieser Übung soll der Umgang mit grundlegenden Matrix-Operationen mithilfe von numpy erlernt werden.
"""
import numpy as np
'''
a) Erstelle einen Zeilenvektor mit den Einträgen 1, 2, 3
'''
'''
b) Erstelle einen Spaltenvektor mit den Einträgen 1, 2, 3
'''
'''
c) Erstelle eine 2x3 Matrix
'''
'''
d) Erstelle eine 6x6 Matrix mit nur 0-Wert Einträgen
'''
'''
e) Erstelle eine 6x6 Matrix mit nur 1-Wert Einträgen
'''
'''
f) Erstelle eine 4x4 Einheitsmatrix
'''
'''
g) Ändere den Wert aus f) aus der zweiten Zeile und dritten Spalte zu dem Wert 5
'''
'''
h) Ändere alle Werte aus f) aus der zweiten Zeile zu dem Wert 4.5
'''
'''
i) Ändere die Werte aus f) aus der zweiten Zeile ab Spalte 3 zu dem Wert 3
'''
'''
j) Multipliziere, addiere, subtrahiere und dividiere die Matrizen a und b
'''

102
0_Einführung/ü2/l_b.py Normal file
View File

@@ -0,0 +1,102 @@
"""
ÜBUNG 1
In dieser Übung soll der Umgang mit grundlegenden Matrix-Operationen mithilfe von numpy erlernt werden.
"""
import numpy as np
'''
a) Erstelle einen Zeilenvektor mit den Einträgen 1, 2, 3
'''
A = [1, 2, 3]
A = np.asarray(A)
print("A", A)
'''
b) Erstelle einen Spaltenvektor mit den Einträgen 1, 2, 3
'''
B = [[1], [2], [3]]
B = np.asarray(B)
print("B")
print(B)
B = [
[1],
[2],
[3]
]
B = np.asarray(B)
print("B")
print(B)
'''
c) Erstelle eine 2x3 Matrix
'''
C = [
[1, 2, 3],
[4, 5, 6]
]
C = np.asarray(C)
print("C")
print(C)
'''
d) Erstelle eine 6x6 Matrix mit nur 0-Wert Einträgen
'''
D = np.zeros(shape=(6, 6))
print("D")
print(D)
'''
e) Erstelle eine 6x6 Matrix mit nur 1-Wert Einträgen
'''
E = np.ones(shape=(6, 6))
print("E")
print(E)
'''
f) Erstelle eine 4x4 Einheitsmatrix
'''
F = np.eye(4)
print("F")
print(F)
'''
g) Ändere den Wert aus f) aus der zweiten Zeile und dritten Spalte zu dem Wert 5
'''
F[1, 2] = 5
print("G")
print(F)
'''
h) Ändere alle Werte aus f) aus der zweiten Zeile zu dem Wert 4.5
'''
F[1] = 4.5
print("H")
print(F)
'''
i) Ändere die Werte aus f) aus der zweiten Zeile ab Spalte 3 zu dem Wert 3
'''
F[1, 2:4] = 3
F[1, 2:] = 3
print("I")
print(F)
'''
j) Multipliziere, addiere, subtrahiere und dividiere die Matrizen a und b
'''
a = np.asarray([[1, 2], [3, 4]])
b = np.asarray([[5, 6], [7, 8]])
print("Elementweise Multiplikation")
print(
a * b
)
print("Matrixoperation Multiplikation")
print(np.matmul(a, b))
print(a + b)
print(a - b)

View File

@@ -0,0 +1,51 @@
# OpenCV in Python
Die beliebte Bildverarbeitungs-Software OpenCV existiert auch in Python. In dieser Übung sollen Sie einige Befehle
erlernen, um grundlegende Funktionen mit OpenCV zu schreiben.
Um OpenCV zu nutzen, müssen Sie zuerst das Paker *cv2* installieren. Wenn Sie dieses Repository installiert haben,
ist das bereits geschehen. Es wird für die folgenden Schritte davon ausgegangen, das *cv2* bereits installiert ist.
Importieren Sie zu Beginn Ihres Skriptes das OpenCV Paket mit dem Befehl
````python
import cv2
````
Nun können Sie alle Funktionen von OpenCV mit dem vorangestellten Kürzel ``cv2.EIN_BEFEHL()`` nutzen.
## Bild laden
Bilder werden als MxN Matrizen (Grau- und Binärbilder) oder als MxNx3 Arrays (RGB-Farbbilder)
gespeichert und interpretiert. Um ein Bild einzulesen, wird die Funktion ``cv2.imread(PFAD_ZUM_BID)`` aufgerufen
und der Dateiname als Parameter in den Klammern übergeben.
````python
I = cv2.imread('myimage.bmp') # Farbbild einlesen
````
Mit dem Befehl ``Ì.shape`` kann die Größe einer Matrix bestimmt werden.
````python
m, n, k = I.shape # Größe ansehen
````
Bei einer Bildmatrix: Der erste Index ist der Zeilenindex (Bildkoordinaten: y-Achse), der zweite
Index ist der Spaltenindex (Bildkoordinaten: x-Achse), wobei der Punkt (0,0) dem linken oberen
Bildpunkt entspricht. Der dritte Index ist der Farbkanal.
## Bild anzeigen
Zum Anzeigen eines Bildes steht die Funktion ``cv2.imshow()`` zur Verfügung. Der Name des Fensters und die Bildvariable wird als
Parameter übergeben. Ein neues Fenster öffnet sich und zeigt das Bild an.
````python
cv2.imshow("Fenstername", I) #Bild anzeigen
cv2.waitKey(0) # Auf Tastendruck warten
````
Der Befehl ``cv2.waitKey(TIME)`` wird benötigt, damit das Programm auf eine Aktion des Benutzers wartet. Wenn der Parameter
dabei 0 ist, wartet das Programm unendlich lange auf den nächsten Tastendruck des Nutzers.
## Bild speichern
Zum Abspeichern der Bilddaten steht die Funktion ``cv2.imwrite(SPEICHER_PFAD, BILD)`` zur Verfügung. Die Bildvariable wird
als zweite Parameter und der Dateiname in Hochkommata als erster Parameter übergeben.
````python
cv2.imwrite('newimage.bmp', I) # Bilddaten abpeichern
````
Wenn die Verzeichnisstruktur nicht angegeben wird, speichert Python die Datei im aktuellen
Arbeitsverzeichnis.
# Aufgabe a)
Implementieren und testen Sie alle Funktion in einem neuen Skript [a.py](a.py)!

View File

@@ -0,0 +1,27 @@
# Übung 4: Grundlagen
Machen Sie sich mit den Grundlagen aus den vorherigen Übungen 1 vertraut. Diese Grundlagen werden nun praktisch angewandt
Nutzen Sie für diese Übung keine Funktionen aus *OpenCV*!
## Aufgabe a)
Führen Sie folgende Aufgabenschritte durch:
1. Erstellen Sie eine 10x10-Matrix mit den Werten von 0 bis 99 wie folgt:
<p align="center">
<img src="https://latex.codecogs.com/svg.image?M&space;=&space;\begin{bmatrix}&space;0&&space;&space;1&&space;\ldots&space;&&space;&space;8&&space;9&space;\\&space;10&&space;&space;11&&space;&space;&&space;&space;18&&space;&space;19\\&space;\vdots&space;&&space;\vdots&space;&space;&&space;\ddots&space;&space;&&space;\vdots&space;&space;&&space;\vdots&space;&space;\\&space;80&&space;&space;81&&space;\ldots&space;&&space;88&space;&&space;89&space;\\&space;90&&space;91&space;&&space;\ldots&space;&&space;98&space;&&space;99&space;\\\end{bmatrix}&space;" title="M = \begin{bmatrix} 0& 1& \ldots & 8& 9 \\ 10& 11& & 18& 19\\ \vdots & \vdots & \ddots & \vdots & \vdots \\ 80& 81& \ldots & 88 & 89 \\ 90& 91 & \ldots & 98 & 99 \\\end{bmatrix} " />
</p>
2. Erzeugen Sie einen Zeilenvektor der Dimension 10. Alle Komponenten sollen den Wert 20 haben:
<p align="center">
<img src="https://latex.codecogs.com/svg.image?v&space;=&space;\begin{bmatrix}&space;20&&space;&space;20&&space;20&&space;20&&space;20&&space;20&&space;20&&space;20&&space;20&&space;20&space;\\\end{bmatrix}" title="v = \begin{bmatrix} 20& 20& 20& 20& 20& 20& 20& 20& 20& 20 \\\end{bmatrix}" />
</p>
3. Subtrahieren Sie die zweite Zeile der Matrix M vom eben erzeugten Vektor v.
4. Multiplizieren Sie den resultierenden Vektor vr als Spaltenvektor von rechts mit der Matrix M
5. Teilen Sie die Komponenten des entstandenen Vektors jeweils durch 100 und runden Sie das Ergebnis.
6. Berechnen Sie das Maximum
Wie lautet das Ergebnis?
Die Musterlösung findet sich in [l_a.py](l_a.py)

24
0_Einführung/ü4/l_a.py Normal file
View File

@@ -0,0 +1,24 @@
import numpy as np
M = np.zeros((10, 10))
for i in range(10):
for j in range(10):
M[i, j] = 10 * i + j
print("(1) M=", M)
v = np.ones((1, 10)) * 20
print("(2) v=", v)
vr = v - M[1, :]
print("(3) vr:", vr)
res = np.matmul(M, np.transpose(vr, axes=(1, 0)))
print("(4) res:", res)
res = res / 100
res = np.round(res) # np.floor() np.ceil()
print("(5) res:", res)
maximum = np.max(res)
print("(6) maximum:", maximum)

17
1_Grundlagen/README.md Normal file
View File

@@ -0,0 +1,17 @@
# Grundlagen
Um mit Methoden der Digitalen Bildverarbeitung zu arbeiten, lohnt sich ein Blick auf die Grundlagen.
Das Unterverzeichnis *1_Grundlagen* bietet Aufgaben zum Themengebiet "Grundlagen" in der Vorlesung.
Dabei sollen ins besondere die Themen
- **Das menschliche visuelle System**
- **Technische Bilderfassung/Sensoren**
- **Das Digitale Bild**
mit zusätzlichem Material unterstützt werden.

8
1_Grundlagen/todos.md Normal file
View File

@@ -0,0 +1,8 @@
# Zu erstellende Übungen
- ~~Luminanz vs. Chrominanz~~
- Machsches Gesetz
- ~~Visualisierung von Rauschen in der Kamera~~
- ~~Rolling Shutter vs. Global Shutter~~
- ~~Quantisierung eines Bildes: Wann erkennt man nichts mehr?~~
<img src="https://latex.codecogs.com/svg.image?\sum_1" title="\sum_1" />

View File

@@ -0,0 +1,21 @@
# Übung 1: Rauschen
In der Vorlesung wurden Ihnen einige Bildsensoren vorgestellt. In dieser Übung sollen Sie Ihren eigenen Bildsensor verwenden:
Ihre Webcam. In dieser Übung sollen Sie erlernen, wie Sie mit OpenCV eine Kamera öffnen und das Bild anzeigen.
Daraufhin werden Sie ein technisches Problem bei der Aufnahme von Bilddaten kennenlernen: Das Rauschen.
## Aufgabe a)
Implementieren Sie in die Datei [a.py](a.py) folgende Schritte:
1. Öffnen Sie Ihre Webcam
2. Schneiden Sie ein Bildausschnitt mit 50x50 Pixels aus dem Bild aus
3. Zeigen Sie den Bildausschnitt auf den ganzen Bildschirm vergrößert an
Recherchieren Sie im Internet und/oder im Einführungskapitel nach den Funktionen `cv2.VideoCapture()`, `cv2.resize()`
und `cv2.imshow()`. Eine Musterlösung finden Sie in der Datei [l_a.py](l_a.py).
Versuchen Sie den Inhalt vor der Kamera konstant zu halten.
## Fragen:
- Bleibt das Bild konstant oder sehen Sie Rauschen?
- Wenn nein: Worin kann das Rauschen begründet liegen?
- Wozu kann das Rauschen führen?

18
1_Grundlagen/ü1/l_a.py Normal file
View File

@@ -0,0 +1,18 @@
import cv2
''' Öffnen einer Kamera '''
cap = cv2.VideoCapture(0)
cv2.namedWindow("Ergebnis", cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty("Ergebnis", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
''' Auslesen, Modifizieren und Ausgeben von Bildern'''
while True:
ret, frame = cap.read()
frame = frame[0:50, 0:50]
cv2.imshow('Ergebnis', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
''' Fenster schließen, nachdem q gedrückt wurde'''''
cap.release()
cv2.destroyAllWindows()

View File

@@ -0,0 +1,10 @@
# Übung 2: Luminanz vs. Chrominanz
Was glauben Sie: Hat die Farbinformation oder die Helligkeitsinformation eine höhere Interpretierbarkeit für den Menschen?
Programmieren Sie ein Skript, mit dem sie Ihre Webcam auslesen, modifizieren und ausgeben. Entfernen Sie die Helligkeitsinformation
bzw. Farbinformation und betrachten das Ergebnis. Wird Ihre initiale Einschätzung bestätigt? Schreiben Sie Ihr Skript in
die Datei [a.py](a.py). Die Lösung ist in der Datei [l_a.py](l_a.py) zu finden!
**Hinweis:** Verwenden Sie in Bild Ihrer Wahl, wenn keine Webcam zur Verfügung steht.

26
1_Grundlagen/ü2/a.py Normal file
View File

@@ -0,0 +1,26 @@
import cv2
import numpy as np
''' Öffnen einer Kamera und Initialisierung von Variablen '''
cap = cv2.VideoCapture(0)
mode = "CHROMINANZ" # CHROMINANZ, LUMINANZ
window_name = "Ergebnis mit %s" % mode
''' Auslesen, Modifizieren und Ausgeben von Bildern'''
while True:
ret, frame = cap.read()
if mode == "CHROMINANZ":
frame = ...
elif mode == "LUMINANZ":
frame = ...
else:
raise Exception("FALSCHER MODE!!!")
cv2.imshow(window_name, frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
''' Fenster schließen, nachdem q gedrückt wurde '''
cap.release()
cv2.destroyAllWindows()

31
1_Grundlagen/ü2/l_a.py Normal file
View File

@@ -0,0 +1,31 @@
import cv2
import numpy as np
''' Öffnen einer Kamera und Initialisierung von Variablen '''
cap = cv2.VideoCapture(0)
mode = "RGB" # CHROMINANZ, LUMINANZ
window_name = "Ergebnis mit %s" % mode
''' Auslesen, Modifizieren und Ausgeben von Bildern'''
while True:
ret, frame = cap.read()
if mode == "LUMINANZ":
# Farbinformationen entfernen
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
elif mode == "CHROMINANZ":
# Normalisieren: p_r / (p_r + p_g + p_b), p_g / (p_r + p_g + p_b), p_b / (p_r + p_g + p_b)
pixel_sum = np.sum(frame, keepdims=True, axis=2)
frame = frame.astype(np.float32) / pixel_sum
elif mode == "RGB":
pass
else:
raise Exception("FALSCHER MODE!!!")
cv2.imshow(window_name, frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
''' Fenster schließen, nachdem q gedrückt wurde'''''
cap.release()
cv2.destroyAllWindows()

View File

@@ -0,0 +1,36 @@
# Übung 3: Diskretisierung und Quantisierung
In dieser Übung wird die Quantisierung und Diskretisierung von Bildern betrachtet. In den folgenden Abbildungen sind beide
Methoden visualisiert.
Quantisierung | Diskretisierung
:---:|:---
![](https://upload.wikimedia.org/wikipedia/commons/thumb/7/70/Quantized.signal.svg/2880px-Quantized.signal.svg.png) | ![](https://upload.wikimedia.org/wikipedia/commons/thumb/1/15/Zeroorderhold.signal.svg/2880px-Zeroorderhold.signal.svg.png)
[Link zum Bild](https://upload.wikimedia.org/wikipedia/commons/thumb/7/70/Quantized.signal.svg/2880px-Quantized.signal.svg.png) | [Link zum Bild](https://upload.wikimedia.org/wikipedia/commons/thumb/1/15/Zeroorderhold.signal.svg/2880px-Zeroorderhold.signal.svg.png)
## Aufgabe a) Diskretisierung
In der Datei [a.py](a.py) wird ein Bild geladen. Das Bild hat die Größe 1526 x 1600 (Breite x Höhe).
Diskretisieren Sie das Bild mit dem Faktor *k* **ohne** und **mit** Verwendung der Funktion `cv2.resize()`. Dabei kann
*k* die Werte 4, 8, und 13.5 annehmen. Zeigen Sie die Bilder für den direkten Vergleich an! Achten Sie dabei darauf,
dass die Bilder in der gleichen Größe dargestellt werden.
## Aufgabe b) Quantisierung
In der Datei [b.py](b.py) wird ein Bild geladen. Das Bild ist im BGR-Farbraum repräsentiert und hat eine 8-Bit Quantisierung
(Wertebereich {0, ..., 255}.
Führen Sie folgende Schritte durch:
1. Quantisieren Sie das Bild in den Wertebereich {0, ..., 127}
2. Quantisieren Sie das Bild aus Schritt 1 in den Wertebereich {0, ..., 3}
3. Quantisieren Sie das Bild aus Schritt 2 zurück in den Wertebereich {0, ..., 255}
Zeigen Sie die Bilder aus allen Schritten für den direkten Vergleich an:
- Wie bewerten Sie die Qualität der Bilder?
- Was fällt auf?

22
1_Grundlagen/ü3/a.py Normal file
View File

@@ -0,0 +1,22 @@
import numpy as np
import cv2
# Hiermit kann die Methode für die Berechnung ausgewählt werden
METHOD = "MANUELL" # OpenCV
# Einlesen des Bildes
filepath = "../../data/flower.jpeg"
img = cv2.imread(filepath)
h, w, c = img.shape
print("Originale Breite:", w)
print("Originale Höhe:", h)
scales = [4, 8, 13.5]
images = []
# todo Methode MANUELL implementieren und 'images' mit diskretisierten Bildern füllen
# todo Methode OpenCV implementieren und 'images' mit diskretisierten Bildern füllen
# todo Bilder darstellen

13
1_Grundlagen/ü3/b.py Normal file
View File

@@ -0,0 +1,13 @@
import numpy as np
import cv2
# Hiermit kann die Methode für die Berechnung ausgewählt werden
METHOD = "MANUELL" # OpenCV
# Einlesen des Bildes
filepath = "../../data/lena.png"
img = cv2.imread(filepath)
h, w, c = img.shape
# todo Schritte 1-3 implementieren!

46
1_Grundlagen/ü3/l_a.py Normal file
View File

@@ -0,0 +1,46 @@
import numpy as np
import cv2
# Hiermit kann die Methode für die Berechnung ausgewählt werden
METHOD = "MANUELL" # OpenCV, MANUELL
# Einlesen des Bildes
filepath = "../../data/flower.jpeg"
img = cv2.imread(filepath)
h, w, c = img.shape
print("Originale Breite:", w)
print("Originale Höhe:", h)
scales = [4, 8, 13.5]
images = []
for scale in scales:
new_w, new_h = round(w / scale), round(h / scale)
if METHOD == "OpenCV":
new_image = cv2.resize(img, (new_w, new_h))
# Frage 1: Welche Interpolations-Methode wird hier verwendet?
elif METHOD == "MANUELL":
new_image = np.zeros((new_h, new_w, c), dtype=np.uint8)
for x in range(new_w):
for y in range(new_h):
x_projected, y_projected = min(w - 1, round(x * scale)), min(h - 1, round(y * scale))
new_image[y, x] = img[y_projected, x_projected]
# Frage 1: Welche Interpolations-Methode wird hier verwendet?
# Frage 2: Welches Mapping wird hier verwendet (For- oder Backwardmapping)?
else:
raise Exception("Da ist wohl ein Fehler unterlaufen!")
images.append(new_image)
# Bilder darstellen
show_w, show_h = 1200, 1200
img = cv2.resize(img, (show_w, show_h))
cv2.imshow("Original", img)
for scale, image in zip(scales, images):
image = cv2.resize(image, (show_w, show_h))
cv2.imshow("Scale %s" % scale, image)
cv2.waitKey(0)

36
1_Grundlagen/ü3/l_b.py Normal file
View File

@@ -0,0 +1,36 @@
import numpy as np
import cv2
# Einlesen des Bildes
filepath = "../../data/lena.png"
img = cv2.imread(filepath)
'''
Anwendung der Schritte:
0. Für die Berechnung werden die Bilder in das kontinuierliche np.float64 konvertiert.
1. In Wertebereich {0, ..., 127}
2. In Wertebereich {0, ..., 3}
3. In Wertebereich {0, ..., 255}
'''
img_step0 = img.astype(np.float64)
img_step1 = np.round(127 * np.copy(img_step0) / 255)
img_step2 = np.round(3 * np.copy(img_step1) / 127)
img_step3 = np.round(255 * np.copy(img_step2) / 3)
'''
Für die Darstellung von np.float64 Bildern wird der Wertebereich von {0, ..., n_max} in [0, 1] projeziert.
Dabei entspricht der Wert 1 dem ehemaligen Maximum n_max.
'''
img_step0 = img_step0 / 255
img_step1 = img_step1 / 127
img_step2 = img_step2 / 3
img_step3 = img_step3 / 255
cv2.imshow("Schritt 0", img_step0)
cv2.imshow("Schritt 1", img_step1)
cv2.imshow("Schritt 2", img_step2)
cv2.imshow("Schritt 3", img_step3)
cv2.waitKey(0)

View File

@@ -0,0 +1,36 @@
# Übung 4: Aliasing
In dieser Übung wird das *Aliasing* betrachtet. Der Effekt des *Aliasing* entsteht,
wenn eine Struktur mit einer zu geringen Rate abgetastet wird. Als Beispiel wird das Bild
![](./data/Mauer.png)
verwendet. Programmieren Sie ein Skript, mit dem das Bild eingelesen und mit den Faktoren 2, 4 und 10 skaliert wird.
Zeigen Sie sich die Bilder an. Verwenden Sie während der Skalierung die Nearest-Neighbour Interpolation!
Schreiben Sie Ihr Skript in die Datei [a.py](a.py). Die Lösung ist in der Datei [l_a.py](l_a.py) zu finden!
Was fällt Ihnen bei der Betrachtung der Bilder auf?
## Technische Beschreibung des Phänomens
Das Aliasing entsteht durch die Unterabtastung eines Signals. Um ein Signal korrekt darstellen zu können, muss das
[Nyquist-Shannon-Abtasttheorem](https://de.wikipedia.org/wiki/Nyquist-Shannon-Abtasttheorem) beachtet werden.
Die Kreisfrequenz der Abtastung <img src="https://latex.codecogs.com/svg.image?\Omega_T" title="\sum_1" />
muss demnach
<p align="center">
<img src="https://latex.codecogs.com/svg.image?\Omega_T>2\Omega_g" title="\sum_1" />
<p>
beziehungsweise die Abtastfrequenz <img src="https://latex.codecogs.com/svg.image?f_T" title="\sum_1" /> muss
<p align="center">
<img src="https://latex.codecogs.com/svg.image?f_T>2f_g" title="\sum_1" />
<p>
mit der maximalen Kreisfrequenz <img src="https://latex.codecogs.com/svg.image?\Omega_g" title="\sum_1" /> bzw.
maximalen Frequenz <img src="https://latex.codecogs.com/svg.image?f_g" title="\sum_1" /> des Signals sein.

7
1_Grundlagen/ü4/a.py Normal file
View File

@@ -0,0 +1,7 @@
import cv2
''' Öffnen einer Kamera und Initialisierung von Variablen '''
img = cv2.imread("./data/Mauer.png")
cv2.imshow("Original", img)
cv2.waitKey(0)

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

19
1_Grundlagen/ü4/l_a.py Normal file
View File

@@ -0,0 +1,19 @@
import cv2
''' Öffnen einer Kamera und Initialisierung von Variablen '''
img = cv2.imread("./data/Mauer.png")
cv2.imshow("Original", img)
downscaled2 = cv2.resize(img, dsize=None, fx=0.5, fy=0.5, interpolation=cv2.INTER_NEAREST)
downscaled2_original_size = cv2.resize(downscaled2, dsize=None, fx=2, fy=2, interpolation=cv2.INTER_NEAREST)
cv2.imshow("Abgetastet mit Faktor 2", downscaled2_original_size)
downscaled4 = cv2.resize(img, dsize=None, fx=0.25, fy=0.25, interpolation=cv2.INTER_NEAREST)
downscaled4_original_size = cv2.resize(downscaled4, dsize=None, fx=4, fy=4, interpolation=cv2.INTER_NEAREST)
cv2.imshow("Abgetastet mit Faktor 4", downscaled4_original_size)
downscaled10 = cv2.resize(img, dsize=None, fx=0.1, fy=0.1, interpolation=cv2.INTER_NEAREST)
downscaled10_original_size = cv2.resize(downscaled10, dsize=None, fx=10, fy=10, interpolation=cv2.INTER_NEAREST)
cv2.imshow("Abgetastet mit Faktor 10", downscaled10_original_size)
cv2.waitKey(0)

View File

@@ -0,0 +1,31 @@
# Übung 5: Global- und Rolling-Shutter
In dieser Übung werden zwei Typen von Bildsensoren betrachtet: Sensoren mit Global- und Rolling-Shutter Auslesefunktion.
Der Unterschied während des Auslesens bezieht sich auf die zeitliche Anordnung der Belichtung der einzelnen Halbleiter
innerhalb des Sensors. Ein Halbleiter entspricht dabei einem einem Bildpunkt. Beim Global-Shutter werden alle Halbleiter
gleichzeitig belichtet. Beim Rolling-Shutter werden die einzelnen Zeilen des Sensors sequenziell belichtet.
Der Schematische Ablauf eines Global-Shutter Sensors ist in dem folgenden Bild dargestellt:
![](data/global_shutter.png)
Der Schematische Ablauf eines Rolling-Shutter Sensors hingegen sieht wie folgt aus:
![](data/rolling_shutter.png)
## Vor- und Nachteile Rolling-/Global-Shutter
Vorteile Rolling-Shutter gegenüber Global-Shutter:
- Preisgünstiger
- Längere Belichtungszeit möglich (weniger Rauschen)
Nachteile Rolling-Shutter gegenüber Global-Shutter:
- Bewegungsverzerrung
## a) Bewegungsverzerrung
In dieser Aufgabe soll die Bewegungsverzerrung eines Rolling-Shutter Sensors künstlich erzeugt werden. In dem Skript
[a.py](a.py) werden drei Bilder aus dem [KITTI](http://www.cvlibs.net/datasets/kitti/) aus einem fahrenden Auto geladen.
Ihre Aufgabe ist die Simulation eines Rolling-Shutter Sensors, indem Sie ein resultierendes Bild aus den drei
Zeitpunkten des Videos konstruieren. Konstruieren Sie ein Bild, indem Sie die Zeile 1, 4, 7 ... aus dem ersten Bild,
Zeile 2, 5, 8 ... sowie die Zeilen 3, 6, 9, ... aus dem dritten Bild verwenden. Visualisieren Sie das Bild.
. Die Lösung ist in der Datei [l_a.py](l_a.py) zu finden!

10
1_Grundlagen/ü5/a.py Normal file
View File

@@ -0,0 +1,10 @@
import cv2
""" Read images """
img1 = cv2.imread("data/kitti1.png")
img2 = cv2.imread("data/kitti2.png")
img3 = cv2.imread("data/kitti3.png")
cv2.imshow("Global-Shutter", img3)
cv2.waitKey(0)

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

16
1_Grundlagen/ü5/l_a.py Normal file
View File

@@ -0,0 +1,16 @@
import cv2
""" Read images """
img1 = cv2.imread("data/kitti1.png")
img2 = cv2.imread("data/kitti2.png")
img3 = cv2.imread("data/kitti3.png")
cv2.imshow("Global-Shutter", img3)
""" Simulate rolling shutter"""
img3[0::3, :, :] = img1[0::3, :, :]
img3[1::3, :, :] = img2[1::3, :, :]
cv2.imshow("Rolling-Shutter", img3)
cv2.waitKey(0)

View File

@@ -0,0 +1,9 @@
# Einführung in die Bildbearbeitung
In diesem Kapitel werden Ihnen verschiedene Klassen von Operationen und Methoden erläutert und mit Beispielen
exemplarisch dargestellt. Die Übungen zeigen Beispiele zu den Themen
- **Punktoperationen (Intensitätstransformationen)**
- **Lokale Operationen (Filterung)**
- **Globale Operationen**
- **Geometrische Transformationen**

View File

@@ -0,0 +1,19 @@
# Übung 1: Helligkeitsausgleich
In dieser Übung wird ein Helligkeitsausgleich durchgeführt. Gegeben sind drei Bilder von Texten wie exemplarisch unten
dargestellt. Zu sehen ist, dass die mittlere Beleuchtung des Bildes lokal stark variiert. Auf Algorithmen, die beispielweise
Schwellwerte auf Gurndlage der Helligkeit nutzen, können diese Unterschiede störend wirken. Im folgenden soll daher ein
Helligkeitsausgleich durchgeführt werden, nachdem die mittlere lokale Helligkeit in jedem Bildsegment gleich ist.
![alt text](../../data/text_3.jpg)
## Aufgabe a)
In der Datei [a.py](a.py) werden drei Bilder geladen. Führen Sie den Helligkeitsausgleich auf allen Bildern durch und zeigen
Sie sie an.

View File

@@ -0,0 +1,13 @@
import numpy as np
import cv2
KERNEL_SIZE = 20
# Einlesen des Bildes
filepath = "../../data/text_%s.jpg"
images = list()
for i in [1, 2, 3]:
img = cv2.imread(filepath % i)
img = cv2.resize(img, (500, 500))
images.append(img)

View File

@@ -0,0 +1,34 @@
import numpy as np
import cv2
KERNEL_SIZE = 20
# Einlesen des Bildes
filepath = "../../data/text_%s.jpg"
images = list()
for i in [1, 2, 3]:
img = cv2.imread(filepath % i)
img = cv2.resize(img, (500, 500))
images.append(img)
def balance(img):
kernel = np.ones((KERNEL_SIZE, KERNEL_SIZE)) / (KERNEL_SIZE * KERNEL_SIZE)
blurred = cv2.filter2D(img, -1, kernel=kernel)
img = img / blurred
img = 255 * (img - np.min(img)) / (np.max(img) - np.min(img)) # Normieren auf Wertebereich {0, ..., 255}
img = img.astype(np.uint8)
print(np.max(img), np.min(img))
return img
balanced_images = list()
for img in images:
balanced_image = balance(img)
balanced_images.append(balanced_image)
for i, (image, balaced_image) in enumerate(zip(images, balanced_images)):
cv2.imshow("Text%s" % (i + 1), image)
cv2.imshow("AusgeglichenerText%s" % (i + 1), balaced_image)
cv2.waitKey(0)

View File

@@ -0,0 +1,22 @@
# Übung 10: (Contrast Limited) Adaptive Histogramm Equalization
In dieser Übung soll die Adaptive Histogramm Equalization und die Contrast Limited Adaptive Histogramm Equalization
verwendet werden, um den Kontrast in einem Bild zu erhöhen.
## a) Adaptive Histogramm Equalization
Wenden Sie die adaptive Histogramm Equalization auf das Bild an, welches in dem Skript [a.py](a.py)
geladen wird. Adaptieren Sie für eine Region von 50x50 Pixel. Die Lösung ist in der Datei [l_a.py](l_a.py) zu finden!
## b) Contrast Limited Adaptive Histogramm Equalization
Wenden Sie die Kontrast-limitierte adaptive Histogramm Equalization auf das Bild an, welches in dem Skript [b.py](b.py)
geladen wird. Sie können ein selbst definiertes Clip-Limit definieren.
Adaptieren Sie für eine Region von 50x50 Pixel. Die Lösung ist in der Datei [l_b.py](l_b.py) zu finden!

View File

@@ -0,0 +1,15 @@
import cv2
import numpy as np
img = cv2.imread("../../data/car.png", cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (500, 500))
# Do some preprocessing
img = img.astype(float)
img = 50 + (105 * img / 255)
cv2.imshow("Original", img.astype(np.uint8))
# Implement AHE
cv2.waitKey(0)

View File

@@ -0,0 +1,15 @@
import cv2
import numpy as np
img = cv2.imread("../../data/car.png", cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (500, 500))
# Do some preprocessing
img = img.astype(float)
img = 50 + (105 * img / 255)
cv2.imshow("Original", img.astype(np.uint8))
# Implement CLAHE
cv2.waitKey(0)

View File

@@ -0,0 +1,25 @@
import cv2
import numpy as np
img = cv2.imread("../../data/car.png", cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (500, 500))
# Do some preprocessing
img = img.astype(float)
img = 50 + (105 * img / 255)
cv2.imshow("Original", img.astype(np.uint8))
# Implement AHE
new_image = np.zeros_like(img)
height, width = img.shape
for x in range(width):
for y in range(height):
x1, x2 = np.maximum(0, x - 25), np.minimum(x + 25, height)
y1, y2 = np.maximum(0, y - 25), np.minimum(y + 25, height)
hist, values = np.histogram(img[y1:y2, x1:x2], bins=256, range=(0, 256))
cum_hist = np.cumsum(hist)
v = round(img[y, x])
new_image[y, x] = 255 * cum_hist[v] / cum_hist[255]
cv2.imshow("AHE", new_image.astype(np.uint8))
cv2.waitKey(0)

View File

@@ -0,0 +1,31 @@
import cv2
import numpy as np
img = cv2.imread("../../data/car.png", cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (500, 500))
# Do some preprocessing
img = img.astype(float)
img = 50 + (105 * img / 255)
cv2.imshow("Original", img.astype(np.uint8))
# Implement CLAHE
new_image = np.zeros_like(img)
height, width = img.shape
for x in range(width):
for y in range(height):
x1, x2 = np.maximum(0, x - 25), np.minimum(x + 25, height)
y1, y2 = np.maximum(0, y - 25), np.minimum(y + 25, height)
hist, values = np.histogram(img[y1:y2, x1:x2], bins=256, range=(0, 256))
clip_limit = 0.6 * round(np.max(hist))
higher_values = hist >= clip_limit
sum_of_higher_values = np.sum(hist[higher_values] - clip_limit)
hist = np.minimum(hist, clip_limit)
hist = hist + round(sum_of_higher_values / 256)
cum_hist = np.cumsum(hist)
v = round(img[y, x])
new_image[y, x] = 255 * cum_hist[v] / cum_hist[255]
cv2.imshow("CLAHE", new_image.astype(np.uint8))
cv2.waitKey(0)

View File

@@ -0,0 +1,109 @@
# Übung 2: Lokale Bildoperationen
Lokale Bildoperationen berechnen den Pixelwert des Ausgangspixels aus der lokalen Umgebung des Eingangspixels. Die
Berechnungsvorschrift ist dabei für jedes Pixel gleich.
In dieser Übung werden einige Beispiele für lokale Bildoperationen behandelt. Dabei werden unter anderem
- Faltung
- Gradienten
- Rangfolgefilter
- Hochpass, Tiefpass, Bandpass
verwendet.
## Aufgabe a)
Das Bild zeigt ***I_in*** zwei sich schneidende Linien (eine horizontalen und eine vertikale) auf hellem
Hintergrund (außerhalb des eingezeichneten Rasters sind die Flächen wie angedeutet störungsfrei
fortgeführt). Es soll nun die Horizontale extrahiert und in binärer Form dargestellt werden, so dass
sich das Binärbild ***I_out*** ergibt. Im folgenden sind
die Bilder visualisiert und als Matrix dargestellt.
<p align="center">
<img src="./data/cross1.png" />
</p>
```python
I_in = [
[200, 200, 100, 200, 200],
[200, 200, 100, 200, 200],
[100, 100, 100, 100, 100],
[200, 200, 100, 200, 200],
[200, 200, 100, 200, 200],
]
```
<p align="center">
<img src="./data/cross2.png" />
</p>
```python
I_out = [
[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0],
[255, 255, 255, 255, 255],
[ 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0],
]
```
Für die Aufgabe stehen die folgenden Operationen zur Verfügung:
- Hochpassfilter mit 3x3 Faltungskern
```python
[
[ -1, -1, -1 ],
[ -1, 8, -1 ],
[ -1, -1, -1 ]
]
```
- Kantenfilter mit 3x3 Faltungskern
```python
[
[ 1, 0, -1 ],
[ 1, 0, -1 ],
[ 1, 0, -1 ]
]
```
- Kantenfilter mit 3x3 Faltungskern
```python
[
[ -1, -1, -1 ],
[ 0, 0, 0 ],
[ 1, 1, 1 ]
]
```
- Punktoperation: Betragsbildung
- Punktoperation: Schwellwertoperation, Punkte mit einer Intensität *I_xy* < 128 werden auf *I_xy* = 0 gesetzt, ansonsten zu 255
- Median-Filter der Größe 3x3
Wählen Sie 4 der 6 Operationen und wenden Sie sie auf die das Bild an. Jede Operation darf dabei nur einmal verwendet werden.
Visualisieren Sie jeden Zwischenschritt.
Die Aufgabe soll in der Datei [a.py](a.py) bearbeitet werden! Die entsprechende Lösung finden Sie in [l_a.py](l_a.py).
**Hinweis:** Mehrere Lösungswege sind möglich!
## Aufgabe b)
Gegeben ist das Bild [edge_01.png](data/edge_01.png). Es zeigt zwei aneinander grenzende graue Flächen die mit Rauschen
versetzt sind. Ziel ist es, das Bild so zu filtern, dass die Kante als weichgezeichnete Linie auf
schwarzem Untergrund resultiert, wie in Bild [edge_02.png](data/edge_02.png) beispielhaft dargestellt ist.
*edge_01.png*:
<p align="center">
<img src="./data/edge_01.png" />
</p>
*edge_02.png*:
<p align="center">
<img src="./data/edge_02.png" />
</p>
Für die Bearbeitung können Sie sich verschiedener Filter bedienen. Beispiele dafür können sein
- Hochpass, Tiefpass, Bandpass
- Diverse Richtungsfilter
- Rangfolgefilter wie Minimum, Maximum, Median
Die Aufgabe soll in der Datei [b.py](b.py) bearbeitet werden! Die entsprechende Lösung finden Sie in [l_b.py](l_b.py).

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

View File

@@ -0,0 +1,50 @@
import numpy as np
import cv2
''' Einlesen des Bildes '''
I_in = [
[200, 200, 100, 200, 200],
[200, 200, 100, 200, 200],
[100, 100, 100, 100, 100],
[200, 200, 100, 200, 200],
[200, 200, 100, 200, 200],
]
I_in = np.asarray(I_in, dtype="uint8")
print("Bild vor der Bearbeitung:")
print(I_in)
print()
print(I_in.dtype)
''' Operation 1: Kantenfilter '''
edge = [
[-1, -1, -1],
[ 0, 0, 0],
[ 1, 1, 1]
]
edge = np.asarray(edge)
edge_conv = np.flip(edge)
I_in = cv2.filter2D(I_in, cv2.CV_64F, edge_conv, borderType=cv2.BORDER_REPLICATE)
print("Operation 1: Kantenfilter")
print(I_in)
print()
''' Operation 2: Absolutwertbildung '''
I_in = np.abs(I_in)
print("Operation 2: Absolutwertbildung")
print(I_in)
print()
''' Operation 3: Medianfilter'''
I_in = cv2.medianBlur(I_in.astype("float32"), 3)
print("Operation 3: Medianfilter")
print(I_in)
print()
''' Operation 4: Schwellwert '''
I_in = np.copy(I_in)
ret, I_out = cv2.threshold(I_in, 127, 255, cv2.THRESH_BINARY)
print("Operation 4: Schwellwert")
print(I_out)
print()

View File

@@ -0,0 +1,36 @@
import numpy as np
import cv2
''' Einlesen des Bildes '''
I_in = cv2.imread("data/edge_01.png")
I_in = cv2.cvtColor(I_in, cv2.COLOR_BGR2GRAY)
cv2.imshow("Bild Schritt 0", I_in)
''' Operation 1: Median-Filter'''
I_in = cv2.medianBlur(I_in, 9)
cv2.imshow("Bild Schritt 1", I_in)
''' Operation 2: Kantenfilter'''
edge = [
[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]
]
edge = np.asarray(edge)
I_in = cv2.filter2D(I_in, cv2.CV_64F, edge, borderType=cv2.BORDER_REPLICATE)
cv2.imshow("Bild Schritt 2", I_in)
''' Operation 3: Glättung / Tiefpass mit Boxfilter'''
edge = [
[1, 1, 1],
[1, 1, 1],
[1, 1, 1]
]
edge = np.asarray(edge) / 9
I_in = cv2.filter2D(I_in, cv2.CV_64F, edge, borderType=cv2.BORDER_REPLICATE)
I_in = I_in / np.max(I_in)
cv2.imshow("Bild Schritt 3", I_in)
''' Bilder anzeigen, Bis Taste gedrückt wird '''
cv2.waitKey(0)

View File

@@ -0,0 +1,56 @@
# Übung 3: Geometrische Transformationen
Geometrische Transformationen können auf Bilder angewendet werden, um den Wert eines Pixels im Eingangsbild auf eine
andere Position im Ausgangsbild abzubilden.
Geometrische Transformationen können unterschieden werden in
|Name| Freiheitsgrade| Beispiel|
|:---:|:---:|:---:|
|Translation|2||
|Rigide/Euklidische Transformation|3||
|Ähnlichkeits- Transformation|4||
|Affine Transformation|6||
|Projektive Transformation|8||
Zusätzliche Informationen über die Implementierung in OpenCV können Sie hier finden: [https://docs.opencv.org/3.4/d4/d61/tutorial_warp_affine.html](https://docs.opencv.org/3.4/d4/d61/tutorial_warp_affine.html)
## Aufgabe a)
Eine häufig verwendete Transformation ist die Skalierung, Diese sollen Sie nun implementieren. Arbeiten Sie dazu die
Fragen bzw. Teilschritte in [a.py](a.py) ab. Die entsprechende Lösung finden Sie in [l_a.py](l_a.py).
.
## Aufgabe b)
Betrachten Sie das folgende Eingangsbild sowie die daraus resultierenden Ausgangsbilder.
**Eingangsbild:**
![](./data/normal.jpg)
**Ausgangsbilder:**
![](./data/center-rotated.jpg)
![](./data/rotated.jpg)
![](./data/shear.jpg)
Gebeben sind folgende Transformationsforschriften:
<p align="center">
<img src="https://latex.codecogs.com/svg.image?T(p)&space;=&space;\begin{pmatrix}cos(-\pi&space;/&space;4)&space;&&space;&space;-sin(-\pi&space;/&space;4)\\sin(-\pi&space;/&space;4)&&space;&space;cos(-\pi&space;/&space;4)\\\end{pmatrix}&space;p" title="T(p) = \begin{pmatrix}cos(\-pi / 4) & -sin(\-pi / 4)\\sin(\-pi / 4)& cos(\-pi / 4)\\\end{pmatrix} p" />
</p>
<p align="center">
<img src="https://latex.codecogs.com/svg.image?T(p)&space;=&space;\begin{pmatrix}cos(-\pi&space;/&space;4)&space;&&space;&space;-sin(-\pi&space;/&space;4)\\sin(-\pi&space;/&space;4)&&space;&space;cos(-\pi&space;/&space;4)\\\end{pmatrix}&space;(p&space;-&space;c)&space;&plus;&space;c" title="T(p) = \begin{pmatrix}cos(\-pi / 4) & -sin(\-pi / 4)\\sin(\-pi / 4)& cos(\-pi / 4)\\\end{pmatrix} (p - c) + c" />
</p>
<p align="center">
<img src="https://latex.codecogs.com/svg.image?T(p)&space;=&space;\begin{pmatrix}1&space;&&space;&space;0.8&space;\\0&space;&&space;&space;1&space;\\\end{pmatrix}&space;(p&space;-&space;c)&space;&plus;&space;c" title="T(p) = \begin{pmatrix}1 & 0.8 \\0 & 1 \\\end{pmatrix} (p - c) + c" /></p>
Wenden Sie die Transformationen in der Datei [b.py](b.py) auf das Eingangsbild an und finden Sie so heraus, welche
Transformation zu welchem Ausgangsbild gehört. Die Lösung findet sich in der Datei [l_b.py](l_b.py).
## Aufgabe c)
Weitere Fragen:
- Wie kann man sich die verschiedenen affinen Transformationsmatrizen aus a) herleiten ?
- Diskutieren Sie Vor- und Nachteile von Forward und Backwardmapping!

View File

@@ -0,0 +1,34 @@
import numpy as np
import cv2
''' Einlesen des Bildes '''
img = cv2.imread("data/normal.jpg")
'''
Schritt 1: Geben Sie eine Transformationvorschrift T an, die das Eingangsbild
- mit dem Faktor s_x ungleich 0 in x-Richtung skaliert
- mit dem Faktor s_y ungleich 0 in y-Richtung skaliert
'''
s_x = 2
s_y = 2
'''
Schritt 2: Geben Sie geben sie die Inverse T_inv zu T an
'''
'''
Schritt 3: Implementieren Sie eine Funktion scale(img, sx, sy), welche das Bild nach der Skalierung wiedergibt.
Verwenden Sie für die Transformation das Backward-Mapping und für die Interpolation Nearest-Neighbour Interpolation.
'''
def scale(img, s_x, s_y):
return new_img
''' Ausgabe des Bildes '''
new_img = scale(img, 2, 2)
cv2.imshow('img', new_img)
cv2.waitKey(0)

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,57 @@
import numpy as np
import cv2
''' Einlesen des Bildes '''
img = cv2.imread("data/normal.jpg")
'''
Schritt 1: Geben Sie eine Transformationvorschrift T an, die das Eingangsbild
- mit dem Faktor s_x ungleich 0 in x-Richtung skaliert
- mit dem Faktor s_y ungleich 0 in y-Richtung skaliert
'''
s_x = 2
s_y = 2
T = np.asarray(
[
[s_x, 0],
[0, s_y]
]
)
'''
Schritt 2: Geben Sie geben sie die Inverse T_inv zu T an
'''
T_inv = np.linalg.inv(T)
'''
Schritt 3: Implementieren Sie eine Funktion scale(img, sx, sy), welche das Bild nach der Skalierung wiedergibt.
Verwenden Sie für die Transformation das Backward-Mapping und für die Interpolation Nearest-Neighbour Interpolation.
'''
def scale(img, s_x, s_y):
rows, cols, channels = img.shape
new_rows, new_cols = int(rows * s_y), int(cols * s_x)
T = np.asarray([[s_x, 0], [0, s_y]])
T_inv = np.linalg.inv(T)
new_img = np.zeros((new_rows, new_cols, channels))
for x in range(new_cols):
for y in range(new_rows):
position = np.asarray([x, y])
old_position = np.matmul(position, T_inv)
old_position = np.round(old_position).astype(int)
old_x, old_y = old_position[0], old_position[1]
# Überstpringen, wenn ausserhalb des Bildes
if not 0 <= old_x < cols or not 0 <= old_y < rows:
continue
new_img[y, x] = img[old_y, old_x]
return new_img.astype(np.uint8)
''' Ausgabe des Bildes '''
new_img = scale(img, 2, 2)
cv2.imshow('new_img', new_img)
cv2.imshow('img', img)
cv2.waitKey(0)

View File

@@ -0,0 +1,106 @@
import numpy as np
import cv2
''' Einlesen des Bildes '''
img = cv2.imread("data/normal.jpg")
rows, cols, channels = img.shape
''' Transformation t1: Implementierung mit OpenCV '''
t_1 = np.float32(
[
[ np.cos(np.pi / 4), np.sin(np.pi / 4), 0],
[-np.sin(np.pi / 4), np.cos(np.pi / 4), 0]]
)
dst = cv2.warpAffine(img, t_1, (cols, rows))
cv2.imshow('img',dst)
cv2.waitKey(0)
''' Transformation t2: Implementierung ohne CV2 '''
c1, c2, c3, c4 = np.cos(np.pi / 4), np.sin(np.pi / 4), -np.sin(np.pi / 4), np.cos(np.pi / 4)
c_y, c_x = rows / 2, cols / 2
def new_pos(x, y):
new_x = round(c1 * (x - c_x) + c2 * (y - c_y) + c_x)
new_y = round(c3 * (x - c_x) + c4 * (y - c_y) + c_y)
return new_x, new_y
def old_pos(new_x, new_y):
x = (new_x/c1) - (c_x / c1) + c_x - c3 * c2 * c_x / (c4 * c1) - (c2 * new_y / (c1 * c4)) + (c_y * c2 / (c1 * c4))
x = x / (1 - c3*c2/(c4*c1))
y = c_y + (new_y - (c3 * (x - c_x)) - c_y) / c4
x = round(x)
y = round(y)
return x, y
# Forwardmapping
new_img = np.zeros_like(img)
for x in range(cols):
for y in range(rows):
new_x, new_y = new_pos(x, y)
# Überstpringen, wenn ausserhalb des Bildes
if not 0 <= new_x < cols or not 0 <= new_y < rows:
continue
new_img[new_y, new_x] = img[y, x]
cv2.imshow('img', new_img)
cv2.waitKey(0)
# Backwardmapping
new_img = np.zeros_like(img)
for x in range(cols):
for y in range(rows):
old_x, old_y = old_pos(x, y)
# Überstpringen, wenn ausserhalb des Bildes
if not 0 <= old_x < cols or not 0 <= old_y < rows:
continue
new_img[y, x] = img[old_y, old_x]
cv2.imshow('img', new_img)
cv2.waitKey(0)
''' Transformation t3: Implementierung ohne CV2 '''
c1, c2, c3, c4 = 1, 0.8, 0, 1
c_y, c_x = rows / 2, cols / 2
def new_pos(x, y):
new_x = round(c1 * (x - c_x) + c2 * (y - c_y) + c_x)
new_y = round(c3 * (x - c_x) + c4 * (y - c_y) + c_y)
return new_x, new_y
def old_pos(new_x, new_y):
x = (new_x/c1) - (c_x / c1) + c_x - c3 * c2 * c_x / (c4 * c1) - (c2 * new_y / (c1 * c4)) + (c_y * c2 / (c1 * c4))
x = x / (1 - c3*c2/(c4*c1))
y = c_y + (new_y - (c3 * (x - c_x)) - c_y) / c4
x = round(x)
y = round(y)
return x, y
# Forwardmapping
new_img = np.zeros_like(img)
for x in range(cols):
for y in range(rows):
new_x, new_y = new_pos(x, y)
# Überstpringen, wenn ausserhalb des Bildes
if not 0 <= new_x < cols or not 0 <= new_y < rows:
continue
new_img[new_y, new_x] = img[y, x]
cv2.imshow('img', new_img)
cv2.waitKey(0)
# Backwardmapping
new_img = np.zeros_like(img)
for x in range(cols):
for y in range(rows):
old_x, old_y = old_pos(x, y)
# Überstpringen, wenn ausserhalb des Bildes
if not 0 <= old_x < cols or not 0 <= old_y < rows:
continue
new_img[y, x] = img[old_y, old_x]
cv2.imshow('img', new_img)
cv2.waitKey(0)

View File

@@ -0,0 +1,24 @@
# Lösung Aufgabe c)
## Wie kann man sich die verschiedenen affnen Transformationsmatrizen herleiten?
Für den Fall das die Abbildung linear ist, muss die Abbildung lediglich auf den Basisbildern
bestimmt werden. Zum Beispiel für die Basis `(1, 0)` und `(0, 1)` berechnet man die transformierten
Vektoren v1 und v2. Dann ist die Transformationsmatrix gegeben durch `T(p) = (v1, v2) p`. Bei
affinen Abbildungen kommt ensprechend noch eine Translation dazu. Wichtig für die Herleitung
der Transformationen sind daher Basiswechsel aus der linearen Algebra.
## Diskutieren Sie Vor- und Nachteile von Forward und Backwardmapping
Vorteile Backwardmapping:
- Geschwindigkeitsvorteil wenn lediglich ein bestimmter Bereich von Interesse ist. Dieser kann
direkt berechnet werden.
- Keine Löcher, keine Überlappungen im Ergebnisbild.
Vorteile Forwardmapping:
- Eventueller Vorteil da die Inverse der Transformation nicht bestimmt werden muss.
Nachteile:
- Es kann zu ÜUberlappung kommen. Mehrere Eingangspixel landen teilweise im gleichen Ergebnispixel.
(Hier muss aus diesen Werten ein finaler Wert interpoliert werden) Interpolation
erst nach kompletter Transformation möglich. Beim Backward-Mapping wird für jede Position
direkt eine Interpolation berechnet.

View File

@@ -0,0 +1,42 @@
# Übung 4: Filterkern
Ein Filterkern ist eine n-dimensionale Matrix, mit der üblicherweise eine lokale und lineare Operation auf Pixel im
Eingangsbild angewendet wird. In dieser Übung sollen Aufgaben mit Filterkernen manuell und mit Python gelößt werden.
## Aufgabe a) Separierung
Unter Umständen ist ein 2-dimensionaler Filterkern separierbar, d.h. er durch zwei 1-dimensonale Filterkerne dargestellt
werden.
Nehmen Sie die bereits separierten Filterkerne
<p align="center">
<img src="https://latex.codecogs.com/svg.image?\bg_white&space;F_a&space;=&space;\begin{bmatrix}1&space;&&space;4&space;&&space;1&space;\\\end{bmatrix}&space;\quad&space;\text{und}\quad&space;F_b&space;=&space;\begin{bmatrix}&space;-1\\&space;0\\1\end{bmatrix}&space;" title="\bg_white F_a = \begin{bmatrix}1 & 4 & 1 \\\end{bmatrix} \quad \text{und} F_b = \begin{bmatrix} -1\\ 0\\1\end{bmatrix} " />
</p>
und erstellen den ursprünglichen Filterken, sowohl "von Hand" als auch in einem Python Skript.
Betrachten und separieren Sie zusätzlich den Filterkern
<p align="center">
<img src="https://latex.codecogs.com/svg.image?\bg_white&space;\inline&space;F_C&space;=&space;\begin{bmatrix}-2&space;&&space;-3&space;&&space;-2&space;\\0&space;&&space;0&space;&&space;0&space;\\2&space;&&space;3&space;&&space;2&space;\\\end{bmatrix}&space;&space;" title="\bg_white \inline F_C = \begin{bmatrix}-2 & -3 & -2 \\0 & 0 & 0 \\2 & 3 & 2 \\\end{bmatrix} " />
</p>
wenn möglich (manuell und in Python)! Die Lösung findet sich in der Datei [l_a.py](l_a.py).
## Aufgabe b)
Stellen Sie sich vor, Sie wenden die separierten oder nicht separierten Filterkerne auf ein durchschnittliches Bild an.
Wie viele Rechenoperationen pro Pixel führen Sie im Durchschnitt pro Pixel aus, wenn sie
- einen separierten 3x3 Filterkern
- einen nicht separierten 3x3 Filterkern
verwenden. Die Musterlösung befindet sich in der Datei [l_b.md](l_b.md).
## Aufgabe c)
Laden Sie ein beliebiges Bild ein und verwenden Sie die OpenCV-Methode *cv2.filter2D()* um Schärfefilter, Mittelwert-Filter und
Kantenfilter auf das Bild anzuwenden. Die Musterlösung befindet sich in der Datei [l_c.py](l_c.py).
## Aufgabe d)
Modifizieren Sie die Aufgabe c), indem Sie nur den Mittelwert-Filter verwenden und diesen vergrößern. Verwenden Sie
verschiedene Boundary Optionen durch das Argument *borderType* in der Funktion *cv2.filter2D()* und betrachten Sie das
Ergebnis. Vergleichen Sie die verschiedenen Optionen *cv2.BORDER_REFLECT*, *cv2.BORDER_REPLICATE* und *cv2.BORDER_CONSTANT*.
Visualisieren Sie weiterhin die Optionen, indem Sie einen Rand mit der Funktion *cv2.copyMakeBorder()* zu dem Bild
hinzufügen. Die Musterlösung befindet sich in der Datei [l_d.py](l_d.py).

View File

@@ -0,0 +1,82 @@
import numpy as np
""" Erstellen des Ursprünglichen Filterkerns"""
f_a = np.expand_dims(np.asarray([1, 4, 1]), 0)
f_b = np.expand_dims(np.asarray([-1, 0, 1]), 1)
f_orig = np.matmul(f_b, f_a)
print("TEIL 1")
print(f_a)
print(f_b)
print(f_orig)
""" Separieren des Filterkerns
Wir können das Problem als
f_a * f_b = f_C
mit
f_a = [a, b, c]^T
f_b = [d, e, f]
f_C = [
[ ad, ae, af ],
[ bd, be, bf ],
[ cd, ce, cf ],
]
modellieren. Wenn wir a=1 setzten, ergibt sich d=-2 e=-3 f=-2 (erste Zeile der Matrix) sodass sich ein linear
unabhängiges Gleichungssystem
f_C = [
[ -2a, -3a, -2a ],
[ -2b, -3b, -2b ],
[ -2c, -3c, -2c ],
] =
[
[-2, -3, -2],
[0, 0, 0],
[2, 3, 2],
]
=> -2a=-2, -2b=0, -2c=2
=> A * x = B
A = [
[-2, 0, 0],
[ 0, -2, 0],
[ 0, 0, -2],
]
x = [a, b, c]^T
B = [-2, 0, 2]^T
erstellen lässt. Dieses lässt sich leicht "von Hand" oder mit Numpy lösen.
=>
f_a = [1, 0, -1]^T
f_b = [-2, -3, -2]
Hinweis: Es gibt unendlich viele Lösungen (wenn separierbar)!
"""
f_C = np.asarray(
[
[-2, -3, -2],
[0, 0, 0],
[2, 3, 2],
]
)
A = np.array([
[-2, 0, 0],
[ 0,-2, 0],
[ 0, 0,-2],
])
B = np.array([-2, 0, 2])
x = np.linalg.solve(A, B)
f_a = np.expand_dims(x, axis=1)
f_b = np.expand_dims(np.asarray([-2, -3, -2]), axis=0)
f_C_new = np.matmul(f_a, f_b)
print("TEIL 2")
print(f_C)
print(f_C_new)

View File

@@ -0,0 +1,32 @@
# Lösung zu Aufgabe b)
Zuerst soll der Rechenaufwand für einen Pixel betrachtet werden:
**Filterkern mit 3x3-Matrix für einen Pixel:**
- 9 x Multiplikation
- 8 x Addition
- Insgesamt 17 x Rechenoperationen
**Filterkern mit separierten 3x1- bzw. 1x3-Vektoren für einen Pixel:**
- 12 x Multiplikation
- 8 x Addition Insgesamt
- 20 x Rechenoperationen
Berechnet man lediglich den Pixelwert in der Mitte, so ergibt sich kein Geschwindigkeitsvorteil.
Für das gesamte Bild ergibt sich jedoch ein Geschwindigkeitsvorteil pro Pixel, da die Zwischenergebnisse
aus der Filterung mit dem ersten Filterkern nicht neu berechnet werden müssen. Anstatt
9 Multiplikationen und 8 Additionen mit dem 3x3-Filterkern ergeben sich mit den separierten
Filterkomponenten durchschnittlich 6 Multiplikationen und 4 Additionen pro Pixel.
Gemittelte Anzahl Rechenoperationen pro Pixel für das gesamte Bild:
**Filterkern mit 3x3-Matrix für einen Pixel:**
- 9 x Multiplikation
- 8 x Addition
- Insgesamt 17 x Rechenoperationen
**Filterkern mit separierten 3x1- bzw. 1x3-Vektoren für einen Pixel:**
- 6 x Multiplikation
- 4 x Addition Insgesamt
- 10 x Rechenoperationen

View File

@@ -0,0 +1,37 @@
import cv2
import numpy as np
img = cv2.imread("../../data/cameraman.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
k1 = (1 / 25) * np.asarray([
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1]
], dtype=np.uint8)
k2 = np.asarray([
[1, 0, -1],
[1, 0, -1],
[1, 0, -1],
], dtype=np.float32)
k3 = np.asarray([
[-1, -1, -1],
[-1, 9, -1],
[-1, -1, -1],
], dtype=np.float32)
img_k1 = cv2.filter2D(img, -1, k1)
img_k2 = cv2.filter2D(img.astype(np.float32), -1, k2)
img_k2 = np.maximum(img_k2, 0).astype(np.uint8)
img_k3 = cv2.filter2D(img.astype(np.float32), -1, k3)
img_k3 = np.maximum(img_k3, 0).astype(np.uint8)
cv2.imshow("img", img)
cv2.imshow("img_k1", img_k1)
cv2.imshow("img_k2", img_k2)
cv2.imshow("img_k3", img_k3)
cv2. waitKey()

View File

@@ -0,0 +1,25 @@
import cv2
import numpy as np
img = cv2.imread("../../data/cameraman.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
k1 = (1 / (33 * 33)) * np.ones((33, 33), dtype=np.uint8)
border_types = [
("cv2.BORDER_REFLECT", cv2.BORDER_REFLECT, 255),
("cv2.BORDER_REPLICATE", cv2.BORDER_REPLICATE, 255),
("cv2.BORDER_CONSTANT", cv2.BORDER_CONSTANT, 0),
("cv2.BORDER_CONSTANT", cv2.BORDER_CONSTANT, 255),
]
cv2.imshow("img", img)
for name, border, value in border_types:
img_border = cv2.copyMakeBorder(img, 50, 50, 50, 50, borderType=border, value=value)
img_k1 = cv2.filter2D(img, -1, k1, borderType=border)
cv2.imshow("img_k1_" + name + str(value), img_k1)
cv2.imshow("img_border_" + name + str(value), img_border)
cv2. waitKey()

View File

@@ -0,0 +1,24 @@
# Übung 5: Nichtlineare Filter
In dieser Übung wird der nichtlineare Median-Filter behandelt. Der Median-Filter ist ein Sonderfall des Rangfolge-Filters.
Bei einem Rangfolge-Filter werden die Werte des zu untersuchenden Bildausschnitts aufsteigend sortiert.
Je nach Filter-Definition wird dann der n-te Wert der Folge als neuer Pixelwert definiert. Sonderfälle des Rangfolge-Filters sind:
- Maximum-Filter: Letzter Wert der Folge
- Minimum-Filter: Erster Wert der Folge
- Median-Filter: Wert der mittleren Position der Folge
## Aufgabe a)
Gegeben ist folgender Bildausschnitt:
<p align="center">
<img src="https://latex.codecogs.com/svg.image?I&space;=&space;\begin{bmatrix}1&space;&4&space;&space;&6&space;&space;\\&space;3&&space;2&space;&&space;1&space;\\&space;6&&space;&space;8&&space;&space;2\end{bmatrix}&space;" title="I = \begin{bmatrix}1 &4 &6 \\ 3& 2 & 1 \\ 6& 8& 2\end{bmatrix} " />
</p>
Geben Sie den Mittelwert und Median des Ausschnitts an!
Die Lösung finden Sie in der Datei [l_a.py](l_a.py).
## Aufgabe b)
Starten Sie das Program [b.py](b.py) um ein verrauschtes Bild zu erhalten und filtern Sie es
mit der OpenCV-Filterfunktion *cv2.medianBlur()*. Führen Sie die Filterung mit den Filtergrößen: 3x3, 5x5,
9x9. Vergleichen Sie die Ergebnisse durch Visualisierung des Ergebnisses mit *cv2.imshow()*.
Die Lösung finden Sie in der Datei [l_b.py](l_b.py).

View File

@@ -0,0 +1,18 @@
import cv2
import numpy as np
img = cv2.imread("../../data/lena.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
''' Rauschen hinzufügen '''
h, w = img.shape
saltpepper_noise = np.zeros((h, w), dtype=np.uint8)
saltpepper_noise = cv2.randu(saltpepper_noise, 0, 255)
black = saltpepper_noise < 15
white = saltpepper_noise > 240
img[white] = 255
img[black] = 0
''' Bild anzeigen '''
cv2.imshow("img", img)
cv2. waitKey()

View File

@@ -0,0 +1,12 @@
import numpy as np
I = [
[1, 4, 6],
[3, 2, 1],
[6, 8, 2],
]
I = np.asarray(I)
print("Median", np.median(I))
print("Mittelwert", np.average(I))

View File

@@ -0,0 +1,24 @@
import cv2
import numpy as np
img = cv2.imread("../../data/lena.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
''' Rauschen hinzufügen '''
h, w = img.shape
saltpepper_noise = np.zeros((h, w), dtype=np.uint8)
saltpepper_noise = cv2.randu(saltpepper_noise, 0, 255)
black = saltpepper_noise < 15
white = saltpepper_noise > 240
img[white] = 255
img[black] = 0
cv2.imshow("img", img)
''' Median Filter anwenden '''
sizes = [3, 5, 9]
for kernel_size in sizes:
img_filtered = cv2.medianBlur(img, kernel_size)
cv2.imshow("img_filtered_" + str(kernel_size), img_filtered)
cv2. waitKey()

View File

@@ -0,0 +1,22 @@
# Übung 6: Forward-Mapping / Backward-Mapping
In dieser Übung wird das *Forward-Mapping* und das *Backward-Mapping* bei geometrischen Transformationen betrachtet.
Sie interessieren Sich für den Wert des Pixels an der Stelle **(x=12, y=38)** des Bildes
![](../../data/car.png)
nach der Transformation mit der Transformationsvorschrift
<p align="center">
<img src="https://latex.codecogs.com/svg.image?\begin{pmatrix}&space;x'\\y'\end{pmatrix}&space;=\begin{pmatrix}&space;0.5&space;&&space;0\\0&space;&&space;0.5\end{pmatrix}\cdot\begin{pmatrix}&space;x\\y\end{pmatrix}.&space;" title="\begin{pmatrix} x'\\y'\end{pmatrix} =\begin{pmatrix} 0.5 & 0\\0 & 0.5\end{pmatrix}\cdot\begin{pmatrix} x\\y\end{pmatrix} ." />
</p>
Berechnen Sie den Wert des Pixels an der Stelle **(x=12, y=38)** nach der Transformation unter Anwendung des Backward- und Forward-Mappings!
Schreiben Sie Ihr Skript in die Datei [a.py](a.py). Die Lösung ist in der Datei [l_a.py](l_a.py) zu finden!
Vergleichen Sie Die Methoden: Wie unterscheiden Sie sich? Welche Vor- und Nachteile haben die Methoden?

View File

@@ -0,0 +1,13 @@
import cv2
import numpy as np
img = cv2.imread("../../data/car.png")
cv2.imshow("Car", img)
print("Image Shape:", img.shape)
print("\nForward-Mapping")
print("\nBackward-Mapping")
cv2.waitKey(0)

View File

@@ -0,0 +1,29 @@
import cv2
import numpy as np
img = cv2.imread("../../data/car.png")
cv2.imshow("Car", img)
print("Image Shape:", img.shape)
print("\nForward-Mapping")
new_image = np.zeros((int(img.shape[0] / 2), int(img.shape[1] / 2), img.shape[2]), dtype=np.uint8)
for x in range(img.shape[1]):
for y in range(img.shape[0]):
new_image[int(y/2), int(x/2)] = img[y, x]
cv2.imshow("Forward-Mapping", new_image)
print("Pixel at position x=12, y=38", new_image[38, 12])
print("\nBackward-Mapping")
# Inverse Transformation:
# (x, y)^T = ([2, 0] * (x', y')^T
# [0, 2])
p_12_38 = img[38*2, 12*2]
print("Pixel at position x=12, y=38", p_12_38)
cv2.waitKey(0)

View File

@@ -0,0 +1,42 @@
# Übung 7: Interpolation
In dieser Übung wird die *Nächste Nachbar (Nearest Neighbour) Interpolation* und die *Bilineare Interpolation*
bei geometrischen Transformationen betrachtet.
Für diese Aufgabe haben Sie ein Bild bestehend aus vier Pixelwerten I_xy:
- I_00 = 1
- I_10 = 2
- I_01 = 3
- I_11 = 4
## a) Interpolation beim Backward Mapping
Sie transformieren das Bild mithilfe der Transformationsvorschrift **T** beziehungsweise der dazugehörigen inversen
Transformationsvorschrift mithilfe des Backward-Mappings. Die ursprüngliche Position I_xy auf dem Eingangsbild des Pixels I'_00 auf dem Ausgangsbild wird durch die die Inverse
Transformation gegeben. Berechnen Sie den Wert des Pixels I'_00 mithilfe der *Nächste Nachbar (Nearest Neighbour) Interpolation* und der *Bilineare Interpolation*, wenn die ursprüngliche Postition I_xy an den Koordinaten
- (x=0.3 | y=0.8)
- (x=0 | y=1)
- (x=0.5 | y=0.5)
liegt.
Sie können die Aufgabe handschriftlich oder mithilfe eines Skripts lösen. Die Lösung ist in der Datei [l_a.py](l_a.py) zu finden!
## b) Interpolation beim Forward Mapping
Sie transformieren das Bild mithilfe der Transformationsvorschrift **T** und des Forward-Mappings. Nach der Transformation ist die neue Position der gegeben
Pixel wie folgt:
- I_00 = 1: (x=0.5 | y=0.5)
- I_10 = 2: (x=1.5 | y=0.5)
- I_01 = 3: (x=0.5 | y=1.5)
- I_11 = 4: (x=1.5 | y=1.5)
Interpolieren Sie die Werte auf dem Zielbild mithilfe der *Nächste Nachbar (Nearest Neighbour) Interpolation* und der *Bilineare Interpolation* für die folgenden Pixel:
- I'_11
- I'_00
Sie können die Aufgabe handschriftlich oder mithilfe eines Skripts lösen. Die Lösung ist in der Datei [l_b.py](l_b.py) zu finden!

View File

@@ -0,0 +1,20 @@
i_00 = 1
i_10 = 2
i_01 = 3
i_11 = 4
# 1)
i_new_bilinear = 0.2 * (0.7 * 1 + 0.3 * 2) + 0.8 * (0.7 * 3 + 0.3 * 4)
i_new_nn = 3
print(i_new_bilinear, i_new_nn)
# 2)
i_new_bilinear = 0 * (1 * 1 + 0 * 2) + 1 * (1 * 3 + 0 * 4)
i_new_nn = 3
print(i_new_bilinear, i_new_nn)
# 3)
i_new_bilinear = 0.5 * (0.5 * 1 + 0.5 * 2) + 0.5 * (0.5 * 3 + 0.5 * 4)
i_new_nn = 4 # Multiple solutions! Here: Ceil
print(i_new_bilinear, i_new_nn)

View File

@@ -0,0 +1,16 @@
i_00 = 1 # New position: (x=0.5, y=0.5)
i_10 = 2 # New position: (x=1.5, y=0.5)
i_01 = 3 # New position: (x=0.5, y=1.5)
i_11 = 4 # New position: (x=1.5, y=1.5)
# 1)
i_new_bilinear = 0.5 * (0.5 * 1 + 0.5 * 2) + 0.5 * (0.5 * 3 + 0.5 * 4)
i_new_nn = 4 # Multiple solutions! Here: Ceil
print(i_new_bilinear, i_new_nn)
# 2)
# This solution is depending on the border behaviour! If no border behaviour is defined, there is not bilinear solution!
i_new_bilinear = ...
i_new_nn = 1
print(i_new_nn)

View File

@@ -0,0 +1,22 @@
# Übung 8: Geometrische Transformation
In dieser Übung soll die Transformationsforschrift
<p align="center">
<img src="https://latex.codecogs.com/svg.image?\begin{pmatrix}&space;x'\\y'\end{pmatrix}&space;=\begin{pmatrix}&space;a&space;&&space;b\\c&space;&&space;d\end{pmatrix}\cdot\begin{pmatrix}&space;x\\y\end{pmatrix}+&space;\begin{pmatrix}&space;e\\f\end{pmatrix}.&space;" title="\begin{pmatrix} x'\\y'\end{pmatrix} =\begin{pmatrix} 0.5 & 0\\0 & 0.5\end{pmatrix}\cdot\begin{pmatrix} x\\y\end{pmatrix} ." />
</p>
für die Transformation des Bildes I1 zu I2 hergeleitet werden.
| I1 | I2 |
| --- | --- |
| ![](./data/original.jpg) | ![](./data/new.jpg) |
Leiten Sie die Transformationsvorschrift her und testen Sie die Vorschrift, indem Sie ein Skript in die Datei [a.py](a.py)
programmieren. Die Lösung ist in der Datei [l_a.py](l_a.py) zu finden!

View File

@@ -0,0 +1,42 @@
import cv2
import numpy as np
img = cv2.imread("../../data/car.png")
img = cv2.resize(img, (500, 500))
cv2.imshow("Car", img)
print("Image Shape:", img.shape)
def T(pos, rotation, translation):
""" Transformation Matrix """
new_pos = np.matmul(rotation, pos) + translation
return new_pos
new_image = np.zeros_like(img)
rotation = [
[..., ...],
[..., ...]
]
translation = [
...,
...
]
for x in range(img.shape[1]):
for y in range(img.shape[0]):
old_pos = np.asarray([[x], [y]])
new_pos = T(old_pos, rotation, translation)
new_pos = new_pos.astype(int)
if 0 <= new_pos[0] < img.shape[1] and 0 <= new_pos[1] < img.shape[0]:
new_image[new_pos[1], new_pos[0]] = img[y, x]
print(new_image.shape)
cv2.imshow("After Transformation", new_image)
cv2.waitKey(0)

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@@ -0,0 +1,88 @@
import cv2
import numpy as np
img = cv2.imread("../../data/car.png")
img = cv2.resize(img, (500, 500))
cv2.imshow("Car", img)
print("Image Shape:", img.shape)
def T(pos, rotation, translation):
""" Transformation Matrix """
new_pos = np.matmul(rotation, pos) + translation
return new_pos
new_image = np.zeros_like(img)
alpha = -0.5 * np.pi
rotation = [
[np.cos(alpha), -np.sin(alpha)],
[np.sin(alpha), np.cos(alpha)]
]
translation = [
[0],
[img.shape[1]]
]
for x in range(img.shape[1]):
for y in range(img.shape[0]):
old_pos = np.asarray([[x], [y]])
new_pos = T(old_pos, rotation, translation)
new_pos = new_pos.astype(int)
if 0 <= new_pos[0] < img.shape[1] and 0 <= new_pos[1] < img.shape[0]:
new_image[new_pos[1], new_pos[0]] = img[y, x]
print(new_image.shape)
cv2.imshow("After Transformation", new_image)
"""
Naive Solution
1. Rotate the image with 90° -> alpha=90°
2. Translate in y axis with image length -> [ [0], [height] ]
"""
"""
Mathematical solution
1. Find correspondences
x,y --> x',y'
0,0 --> 0,1
1,0 --> 0,0
0,1 --> 1,1
1,1 --> 1,0
0.5,0.5 --> 0.5,0.5
2. Solve system:
[a, b] * [x, y]^T + [e, f]^T = [x', y']^T
[c, d]
ax + by + e = x'
cx + dy + f = y'
Translation (0,0 --> 0,1):
0 + 0 + e = 0 --> e = 0
0 + 0 + f = 1 --> f = 1
Rotation (1,0 --> 0,0):
a + 0 + 0 = 0 --> a = 0
c + 0 + 1 = 0 --> c = -1
Rotation (0,1 --> 1,1):
0 + b + 0 = 1 --> b = 1
0 + d + 1 = 1 --> d = 0
"""
cv2.waitKey(0)

View File

@@ -0,0 +1,18 @@
# Übung 9: Gamma-Korrektur
In dieser Übung soll die Gamma-Korrektur
<p align="center">
<img src="https://latex.codecogs.com/svg.image?g'(x,y)=\frac{255}{255^\gamma}\cdot&space;g(x,y)^\gamma" title="" />
</p>
für die bessere Sichtbarkeit des Bilder
![](../../data/car.png)
verwendet werden.
Wenden Sie die Gamma Korrektur mit den Gamma-Werte 0.5,1 und 2 auf das Bild an, indem Sie ein Skript in die Datei [a.py](a.py)
programmieren. Die Lösung ist in der Datei [l_a.py](l_a.py) zu finden!

View File

@@ -0,0 +1,11 @@
import cv2
import numpy as np
img = cv2.imread("../../data/car.png")
img = cv2.resize(img, (500, 500))
cv2.imshow("Original", img)
cv2.waitKey(0)

View File

@@ -0,0 +1,21 @@
import cv2
import numpy as np
img = cv2.imread("../../data/car.png")
img = cv2.resize(img, (500, 500))
cv2.imshow("Original", img)
def gamma_correction(img, gamma):
img = img.astype(np.float)
img = 255 * np.power(img, gamma) / np.power(255, gamma)
print(np.max(img))
img = img.astype(np.uint8)
return img
for gamma in [0.5, 1, 2]:
cv2.imshow("%s" % gamma, gamma_correction(img, gamma))
cv2.waitKey(0)

View File

@@ -0,0 +1,15 @@
# Signalorientierte Bildverarbeitung
Bilder werden üblicherweise als örtlich-/zeitliches Signal betrachtet. In der Digitalen Bildverarbeitung werden Bilder
häufig auch in anderer Signalform betrachtet, z.B. im Frequenzraum. Die Grundlagen der signalorientierten Bildverarbeitung
werden in diesem Kapitel behandeln. Die Aufgaben in diesem Unterverzeichnis geben dazu Informationen und Beispiele zu den Themen
- **Das Bild als Signal**
- **Grundlagen unitäre Transformation**
- **Fourier-Transformation**
- **LSISysteme, Faltung und FourierTransformation**
- **Abtastung und Rekonstruktion, Abtasttheorem**
- **Filterung des Bildes**
- **Unitäre Transformationen: DCT, Hadamard, Haar, WaveletTransformation**
- **Bildpyramiden und Multiresolutiondarstellung**

View File

@@ -0,0 +1,38 @@
# Übung 1: Bildkompression
In dieser Übung wird der Informationsgehalt eines Bildes reduziert und die Auswirkung auf die Interpretierbarkeit
für den Menschen betrachtet. Die Reduzierung der Informationen kann für eine Komprimierung verwendet.
Wenden Sie die folgenden Aufgaben auf die Bilder **data/yellowlilly.jpg** und **data/cameraman.png** an.
## Aufgabe a) Entfernen hochfrequenter Frequenzen
Implementieren sie die Funktion und *remove_dft(img, rate)*, wobei mit der
diskreten Kosinus- bzw. Fouriertransformation folgende Schritte durchgeführt werden sollen:
- Iterieren Sie Blockweise über das Bild mithilfe zweier for-Schleifen und einer Blockgröße von 8x8 Pixel
- Verwenden Sie `np.fft.fft2()` um den Block in den Frequenzraum zu transformieren
- Entfernen Sie die *n* hochfrequentesten Koefizienten mit `n = 8 * 8 * rate`
- Transformieren Sie den Block zurück in den Ortsraum mit `np.fft.ifft2`
Wie stark wirkt sich der Parameter `rate` auf die Interpretierbarkeit des Bildes für den Menschen aus?
Die Lösung ist in der Date [l_a.py](l_a.py) zu finden.
## Aufgabe b) Diskrete Kosinus Transformation
Implementieren Sie eine Funktion `dct(a: np.ndarray)`, welche als input eine zweidimensionale Matrix erhält und
auf diese die 2D Diskrete Kosinus Transformation nach
![](./data/dct.png)
anwendet und ausgibt.
Validieren Sie Ihre Funktion, indem Sie die `fft2()` und `ifft2()` Funktion aus Aufgabe a) durch die `dct()` und `idct()` Funktionen ersetzen.
Die inverse der Transformation ist mit der Funktion `idct(a: np.ndarray)` gegeben, welche bereits in *l_b.py* implementiert ist.
## Aufgabe c) Fragen
- Wie kann die Reduzierung der Fourier-Koeffizienten für eine Komprimierung genutzt werden?
- Was versteht man unter diskreter Kosinustransformation und was ist der wesentliche Unterschied zur DFT?
- Welchen Vorteil bietet die DCT gegenüber der FFT in der Praxis?

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,57 @@
import numpy as np
import cv2
import math
def remove_dft(img, rate):
"""
Diese Implementierung wendet die diskrete Fourier Transformation auf das Bild img an. Daraufhin werden die hoch-
frequenten Anteile anteilig der Rate rate entfernt. Am Ende wird das Bild wieder in den Bildbereich transformiert.
:param img:
:param rate:
:return:
"""
assert rate <= 1, "Die Rate muss kleiner gleich 1 sein!"
height, width = img.shape
for i in range(math.ceil(width / 8)):
for j in range(math.ceil(height / 8)):
# Block extrahieren
block = np.zeros((8, 8))
horizontal_pixel, vertical_pixel = min(8, width - i * 8), min(8, height - j * 8)
block[0:vertical_pixel, 0:horizontal_pixel] = \
img[j * 8: (j * 8) + vertical_pixel, i * 8: (i * 8) + horizontal_pixel]
# In den Frequenzbereich tranformieren
block_freq = np.fft.fft2(block)
# Hochfrequente Anteile löschen
values_to_delete = 8 * 8 * rate
values_deleted = 0
for m in range(0, 16):
for n in range(0, m + 1):
if values_deleted >= values_to_delete:
break
if 7 - m + n < 0 or 7 - n < 0:
continue
block_freq[7 - m + n, 7 - n] = 0. + 0.j
values_deleted += 1
# Rücktransformation in den Bildbereich
block = np.fft.ifft2(block_freq)
# Einfügen in Ursprungsbild
img[j * 8: (j * 8) + vertical_pixel, i * 8: (i * 8) + horizontal_pixel] = \
block[0:vertical_pixel, 0:horizontal_pixel]
return img
''' Bild laden '''
img = cv2.imread("../../data/cameraman.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = (img / 256).astype(np.float32)
''' Funktion anwenden '''
img = remove_dft(img, 0.9)
''' Bild anzeigen '''
cv2.imshow("IMG", img)
cv2.waitKey(0)

View File

@@ -0,0 +1,93 @@
import numpy as np
import cv2
import math
def dct(a: np.ndarray):
a_freq = np.zeros_like(a)
N, M = a_freq.shape[1], a_freq.shape[0]
# Iteriere über jeden Koeffizienten
for k in range(N):
for m in range(M):
dct_k_m = 0
# Iteriere über jede Position im Ortsraum
for x in range(N):
for y in range(M):
dct_k_m += a[y, x] * np.cos(k*np.pi*(2*x+1)/(2*N)) * np.cos(m*np.pi*(2*y+1)/(2*M))
dct_k_m = 4 * dct_k_m / (np.sqrt(2*N) * np.sqrt(2*M))
a_freq[m, k] = dct_k_m
return a_freq
def idct(a_freq: np.ndarray):
a = np.zeros_like(a_freq)
N, M = a_freq.shape[1], a_freq.shape[0]
a_freq = (np.sqrt(2*N) * np.sqrt(2*M)) * a_freq / 16
a_freq[0, :] = a_freq[0, :] / 2
a_freq[:, 0] = a_freq[:, 0] / 2
# Iteriere über jeden Koeffizienten
for x in range(N):
for y in range(M):
f_x_y = 0 #a_freq[0, 0] / (4)# * np.sqrt(2))
# Iteriere über jede Position im Ortsraum
for k in range(N):
for m in range(M):
f_x_y += a_freq[m, k] * np.cos(k*np.pi*(2*x+1)/(2*N)) * np.cos(m*np.pi*(2*y+1)/(2*M))
f_x_y = f_x_y / 4
a[y, x] = f_x_y
return a
def remove_dct(img, rate):
"""
Diese Implementierung wendet die diskrete Fourier Transformation auf das Bild img an. Daraufhin werden die hoch-
frequenten Anteile anteilig der Rate rate entfernt. Am Ende wird das Bild wieder in den Bildbereich transformiert.
:param img:
:param rate:
:return:
"""
assert rate <= 1, "Die Rate muss kleiner gleich 1 sein!"
height, width = img.shape
for i in range(math.ceil(width / 8)):
for j in range(math.ceil(height / 8)):
# Block extrahieren
block = np.zeros((8, 8))
horizontal_pixel, vertical_pixel = min(8, width - i * 8), min(8, height - j * 8)
block[0:vertical_pixel, 0:horizontal_pixel] = \
img[j * 8: (j * 8) + vertical_pixel, i * 8: (i * 8) + horizontal_pixel]
# In den Frequenzbereich tranformieren
block_freq = dct(block)
# Hochfrequente Anteile löschen
values_to_delete = 8 * 8 * rate
values_deleted = 0
for m in range(0, 16):
for n in range(0, m + 1):
if values_deleted >= values_to_delete:
break
if 7 - m + n < 0 or 7 - n < 0:
continue
block_freq[7 - m + n, 7 - n] = 0.
values_deleted += 1
# Rücktransformation in den Bildbereich
block = idct(block_freq)
# Einfügen in Ursprungsbild
img[j * 8: (j * 8) + vertical_pixel, i * 8: (i * 8) + horizontal_pixel] = \
block[0:vertical_pixel, 0:horizontal_pixel]
return img
''' Bild laden '''
img = cv2.imread("../../data/cameraman.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.resize(img, (160, 160))
img = (img.astype(np.float64) / 256)
cv2.imshow("ORIGINAL", img)
''' Funktion anwenden '''
img = remove_dct(img, 0.8)
''' Bild anzeigen '''
cv2.imshow("IMG", img)
cv2.waitKey(0)

View File

@@ -0,0 +1,5 @@
# Lösung zu 1c)
- entfernen hoher Frequenzen, da diese für den Menschen nicht so **aussagekräftig** erscheinen
- siehe Formeln
- keine komplexen Zahlen

View File

@@ -0,0 +1,25 @@
# Übung 2: Bildrekonstruktion mit Wiener Filter & Inverser Filterung
Gegeben ist eine Abbildung der ETH-Zürich mit einer Bildstörung wie im folgenden zu sehen:
![../../data/eth_blurred.png](../../data/eth_blurred.png)
Als Bildstörung ist hier Motion Blur einer gleichförmigen Kamerabewegung von 31 Pixel
und einem Winkel von 11 Grad (gegen den Uhrzeigersinn) verwendet.
Berechnen
# Aufgabe a) Inverse Filterung
Benutzen Sie inverse Filterung um die Bildstörung (Motion Blur und Rauschen) im Bild
zu entfernen. Die Inverse Filterung muss anhand der Formeln von der Vorlesung selbst programmiert werden.
Verwenden Sie eine geeignete "cutoff"-Frequenz.
Nutzen Sie die Datei [a.py](a.py) um Ihre Lösung zu implementieren. In der Datei ist die "Point Spread Function" (PSF)
bereits implementiert und in den Frequenzbereich transformiert. Die PSF entspricht der Transferfuntkion der
Bildstörung.
# Aufgabe b) Wiener Filterung
Nutzen Sie ebenfalls Datei [a.py](a.py),
Benutzen Sie Wiener Filterung um die Bildstörung (Motion Blur und Rauschen) im Bild
zu entfernen. Die Wiener Filterung muss anhand der Formeln von der Vorlesung selbst programmiert werden.
Verwenden Sie einen geeigneten Parameter K für den Wiener Filter.

View File

@@ -0,0 +1,41 @@
import numpy as np
import cv2
def get_motion_psf(kernel_size, motion_angle, motion_dis):
""" "Point Spread Function" um Bewegung zu simulieren """
psf = np.zeros(kernel_size) # point spread function
x_center = (kernel_size[0] - 1) / 2
y_center = (kernel_size[1] - 1) / 2
sin_val = np.sin(motion_angle * np.pi / 180)
cos_val = np.cos(motion_angle * np.pi / 180)
for i in range(motion_dis):
x_offset = round(sin_val * i)
y_offset = round(cos_val * i)
psf[int(x_center - x_offset), int(y_center + y_offset)] = 1
return psf / psf.sum()
''' Bild laden und in den Frequenzraum transformieren '''
img = cv2.imread("../../data/eth_blurred.png", cv2.IMREAD_GRAYSCALE)
IMG = np.fft.fft2(img)
''' Erstellen des Filterkernels und Transformation in den Frequenzraum '''
rows, cols = img.shape
h = get_motion_psf(kernel_size=(rows, cols), motion_angle=11, motion_dis=31)
h = np.fft.fftshift(h) # Muss gemacht werden, da der "Motion Blur Vector" mittig zentriert ist
H = np.fft.fft2(h)
''' Ergebnis anzeigen '''
cv2.imshow("Original", img)
cv2.imshow("Inverses Filter", img_filtered)
cv2.waitKey()

View File

@@ -0,0 +1,41 @@
import numpy as np
import cv2
def get_motion_psf(kernel_size, motion_angle, motion_dis):
""" "Point Spread Function" um Bewegung zu simulieren """
psf = np.zeros(kernel_size) # point spread function
x_center = (kernel_size[0] - 1) / 2
y_center = (kernel_size[1] - 1) / 2
sin_val = np.sin(motion_angle * np.pi / 180)
cos_val = np.cos(motion_angle * np.pi / 180)
for i in range(motion_dis):
x_offset = round(sin_val * i)
y_offset = round(cos_val * i)
psf[int(x_center - x_offset), int(y_center + y_offset)] = 1
return psf / psf.sum()
''' Bild laden und in den Frequenzraum transformieren '''
img = cv2.imread("../../data/eth_blurred.png", cv2.IMREAD_GRAYSCALE)
IMG = np.fft.fft2(img)
''' Erstellen des Filterkernels und Transformation in den Frequenzraum '''
rows, cols = img.shape
h = get_motion_psf(kernel_size=(rows, cols), motion_angle=11, motion_dis=31)
h = np.fft.fftshift(h) # Muss gemacht werden, da der "Motion Blur Vector" mittig zentriert ist
H = np.fft.fft2(h)
H[np.abs(H) < 0.01] = 0.01 # Begrenzen der Degradationsfunktion um numerische Probleme zu verhindern
''' Inverses Filter anwenden '''
IMG_FILTERED = IMG / H
img_filtered = np.fft.ifft2(IMG_FILTERED).clip(0, 255).astype(np.uint8)
''' Ergebnis anzeigen '''
cv2.imshow("Original", img)
cv2.imshow("Inverses Filter", img_filtered)
cv2.waitKey()

View File

@@ -0,0 +1,43 @@
import numpy as np
import cv2
def get_motion_psf(kernel_size, motion_angle, motion_dis):
""" "Point Spread Function" um Bewegung zu simulieren """
psf = np.zeros(kernel_size) # point spread function
x_center = (kernel_size[0] - 1) / 2
y_center = (kernel_size[1] - 1) / 2
sin_val = np.sin(motion_angle * np.pi / 180)
cos_val = np.cos(motion_angle * np.pi / 180)
for i in range(motion_dis):
x_offset = round(sin_val * i)
y_offset = round(cos_val * i)
psf[int(x_center - x_offset), int(y_center + y_offset)] = 1
return psf / psf.sum()
''' Bild laden und in den Frequenzraum transformieren '''
img = cv2.imread("../../data/eth_blurred.png", cv2.IMREAD_GRAYSCALE)
IMG = np.fft.fft2(img)
''' Erstellen des Filterkernels und Transformation in den Frequenzraum '''
rows, cols = img.shape
h = get_motion_psf(kernel_size=(rows, cols), motion_angle=11, motion_dis=31)
h = np.fft.fftshift(h) # Muss gemacht werden, da der "Motion Blur Vector" mittig zentriert ist
H = np.fft.fft2(h)
H[np.abs(H) < 0.01] = 0.01 # Begrenzen der Degradationsfunktion um numerische Probleme zu verhindern
''' Wiener Filter anwenden '''
K = 0.001
W = np.power(np.abs(H), 2) / (H * np.power(np.abs(H), 2) + K)
IMG_FILTERED = W * IMG
img_filtered = np.fft.ifft2(IMG_FILTERED).clip(0, 255).astype(np.uint8)
''' Ergebnis anzeigen '''
cv2.imshow("Original", img)
cv2.imshow("Wiener Filter", img_filtered)
cv2.waitKey()

View File

@@ -0,0 +1,40 @@
# Übung 3: Fouriertransformation
In dieser Übung soll die Fouriertransformation betrachtet werden.
## Aufgabe a) Rauschen im Orts- und Frequenzbereich
Um den Umgang mit Orts- und Frequenzbereich in Python zu verdeutlichen und die Grundlage
für die Folgeaufgaben zu stellen, soll ein Programm geschrieben werden, das die folgenden Schritte
durchführt:
1. Öffnen Sie das Skript [a.py](a.py)
2. Transformieren Sie die Bilder *img* und *img_noise* mit ``np.fft.fft2(img)`` in den Frequenzbereich.
3. Berechnen Sie den Betrag/Amplitude der Transformierten und stellen Sie diese als Bild dar.
4. Worin unterscheiden sich die Bilder?
Die Musterlösung befindet sich in der Datei [l_a.py](l_a.py).
**Hinweise:**
- Mit der Funktion ``np.fft.fftshift(IMAGE)`` wird der Gleichanteil des Frequenzbereiches in die Mitte
des Bildes gelegt.
- Zur besseren Visualisierung des Frequenzbereiches ist es empfehlenswert, die anzuzeigenden Werte mit einem Faktor
zu reduzieren. Das kann z.B. ``magnitudes = magnitudes / 100000`` sein.
## Aufgabe b) Amplituden und Phasenspektrum vertauschen
In der Datei [b.py](b.py) werden zwei Bilder geladen. Transformieren Sie sie in den Frequenzbereich und tauschen Sie die
Winkel und Amplituden. Stellen Sie die Bilder vor und nach dem Tausch dar!
Die Musterlösung befindet sich in der Datei [l_b.py](l_b.py).
## Aufgabe c) Tiefpassfilter
In der Datei [c.py](c.py) wird ein Bild eines Teppichs geladen. Der Teppich hat Fehler in den Maschen, die Sie finden möchten.
Um die Fehler besser finden zu können, möchten Sie das Bild mithilfe der Fourier-Transformation optisch anpassen.
Transformieren Sie das Bild in den Frequenzbereich und löschen Sie verschiedene hochfrequente und/oder niederfrequente Anteile.
Transformieren Sie das Spektrum darauf wieder in den Ortsbereich und stellen die Bilder dar!
Welche Frequenzen scheinen für die Aufgabe interessanter zu sein?
Die Musterlösung befindet sich in der Datei [l_c.py](l_c.py).

View File

@@ -0,0 +1,20 @@
import cv2
import numpy as np
img = cv2.imread("../../data/lena.png", cv2.IMREAD_GRAYSCALE)
''' Rauschen hinzufügen '''
h, w = img.shape
saltpepper_noise = np.zeros((h, w), dtype=np.uint8)
saltpepper_noise = cv2.randu(saltpepper_noise, 0, 255)
black = saltpepper_noise < 15
white = saltpepper_noise > 240
img_noise = np.copy(img)
img_noise[white] = 255
img_noise[black] = 0
''' Bild anzeigen '''
cv2.imshow("img", img)
cv2.imshow("img_noise", img_noise)
cv2. waitKey()

View File

@@ -0,0 +1,10 @@
import cv2
import numpy as np
car = cv2.imread("../../data/car.png", cv2.IMREAD_GRAYSCALE)
dog = cv2.imread("../../data/dog.png", cv2.IMREAD_GRAYSCALE)
cv2.imshow("car", car)
cv2.imshow("dog", dog)
cv2. waitKey()

View File

@@ -0,0 +1,24 @@
import cv2
import numpy as np
img = cv2.imread("../../data/teppich.png", cv2.IMREAD_GRAYSCALE)
''' FFT '''
IMG = np.fft.fft2(img)
MAGNITUDE = np.abs(IMG)
ANGLE = np.angle(IMG)
''' Filter out frequencies '''
''' IFFT '''
IMG = MAGNITUDE * np.exp(1j * ANGLE)
filtered_image = np.fft.ifft2(IMG).astype(np.float32)
''' Bild anzeigen '''
cv2.imshow("img", img)
cv2.imshow("filtered", filtered_image / np.max(filtered_image))
cv2.waitKey(0)

Some files were not shown because too many files have changed in this diff Show More