Filter

Aufgabe
So funktionieren die vier Filter
filter.c liest das Bild in ein zweidimensionales Array image mit height Zeilen und width Spalten und übergibt es Ihren Funktionen. Jede Funktion ändert die Pixel direkt in image.
- Pixel:
image[i][j]ist der Pixel in Zeileiund Spaltej;image[0][0]ist die Ecke oben links. Jeder Pixel ist einRGBTRIPLEmit drei FarbwertenrgbtRed,rgbtGreenundrgbtBlue, jeweils eine ganze Zahl von 0 bis 255. - Farben: Alle drei Werte 0 ergeben Schwarz, alle drei 255 Weiß. Sind alle drei Werte gleich, ist der Pixel grau – je höher der Wert, desto heller.
- Graustufen (
grayscale): Alle drei Farbwerte werden durch den Mittelwert der ursprünglichen drei Werte ersetzt, gerundet auf die nächste ganze Zahl. So bleibt jeder Pixel etwa so hell wie vorher. - Sepia (
sepia): Die neuen Werte berechnen sich aus den ursprünglichen Werten , , des Pixels: Jedes Ergebnis wird auf die nächste ganze Zahl gerundet; liegt es über 255, wird es auf 255 gesetzt. - Spiegeln (
reflect): In jeder Zeile tauschen die Pixel links und rechts die Plätze: Spalte0mit Spaltewidth - 1, Spalte1mit Spaltewidth - 2und so weiter. Bei ungerader Breite bleibt der mittlere Pixel stehen. - Weichzeichnen (
blur): Jeder Farbwert wird durch den Mittelwert desselben Farbwerts aller Pixel im 3×3-Kasten um den Pixel ersetzt, der Pixel selbst eingeschlossen, gerundet auf die nächste ganze Zahl. Für Pixel 6 im Raster unten sind das die Pixel 1, 2, 3, 5, 6, 7, 9, 10 und 11. - Rand beim Weichzeichnen: Am Rand und in den Ecken zählen nur die Pixel des Kastens, die im Bild liegen: am Rand 6, in einer Ecke 4. Für Pixel 15 sind das 10, 11, 12, 14, 15 und 16.
- Originalwerte beim Weichzeichnen: Gemittelt wird immer über die ursprünglichen Farbwerte, nicht über Werte, die Ihr Programm schon weichgezeichnet hat.
![]()
Beispiel Graustufen und Sepia für einen Pixel mit Rot 220, Grün 190, Blau 140:
| Filter | Rechnung | Ergebnis |
|---|---|---|
| Graustufen | alle drei Werte 183 | |
| Sepia, Rot | 259, gedeckelt: 255 | |
| Sepia, Grün | 231 | |
| Sepia, Blau | 180 |
Beispiel Weichzeichnen für Pixel 2 am oberen Rand. Sein Kasten umfasst nur die Pixel 1, 2, 3, 5, 6 und 7. Deren Rotwerte:
| Zeile | Spalte 0 | Spalte 1 | Spalte 2 |
|---|---|---|---|
| 0 | Pixel 1: 100 | Pixel 2: 120 | Pixel 3: 90 |
| 1 | Pixel 5: 80 | Pixel 6: 110 | Pixel 7: 105 |
Neuer Rotwert von Pixel 2: , gerundet 101. Grün und Blau rechnen Sie genauso.
Den Umgang mit image üben Warm-up 3 (Pixel lesen und ändern) und Warm-up 4 (Nachbarpixel ohne Griff über den Bildrand). Wie eine BMP-Datei aufgebaut ist, steht am Ende der Seite unter „Zum Weiterlesen“.
Demo
Aufgabenmaterial
Für diese Aufgabe werden Sie ein von uns zur Verfügung gestelltes Codegerüst vervollständigen.
Aufgabenmaterial herunterladen
Öffnen Sie VS CodeVisual Studio Code, der kostenlose Code-Editor von Microsoft, in dem Sie im Kurs programmieren. Glossar → entsprechend Ihrem Setup.
Öffnen Sie Ihr TerminalfensterFenster, in dem Sie dem Computer Befehle als Text eintippen statt zu klicken. In VS Code liegt es im unteren Bereich des Fensters. Glossar → und führen Sie dann cdWechselt das Verzeichnis: cd me geht in den Ordner me, cd .. eine Ebene nach oben, cd allein ins Homeverzeichnis. Glossar → aus. Die EingabeaufforderungDas Zeichen am Anfang der Zeile im Terminal, etwa $ oder me/ $. Es zeigt, dass das Terminal auf Ihren nächsten Befehl wartet. Steht davor ein Ordnername wie me/, befinden Sie sich gerade in diesem Ordner. Glossar → Ihres Terminalfensters sollte wie folgt aussehen:
$Geben Sie dann
wget https://dev.inf.zone/download/exercises/06/filter.zipein und führen Sie den Befehl mit der Eingabetaste aus, um eine ZIP-Datei namens filter.zip in den aktuellen Ordner herunterzuladen. Achten Sie darauf, dass Sie das Leerzeichen zwischen wgetLädt eine Datei aus dem Internet in das aktuelle Verzeichnis herunter, im Kurs zum Beispiel das Aufgabenmaterial einer Übung. Glossar → und der folgenden URL nicht übersehen, und auch kein anderes Zeichen!
Führen Sie jetzt
unzip filter.zipaus, um das ZIP-Archiv in einen Ordner namens filter zu extrahieren. Sie brauchen die ZIP-Datei nicht mehr, also können Sie
rm filter.zipausführen. Antworten Sie mit “y”, gefolgt von der Eingabetaste, um die heruntergeladene ZIP-Datei zu entfernen.
Führen Sie dann
cd filteraus, um in dieses Verzeichnis zu wechseln. Ihre Eingabeaufforderung sollte nun wie folgt aussehen:
filter/ $Wenn alles wie beschrieben funktioniert hat, sollten Sie nach dem Ausführen von
lsmehrere Dateien sehen: bmp.h, filter.c, helpers.h, helpers.c, und Makefile. Sie sollten auch einen Ordner images/ mit vier BMP-Dateien sehen. Wenn diese Dateien nicht angezeigt werden, führen Sie die gleichen Schritte noch einmal durch und schauen Sie, ob Sie herausfinden können, wo Sie einen Fehler gemacht haben!
Überblick über das Codegerüst
Sie müssen filter.c nicht vollständig verstehen. Wichtig ist, was Ihre Funktionen übergeben bekommen.
| Datei | Inhalt |
|---|---|
bmp.h | Das struct RGBTRIPLE mit den Feldern rgbtBlue, rgbtGreen, rgbtRed (in dieser Reihenfolge) sowie die beiden Header-structs der BMP-Datei. Der Typ BYTE ist nur ein anderer Name für uint8_t, also ein Byte (0 bis 255). |
filter.c | Fertig. Liest den Filter-Buchstaben (die erlaubten stehen in Zeile 10: b, g, r, s), öffnet die Bilddatei, prüft, ob es eine 24-Bit-BMP ist, und liest alle Pixel in das 2D-Array image. Die switch-Anweisung ab Zeile 101 ruft je nach Buchstabe blur, grayscale, reflect oder sepia auf. Danach schreibt das Programm image in die Ausgabedatei. |
helpers.h | Die Prototypen der vier Funktionen. Jede bekommt height, width und RGBTRIPLE image[height][width]: ein Array aus height Zeilen, jede Zeile ein Array aus width Pixeln. |
helpers.c | Die vier Funktionen, noch leer. Hier schreiben Sie Ihren Code. |
Makefile | Sagt make, wie aus den beiden Dateien filter.c und helpers.c ein Programm wird. Deshalb kompilieren Sie mit make filter. |
Kompilieren und starten Sie das Gerüst einmal:
make filter
./filter -g images/yard.bmp out.bmpDa grayscale noch nichts tut, sieht out.bmp genauso aus wie images/yard.bmp.
Spezifikation
Implementieren Sie die vier Funktionen in helpers.c:
grayscalewandelt das Bild in Graustufen um (Regel 3).sepiawandelt das Bild in Sepia um (Regel 4). Jeder Farbwert ist danach eine ganze Zahl von 0 bis 255.reflectspiegelt das Bild horizontal, also links ↔ rechts (Regel 5).blurzeichnet das Bild mit dem 3×3-Kasten weich, auch am Rand und in den Ecken (Regeln 6 bis 8).- Die Filter sind voneinander unabhängig. Am einfachsten beginnen Sie in dieser Reihenfolge.
Ändern Sie keine der Funktionssignaturen und keine andere Datei als helpers.c. Wenn Ihr Vorgehen nur mit einer Änderung an anderer Stelle funktioniert, überdenken Sie es.
Hilfestellung
Klicken Sie auf die folgenden Tipps, um einige Ratschläge zu erhalten. Versuchen Sie aber zunächst, selbst so weit wie möglich zu kommen.
Implementierung von grayscale
Wandeln Sie das Vorgehen zuerst in Pseudocode um:
void grayscale(int height, int width, RGBTRIPLE image[height][width])
{
// Loop over all pixels
// Take average of red, green, and blue
// Update pixel values
}Um über ein zweidimensionales Array zu iterieren, brauchen Sie zwei verschachtelte Schleifen:
void grayscale(int height, int width, RGBTRIPLE image[height][width])
{
// Loop over all pixels
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// Take average of red, green, and blue
// Update pixel values
}
}
}Nun erreichen Sie mit image[i][j] jeden einzelnen Pixel. Auf die Farbwerte greifen Sie wie bei jedem struct mit dem Punkt zu: image[i][j].rgbtRed, image[i][j].rgbtGreen, image[i][j].rgbtBlue.
Die drei Farbwerte sind ganze Zahlen. Runden Sie den Mittelwert mit round auf die nächste ganze Zahl, bevor Sie ihn zuweisen. Und warum sollten Sie die Summe durch 3.0 und nicht durch 3 teilen?
Implementierung von sepia
Auch hier laufen Sie mit zwei verschachtelten Schleifen über alle Pixel:
void sepia(int height, int width, RGBTRIPLE image[height][width])
{
// Loop over all pixels
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// Compute sepia values
// Update pixel with sepia values
}
}
}Die Formel aus Regel 4 hat zwei Haken. Sie müssen
- das Ergebnis jeder Berechnung auf die nächste ganze Zahl runden und
- sicherstellen, dass der Wert nicht größer als 255 ist.
Wie könnte eine Funktion helfen, die von zwei ganzen Zahlen die kleinere zurückgibt? Sie können so eine Hilfsfunktion selbst schreiben, müssen aber nicht.
Achten Sie außerdem darauf, dass alle drei Formeln die ursprünglichen Werte des Pixels brauchen. Was passiert, wenn Sie rgbtRed schon überschrieben haben, bevor Sie den neuen Grünwert berechnen?
Implementierung von reflect
In reflect vertauschen Sie die Werte von Pixeln auf gegenüberliegenden Seiten einer Zeile. Als Pseudocode:
void reflect(int height, int width, RGBTRIPLE image[height][width])
{
// Loop over all pixels
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
// Swap pixels
}
}
}Erinnern Sie sich daran, wie wir in der Vorlesung zwei Werte mit einer temporären Variablen vertauscht haben. Eine eigene Funktion für das Vertauschen brauchen Sie nicht, dürfen Sie aber schreiben.
Denken Sie noch einmal über die innere Schleife nach: Muss sie wirklich über alle Pixel einer Zeile laufen? Was passiert, wenn jedes Paar zweimal vertauscht wird?
Implementierung von blur
Die Unschärfe eines Pixels hängt von seinen Nachbarn ab. Wenn Sie image direkt überschreiben, rechnen Sie für die folgenden Pixel schon mit weichgezeichneten Werten (Regel 8). Legen Sie deshalb zuerst eine Kopie an, etwa mit RGBTRIPLE copy[height][width];, und kopieren Sie Pixel für Pixel hinein:
void blur(int height, int width, RGBTRIPLE image[height][width])
{
// Create a copy of image
RGBTRIPLE copy[height][width];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
copy[i][j] = image[i][j];
}
}
}Jetzt lesen Sie die Farbwerte aus copy, rechnen und schreiben die Ergebnisse nach image.
Für den Kasten brauchen Sie zwei weitere Schleifen über Zeile i - 1 bis i + 1 und Spalte j - 1 bis j + 1. Zählen Sie nur Nachbarn, die im Bild liegen, und merken Sie sich, wie viele es waren – durch diese Zahl teilen Sie. Genau das übt Warm-up 4.
Testen
Kompilieren Sie filter mit:
make filterAnschließend starten Sie das Programm zum Beispiel so:
./filter -g images/yard.bmp out.bmpDas erzeugt aus images/yard.bmp ein Graustufenbild out.bmp. Testen Sie alle vier Filter an allen mitgelieferten Bildern; statt -g verwenden Sie -s, -r oder -b.
Korrektheit
Führen Sie in Ihrem Terminal den folgenden Befehl aus, um die Korrektheit Ihrer Arbeit zu überprüfen:
check50 -l inf-zone/exercises/2026/filterStyle
Führen Sie den folgenden Befehl aus, um den Stil Ihres Codes mit style50Prüfprogramm von CS50, das zeigt, wo Ihr Code von den Formatierungsregeln abweicht. In Grün zeigt es, was Sie ergänzen sollten, in Rot, was wegfallen soll. Glossar → zu analysieren:
style50 helpers.cAbgeben
Geben Sie im Ordner filter ab:
inf upload filterDanach sehen Sie, welche Tests Ihr Programm besteht und ob die Übung für die Bonuspunkte zählt. Wie Sie inf installieren und sich anmelden, steht unter Abgeben mit inf.
Zum Weiterlesen: Wie eine BMP-Datei aufgebaut ist
Für die Lösung der Aufgabe nicht nötig; das Einlesen und Schreiben erledigt filter.c.
Die einfachste Art, ein Bild darzustellen, ist ein Raster aus Pixeln (Punkten), von denen jeder eine eigene Farbe haben kann. Für ein Schwarz-Weiß-Bild genügt 1 Bit pro Pixel, zum Beispiel 0 für Schwarz und 1 für Weiß:

