Bildbearbeitung

Bilder laden und anzeigen

Eingeführte Befehle: loadImage(dateiName), image(bild,x,y,breite,hoehe), tint(r,g,b,transparenz). Klasse: PImage.

Das Einlesen eines Bildes übernimmt der Befehl PImage meinBild = loadImage(dateiName). Die Bilddatei muss dabei im Ordner src/data liegen. Unterstützte Bildformate sind GIF, JPEG, PNG.)

Die Anzeige eines Bildes geschieht mit dem Befehl image(meinBild,x,y,breite,hoehe). Das Bild wird dabei auf die angegebene Breite und Hoehe skaliert.

Der Farbton eines Bildes wird mit tint(r,g,b,transparenz) gesetzt und mit noTint() wieder zurückgesetzt.

Beispielprogramm: Bild skaliert anzeigen

Das Programm skaliert ein Bild horizontal bis zu maximal doppelter Breite:

Source Code: BildAnzeigenSkaliert.java

Beispielprogramm: Eingefärbtes Bild anzeigen

Das Programm zeigt Originalbild und eine rötlich eingefärbte, halbtransparente Version des Bildes:

Source Code: BildAnzeigenMitFarbton.java

Bildbearbeitung: Struktur- und Farbveränderungen

Eingeführte Befehle: new PImage(breite, hoehe), loadPixels(), updatePixels(), red(farbe), green(farbe), blue(farbe), color(rot,gruen,blau)

Um ein Bild zu verändern, muss man typischerweise Pixel für Pixel neu berechnen. Processing erlaubt es, mit loadPixels() alle Pixels eines Bildes in einen Array von Integer-Zahlen zu laden. Jede Zahl repräsentiert die Farbe eines Pixels. Bildlich dargestellt ist der Zusammenhang zwischen dargestelltem Bild und dem Pixel-Array wie folgt:


Bild von Images and Pixels, processing.org

Häufig möchte man die Pixels eines Bildes in Abhängigkeit von (x,y)-Koordinaten der Pixel verändern. Dazu muss man aus einer (x,y)-Koordinate den Array-Index berechnen. Grafisch sieht das wie folgt aus:


Bild von Images and Pixels, processing.org

Konkret gibt die folgende, selbstgeschriebene Methode getColor die Farbe an Koordinate (x,y) im Bild image zurück:

int getColor(PImage image, int x, int y) {
	return image.pixels[y * image.width + x];
}

Processing bietet die Methoden red(farbe), green(farbe) und blue(farbe) an, um Rot-, Grün- und Blau-Anteil von Farben zu extrahieren, so dass man sich nicht selbst um entsprechende Bit-Manipulationen kümmern muss. Der Parameter farbe ist dabei ein Integer, der eine Farbe in 32 Bit-Darstellung repräsentiert (3x8 Bit für die Farben, 8 Bit für Transparenz). Um umgekehrt aus Rot-, Grün- und Blau-Anteil einen Integer zu erzeugen, der diese Farbe repräsentiert, kann die Methode color(rot,gruen,blau) verwendet werden.

Beispielprogramm: Farbe rot entfernen

Betrachten wir ein Programm, das die Farbe rot entfernt:

Die Setup-Methode lädt zuerst das Original-Bild und erstellt dann ein leeres (schwarzes) Bild gleicher Grösse. Anschliessend wird für jeden Pixel im bearbeiteten Bild berechnet, indem als Rotanteil 0 und als Grün- und Blau-Anteil die Werte aus dem Originalbild verwendet werden:

original = loadImage("goldengate.jpg");
original.loadPixels(); // initialisiere original.pixels

size(original.width * 2, original.height);

// erstelle neues, leeres Bild
bearbeitet = new PImage(original.width, original.height);
bearbeitet.loadPixels(); // initialisiere bearbeitet.pixels
// bearbeitet.pixels enthält original.width * original.height
// schwarze Pixel

for (int i = 0; i < original.pixels.length; i++) {
	// Grün- und Blauanteil des i-ten Pixels ermitteln
	float gruen = green(original.pixels[i]);
	float blau = blue(original.pixels[i]);

	// neue Farbe berechnen mit Rotanteil=0,
	// Grün- und Blauanteil wie im Originalbild
	bearbeitet.pixels[i] = color(0, gruen, blau);
}
bearbeitet.updatePixels(); // aktualisiere bearbeitet.pixels

Source Code: BildbearbeitungRotEntfernen.java

Source Code zu Variation davon, mit animierter, zeilenweiser Rot-Entfernung: BildbearbeitungRotEntfernenAnimiert.java

