Informatik

Punktfilter

Der einfachste Filter, den es gibt, schaut sich jeden Bildpunkt einzeln an, rechnet eine neue Zahl aus und schreibt sie ins Ergebnis. Was rundherum liegt, interessiert ihn nicht. Solche Filter heißen Punktoperatoren – und überraschend viel von dem, was eine Kamera-App anbietet, gehört dazu.

Ein neues Gitter anlegen

Auf der letzten Seite hast du Gitter nur gelesen. Ein Filter muss aber ein neues Bild herstellen, und dafür braucht er ein leeres Gitter, in das er schreiben kann. Das legst du so an, wie du es vom eindimensionalen Feld kennst, nur mit zwei Größenangaben:

eindimensional zweidimensional
int[] werte = new int[10]; int[][] bild = new int[72][96];
10 Einträge 72 Zeilen mit je 96 Spalten
überall steht 0 überall steht 0

Die Reihenfolge ist dieselbe wie beim Zugriff: zuerst die Zeilen, dann die Spalten. new int[72][96] legt also ein Bild an, das 96 Bildpunkte breit und 72 hoch ist.

Das folgende Programm legt ein solches Gitter an, füllt es mit den zwei geschachtelten Schleifen und zeigt es an. Ein Foto braucht es dafür nicht.

a) Sage vorher, wie das Bild aussieht: Wo ist es hell, wo dunkel? Starte das Programm dann.

b) Ändere die eine Zeile im Schleifenrumpf so, dass der Verlauf von oben nach unten geht: oben schwarz, unten hell.

c) Male ein Schachbrett aus Feldern mit einer Größe von 12 × 12 Bildpunkten, abwechselnd schwarz (0) und weiß (255).

d) Male einen weißen Rahmen, der 4 Bildpunkte breit ist, um ein schwarzes Bild. Lass die Zahlen 72 und 96 dabei nicht im Schleifenrumpf stehen, sondern benutze verlauf.length und verlauf[zeile].length.

Tipp zu b)

In spalte * 2 steckt, dass die Helligkeit nach rechts zunimmt. Nach unten nimmt die Zeile zu. Überlege dir, womit du die Zeile malnehmen musst, damit in der untersten Zeile 71 ein Wert nahe 255 herauskommt.

Tipp zu c)

spalte / 12 sagt dir, im wievielten Feld von links ein Bildpunkt liegt, und zeile / 12, im wievielten Feld von oben. Ein Feld ist weiß, wenn die Summe beider Nummern gerade ist. Ob eine Zahl gerade ist, prüfst du mit % 2 == 0.

Tipp zu d)

Ein Bildpunkt gehört zum Rahmen, wenn er nah genug an einem der vier Ränder liegt. Der obere Rand ist zeile < 4. Der untere Rand ist zeile >= verlauf.length - 4. Die beiden anderen findest du selbst. Verbinde die vier Bedingungen mit ||.

Eine mögliche Lösung. Erfrage das Passwort bei deiner Lehrkraft oder sieh auf der Seite Lösungspasswörter nach.

Dein erster Filter

Jetzt kommt beides zusammen: ein Gitter lesen, wie auf der letzten Seite, und ein neues Gitter schreiben, wie gerade eben. Das ist schon der ganze Bauplan eines Filters.

Starte das Programm zuerst unverändert: Beide Bilder sehen gleich aus. Ergänze dann in der markierten Zeile + pStufen und starte es erneut.

Selbstkontrolle

Die markierte Zeile lautet danach:

int wert = pGrau[zeile][spalte] + pStufen;

Das zweite Bild muss nun deutlich heller aussehen. Falls beide Bilder weiterhin gleich aussehen, prüfe, ob du das Programm nach der Änderung erneut gestartet hast.

Ein Filter liefert ein neues Gitter, er ändert das alte nicht.

int[][] neu = new int[pGrau.length][pGrau[0].length];

Diese Zeile steht am Anfang fast jedes Filters. Sie legt ein Gitter mit so vielen Zeilen und Spalten an, wie das Original hat, und das Gitter ist mit Nullen gefüllt, die gleich überschrieben werden. Nur so kannst du das Original hinterher noch danebenstellen.

Danach folgen immer dieselben drei Schritte für jeden Bildpunkt: den alten Wert aus pGrau[zeile][spalte] lesen, einen neuen ausrechnen und ihn nach neu[zeile][spalte] schreiben.

Heller und dunkler

