Initial commit with project files
11
5_Bildanalyse/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Bildanalyse
|
||||
|
||||
Ein Ziel der Digitalen Bildverarbeitung ist die Extrahierung von Informationen aus Bilddaten, um nachfolgende Aufgaben
|
||||
zu lösen. In diesem Kapitel werden einige Beispiele und Aufgaben zur Bildanalyse bereitgestellt. Dabei werden die Themen
|
||||
|
||||
- **Diskrete Geometrie und Analyse von Binärbildern**
|
||||
- **Bildsegmentierung**
|
||||
- **Template-Matching und Korrelation**
|
||||
- **Hough**
|
||||
|
||||
behandelt.
|
||||
37
5_Bildanalyse/ü1/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Übung 1: Differenzbild
|
||||
|
||||
In dieser Übung wird Bewegung mithilfe eines Differenzbildes detektiert. Mithilfe von Bewegungsdetektion können Applikationen
|
||||
wie z.B. Lichtsteuerungen oder Alarmsysteme gefertigt werden.
|
||||
|
||||
Gegeben ist eine statische Kamera, welche
|
||||
zwei Bilder in einer zeitlichen Abfolge aufgenommen hat. Die Bilder sehen Sie in den folgenden Abbildungen:
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img src="../../data/surv_01.png" />
|
||||
</p>
|
||||
<p align="center">
|
||||
<img src="../../data/surv_02.png" />
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
## Aufgabe a)
|
||||
Implementieren sie in der Datei [a.py](a.py) die folgenden Schritte, um die Bewegungsdetektion zu realisieren.
|
||||
|
||||
1. Öffnen Sie die Bilder mit der Funktion `cv2.imread()`
|
||||
2. Konvertieren Sie die Bilder in Grauwertbilder mit der Funktion `cv2.cvtColor()`
|
||||
3. Erzeugen Sie das Differenzbild unter Ausnutzung des vollen Wertebereichs von 0 bis 1 unter Verwendung der folgenden
|
||||
Punktoperationen:
|
||||
- Pixelweise Addition/Subtraktion
|
||||
- Addition/Subtraktion einer Konstanten
|
||||
- Multiplikation/Division mit einer Konstanten
|
||||
4. Stellen Sie das Differenzbild mithilfe von `cv2.imshow()` dar
|
||||
5. Setzen Sie alle Pixel mit Intensität kleiner 0.5 auf 0 und zeigen Sie das resultierende Bild an
|
||||
|
||||
Die Musterlösung findet sich unter [l_a.py](l_a.py).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
13
5_Bildanalyse/ü1/a.py
Normal 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)
|
||||
|
||||
34
5_Bildanalyse/ü1/l_a.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
KERNEL_SIZE = 20
|
||||
|
||||
''' Schritt 1: Einlesen der Bilder '''
|
||||
surv1 = cv2.imread("../../data/surv_01.png")
|
||||
surv2 = cv2.imread("../../data/surv_02.png")
|
||||
|
||||
''' Schritt 2: Konvertieren in den Grauwertbereich '''
|
||||
surv1 = cv2.cvtColor(surv1, cv2.COLOR_BGR2GRAY)
|
||||
surv2 = cv2.cvtColor(surv2, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
''' Schritt 3: Erzeugen des Differenzbildes '''
|
||||
print("Minimam und Maximum bevor Transformation:", np.min(surv1), np.max(surv1))
|
||||
print(" -> Wertebereich ist {0, ..., 255}")
|
||||
surv1 = surv1 / 255
|
||||
surv2 = surv2 / 255
|
||||
print("Minimam und Maximum nach Transformation:", np.min(surv1), np.max(surv1))
|
||||
print(" -> Wertebereich ist [0, 1]")
|
||||
|
||||
diff1 = surv1 - surv2
|
||||
diff1 = np.abs(diff1) # Absolutwertbildung für die Darstellellung (OpenCV kennt nur positive Werte!)
|
||||
|
||||
''' Schritt 4: Darstellen des Differenzbildes '''
|
||||
cv2.imshow("Differenz ohne Schwellwert ", diff1)
|
||||
|
||||
''' Schritt 5: Darstellen des Differenzbildes mit Schwellwert '''
|
||||
diff2 = np.copy(diff1)
|
||||
diff2[diff2 < 0.5] = 0
|
||||
cv2.imshow("Differenz mit Schwellwert ", diff2)
|
||||
|
||||
cv2.waitKey(0) # Dieser Befehl ist nötig, um die Darstellung auf dem Bildschirm zu behalten
|
||||
|
||||
32
5_Bildanalyse/ü10/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Übung 10: Objekt Detektion
|
||||
|
||||
Sie haben folgendes Bild gegeben, in dem Sie eine Flasche detektieren wollen:
|
||||

|
||||
|
||||
Weiterhin haben Sie bereits ein Kantenbild und ein Template erhalten:
|
||||
|
||||
| Kantenbild | Template |
|
||||
| ---------- | -------- |
|
||||
|  |  |
|
||||
|
||||
|
||||
## a) Template Matching
|
||||
|
||||
Sie sollen ein Template Matching durchführen, um die Position zu finden,
|
||||
an welcher eine Flasche mit der größten Wahrscheinlichkeit steht. Nutzen Sie dafür das gegebene Template einer Flasche.
|
||||
Um die Aufgabe zu lösen, implementieren Sie die folgenden Schritte:
|
||||
|
||||
1. Laden Sie das Template und das Kantenbild
|
||||
2. Legen Sie das Template auf jeden möglichen Bildausschnitts des Kantenbildes und erstellen Sie einen Matching-Score.
|
||||
1. Schneiden Sie einen Bildausschnitt in der Größe des Templates aus
|
||||
2. Berechnen Sie Fläche des Querschnitt des Templates und des Bildausschnitts. Dies ist der Matching-Score dieser Position.
|
||||
3. Speichern Sie den Matching Score für die Position
|
||||
3. Finden Sie die Position mit dem größten Matching-Score und visualisieren Sie ihn in dem originalen Bild.
|
||||
|
||||
**Hinweise:**
|
||||
- In dieser Übung sollen nur Bildausschnitte betrachtet werden, auf die das gesamte Template passt
|
||||
- Das Template soll **NICHT** skaliert werden (Größen-Variant)
|
||||
|
||||
|
||||
Bitte führen Sie für die Bearbeitung der Aufgabe das Skript [a.py](a.py) fort.
|
||||
Die Lösung befindet sich in Datei [l_a.py](l_a.py).
|
||||
31
5_Bildanalyse/ü10/a.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
# Load images and template
|
||||
original_img = cv2.imread("../../data/flasche_rechteckig.png")
|
||||
original_img = cv2.resize(original_img, (int(original_img.shape[1]/ 2), int(original_img.shape[0] / 2)))
|
||||
cv2.imshow("original_img", original_img)
|
||||
|
||||
# The original canny edge code wascode was:
|
||||
# img = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
|
||||
# edges = cv2.Canny(img, 10, 100)
|
||||
|
||||
edges = cv2.imread("data/edges.png")[:, :, 0] / 255
|
||||
cv2.imshow("canny-edges", edges.astype(np.float32) * 255)
|
||||
template = cv2.imread("data/template.png")[:, :, 0] / 255
|
||||
cv2.imshow("template", template.astype(np.float32) * 255)
|
||||
|
||||
# Sliding window over the edge image
|
||||
h_edge, w_edge = edges.shape
|
||||
h_template, w_template = template.shape
|
||||
offset_h, offset_w = int(h_template / 2), int(w_template / 2)
|
||||
print("Shape of edge image:", edges.shape)
|
||||
print("Shape of template:", template.shape)
|
||||
print("Offset of template:", offset_h, offset_w)
|
||||
|
||||
|
||||
# YOUR CODE
|
||||
# ...
|
||||
|
||||
|
||||
cv2.waitKey(0)
|
||||
BIN
5_Bildanalyse/ü10/data/edges.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
5_Bildanalyse/ü10/data/template.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
44
5_Bildanalyse/ü10/l_a.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
# Load images and template
|
||||
original_img = cv2.imread("../../data/flasche_rechteckig.png")
|
||||
original_img = cv2.resize(original_img, (int(original_img.shape[1]/ 2), int(original_img.shape[0] / 2)))
|
||||
cv2.imshow("original_img", original_img)
|
||||
|
||||
# The original canny edge code wascode was:
|
||||
# img = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
|
||||
# edges = cv2.Canny(img, 10, 100)
|
||||
|
||||
edges = cv2.imread("data/edges.png")[:, :, 0] / 255
|
||||
cv2.imshow("canny-edges", edges.astype(np.float32) * 255)
|
||||
template = cv2.imread("data/template.png")[:, :, 0] / 255
|
||||
cv2.imshow("template", template.astype(np.float32) * 255)
|
||||
|
||||
# Sliding window over the edge image
|
||||
h_edge, w_edge = edges.shape
|
||||
h_template, w_template = template.shape
|
||||
offset_h, offset_w = int(h_template / 2), int(w_template / 2)
|
||||
print("Shape of edge image:", edges.shape)
|
||||
print("Shape of template:", template.shape)
|
||||
print("Offset of template:", offset_h, offset_w)
|
||||
|
||||
heatmap = np.zeros_like(edges).astype(np.float32)
|
||||
for x in range(0, w_edge - w_template):
|
||||
for y in range(0, h_edge - h_template):
|
||||
overlapping_pixel = edges[y:y+h_template, x:x+w_template] * template
|
||||
num_overlapping_pixel = np.sum(overlapping_pixel)
|
||||
heatmap[y + offset_h, x + offset_w] = num_overlapping_pixel
|
||||
|
||||
heatmap = heatmap / np.max(heatmap)
|
||||
cv2.imshow("heatmap", heatmap)
|
||||
|
||||
# Find maximum and print it so the original image
|
||||
max_pos = np.unravel_index(heatmap.argmax(), heatmap.shape)
|
||||
print("The maximal position is:", max_pos)
|
||||
red = [0, 0, 255]
|
||||
original_img[max_pos[0]-5:max_pos[0]+5, max_pos[1]-5:max_pos[1]+5] = red
|
||||
cv2.imshow("best_match", original_img)
|
||||
|
||||
|
||||
cv2.waitKey(0)
|
||||
29
5_Bildanalyse/ü11/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Übung 11: Objekt Klassifikation
|
||||
|
||||
In dieser Aufgabe sollen Sie einen Algorithmus für die Klassifikation von bereits segmentierten Objekten schreiben.
|
||||
Dabei sollen Sie jedem der 6 folgenden Bilder eine Klasse zuordnen. Die Möglichen Klassen sind dabei
|
||||
|
||||
- Mensch
|
||||
- Ball
|
||||
- Fenster
|
||||
|
||||
|  |  |  |
|
||||
| ---------- | -------- | ----- |
|
||||
|  |  |  |
|
||||
|
||||
|
||||
## a) Erzeugung von Merkmalen
|
||||
|
||||
Erstellen Sie enige Merkmale, welche Größe oder Form der Objekte quantitativ beschreiben. Achten Sie darauf,
|
||||
dass die Merkmale möglichst diskriminativ sind, sodass sie zur Unterscheidung in die verschiedenen Klassen genutzt
|
||||
werden können.
|
||||
|
||||
|
||||
Bitte führen Sie für die Bearbeitung der Aufgabe das Skript [a.py](a.py) fort.
|
||||
Die Lösung befindet sich in Datei [l_a.py](l_a.py).
|
||||
|
||||
## b) Klassifikation von Merkmalen
|
||||
Erstellen Sie nun qualitative Regeln mit den in Aufgabe a) erstellen Merkmalen, um die Elemente in den Bildern zu
|
||||
klassifizieren. Sie brauchen dabei nicht programmieren, sondern lediglich Regeln für die Klassifizierung erstellen.
|
||||
|
||||
Eine mögliche Lösung befindet sich in Datei [l_b.md](l_b.md).
|
||||
20
5_Bildanalyse/ü11/a.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
|
||||
''' Load images '''
|
||||
img1 = cv2.imread("data/mensch.png", cv2.IMREAD_GRAYSCALE)
|
||||
img2 = cv2.imread("data/mensch2.png", cv2.IMREAD_GRAYSCALE)
|
||||
img3 = cv2.imread("data/kasten.png", cv2.IMREAD_GRAYSCALE)
|
||||
img4 = cv2.imread("data/kasten2.png", cv2.IMREAD_GRAYSCALE)
|
||||
img5 = cv2.imread("data/ball.png", cv2.IMREAD_GRAYSCALE)
|
||||
img6 = cv2.imread("data/ball2.png", cv2.IMREAD_GRAYSCALE)
|
||||
|
||||
''' Define features '''
|
||||
|
||||
|
||||
''' Show parameter '''
|
||||
for name, img in [("img1", img1), ("img2", img2), ("img3", img3), ("img4", img4), ("img5", img5), ("img6", img6)]:
|
||||
print("Image:", name)
|
||||
print(" feature =", ...)
|
||||
|
||||
BIN
5_Bildanalyse/ü11/data/ball.png
Normal file
|
After Width: | Height: | Size: 527 B |
BIN
5_Bildanalyse/ü11/data/ball2.png
Normal file
|
After Width: | Height: | Size: 925 B |
BIN
5_Bildanalyse/ü11/data/kasten.png
Normal file
|
After Width: | Height: | Size: 830 B |
BIN
5_Bildanalyse/ü11/data/kasten2.png
Normal file
|
After Width: | Height: | Size: 782 B |
BIN
5_Bildanalyse/ü11/data/mensch.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
5_Bildanalyse/ü11/data/mensch2.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
60
5_Bildanalyse/ü11/l_a.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
''' Load images '''
|
||||
img1 = cv2.imread("data/mensch.png", cv2.IMREAD_GRAYSCALE)
|
||||
img2 = cv2.imread("data/mensch2.png", cv2.IMREAD_GRAYSCALE)
|
||||
img3 = cv2.imread("data/kasten.png", cv2.IMREAD_GRAYSCALE)
|
||||
img4 = cv2.imread("data/kasten2.png", cv2.IMREAD_GRAYSCALE)
|
||||
img5 = cv2.imread("data/ball.png", cv2.IMREAD_GRAYSCALE)
|
||||
img6 = cv2.imread("data/ball2.png", cv2.IMREAD_GRAYSCALE)
|
||||
|
||||
''' Define features '''
|
||||
|
||||
|
||||
def height_and_width(img):
|
||||
rows, cols = np.where(img != 0)
|
||||
x1, x2, y1, y2 = np.min(cols), np.max(cols), np.min(rows), np.max(rows)
|
||||
h = y2 - y1
|
||||
w = x2 - x1
|
||||
return h, w
|
||||
|
||||
|
||||
def height_over_width(w, h):
|
||||
return h / w
|
||||
|
||||
|
||||
def perimeter(img):
|
||||
kernel = np.asarray([[0, 1, 0], [1, 1, 1], [0, 1, 0]], dtype=np.uint8)
|
||||
small_img = cv2.erode(img, kernel)
|
||||
edges = img - small_img
|
||||
peri = np.sum(edges / np.max(edges))
|
||||
return peri
|
||||
|
||||
|
||||
def area(img):
|
||||
return np.sum(img / np.max(img))
|
||||
|
||||
|
||||
def roundness(perimeter, area, h, w):
|
||||
# (2 pi r) / (pi r^2) = perimeter / area = 2 / r for round objects
|
||||
# r = h / 2 = w / 2
|
||||
r = 0.5 * (h / 2 + w / 2)
|
||||
round = r * perimeter / (2 * area)
|
||||
return round
|
||||
|
||||
|
||||
''' Show parameter '''
|
||||
for name, img in [("img1", img1), ("img2", img2), ("img3", img3), ("img4", img4), ("img5", img5), ("img6", img6)]:
|
||||
print("Image:", name)
|
||||
object_height, object_width = height_and_width(img)
|
||||
print(" h =", object_height)
|
||||
print(" w =", object_width)
|
||||
object_height_over_width = height_over_width(object_width, object_height)
|
||||
print(" height_over_width =", object_height_over_width)
|
||||
object_perimeter = perimeter(img)
|
||||
print(" perimeter =", object_perimeter)
|
||||
object_area = area(img)
|
||||
print(" area =", object_area)
|
||||
object_roundness = roundness(object_perimeter, object_area, object_height, object_width)
|
||||
print(" roundness =", object_roundness)
|
||||
60
5_Bildanalyse/ü11/l_b.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Lösung b): Klassifikation von Merkmalen
|
||||
|
||||
Basierend auf den Merkmalen in Teilaufgabe a) können einige Regelen für die Klassifikation von Objekten erstellt werden.
|
||||
Diese Lösung ist eine von vielen möglichen Lösungen und bezieht sich auf die Ausgabe aus Aufgabe a):
|
||||
|
||||
````shell
|
||||
Image: img1
|
||||
h = 119
|
||||
w = 60
|
||||
height_over_width = 1.9833333333333334
|
||||
perimeter = 535.0
|
||||
area = 2925.0
|
||||
roundness = 4.092521367521368
|
||||
Image: img2
|
||||
h = 96
|
||||
w = 53
|
||||
height_over_width = 1.8113207547169812
|
||||
perimeter = 494.0
|
||||
area = 1981.0
|
||||
roundness = 4.6444977284199895
|
||||
Image: img3
|
||||
h = 98
|
||||
w = 117
|
||||
height_over_width = 0.8376068376068376
|
||||
perimeter = 1120.0
|
||||
area = 2489.0
|
||||
roundness = 12.093210124548012
|
||||
Image: img4
|
||||
h = 73
|
||||
w = 56
|
||||
height_over_width = 1.3035714285714286
|
||||
perimeter = 631.0
|
||||
area = 884.0
|
||||
roundness = 11.510039592760181
|
||||
Image: img5
|
||||
h = 48
|
||||
w = 50
|
||||
height_over_width = 0.96
|
||||
perimeter = 140.0
|
||||
area = 1960.0
|
||||
roundness = 0.875
|
||||
Image: img6
|
||||
h = 49
|
||||
w = 56
|
||||
height_over_width = 0.875
|
||||
perimeter = 382.0
|
||||
area = 2111.0
|
||||
roundness = 2.3750592136428232
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
````
|
||||
|
||||
- **Menschen** sind im stehenden Zustand ca. doppelt so hoch wie breit. Daher sollte bei einem Menschen *height_over_width* ca. 2 sein. Zusätzlich sollte die *roundness* deutlich ungleich 1 sein.
|
||||
- **Bälle** sind unabhängig von ihrer Größe rund. Die *roundness* sollte daher in der Nähe von 1 liegen.
|
||||
- **Fenster** haben meistens aufgrund der "Kacheln" eine geringe Fläche, verglichen zu dem Umfang.
|
||||
|
||||
|
||||
Diese Regeln müssen nicht auf jedes Objekt zutreffen. Sie sollen lediglich veranschaulichen, wie Regeln für einen Klassifikator
|
||||
aus Merkmalen erstellt werden können.
|
||||
53
5_Bildanalyse/ü12/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Übung 12: Automatische Schwellwertfindung nach Otsu
|
||||
|
||||
Sie haben folgendes Bild gegeben und sollen den Kamera-Mann und seine Kamera möglichst gut mit einem binären Schwellwert
|
||||
segmentieren:
|
||||