Wenn Sie mit Farbwerten rechnen, können Ihnen folgende Hinweise hilfreich sein.

Farbwerte sind ganzzahlig. Wenn Sie nun zum Beispiel den Rotanteil eines jeden Pixels auf 20% des Orginalwertes setzen möchten, können Sie das wie folgt berechnen:

int neuerRotWert = (int) (red(originalFarbe) * 0.2); 

Die Multiplikation von red(originalFarbe) mit 0.2 = 20/100 liefert als Resultat eine sog. Fliesskommazahl. War der Rotwert des Pixels zum Beispiel 87, wäre das Resultat 17.4. Aber für den neuen Rotwert braucht es wieder eine Ganzahl. Die Angabe von (int) nach der Zuweisung mit = sorgt dafür, dass die Fliesskommazahl in eine Ganzzahl umgewandelt wird. In diesem Beispiel könnten Sie alternativ auch schreiben:

int neuerRotWert = red(originalFarbe) * 20 / 100; 

So wird eine ganzzahlige Multiplikation und Division durchgeführt. Das Resultat ist in beiden Formeln dasselbe.

Farbwerte sind ganze Zahlen im Bereich 0..255. Vielleicht verwenden Sie eine Berechnung, die einen Wert ausserhalb dieser Grenzen liefert. Sie könnten mit if-Anweisungen sicherstellen, dass der neue Wert nie unter Null oder über Null liegt. Angenommen, wir möchten den Rotanteil um 45% erhöhen, dann könnte ja der neue Rotwert über 255 sein:

int neuerRotWert = red(originalFarbe) * 145 / 100; 
if (neuerRotWert > 255)  {
	neuerRotWert = 255;
}

Kürzer lässt sich das schreiben, wenn man eine Methode verwendet, die das Minimum zweier Werte zurückgibt:

int neuerRotWert = min(255, red(originalFarbe) * 145 / 100);

Analog kann auch max(0, irgendEineBerechnungFuerNeuenFarbwert) verwendet werden, um sicherzustellen, dass nie ein negativer Farbwert berechnet wird.

Beispielprogramm: Bild vertikal spiegeln

Betrachten wir nun ein Programm, das Bilder vertikal spiegelt:

Die Setup-Methode lädt zuerst das Original-Bild und erstellt dann ein leeres (schwarzes) Bild gleicher Grösse. Anschliessend wird das Bild zeilenweise bearbeitet (y-Richtung), und innerhalb jeder Zeile spaltenweise (x-Richtung). Für jeden Pixel im neuen, bearbeiteten Bild an der Koordinate (bearbeitetX, bearbeitetY) wird die Farbe bestimmt, indem die Farbe vom entsprechend gespiegelten Bildpunkt im Originalbild gelesen wird:

original = loadImage("goldengate.jpg");
original.loadPixels();

bearbeitet = new PImage(original.width, original.height);
bearbeitet.loadPixels();

for (int bearbeitetY = 0; bearbeitetY < bearbeitet.height; bearbeitetY++) {
	for (int bearbeitetX = 0; bearbeitetX < bearbeitet.width; bearbeitetX++) {
		int originalX = bearbeitetX;
		int originalY = bearbeitet.height - 1 - bearbeitetY;
		int originalFarbe = getColor(original, originalX, originalY);

		setColor(bearbeitet, bearbeitetX, bearbeitetY, originalFarbe);
	}
}
bearbeitet.updatePixels();
size(original.width * 2, original.height);

Die folgende Grafik soll die Umrechnung der Koordinaten des Zielbildes in Koordinaten des Originalbildes veranschaulichen:

Die Struktur des obigen Programmes ist die Struktur vieler Bildbearbeitungs-Algorithmen:

Diese Koordinaten-Umrechnung kann beliebig komplex sein: So entstehen Bilder, die verzerrt, gedreht oder irgendwie transformiert werden. Auch beim Berechnen der neuen Farbe sind der Phantasie keine Grenzen gesetzt: Die neue Farbe kann auf der Farbe eines Pixels im Originalbild basieren, aber sie kann genausogut aus den Farben mehrerer Pixels im Originalbild basieren.

Source Code: BildbearbeitungVertikalSpiegeln.java

Aufgaben

Aufgabe Farbveränderungen: Rot und Grün vertauschen

Schreiben Sie ein Programm, das für jeden Bildpunkt die Farben Rot und Grün vertauscht (oder ähnliche Farbveränderungen vornimmt). Sie können dafür die noch leere Klasse AufgabeBildbearbeitungRotGruenTauschen verwenden. Probieren Sie es mit einer einfachen Schleifen über alle Pixel sowie einer doppelt verschachtelten Schleife mit Index-Berechnung aus!