Dunkler ist dasselbe wie heller, nur mit einer negativen Zahl. Das Programm zeigt beides neben dem Original.

In heller wird nach oben abgeschnitten, nach unten aber nicht. Trotzdem sieht das linke Bild richtig aus.

a) Warum stürzt dunkler(grau, 60) nicht ab, obwohl dabei negative Werte entstehen?

b) Wo genau werden diese negativen Werte abgefangen? Sieh in Leinwand.java nach.

c) Warum ist es trotzdem besser, im Filter selbst zu begrenzen?

Auflösung

a) und b) Die Klasse Leinwand hat eine private Methode begrenzt, die jeden Wert vor dem Zeichnen auf 0 bis 255 zurechtstutzt. Ein Wert von −40 wird dort zu 0.

c) Weil das nur beim Zeichnen hilft. Sobald du zwei Filter hintereinander anwendest, rechnet der zweite mit den unbegrenzten Werten weiter – und −40 ist etwas anderes als 0. Auf der Seite Filter als Klassen steckst du Filter zu Ketten zusammen; spätestens dort fällt das auf.

Aufgabe 1: Invertieren und Schwellenwert

Schreibe zwei weitere Punktfilter.

a) invertiert – aus hell wird dunkel und umgekehrt. Ein Wert von 0 wird zu 255, ein Wert von 200 wird zu 55.

b) schwelle – jeder Bildpunkt wird entweder ganz schwarz oder ganz weiß, je nachdem, ob er über oder unter einer Grenze liegt. Das Ergebnis hat nur noch zwei Farben.

Sage vorher: Welche Grenze braucht schwelle, damit von dem Foto ungefähr die Umrisse der LED übrig bleiben? Du hast auf der vorigen Seite ausgerechnet, dass die mittlere Helligkeit bei 115 liegt.

Tipp 1: Der Bauplan

Beide Filter haben denselben Rumpf wie heller. Es ändert sich nur die eine Zeile, in der gerechnet wird:

int[][] neu = new int[pGrau.length][pGrau[0].length];
for (int zeile = 0; zeile < pGrau.length; zeile++) {
    for (int spalte = 0; spalte < pGrau[zeile].length; spalte++) {
        neu[zeile][spalte] = /* hier */;
    }
}
return neu;
Tipp 2: Die beiden Vorschriften

Invertieren: Du suchst eine Rechnung, die 0 auf 255 schickt und 255 auf 0. Probiere sie mit beiden Werten durch, bevor du sie hinschreibst.

Schwellenwert: Hier wird nicht gerechnet, sondern entschieden. Ein if mit einem else genügt.

Eine mögliche Lösung. Erfrage das Passwort bei deiner Lehrkraft oder sieh auf der Seite Lösungspasswörter nach.

Aufgabe 2: Kontrast

Heller machen verschiebt alle Werte. Kontrast dagegen zieht sie auseinander: Was über der Mitte liegt, wird heller, was darunter liegt, wird dunkler.

Die Vorschrift lautet

neu = 128 + (alt - 128) * faktor

a) Rechne von Hand nach, was aus den Werten 40, 128 und 220 bei faktor = 2 wird. Welche davon musst du begrenzen?

b) Schreibe die Methode kontrast(int[][] pGrau, int pFaktor) und probiere die Faktoren 2, 3 und 4 aus.

c) Was passiert bei faktor = 0? Sage es vorher, dann probiere es.

Tipp: Begrenzen an zwei Enden

Anders als bei heller läuft der Wert hier nach beiden Seiten aus dem Bereich heraus. Zwei if-Abfragen nacheinander sind völlig in Ordnung:

if (wert < 0) { wert = 0; }
if (wert > 255) { wert = 255; }
Eine mögliche Lösung. Erfrage das Passwort bei deiner Lehrkraft oder sieh auf der Seite Lösungspasswörter nach.

Zum Weiterdenken

schwelle ist der Filter, mit dem Scanner-Apps arbeiten, bevor sie Text erkennen. Eine feste Grenze funktioniert dabei schlecht: Auf einem Foto mit Schatten ist dieselbe Grenze in der einen Bildhälfte zu hoch und in der anderen zu niedrig.

Wie könnte ein Schwellenwert aussehen, der sich an die Umgebung anpasst? Skizziere die Idee in Worten – programmieren kannst du sie, sobald du die Seite Weichzeichnen durchhast.

Punktfilter

Teilbare URL erstellen

Abschnitte auswählen