|
||||
|
||||
Hierzu soll das Verfahren von Otsu verwendet werden.
|
||||
|
||||
Quelle: [https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=4310076](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=4310076)
|
||||
|
||||
## a) Otsu
|
||||
|
||||
Berechnen Sie für jeden Schwellwert zwischen {0, ..., 255} die Mittelwerte und Varianzen der beiden Gaussverteilungen
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?n_1(s)=\sum_{k=0}^sh(k)" title="\sum_1" />
|
||||
<p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?n_2(s)=\sum_{k=s+1}^{255}h(k)" title="\sum_1" />
|
||||
<p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?\mu_1(s)=\frac{1}{n_1}\sum_{k=0}^sh(k)k" title="\sum_1" />
|
||||
<p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?\mu_2(s)=\frac{1}{n_2}\sum_{k=s+1}^{255}h(k)k" title="\sum_1" />
|
||||
<p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?\sigma_1(s)=\sqrt{\frac{1}{n_1}\sum_{k=0}^sh(k)(k-\mu_1)^2}" title="\sum_1" />
|
||||
<p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?\sigma_2(s)=\sqrt{\frac{1}{n_2}\sum_{k=s+1}^{255}h(k)(k-\mu_2)^2}" title="\sum_1" />
|
||||
<p>
|
||||
|
||||
und maximieren Sie den Quotienten
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?Q(s)=\frac{\sigma(s)_{zw}^2}{\sigma(s)_{in}^2}=\frac{n_1(s)(\mu_1(s)-\mu)^2+n_2(s)(\mu_2(s)-\mu)^2}{n_1(s)\sigma_1(s)^2+n_2(s)\sigma_2(s)^2}" title="\sum_1" />
|
||||
<p>
|
||||
|
||||
mit dem Mittelwert des Grauwertbildes
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?\mu=\frac{1}{n_1(255)}\sum_{k=0}^{255}h(k)k," title="\sum_1" />
|
||||
<p>
|
||||
|
||||
sodass Sie den optimalen Schwellwert s bekommen.
|
||||
|
||||
Bitte führen Sie für die Bearbeitung der Aufgabe das Skript [a.py](a.py) fort.
|
||||
Die Lösung befindet sich in Datei [l_a.py](l_a.py).
|
||||
41
5_Bildanalyse/ü12/a.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
|
||||
def n1(hist, s):
|
||||
...
|
||||
|
||||
|
||||
def n2(hist, s):
|
||||
...
|
||||
|
||||
|
||||
def mean(hist):
|
||||
...
|
||||
|
||||
|
||||
def mu1(hist, s, n):
|
||||
...
|
||||
|
||||
|
||||
def mu2(hist, s, n):
|
||||
...
|
||||
|
||||
|
||||
def sigma1(hist, s, n, mu):
|
||||
...
|
||||
|
||||
|
||||
def sigma2(hist, s, n, mu):
|
||||
...
|
||||
|
||||
|
||||
def Q(hist, s):
|
||||
...
|
||||
|
||||
|
||||
''' Load data '''
|
||||
# Load images and template
|
||||
img = cv2.imread("../../data/cameraman.png", cv2.IMREAD_GRAYSCALE)
|
||||
|
||||
77
5_Bildanalyse/ü12/l_a.py
Normal file
@@ -0,0 +1,77 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
|
||||
def n1(hist, s):
|
||||
return np.sum(hist[0:s+1])
|
||||
|
||||
|
||||
def n2(hist, s):
|
||||
return np.sum(hist[s+1:256])
|
||||
|
||||
|
||||
def mean(hist):
|
||||
k = np.arange(0, 256)
|
||||
return np.sum((hist * k)) / np.sum(hist)
|
||||
|
||||
|
||||
def mu1(hist, s, n):
|
||||
k = np.arange(0, 256)
|
||||
return (1 / n) * np.sum((hist * k)[0:s+1])
|
||||
|
||||
|
||||
def mu2(hist, s, n):
|
||||
k = np.arange(0, 256)
|
||||
return (1 / n) * np.sum((hist * k)[s+1:256])
|
||||
|
||||
|
||||
def sigma1(hist, s, n, mu):
|
||||
k = np.arange(0, 256)
|
||||
return np.sqrt((1 / n) * np.sum((hist * np.power(k - mu, 2))[0:s+1]))
|
||||
|
||||
|
||||
def sigma2(hist, s, n, mu):
|
||||
k = np.arange(0, 256)
|
||||
return np.sqrt((1 / n) * np.sum((hist * np.power(k - mu, 2))[s+1:256]))
|
||||
|
||||
|
||||
def Q(hist, s):
|
||||
n_1, n_2 = n1(hist, s), n2(hist, s)
|
||||
if n_1 == 0 or n_2 == 0:
|
||||
return 0
|
||||
mu_1, mu_2 = mu1(hist, s, n_1), mu2(hist, s, n_2)
|
||||
sigma_1, sigma_2 = sigma1(hist, s, n_1, mu_1), sigma2(hist, s, n_2, mu_2)
|
||||
_mean = mean(hist)
|
||||
|
||||
sigma_zw = n_1 * np.power(mu_1 - _mean, 2) + n_2 * np.power(mu_2 - _mean, 2)
|
||||
sigma_in = n_1 * np.power(sigma_1, 2) + n_2 * np.power(sigma_2, 2)
|
||||
q = np.power(sigma_zw, 2) / np.power(sigma_in, 2)
|
||||
return q
|
||||
|
||||
''' Load data '''
|
||||
# Load images and template
|
||||
img = cv2.imread("../../data/cameraman.png", cv2.IMREAD_GRAYSCALE)
|
||||
|
||||
histr = cv2.calcHist([img], [0], None, [256], [0, 256])
|
||||
histr = histr[:, 0]
|
||||
plt.plot(histr)
|
||||
plt.xlim([0, 256])
|
||||
|
||||
''' Iterate over all s '''
|
||||
q_list = list()
|
||||
|
||||
for s in range(1, 255):
|
||||
q_list.append(Q(histr, s))
|
||||
|
||||
optimal_s = np.argmax(np.asarray(q_list))
|
||||
print("Threshold:", optimal_s)
|
||||
|
||||
''' Visualize result '''
|
||||
mask = img >= optimal_s
|
||||
|
||||
cv2.imshow("img", img)
|
||||
cv2.imshow("mask", mask.astype(np.uint8) * 255)
|
||||
|
||||
plt.show()
|
||||
cv2.waitKey(0)
|
||||
49
5_Bildanalyse/ü13/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Übung 13: Instanzsegmentierung
|
||||
|
||||
In dieser Aufgabe sollen verschiedene Instanzen von Objekten in einem binär segmentierten Bild gefunden werden.
|
||||
|
||||
|
||||
## a) Zeilenkoinzidenzverfahren
|
||||
|
||||
Betrachten Sie das folgende Bild und die dazugehörige Instanzsegmentierung:
|
||||
|
||||
| Binärbild | Instanz-segmentiertes Bild |
|
||||
| --- | --- |
|
||||
|  | |
|
||||
|
||||
|
||||
Finden Sie in dem Binärbild zusammenhängende bzw. getrennte Objekte nach dem Zeilenkoinzidenzverfahren nach der foldengen
|
||||
Anleitung:
|
||||
|
||||