Ein Bild ist in diesem Sinne ein Raster aus Bits, eine Bitmap. Für Farben braucht man mehr Bits pro Pixel:
- Farbtiefe: Formate wie BMP, JPEG oder PNG können 24 Bit pro Pixel speichern. BMP kennt 1, 4, 8, 16, 24 und 32 Bit pro Pixel.
- RGB: Bei 24 Bit stehen je 8 Bit für den Rot-, Grün- und Blauanteil. Rot
0xff(dezimal 255), Grün0x00, Blau0x00ergibt reines Rot. - Header: Am Anfang der Datei stehen Metadaten wie Breite und Höhe, in zwei Datenstrukturen („Header“, nicht zu verwechseln mit Header-Dateien in C):
BITMAPFILEHEADERmit 14 Byte undBITMAPINFOHEADERmit 40 Byte.filter.cprüft unter anderem, ob die Pixeldaten genau nach diesen 54 Byte beginnen. - BGR: Danach folgen die Pixel, je drei Byte – aber in der Reihenfolge Blau, Grün, Rot. Deshalb steht in
RGBTRIPLEzuerstrgbtBlue. - Zeilenreihenfolge: Viele BMP-Dateien speichern die unterste Bildzeile zuerst. Die Bilder dieser Aufgabe sind umgekehrt gespeichert, oberste Zeile zuerst; im Header steht dafür eine negative Höhe.
Wandelt man den Smiley oben in 24 Bit um und ersetzt Schwarz durch Rot, speichert eine BMP-Datei ihn so; 0000ff ist Rot (in der Reihenfolge Blau, Grün, Rot), ffffff Weiß:

Mit etwas Abstand erkennen Sie den Smiley in den Hexziffern. Eine Hexziffer steht für 4 Bit; ffffff sind also 6 · 4 = 24 Bit, binär 111111111111111111111111.
Warum structs wie RGBTRIPLE nützlich sind: Eine Datei ist nur eine Folge von Bytes, und ein Dateiformat legt fest, welche Bytes was bedeuten. Man könnte die Datei als ein großes Byte-Array einlesen und sich merken, dass array[i] das eine und array[j] das andere bedeutet. Ein struct gibt diesen Bytes Namen: Statt einer langen Folge von Bytes liest filter.c eine Folge von structs.
Diese Seite als Markdown: ansehen herunterladen