# Filter

Quelle: https://dev.inf.zone/exercises/06/filter/

![Harvard Hof in Graustufen](https://dev.inf.zone/exercises/06/filter/./yard-grayscale.bmp)

## Aufgabe

**Auf einen Blick**

- **Was:** Sie vervollständigen das Programm `filter`, das vier Bildfilter auf BMP-Bilder anwendet: Graustufen, Sepia, Spiegeln und Weichzeichnen.
- **Datei:** `helpers.c` im Ordner `filter`. Alle anderen Dateien und die Funktionssignaturen bleiben unverändert.
- **Aufruf:** `./filter -g images/yard.bmp out.bmp` – der Buchstabe wählt den Filter: `-g` Graustufen, `-s` Sepia, `-r` Spiegeln, `-b` Weichzeichnen.
- **Ausgabe:** das gefilterte Bild als neue Datei (hier `out.bmp`).
- **Ihre Arbeit:** die vier Funktionen `grayscale`, `sepia`, `reflect` und `blur`. Einlesen und Schreiben der Bilddatei erledigt `filter.c` bereits.

### 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`.

1. **Pixel:** `image[i][j]` ist der Pixel in Zeile `i` und Spalte `j`; `image[0][0]` ist die Ecke oben links. Jeder Pixel ist ein `RGBTRIPLE` mit drei Farbwerten `rgbtRed`, `rgbtGreen` und `rgbtBlue`, jeweils eine ganze Zahl von 0 bis 255.
2. **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.
3. **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.
4. **Sepia (`sepia`):** Die neuen Werte berechnen sich aus den ursprünglichen Werten \(R\), \(G\), \(B\) des Pixels:
   \[
   \begin{aligned}
   \text{Rot} &= 0.393 \cdot R + 0.769 \cdot G + 0.189 \cdot B \\
   \text{Grün} &= 0.349 \cdot R + 0.686 \cdot G + 0.168 \cdot B \\
   \text{Blau} &= 0.272 \cdot R + 0.534 \cdot G + 0.131 \cdot B
   \end{aligned}
   \]
   Jedes Ergebnis wird auf die nächste ganze Zahl gerundet; liegt es über 255, wird es auf 255 gesetzt.
5. **Spiegeln (`reflect`):** In jeder Zeile tauschen die Pixel links und rechts die Plätze: Spalte `0` mit Spalte `width - 1`, Spalte `1` mit Spalte `width - 2` und so weiter. Bei ungerader Breite bleibt der mittlere Pixel stehen.
6. **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.
7. **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.
8. **Originalwerte beim Weichzeichnen:** Gemittelt wird immer über die ursprünglichen Farbwerte, nicht über Werte, die Ihr Programm schon weichgezeichnet hat.

![Raster aus 16 nummerierten Pixeln, 3×3-Kasten um Pixel 6](https://dev.inf.zone/exercises/06/filter/./grid_pixel_6_3x3_grid.png)

**Beispiel Graustufen und Sepia** für einen Pixel mit Rot 220, Grün 190, Blau 140:

| Filter           | Rechnung                                                         | Ergebnis              |
| ---------------- | ---------------------------------------------------------------- | --------------------- |
| Graustufen       | \((220 + 190 + 140) / 3 = 183.33\)                                 | alle drei Werte 183   |
| Sepia, Rot       | \(0.393 \cdot 220 + 0.769 \cdot 190 + 0.189 \cdot 140 = 259.03\) | 259, gedeckelt: 255   |
| Sepia, Grün      | \(0.349 \cdot 220 + 0.686 \cdot 190 + 0.168 \cdot 140 = 230.64\) | 231                   |
| Sepia, Blau      | \(0.272 \cdot 220 + 0.534 \cdot 190 + 0.131 \cdot 140 = 179.64\) | 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: \((100 + 120 + 90 + 80 + 110 + 105) / 6 = 605 / 6 = 100.83\), gerundet 101. Grün und Blau rechnen Sie genauso.

Den Umgang mit `image` üben [Warm-up 3](https://dev.inf.zone/exercises/06/warm-ups/#warm-up-3) (Pixel lesen und ändern) und [Warm-up 4](https://dev.inf.zone/exercises/06/warm-ups/#warm-up-4) (Nachbarpixel ohne Griff über den Bildrand). Wie eine BMP-Datei aufgebaut ist, steht am Ende der Seite unter „Zum Weiterlesen“.

## Demo

[Terminal-Aufzeichnung ansehen](https://asciinema.org/a/QnLel70SPmbW9nswXTb9Yu9ZD)

## Aufgabenmaterial

Für diese Aufgabe werden Sie ein von uns zur Verfügung gestelltes Codegerüst vervollständigen.

**Aufgabenmaterial herunterladen**

Öffnen Sie VS Code entsprechend Ihrem [Setup](https://dev.inf.zone/extras/setup/).

Öffnen Sie Ihr Terminalfenster und führen Sie dann `cd` aus. Die Eingabeaufforderung Ihres Terminalfensters sollte wie folgt aussehen:

```bash
$
```

Geben Sie dann

```bash
wget https://dev.inf.zone/download/exercises/06/filter.zip
```

ein 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 `wget` und der folgenden URL nicht übersehen, und auch kein anderes Zeichen!

Führen Sie jetzt

```bash
unzip filter.zip
```

aus, um das ZIP-Archiv in einen Ordner namens `filter` zu extrahieren. Sie brauchen die ZIP-Datei nicht mehr, also können Sie

```bash
rm filter.zip
```

ausführen.
Antworten Sie mit "y", gefolgt von der Eingabetaste, um die heruntergeladene ZIP-Datei zu entfernen.

Führen Sie dann

```bash
cd filter
```

aus, um in dieses Verzeichnis zu wechseln. Ihre Eingabeaufforderung sollte nun wie folgt aussehen:

```bash
filter/ $
```

Wenn alles wie beschrieben funktioniert hat, sollten Sie nach dem Ausführen von

```bash
ls
```

mehrere 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:

```bash
make filter
./filter -g images/yard.bmp out.bmp
```

Da `grayscale` noch nichts tut, sieht `out.bmp` genauso aus wie `images/yard.bmp`.

## Spezifikation

Implementieren Sie die vier Funktionen in `helpers.c`:

-   `grayscale` wandelt das Bild in Graustufen um (Regel 3).
-   `sepia` wandelt das Bild in Sepia um (Regel 4). Jeder Farbwert ist danach eine ganze Zahl von 0 bis 255.
-   `reflect` spiegelt das Bild horizontal, also links ↔ rechts (Regel 5).
-   `blur` zeichnet 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:

```c
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:

```c
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](https://manual.cs50.io/3/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:

```c
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:

```c
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:

```c
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](https://dev.inf.zone/exercises/06/warm-ups/#warm-up-4).

## Testen

Kompilieren Sie `filter` mit:

```bash
make filter
```

Anschließend starten Sie das Programm zum Beispiel so:

```bash
./filter -g images/yard.bmp out.bmp
```

Das 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:

```bash
check50 -l inf-zone/exercises/2026/filter
```

### Style

Führen Sie den folgenden Befehl aus, um den Stil Ihres Codes mit `style50` zu analysieren:

```bash
style50 helpers.c
```

## Abgeben

Geben Sie im Ordner `filter` ab:

```bash
inf upload filter
```

Danach 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`](https://dev.inf.zone/faq/uebung-solutions/#inf-installieren).

## 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ß:

![eine einfache Bitmap](https://dev.inf.zone/exercises/06/filter/./bitmap.png)

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](https://en.wikipedia.org/wiki/BMP_file_format), [JPEG](https://en.wikipedia.org/wiki/JPEG) oder [PNG](https://en.wikipedia.org/wiki/Portable_Network_Graphics) 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ün `0x00`, Blau `0x00` ergibt 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): `BITMAPFILEHEADER` mit 14 Byte und `BITMAPINFOHEADER` mit 40 Byte. `filter.c` prü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 `RGBTRIPLE` zuerst `rgbtBlue`.
-   **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ß:

![rotes Smiley](https://dev.inf.zone/exercises/06/filter/./red_smile.png)

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.