|
||||
|
||||
Geben Sie dabei jedem neuen Objekt eine eigene ID und visualisieren Sie das Ergebnis.
|
||||
|
||||
|
||||
Bitte führen Sie für die Bearbeitung der Aufgabe das Skript [a.py](a.py) fort.
|
||||
Eine Lösung befindet sich in Datei [l_a.py](l_a.py).
|
||||
|
||||
|
||||
## b) Watershed Algorithmus
|
||||
|
||||
Betrachten Sie das folgende Bild und die dazugehörige Instanzsegmentierung:
|
||||
|
||||
| RGB-Bild | Instanz-segmentiertes Bild |
|
||||
| --- | --- |
|
||||
|  | |
|
||||
|
||||
|
||||
|
||||
Führen Sie nun die Instanzsegmentierung mit der folgenden
|
||||
Anleitung selbst durch:
|
||||
|
||||
- Binarisieren Sie das Bild, sodass der Hintergrund das Label 0 und die Sticker das Label 1 haben
|
||||
- Füllen Sie im Binärbild kleine Löcher und entfernen Sie kleine Regionen (Rauschen)
|
||||
- Erstellen Sie Seed-Punkte/Regionen, indem Sie das Binärbild stark erodieren und das Zeilenkoinzidenzverfahren (siehe oben) anwenden
|
||||
- "Füllen" Sie iterativ die Pixel mit dem Label 1 zu den Seedpunkten hinzu
|
||||
|
||||
Geben Sie am Ende jedem Objekt eine eigene ID und visualisieren Sie das Ergebnis.
|
||||
|
||||
|
||||
Bitte führen Sie für die Bearbeitung der Aufgabe das Skript [b.py](b.py) fort.
|
||||
Eine Lösung befindet sich in Datei [l_b.py](l_b.py).
|
||||
9
5_Bildanalyse/ü13/a.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
|
||||
''' Load image '''
|
||||
img = cv2.imread("data/bild.png", cv2.IMREAD_GRAYSCALE)
|
||||
cv2.imshow("img", img)
|
||||
|
||||
cv2.waitKey(0)
|
||||
BIN
5_Bildanalyse/ü13/data/bild.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
5_Bildanalyse/ü13/data/result.jpg
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
5_Bildanalyse/ü13/data/students_checklist.jpg
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
5_Bildanalyse/ü13/data/students_checklist_result.jpg
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
5_Bildanalyse/ü13/data/verfahren.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
61
5_Bildanalyse/ü13/l_a.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
|
||||
''' Load image '''
|
||||
img = cv2.imread("data/bild.png", cv2.IMREAD_GRAYSCALE)
|
||||
cv2.imshow("img", img)
|
||||
|
||||
''' Iterate over rows / columns'''
|
||||
label_map = np.zeros_like(img)
|
||||
next_id = 1
|
||||
for i in range(img.shape[0]):
|
||||
for j in range(img.shape[1]):
|
||||
if img[i, j] != 0:
|
||||
upper_label = label_map[i - 1, j] if i > 0 else 0
|
||||
left_label = label_map[i, j - 1] if j > 0 else 0
|
||||
if upper_label == 0 and left_label == 0:
|
||||
label_map[i, j] = next_id
|
||||
next_id += 1
|
||||
elif upper_label == 0 and left_label != 0:
|
||||
label_map[i, j] = left_label
|
||||
elif upper_label != 0 and left_label == 0:
|
||||
label_map[i, j] = upper_label
|
||||
elif upper_label != 0 and left_label != 0:
|
||||
if upper_label == left_label:
|
||||
label_map[i, j] = upper_label
|
||||
else:
|
||||
new_label = min(upper_label, left_label)
|
||||
old_label = max(upper_label, left_label)
|
||||
label_map[label_map == old_label] = new_label
|
||||
label_map[i, j] = new_label
|
||||
|
||||
''' Rename labels (not necessary, but for visualisation) '''
|
||||
labels = sorted(np.unique(label_map))
|
||||
next_id = 1
|
||||
for l in labels:
|
||||
if l == 0:
|
||||
continue
|
||||
label_map[label_map == l] = next_id
|
||||
next_id += 1
|
||||
|
||||
''' Visualize label map '''
|
||||
color_map = {
|
||||
1: [255, 0, 0],
|
||||
2: [255, 255, 0],
|
||||
3: [255, 255, 255],
|
||||
4: [0, 255, 0],
|
||||
5: [0, 255, 255],
|
||||
6: [0, 0, 255],
|
||||
7: [100, 100, 100],
|
||||
8: [50, 200, 80],
|
||||
9: [200, 140, 88],
|
||||
10: [120, 0, 190],
|
||||
}
|
||||
|
||||
colored_image = np.zeros((img.shape[0], img.shape[1], 3))
|
||||
for c, value in color_map.items():
|
||||
colored_image[label_map == c] = value
|
||||
|
||||
cv2.imshow("colored_image", colored_image.astype(np.uint8))
|
||||
cv2.waitKey(0)
|
||||
91
5_Bildanalyse/ü13/l_b.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
|
||||
''' Load image and apply histogramm equalization '''
|
||||
img = cv2.imread("data/students_checklist.jpg").astype(np.float32)
|
||||
img[:, :, 0] = 255 * (img[:, :, 0] - np.min(img[:, :, 0])) / (np.max(img[:, :, 0]) - np.min(img[:, :, 0]))
|
||||
img[:, :, 1] = 255 * (img[:, :, 1] - np.min(img[:, :, 1])) / (np.max(img[:, :, 1]) - np.min(img[:, :, 1]))
|
||||
img[:, :, 2] = 255 * (img[:, :, 2] - np.min(img[:, :, 2])) / (np.max(img[:, :, 2]) - np.min(img[:, :, 2]))
|
||||
cv2.imshow("img", img.astype(np.uint8))
|
||||
|
||||
''' Binary segmentation '''
|
||||
mask = (img[:, :, 0] > 100) * (img[:, :, 1] > 100) * (img[:, :, 2] > 100) *\
|
||||
(img[:, :, 2] - img[:, :, 1] < 20) * (img[:, :, 2] - img[:, :, 0] < 20)
|
||||
mask = 1 - mask
|
||||
|
||||
''' Morphologigcal operations '''
|
||||
kernel = np.ones((3, 3))
|
||||
mask = cv2.erode(mask.astype(np.uint8) * 255, kernel, iterations=15)
|
||||
mask = cv2.dilate(mask, kernel, iterations=15)
|
||||
|
||||
cv2.imshow("mask", mask)
|
||||
|
||||
''' Finding seed point '''
|
||||
seeds = cv2.erode(mask, kernel, iterations=70)
|
||||
cv2.imshow("seeds", seeds)
|
||||
|
||||
''' Labeling seeds '''
|
||||
label_map = np.zeros_like(seeds)
|
||||
next_id = 1
|
||||
for i in range(img.shape[0]):
|
||||
for j in range(img.shape[1]):
|
||||
if seeds[i, j] != 0:
|
||||
upper_label = label_map[i - 1, j] if i > 0 else 0
|
||||
left_label = label_map[i, j - 1] if j > 0 else 0
|
||||
if upper_label == 0 and left_label == 0:
|
||||
label_map[i, j] = next_id
|
||||
next_id += 1
|
||||
elif upper_label == 0 and left_label != 0:
|
||||
label_map[i, j] = left_label
|
||||
elif upper_label != 0 and left_label == 0:
|
||||
label_map[i, j] = upper_label
|
||||
elif upper_label != 0 and left_label != 0:
|
||||
if upper_label == left_label:
|
||||
label_map[i, j] = upper_label
|
||||
else:
|
||||
new_label = min(upper_label, left_label)
|
||||
old_label = max(upper_label, left_label)
|
||||
label_map[label_map == old_label] = new_label
|
||||
label_map[i, j] = new_label
|
||||
labels = sorted(np.unique(label_map))
|
||||
next_id = 1
|
||||
for l in labels:
|
||||
if l == 0:
|
||||
continue
|
||||
label_map[label_map == l] = next_id
|
||||
next_id += 1
|
||||
|
||||
''' Create distance labels for all labels '''
|
||||
mask = mask != 0
|
||||
|
||||
while np.sum(mask > 0):
|
||||
for l in np.unique(label_map):
|
||||
if l == 0:
|
||||
continue
|
||||
kernel = np.ones((3, 3))
|
||||
current_label = label_map == l
|
||||
current_label = cv2.dilate(current_label.astype(np.uint8), kernel, iterations=1)
|
||||
current_label = current_label * mask
|
||||
mask[current_label != 0] = 0
|
||||
label_map[current_label != 0] = l
|
||||
|
||||
|
||||
''' Visualize label map '''
|
||||
color_map = {
|
||||
1: [255, 0, 0],
|
||||
2: [255, 255, 0],
|
||||
3: [255, 255, 255],
|
||||
4: [0, 255, 0],
|
||||
5: [0, 255, 255],
|
||||
6: [0, 0, 255],
|
||||
7: [100, 100, 100],
|
||||
8: [50, 200, 80],
|
||||
9: [200, 140, 88],
|
||||
10: [120, 0, 190],
|
||||
}
|
||||
colored_image = np.zeros((img.shape[0], img.shape[1], 3))
|
||||
for c, value in color_map.items():
|
||||
colored_image[label_map == c] = value
|
||||
cv2.imshow("colored_image", colored_image.astype(np.uint8))
|
||||
cv2.waitKey(0)
|
||||
21
5_Bildanalyse/ü2/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Übung 2: Morphologische Operatoren
|
||||
|
||||
In dieser Übung werden die morphologischen Operatoren *Dilatation* und *Erosion* behandelt.
|
||||
|
||||
## Aufgabe a)
|
||||
Geben Sie die Definition der Dilatation und der Erosion an! Die Lösung findet sich in [l_a.md](l_a.md).
|
||||
|
||||
## Aufgabe b)
|
||||
Geben Sie die Definition von *Opening* und *Closing* als Funktion von Dilatation und der Erosion an! Die Lösung findet sich in [l_b.md](l_b.md).
|
||||
|
||||
## Aufgabe c)
|
||||
Wenn ein Binärbild nach einer Erosion an einer Position (x, y) den Wert 1 hat, muss dann
|
||||
das ursprüngliche Bild ebenfalls an der Stelle (x, y) den Wert 1 haben? Die Lösung findet sich in [l_c.md](l_c.md).
|
||||
|
||||
## Aufgabe d)
|
||||
Gegeben seien folgendes Binärbild (links) und Strukturelement (rechts, Ursprung in der Mitte).
|
||||
Berechnen Sie das Ergebnisbild nach dem Ausführen einer Opening-Operation.
|
||||
|
||||

