from skimage import io
import matplotlib.pyplot as plt
import numpy as np
Lineare Nachbarschaftsfilter
Aufgabe 2
Faltet ein Bild mit einer Filtermatrix ohne spezielle Randbehandlung
def simple_filter(in_image, filter_matrix, offset=1):
img_h, img_w = in_image.shape
filter_size = filter_matrix.shape[0]
out_h = (img_h - filter_size) // offset + 1
out_w = (img_w - filter_size) // offset + 1
out_image = np.zeros((out_h, out_w), dtype=int)
for i in range(out_h):
for j in range(out_w):
sum_value = 0.0
for u in range(filter_size):
for v in range(filter_size):
xi = i * offset + u
yj = j * offset + v
if 0 <= xi < img_h and 0 <= yj < img_w:
sum_value += in_image[xi, yj] * filter_matrix[u, v]
out_image[i, j] = np.clip(int(sum_value), 0, 255)
return out_image
def get_pixel(in_image, x, y, edge='min'):
img_h, img_w = in_image.shape
if 0 <= x < img_h and 0 <= y < img_w:
return in_image[x, y]
else:
if edge == 'min':
return 0
elif edge == 'max':
return 255
elif edge == 'continue':
x = min(max(x, 0), img_h - 1)
y = min(max(y, 0), img_w - 1)
return in_image[x, y]
else:
raise ValueError(f"Unbekannte Randbehandlung: {edge}")
Aufgabe 3
Faltet ein Bild mit einer Filtermatrix und behandelt die RĂ€nder entsprechend âminâ, âmaxâ oder âcontinueâ
def advanced_filter(in_image, filter_matrix, offset=1, edge='continue'):
img_h, img_w = in_image.shape
filter_size = filter_matrix.shape[0]
k = filter_size // 2 # half filter size
out_h = (img_h - 1) // offset + 1
out_w = (img_w - 1) // offset + 1
out_image = np.zeros((out_h, out_w), dtype=int)
for i in range(out_h):
for j in range(out_w):
sum_value = 0.0
center_x = i * offset
center_y = j * offset
for u in range(filter_size):
for v in range(filter_size):
xi = center_x + (u - k)
yj = center_y + (v - k)
pixel = get_pixel(in_image, xi, yj, edge)
sum_value += pixel * filter_matrix[u, v]
out_image[i, j] = np.clip(int(sum_value), 0, 255)
return out_image
def plot_images(images, titles=None, rows=1):
cols = int(np.ceil(len(images) / rows)) # Calculate the number of columns
fig, axes = plt.subplots(rows, cols, figsize=(15, 10))
axes = np.array(axes).reshape(-1) # Flatten axes for easy iteration
for i, img in enumerate(images):
axes[i].imshow(img, cmap='gray')
if titles:
axes[i].set_title(titles[i])
axes[i].axis('off')
# Hide any unused subplots
for j in range(len(images), len(axes)):
axes[j].axis('off')
plt.tight_layout()
plt.show()
Testbilder
img = np.random.randint(0, 256, (10, 10), dtype=int)
filter_matrix = np.array([
[1, 1, 1],
[1, 3, 1],
[1, 1, 1]
], dtype=float) / 11
out_simple = simple_filter(img, filter_matrix, offset=1)
plot_images([img, out_simple], titles=['Original', 'Einfacher Filter'])
img = np.full((10, 10), 255, dtype=int)
img[3:7, 3:7] = 0
filter_matrix = np.array([
[1, 1, 1],
[1, 3, 1],
[1, 1, 1]
], dtype=float) / 11
out_min = advanced_filter(img, filter_matrix, offset=1, edge='min')
out_max = advanced_filter(img, filter_matrix, offset=1, edge='max')
out_continue = advanced_filter(img, filter_matrix, offset=1, edge='continue')
plot_images([img, out_min, out_max, out_continue],
titles=['Original', 'min', 'max', 'continue'])
img = io.imread('lena.jpg', as_gray=True)
img = (img * 255).astype(int)
filters = {
'sharp' : np.array([
[0, -1, 0],
[-1, 5, -1],
[0, -1, 0]
], dtype=float),
'box' : np.array([
[1, 1, 1],
[1, 1, 1],
[1, 1, 1]
], dtype=float) / 9,
'gaussian' : np.array([
[1, 2, 1],
[2, 4, 2],
[1, 2, 1]
], dtype=float) / 16,
'laplace' : np.array([
[0, -1, 0],
[-1, 4, -1],
[0, -1, 0]
], dtype=float),
'sobel' : np.array([
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]
], dtype=float)
}
out_images = [img]
titles = ['Original']
for name, filter_matrix in filters.items():
out_image = advanced_filter(img, filter_matrix, offset=1, edge='continue')
out_images.append(out_image)
titles.append(name)
plot_images(out_images, titles=titles, rows=2)
a) Nennen Sie die Arten und Eigenschaften von linearen Filtern
-
Arten von linearen Filtern:
- Gaussian-Filter
- Sobel-Filter
- Laplace-Filter
-
Eigenschaften von linearen Filtern:
- LinearitÀt: Die Ausgabe ist eine lineare Kombination der Eingabewerte.
- Zeitinvarianz: Die Filtereigenschaften Àndern sich nicht mit der Zeit.
- StabilitĂ€t: Kleine Ănderungen in den Eingabewerten fĂŒhren zu kleinen Ănderungen in den Ausgabewerten.
- fĂŒr eine Konstante k
- KausalitĂ€t: Der Filter reagiert nur auf aktuelle und vergangene Eingabewerte, nicht auf zukĂŒnftige Werte.
- LinearitÀt: Die Ausgabe ist eine lineare Kombination der Eingabewerte.
b) Was ist der Unterschied zwischen linearen und nichtlinearen Filtern?
Kriterium | Lineare Filter | Nichtlineare Filter |
---|---|---|
Mathematische Eigenschaft | Linear: Addition und Skalierung bleibt erhalten | Nichtlinear: Addition und Skalierung funktionieren nicht so |
Beispiele | Mittelwertfilter, Sobel-Filter | Medianfilter, Min/Max-Filter |
Typisches Verhalten | Verwischen von Rauschen, KantenglĂ€ttung | Entfernung von AusreiĂern (z.B. Salt & Pepper Noise) |
Operation | Faltung (Summe von gewichteten Pixeln) | Sortieren, Maximum, Median, andere nichtlineare Berechnungen |
Vorteil | Einfach zu berechnen, schnelle Filterung | Besser bei impulsartigem Rauschen (z.B. Störpixel) |
Nachteil | Kann Kanten verwischen | Aufwendigere Berechnung, komplexere Mathematik |
Nichtlineare Nachbarschaftsfilter
Holt den Pixelwert mit Randbehandlung (hier: 0 auĂerhalb)
def get_pixel_median(in_image, x, y):
img_h, img_w = in_image.shape
if 0 <= x < img_h and 0 <= y < img_w:
return in_image[x, y]
else:
return 0 # Zero padding (min)
get_pixel_median(img, 0, 0)
np.int64(154)
Sammelt alle Pixel in der Umgebung (Neighborhood) um (center_x, center_y)
def get_neighborhood(in_image, center_x, center_y, filtersize):
k = filtersize // 2
neighborhood = []
for u in range(filtersize):
for v in range(filtersize):
xi = center_x + (u - k)
yj = center_y + (v - k)
pixel = get_pixel_median(in_image, xi, yj)
neighborhood.append(pixel)
return neighborhood
get_neighborhood(img, 5, 5, 3) # Test neighborhood function
[np.int64(153),
np.int64(153),
np.int64(153),
np.int64(152),
np.int64(152),
np.int64(152),
np.int64(151),
np.int64(150),
np.int64(150)]
Berechnet den Median eines gegebenen Neighborhoods (Pixelwerte)
def compute_median(neighborhood):
neighborhood.sort()
median_value = neighborhood[len(neighborhood) // 2]
return median_value
compute_median([1, 6, 3, 9, 4]) # Test median function
Hauptfunktion: Wendet Medianfilter mit gegebenem filtersize und offset an
def medianFilter(in_image, filtersize, offset=1):
img_h, img_w = in_image.shape
out_h = (img_h - 1) // offset + 1
out_w = (img_w - 1) // offset + 1
out_image = np.zeros((out_h, out_w), dtype=int)
for i in range(out_h):
for j in range(out_w):
center_x = i * offset
center_y = j * offset
neighborhood = get_neighborhood(in_image, center_x, center_y, filtersize)
median_value = compute_median(neighborhood)
out_image[i, j] = median_value
return out_image
img = np.random.randint(0, 256, (10, 10), dtype=int)
filtered_img = medianFilter(img, filtersize=3, offset=1)
plot_images([img, filtered_img], titles=['Original', 'Median-Filter'])
image_files = ['tree.png', 'pepper.jpg']
for image_file in image_files:
img = io.imread(image_file)
filter_sizes = range(2, 5)
filtered_images = [medianFilter(img, filtersize=fsize, offset=1) for fsize in filter_sizes]
plot_images([img] + filtered_images,
titles=[f'{image_file} - Original'] + [f'Filter size: {fsize}' for fsize in filter_sizes])
a) Vergleichen Sie die Ergebnisse der verschiedenen Filter miteinander und begrĂŒnden Sie diese.
-
Lineare Filter:
- GlÀtten das Bild, indem sie Pixelwerte mitteln.
- Reduzieren gleichmĂ€Ăiges Rauschen.
- Problem: Kanten werden dabei oft unscharf oder verschwimmen, weil auch starke Pixelunterschiede gemittelt werden.
-
Nichtlineare Filter:
- Ersetzen einen Pixel durch den Medianwert seiner Nachbarn.
- Sehr gut bei impulsartigem Rauschen (z.B. Salt & Pepper Noise).
- Vorteil: Kanten bleiben besser erhalten, weil der Median extreme AusreiĂer ignoriert.
b) Warum ist es beim Medianfilter sinnvoll fĂŒr die Sortierung Heap Sort zu verwenden?
Heap Sort ist effizient, da es auch bei groĂen Nachbarschaften eine gute Laufzeit hat und wenig Speicherplatz benötigt.
Weil beim Medianfilter nicht die vollstÀndige Sortierung aller Werte nötig ist, eignet sich Heap Sort besonders gut.
c) Untersuchen Sie, welche Effekte bei mehrmaligem Anwenden eines Filters auf das jeweilige Ergebnisbild auftreten.
Bei mehrmaliger Anwendung eines Filters wird das Bild zunehmend geglÀttet.
Details und feine Strukturen verschwinden nach und nach, bei Medianfiltern bleiben Kanten zunÀchst erhalten, werden aber mit der Zeit breiter und weicher.
d) Welche Effekte treten bei grossen und bei kleinen Filtermasken auf?
Kleine Filtermasken erhalten Details besser und glÀtten nur leichtes Rauschen.
GroĂe Masken sorgen fĂŒr starke GlĂ€ttung und entfernen auch starkes Rauschen, fĂŒhren aber dazu, dass Kanten und Details verloren gehen.