Aufgabe Bildstruktur verändern: Bild horizontal spiegeln

Erstellen Sie eine Klasse, die ein Bild horizontal spiegelt. Sie können dafür die noch leere Klasse AufgabeBildbearbeitungHorizontalSpiegeln verwenden.

Aufgabe Bildstruktur verändern: Bild um 90°, 180° und 270° drehen

Schreiben Sie drei Bildbearbeitungsprogramme:

  1. Drehung um 90° nach rechts [1 Punkt]
  2. Drehung um 180° (entspricht Punktspiegelung am Zentrum) [3 Punkte]
  3. Drehung um 270° nach rechts (entspricht Drehung um 90° nach links) [1 Punkt]

Achten Sie darauf, dass bei Drehungen um 90° und 270° Höhe und Breite beim gedrehten Bild der Breite und Höhe des Originalbildes entsprechen!

Aufgabe Farbveränderungen: Bildfarben in Graustufen

Hintergrundsinformationen zu Graustufen: https://de.wikipedia.org/wiki/Graustufen.

Die Formel für Umrechnung von RGB-Farben in Graustufen: https://de.wikipedia.org/wiki/Grauwert.

Sie können auch mit anderen Formeln für die Grauwerte experimentieren – je nach Bild liefert eine andere Formel anschaulichere Grauwerte.

Aufgabe Farbveränderungen: Farbwerte der einzelnen Pixel neu berechnen

Sie können selbst entscheiden, wie Sie die Farbwerte neu berechnen möchten!

Aufgabe: Bildgrösse halbieren

Schreiben Sie ein Programm, das die Grösse eines Bildes halbiert, horizontal und vertikal.

Abstrakt dargestellt würde ein Bild mit vier Pixel reduziert auf ein Bild mit einem Pixel:

Eine einfache Lösung ist dabei, jede zweite Spalte und jede zweite Zeile wegzulassen. Die Farben von "Pixel" in der Abbildung oben rechts wären dann einfach die Farben von "Pixel 1" in der Abbildung oben links. Allerdings leidet die Bildqualität erheblich darunter.

Aufgabe: Schreiben Sie ein Programm, das dieses einfache Verfahren umsetzt.

Eine anspruchsvollere Lösung berechnet die Farben von "Pixel"" in der Abbildung oben rechts unter Berücksichtigung von mehreren Pixeln im Orginalbild, zum Beispiel unter Berücksichtigung der vier abgebildeten Pixel oder unter Berücksichtigung aller acht Nachbarpixel berücksichtigen.

Aufgabe: Finden Sie ein Verfahren, das mehrere Pixel des Originalbildes verwendet, um so die Bildqualität zu steigern. Am besten, Sie experimentieren dazu mit verschiedenen Formeln für die Berechnung der Farben der neuen Pixel.

Aufgabe: Farben in Abhängigkeit von Position verändern

Schreiben Sie ein Programm, das die Farbwerte der Pixel in Abhängigkeit Ihrer Position verändert.

Sie könnten zum Beispiel zum Beispiel einen Farbwert von links nach rechts ausblenden, d.h. den neuen Farbwert von 100% des Originalfarbwertes ganz links kontinuierlich auf 0% ganz rechts reduzieren:

Hilfreich für solche Berechnungen sind folgende Formeln (analoge Formeln können Sie für Y-Richtung verwenden):

double deltaX = original.width - x;
// wie weit ist Pixel vom rechten Rand entfernt?
double prozentDeltaX = deltaX / original.width;
// Abstand des Pixel vom rechten Rand in Prozent

Sie könnten auch ein Bild von der Mitte her "ausdunkeln":

Hilfreiche Formeln dazu:

double deltaX = Math.abs(original.width / 2.0 - x);
// wie weit ist Pixel von der Mitte entfernt?
double deltaXProzent = 1 - deltaX / (original.width / 2);
// wie weit ist Pixel prozentual von der Mitte entfernt?

Sie könnten das Bild auch in horizontaler und vertikaler Richtung von der Mitte her ausdunkeln.

Sie könnten auch die Helligkeit eines Bilder in der Mitte am stärksten erhöhen, gegen den Bildrand hin immer weniger.

Sie könnten aber auch nur einen bestimmten Bildausschnitt verändern. Wenn Sie zum Beispiel ein Foto einer Person mit roten Augen nehmen, könnten Sie in den Bereichen der roten Augen den Rotwert reduzieren. Die Koordinaten können Sie mit Hilfe des Rahmenprogramms bestimmen.

Sie können selber wählen, welchen Effekt Sie berechnen wollen!