|
||||
|
||||
Das Binärbild finden Sie in der Datei [d.py](d.py). Die dazugehörige Musterlösung in der Datei [l_d.py](l_d.py).
|
||||
22
5_Bildanalyse/ü2/d.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
I = np.asarray([
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 0, 1, 1, 0, 1, 0],
|
||||
[0, 1, 0, 1, 1, 0, 1, 1, 1, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 0, 0, 1, 0, 0, 1, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
|
||||
[0, 0, 0, 1, 1, 1, 0, 1, 1, 0],
|
||||
[0, 1, 0, 1, 1, 1, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
], dtype=np.uint8)
|
||||
|
||||
s = np.asarray([
|
||||
[0, 1, 0],
|
||||
[0, 1, 1],
|
||||
[0, 0, 0],
|
||||
], dtype=np.uint8)
|
||||
|
||||
BIN
5_Bildanalyse/ü2/data/dilatation.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
5_Bildanalyse/ü2/data/erosion.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
5_Bildanalyse/ü2/data/morph.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
20
5_Bildanalyse/ü2/l_a.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Musterlösung Aufgabe a)
|
||||
|
||||
Die beiden Basisoperatoren in der morphologischen Filterung sind die dualen Filter Erosion
|
||||
(Verkleinerung, Ausdünnung) und Dilatation (Vergrößerung, Aufblähung).
|
||||
|
||||
Bei einer Dilatation (lat: dilaterare: ausbreiten, dehnen) werden Hintergrundpixel, die bestimmte
|
||||
Voraussetzungen erfüllen, in den Vordergrund aufgenommen. Eine bestimmte Pixelposition (Ankerpunkt)
|
||||
wird auf 1 gesetzt (also dem Vordergrund zugeordnet), falls das Strukturelement mindestens
|
||||
ein Pixel mit Wert 1 des Bildes überdeckt.
|
||||
|
||||

|
||||
|
||||
|
||||
Die der Dilatation entgegengesetzte Operation nennt man Erosion (lat: erodere = abnagen). Diese
|
||||
Operation verkleinert die Segmente, indem bestimmte Segmentpixel dem Hintergrund beigeordnet
|
||||
werden. Hier wird der Ankerpunkt auf 0 gesetzt (also dem Hintergrund zugeordnet), falls das
|
||||
Strukturelement mindestens ein Pixel des Binärbildes mit den Wert 0 überdeckt. Andernfalls wird
|
||||
der Ankerpunkt auf 1 gesetzt.
|
||||
|
||||

|
||||
26
5_Bildanalyse/ü2/l_b.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Musterlösung Aufgabe b)
|
||||
|
||||
**Erosion:**
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?A&space;\ominus&space;b" title="A \ominus b" />
|
||||
</p>
|
||||
|
||||
**Dilatation:**
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?A&space;\oplus&space;b" title="A \oplus b" />
|
||||
</p>
|
||||
|
||||
**Opening:**
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?(A&space;\ominus&space;b)\oplus&space;b" title="(A \ominus b)\oplus b" />
|
||||
</p>
|
||||
|
||||
**Closing:**
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?(A&space;\oplus&space;b)\ominus&space;b" title="(A \oplus b)\ominus b" />
|
||||
</p>
|
||||
|
||||
10
5_Bildanalyse/ü2/l_c.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Musterlösung Aufgabe c)
|
||||
Antwort: Nein!
|
||||
|
||||
Beweis:
|
||||
Verwendet man als Strukturelement zum Beispiel
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?\begin{bmatrix}&space;0&&space;1&space;&&space;0&space;\\&space;1&&space;0&space;&&space;1&space;\\&space;0&&space;1&space;&&space;0&space;\\\end{bmatrix}" title="\begin{bmatrix} 0& 1 & 0 \\ 1& 0 & 1 \\ 0& 1 & 0 \\\end{bmatrix}" />
|
||||
</p>
|
||||
so kann die Erosion zusätzliche
|
||||
Binärpixel erzeugen, welche nicht im Ursprungsbild waren.
|
||||
35
5_Bildanalyse/ü2/l_d.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
I = np.asarray([
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 1, 1, 0, 1, 1, 0, 1, 0],
|
||||
[0, 1, 0, 1, 1, 0, 1, 1, 1, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0, 1, 1, 0],
|
||||
[0, 0, 1, 0, 0, 1, 0, 0, 1, 0],
|
||||
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
|
||||
[0, 0, 0, 1, 1, 1, 0, 1, 1, 0],
|
||||
[0, 1, 0, 1, 1, 1, 0, 1, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
], dtype=np.uint8)
|
||||
|
||||
s = np.asarray([
|
||||
[0, 1, 0],
|
||||
[0, 1, 1],
|
||||
[0, 0, 0],
|
||||
], dtype=np.uint8)
|
||||
|
||||
|
||||
I_new = cv2.erode(I, s)
|
||||
I_new = cv2.dilate(I_new, s)
|
||||
|
||||
# Resize image
|
||||
I = np.repeat(I, 50, axis=1)
|
||||
I = np.repeat(I, 50, axis=0)
|
||||
I_new = np.repeat(I_new, 50, axis=1)
|
||||
I_new = np.repeat(I_new, 50, axis=0)
|
||||
|
||||
cv2.imshow("Original", I * 255)
|
||||
cv2.imshow("Opening", I_new * 255)
|
||||
cv2.waitKey(0)
|
||||
25
5_Bildanalyse/ü3/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Übung 3: Split and Merge
|
||||
|
||||
Sie haben folgendes Grauwertbild gegeben:
|
||||
|
||||
|
||||

|
||||
|
||||
Dabei gilt die Homogenitätsbedingung:
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?y&space;=&space;\left\{\begin{matrix}&space;1,&space;\&space;\max(R_i)&space;-&space;\min(R_i)&space;<&space;3&space;\\&space;0,&space;sonst&space;\end{matrix}\right." title="y = \left\{\begin{matrix} 1, \ \max(R_i) - \min(R_i) < 3 \\ 0, sonst \end{matrix}\right." />
|
||||
</p>
|
||||
|
||||
|
||||
## Aufgabe a:
|
||||
Wenden Sie den "Split and Merge" Algorithmus auf **F** an.
|
||||
|
||||
## Aufgabe b:
|
||||
Zerlegen Sie das Bild in eine Quad-Tree Sruktur.
|
||||
|
||||
## Aufgabe c)
|
||||
Zeichnen Sie das Segmentierungsergebnis.
|
||||
|
||||
## Aufgabe d)
|
||||
Ist das Ergebnis eindeutig?
|
||||
BIN
5_Bildanalyse/ü3/data/l_ac.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
5_Bildanalyse/ü3/data/l_b.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
5_Bildanalyse/ü3/data/splitmerge.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
12
5_Bildanalyse/ü3/l_ac.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Lösung Aufgabe a)
|
||||
1. Rekursive Zerlegung aller Regionen in vier nicht zusammenhängende Quadraten, falls das
|
||||
Homogenitätskriterium für die entsprechende Region nicht erfüllt ist.
|
||||
2. Rekursive Zusammenfassung
|
||||
aller benachbarten Regionen, sofern auch dies mit dem Homogenitätskriterium
|
||||
vereinbaren lässt.
|
||||
|
||||
# Lösung Aufgabe c)
|
||||
Die Abbildung i) zeigt das Zwischenergebnis nach dem Split. Die Lösung ii) zeigt das Ergebnis
|
||||
nach dem Merge.
|
||||
|
||||

|
||||
4
5_Bildanalyse/ü3/l_b.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Lösung Aufgabe b)
|
||||
|
||||
|
||||

|
||||
3
5_Bildanalyse/ü3/l_d.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Lösung Aufgabe d)
|
||||
Das Segmentierungsergebnis ist nur dann eindeutig, wenn der Quad-Tree während der Zusammenfassung
|
||||
immer in der gleichen Reihenfolge durchlaufen wird.
|
||||
15
5_Bildanalyse/ü4/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Übung 4: Region Growing
|
||||
Gegeben ist der folgende Bildausschnitt:
|
||||
|
||||
<p align="center">
|
||||
<img src="data/a.png">
|
||||
</p>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img src="https://latex.codecogs.com/svg.image?\begin{bmatrix}64&space;&179&space;&54&space;&164&space;&248&space;&94&space;&77&space;&55&space;\\208&space;&170&space;&233&space;&134&space;&222&space;&179&space;&178&space;&77&space;\\34&space;&154&space;&182&space;&69&space;&10&space;&228&space;&172&space;&178&space;\\224&space;&112&space;&89&space;&118&space;&202&space;&219&space;&63&space;&102&space;\\78&space;&251&space;&65&space;&255&space;&249&space;&79&space;&72&space;&150&space;\\160&space;&164&space;&202&space;&252&space;&56&space;&178&space;&208&space;&200&space;\\208&space;&0&space;&83&space;&172&space;&30&space;&210&space;&97&space;&255&space;\\100&space;&20&space;&187&space;&235&space;&40&space;&156&space;&92&space;&54&space;\end{bmatrix}&space;" title="\begin{bmatrix}64 &179 &54 &164 &248 &94 &77 &55 \\208 &170 &233 &134 &222 &179 &178 &77 \\34 &154 &182 &69 &10 &228 &172 &178 \\224 &112 &89 &118 &202 &219 &63 &102 \\78 &251 &65 &255 &249 &79 &72 &150 \\160 &164 &202 &252 &56 &178 &208 &200 \\208 &0 &83 &172 &30 &210 &97 &255 \\100 &20 &187 &235 &40 &156 &92 &54 \end{bmatrix} " />
|
||||
</p>
|
||||
|
||||
## Aufgabe a)
|
||||
Wenden Sie auf diesen Bildausschnitt das Region Growing Verfahren sowohl für die 4-Nachbarschaft als auch für die 8-Nachbarschaft an. Der Seed-Punkt ist in der ersten Reihe, Spalte 5. Verwenden Sie als Homogenitätskriterium den Abstand zum mittleren Grauwert
|
||||
der Region und als Schwellwert 30.
|
||||
BIN
5_Bildanalyse/ü4/data/a.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
5_Bildanalyse/ü4/data/n4.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
5_Bildanalyse/ü4/data/n8.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
11
5_Bildanalyse/ü4/l_a.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Lösung Aufgabe a)
|
||||
|
||||
N4 Nachbarschadt | N8 Nachbarschaft
|
||||
---|---
|
||||
 | 
|
||||
|
||||
|
||||
|
||||
Der in der Vorlesung beschriebene Algorithmus ist nicht deterministisch. Damit der Alsgorithmus deterministisch ist, muss er genauer spezifiziert und stets eingehalten werden:
|
||||
- Reihenfolge in der die Nachbarschaft abgelaufen wird
|
||||
- Werden Pixel die nicht zur Region gehören nur einmal oder mehrmals getestet?
|
||||
8
5_Bildanalyse/ü5/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Übung 5: Medialachsentransformation
|
||||
|
||||
Führen Sie die Medialachsentransformation mit der 8-Nachbarschaft (N8) für die folgende Region
|
||||
durch:
|
||||
|
||||

|
||||
|
||||
Die Lösung befindet sich in Datei [l_a.md](l_a.md).
|
||||
BIN
5_Bildanalyse/ü5/data/mat1.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
5_Bildanalyse/ü5/data/mat2.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
5_Bildanalyse/ü5/data/mat3.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
6
5_Bildanalyse/ü5/l_a.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Lösung Aufgabe a)
|
||||
|
||||
Schritt 1 | Schritt 2
|
||||
---|---
|
||||
Berechnung der Abstände zum Rand | Bestimmung der Maxima (in grün eingezeichnet)
|
||||
 | 
|
||||
29
5_Bildanalyse/ü6/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Übung 6: Hough-Akkumulator
|
||||
|
||||
Betrachten Sie das folgende Binärbild:
|
||||
|
||||

|
||||
|
||||
|
||||
Dabei sind die schwarzen Pixel als Kantenpixel zu verstehen.
|
||||
Im folgenden sollen Sie mittels der Hough-Transformation Linien im Binärbild finden.
|
||||
Bearbeiten Sie dazu folgende Aufgaben:
|
||||
|
||||
## a) Akkumulator
|
||||
|
||||
Nutzen Sie für die Hough-Transformation den folgenden Hough-Akkumulator mit der
|
||||
Parametrisierung **y=mx+n**.
|
||||
|
||||

|
||||
|
||||
|
||||
Erstellen Sie die finale Akkumulator-Matrix H.
|
||||
|
||||
Die Lösung befindet sich in Datei [l_a.py](l_a.py).
|
||||
|
||||
## b) Parametrierung
|
||||
|
||||
Geben Sie die Parametrierung der Geraden an,
|
||||
fuer welche die Akkumulator-Matrix H den größten Wert hat.
|
||||
|
||||
Die Lösung befindet sich in Datei [l_b.py](l_b.py).
|
||||
BIN
5_Bildanalyse/ü6/data/houghAcc.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
5_Bildanalyse/ü6/data/houghImg.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
10
5_Bildanalyse/ü6/l_a.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import numpy as np
|
||||
|
||||
acc = [
|
||||
[1, 0, 1, 1, 0, 0, 1, 0, 0],
|
||||
[0, 0, 0, 0, 1, 2, 0, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 1, 3]
|
||||
]
|
||||
|
||||
acc = np.asarray(acc)
|
||||
print(acc)
|
||||
6
5_Bildanalyse/ü6/l_b.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import numpy as np
|
||||
|
||||
n = 4
|
||||
m = -1
|
||||
|
||||
print("%s * x + %s" % (m, n))
|
||||
18
5_Bildanalyse/ü7/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Übung 7: Distanzmetriken
|
||||
|
||||
Betrachten Sie das folgende Binärbild:
|
||||
|
||||

|
||||
|
||||
|
||||
Es soll der Abstand zwischen dem obersten Pixel an der Position (0,1) und dem
|
||||
Pixel an der Stelle (5, 4) berechnet werden.
|
||||
|
||||
## a)
|
||||
|
||||
Berechnen Sie
|
||||
- die City-Block Distanz
|
||||
- den euklidischen Abstand
|
||||
- die Schachbrett Distanz (Tschebyschew-Abstand).
|
||||
|
||||
Die Lösung befindet sich in Datei [l_a.py](l_a.py).
|
||||
BIN
5_Bildanalyse/ü7/data/a.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
44
5_Bildanalyse/ü7/l_a.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
p_1 = (0, 1)
|
||||
p_2 = (5, 4)
|
||||
matrix = np.zeros((6, 6))
|
||||
matrix[p_1[1], p_1[0]] = 1
|
||||
matrix[p_2[1], p_2[0]] = 1
|
||||
|
||||
# Resize image
|
||||
matrix = np.repeat(matrix, 50, axis=1)
|
||||
matrix = np.repeat(matrix, 50, axis=0)
|
||||
|
||||
# Add seperators
|
||||
matrix[0::2, ::50] = 1
|
||||
matrix[1::2, ::50] = 0
|
||||
matrix[::50, 0::2] = 1
|
||||
matrix[::50, 1::2] = 0
|
||||
|
||||
|
||||
def city_block(p1, p2):
|
||||
dx, dy = p2[0] - p1[0], p2[1] - p1[1]
|
||||
return np.abs(dx) + np.abs(dy)
|
||||
|
||||
|
||||
def euklidean(p1, p2):
|
||||
dx, dy = p2[0] - p1[0], p2[1] - p1[1]
|
||||
return np.sqrt(dx * dx + dy * dy)
|
||||
|
||||
|
||||
def tschebyschew(p1, p2):
|
||||
dx, dy = p2[0] - p1[0], p2[1] - p1[1]
|
||||
return np.maximum(np.abs(dx), np.abs(dy))
|
||||
|
||||
|
||||
print("City-Block:", city_block(p_1, p_2))
|
||||
print("Euklidischer Abstand:", euklidean(p_1, p_2))
|
||||
print("Schachbrett Distanz (Tschebyschew):", tschebyschew(p_1, p_2))
|
||||
|
||||
# Show image
|
||||
matrix = matrix.astype(np.float64)
|
||||
cv2.imshow("a", matrix)
|
||||
cv2.imwrite("data/a.png", matrix * 255)
|
||||
cv2.waitKey(0)
|
||||
13
5_Bildanalyse/ü8/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Übung 8: Vereinigung und Querschnitt
|
||||
|
||||
Betrachten Sie die folgenden Binärbilder:
|
||||
|
||||
| Objekt 1 | Objekt 2 |
|
||||
| -------- | -------- |
|
||||
|  |  |
|
||||
|
||||
|
||||
## a) Vereinigung
|
||||
|
||||
Zeichnen Sie die Vereinigung der beiden Objekte.
|
||||
Die Lösung befindet sich in Datei [l_a.py](l_a.py).
|
||||
BIN
5_Bildanalyse/ü8/data/a.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
5_Bildanalyse/ü8/data/b.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
66
5_Bildanalyse/ü8/l_a.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
obj1 = np.asarray([
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 1, 1, 0, 1, 1, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
])
|
||||
|
||||
obj2 = np.asarray([
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 0, 1, 1, 0, 0, 0],
|
||||
[0, 0, 0, 1, 1, 0, 0, 0],
|
||||
[0, 0, 0, 1, 1, 1, 0, 0],
|
||||
[0, 0, 0, 0, 1, 1, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
])
|
||||
|
||||
# obj1 = np.ones((5, 5))
|
||||
# obj2 = np.ones((5, 5))
|
||||
|
||||
union = np.maximum(obj1, obj2)
|
||||
|
||||
|
||||
# Visualize images
|
||||
|
||||
# Resize image
|
||||
obj1 = np.repeat(obj1, 50, axis=1)
|
||||
obj1 = np.repeat(obj1, 50, axis=0)
|
||||
obj2 = np.repeat(obj2, 50, axis=1)
|
||||
obj2 = np.repeat(obj2, 50, axis=0)
|
||||
union = np.repeat(union, 50, axis=1)
|
||||
union = np.repeat(union, 50, axis=0)
|
||||
|
||||
# Add seperators
|
||||
obj1[0::2, ::50] = 1
|
||||
obj1[1::2, ::50] = 0
|
||||
obj1[::50, 0::2] = 1
|
||||
obj1[::50, 1::2] = 0
|
||||
obj2[0::2, ::50] = 1
|
||||
obj2[1::2, ::50] = 0
|
||||
obj2[::50, 0::2] = 1
|
||||
obj2[::50, 1::2] = 0
|
||||
union[0::2, ::50] = 1
|
||||
union[1::2, ::50] = 0
|
||||
union[::50, 0::2] = 1
|
||||
union[::50, 1::2] = 0
|
||||
|
||||
# Show image
|
||||
obj1 = obj1.astype(np.float64)
|
||||
obj2 = obj2.astype(np.float64)
|
||||
union = union.astype(np.float64)
|
||||
|
||||
cv2.imshow("obj1", obj1)
|
||||
cv2.imshow("obj2", obj2)
|
||||
cv2.imshow("union", union)
|
||||
cv2.imwrite("data/white.png", obj1 * 255)
|
||||
|
||||
cv2.waitKey(0)
|
||||
63
5_Bildanalyse/ü8/l_b.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
obj1 = np.asarray([
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 1, 1, 0, 1, 1, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
])
|
||||
|
||||
obj2 = np.asarray([
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 1, 0, 0, 0],
|
||||
[0, 0, 0, 1, 1, 0, 0, 0],
|
||||
[0, 0, 0, 1, 1, 0, 0, 0],
|
||||
[0, 0, 0, 1, 1, 1, 0, 0],
|
||||
[0, 0, 0, 0, 1, 1, 0, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0],
|
||||
])
|
||||
|
||||
|
||||
intersection = obj1 * obj2
|
||||
|
||||
|
||||
# Visualize images
|
||||
|
||||
# Resize image
|
||||
obj1 = np.repeat(obj1, 50, axis=1)
|
||||
obj1 = np.repeat(obj1, 50, axis=0)
|
||||
obj2 = np.repeat(obj2, 50, axis=1)
|
||||
obj2 = np.repeat(obj2, 50, axis=0)
|
||||
intersection = np.repeat(intersection, 50, axis=1)
|
||||
intersection = np.repeat(intersection, 50, axis=0)
|
||||
|
||||
# Add seperators
|
||||
obj1[0::2, ::50] = 1
|
||||
obj1[1::2, ::50] = 0
|
||||
obj1[::50, 0::2] = 1
|
||||
obj1[::50, 1::2] = 0
|
||||
obj2[0::2, ::50] = 1
|
||||
obj2[1::2, ::50] = 0
|
||||
obj2[::50, 0::2] = 1
|
||||
obj2[::50, 1::2] = 0
|
||||
intersection[0::2, ::50] = 1
|
||||
intersection[1::2, ::50] = 0
|
||||
intersection[::50, 0::2] = 1
|
||||
intersection[::50, 1::2] = 0
|
||||
|
||||
# Show image
|
||||
obj1 = obj1.astype(np.float64)
|
||||
obj2 = obj2.astype(np.float64)
|
||||
intersection = intersection.astype(np.float64)
|
||||
|
||||
cv2.imshow("obj1", obj1)
|
||||
cv2.imshow("obj2", obj2)
|
||||
cv2.imshow("intersection", intersection)
|
||||
|
||||
cv2.waitKey(0)
|
||||
14
5_Bildanalyse/ü9/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Übung 9: Kantendetektion
|
||||
|
||||
Sie möchten die Kanten des Kopfhörers in dem folgenden Bild detektieren:
|
||||

|
||||
|
||||
|
||||
## a) Morphologische Operationen
|
||||
|
||||
Finden Sie die Kanten, indem Sie das Bild zuerst mit einem geeigneten Schwellwert binarisieren. Verwenden Sie
|
||||
dann einer der beiden morphologischen Operationen Dilatation oder Erodieren. Wenden Sie dann auf das
|
||||
Binärbild und morphologisch modifizierte Binärbild eine Subtraktion an und visualisieren Sie die so gefundenen
|
||||
Kanten in dem originalen Bild.
|
||||
|
||||
Die Lösung befindet sich in Datei [l_a.py](l_a.py).
|
||||
31
5_Bildanalyse/ü9/l_a.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
# Load image
|
||||
original_img = cv2.imread("../../data/headphones.jpg")
|
||||
original_img = cv2.resize(original_img, (int(original_img.shape[1]/ 2), int(original_img.shape[0] / 2)))
|
||||
|
||||
img = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)
|
||||
cv2.imshow("original_img", original_img)
|
||||
|
||||
# Binary image
|
||||
threshold = 80
|
||||
binary_mask = np.copy(img)
|
||||
binary_mask[img < threshold] = 1
|
||||
binary_mask[img >= threshold] = 0
|
||||
cv2.imshow("binary_mask", binary_mask.astype(np.float32))
|
||||
|
||||
# Morphing
|
||||
kernel = np.ones((9, 9))
|
||||
eroded_mask = cv2.erode(binary_mask, kernel, iterations=1)
|
||||
cv2.imshow("eroded_mask", eroded_mask.astype(np.float32))
|
||||
|
||||
# Subtraction
|
||||
edges = binary_mask - eroded_mask
|
||||
cv2.imshow("edges", edges.astype(np.float32))
|
||||
|
||||
# Modify original image
|
||||
original_img[:, :, 2] = np.maximum(original_img[:, :, 2], edges * 255)
|
||||
cv2.imshow("modified_image", original_img)
|
||||
|
||||
cv2.waitKey(0)
|
||||