Bei Zurück in die Schule: gefunden: Processing, eine Java-Programmierumgebung, mit der man Bilder erzeugen kann. Processing ist eine Lernumgebung und Einführung in das (Java-)Programmieren. Man kann damit einerseits voll objektorientiert schreiben, andererseits kann man genauso gut ohne Objektkrams die vorhanden Methoden benutzen, deren Überbau wunderschön transparent ist.
(Fußnote: Transparent heißt überall anders soviel wie: „Durchsichtig, so dass man ins Innere blicken und die Zusammenhänge verstehen kann.“ In der Informatik heißt transparent allerdings: „Durchsichtig, so dass man hindurchschaut und nichts sieht, also quasi unsichtbar und vor leicht zu verwirrenden Augen verborgen.“)
Man kann mit Processing neue Bilder zeichnen oder bestehende Bilder bearbeiten. Das kann man statisch machen oder dynamisch: dann werden daraus bewegte Bilder, entweder automatisch oder durch den Benutzer mit Maus oder Tastatur gesteuert. So kann man auch ganze Simulationen entwerfen, wenn man möchte. Man kann sich auch eine Methode schreiben, um die ausgegebenen Bilder zu speichern, die sieht so aus:
void mousePressed() {
save("bild.jpg");
}
Viel einfacher geht es wirklich nicht.
Ich wollte aber Filter schreiben, so wie man sie aus Bildbearbeitungsprogrammen kennt. Hier sind ein paar davon. Die Vorgehensweise ist meist die: Man lädt erst einmal das Originalbild in den Speicher und legt eine – noch leere – Zeichenfläche im gleichen Format an. Dann schaut man sich der Reihe nach jeden Pixel des Ursprungsbildes an, merkt sich dessen Farbe (bzw. den Rot-, Grün-, Blauanteil davon), verändert diese Farbe, und schreibt an dieselbe Position auf der Zeichenfläche einen Pixel mit der neuen Farbe.
1. Hier ein einfaches Weichzeichnen, das erste Bild ist das Original:



Man ändert dabei jede Farbe so, dass man sich von einem Pixel und allen seinen umliegenden Nachbarpixeln (im ersten Beispiel nur die direkt umliegenden, im zweiten auch die in etwas weiterem Abstand) die Durchschnittsfarbe ausrechnet und diese dem Pixel zuweist. Das macht man mit allen Pixeln so und heraus kommt eine Weichzeichnung.
2. Hier eine Gammakorrektur. Die dient zu einer differenzierten Aufhellung oder Abdunklung von Bildern. Dabei wird nicht jeder Pixel im gleichen Maß heller oder dunkler gemacht, sondern es werden zum Beispiel die dunklen Pixel mehr aufgehellt als die (ja eh schon hellen) helleren. Im ersten Bild mit gamma=0.5, im zweiten gamma = 1.5:



Hier sind einem die Nachbarn egal. Man wendet einfach auf den Rot-, Grün-, Blauteil jedes Pixels die überraschend einfache Funktion an:
farbeneu = (farbealt/255)gamma * 255
Die Zahl 255 kommt daher, weil es für jeden Farbton 255 Möglichkeiten gibt. Der Wert des ursprünglichen Blauanteils (von 0-255) wird durch 255 geteilt, womit man diesen Anteil auf eine Zahl zwischen 0 und 1 normalisiert hat. Das wird dann mit dem Gammawert potenziert (bzw. je nach Definition auch dessen Kehrwert) und dann mit dem letzten Faktor wieder auf den ursprünglichen Raum (zwischen 0 und 255) gestreckt.
3. Gemischte weitere Filter:



Der erste ist ein einfaches Schärfen: Man schaut sich wieder die Rot-, Grün- Blauwerte eines Pixels und seiner Nachbarn an und errechnet daraus wieder einen Durchschnitt. Allerdings werden vor der Berechnung die Nachbarn und der Pixel selber noch gewichtet: die Werte der 8 unmittelbaren Nachbarn werden zum Beispiel jeweils um 1 reduziert, die des zentralen Pixels um 9 erhöht – die Durchschnittswerte bleiben also gleich (das Bild wird insgesamt nicht heller oder dunkler), aber mit Betonung des Zentrums.
Der zweite zeichnet einfach an jede n-te Stelle des Bildes einen Kreis mit Durchmesser n von der Farbe des Pixels, der sich im Originalbild an dieser Stelle befindet.
Der dritte ist ein Mosaik. Man könnte zwar, ähnlich wie bei den Ellipsen, an jede n-te Stelle des Bildes ein Quadrat zeichnen, aber so wollte ich das nicht machen. Jeder Pixel sollte extra gezeichnet werden. Ich machte mir dazu folgende Skizze:

Der erste Pixel in der Reihe (in der 0-Spalte) sei zum Beispiel rot. Die beiden Pixel rechts daneben sollten dessen Farbe annehmen. Die Pixel in den Spalten 3-7 sollten die Farbe des Pixels in Spalte 5 annehmen, und so weiter. (Für die y-Koordinate analog, das habe ich bald gemerkt.)
Weil ich in Mathe nicht gut bin, zeichnete ich mir eine Tabelle auf: links die Position des Pixels, rechts die Position des Pixels, dessen Farbe angenommen werden sollte. f(linkeSpalte) = rechteSpalte. Der Rest ist einfach nur Rechnen. Mathekönner sehen das durch Überlegen, ich habe halt so lange herumgepfuscht, bis ich die entsprechende Funktion hatte. Die musste ich dann noch etwas verallgemeinern, damit sie nicht nur für die Rechnung mit den Mosaiksteinchen von Seitenlänge 5, sondern für beliebige Seitenlängen gilt. Sieht dann so aus:
xZentrumspixel = (x+size/2)/size*size bzw.
yZentrumspixel = (y+size/2)/size*size,
wobei size
die Seitenlänge des Mosaiksteins ist. Und nein, das /size
und *size
kann man nicht einfach kürzen, da x
eine Ganzzahl und die Divison in diesem Fall eine Ganzzahldivision ist – es wird immer abgerundet, der Rest verworfen.
Processing gibt’s für alle gängigen Betriebssystem, läuft unter Windows auch ohne Installationsrechte. Tutorials gibt es dort auch ein paar.
***
Anhang: So sieht ein vollständiger Sketch in Processing aus. Java-Code, ohne Klassendefinitionen, besteht aus 1 Attribut und den Methoden setup()
und draw()
und der Berechnungsmethode für das Mosaik, mosaik()
.
PImage img; //Platz fuer das im Speicher gehaltene Bild
//einmaliges Aufrufen am Anfang
void setup() {
img = loadImage("test.jpg"); //Laden des Bildes
size(img.width, img.height); //Groesse der Zeichenflaeche
}
//das eigentliche Zeichnen
void draw() {
//Pixel-Feld zur Verfuegung stellen
loadPixels();
//Zaehlschleife fuer alle Pixel mit x- und y-Koordinate
for (int x = 0; x < img.width; x++) {
for (int y = 0; y < img.height; y++ ) {
//Berechnen der neuen Farbe - hier koennte man auch andere Methoden aufrufen
color c = mosaik(x, y, 9);
//Umrechnen der Koordinaten in Index des eindimensionalen Pixel-Felds
int loc = x + y*img.width;
//Aendern des Pixels im Pixel-Feld
pixels[loc] = c;
}
}
//Zeichnen aller Pixel
updatePixels();
}
//Methode zur Farbberechnung beim Mosaik
color mosaik(int x, int y, int groesse) {
//Koordinaten des Zentrums ermitteln
int xZentrum = (x+groesse/2)/groesse*groesse;
int yZentrum = (y+groesse/2)/groesse*groesse;
//Umrechnen der Koordinaten in Index des eindimensionalen Pixel-Felds
int loc = xZentrum + yZentrum*img.width;
//sicher stellen, dass Bildgrenzen nicht ueberschritten werden
loc = constrain(loc, 0, img.pixels.length-1);
//Rot-, Gruen-, Blauanteile des Zentrumspixels speichern
float rtotal = red(img.pixels[loc]);
float gtotal = green(img.pixels[loc]);
float btotal = blue(img.pixels[loc]);
// Rueckgabe der neuen Farbe
return color(rtotal, gtotal, btotal);
}
Nachtrag: Schöne weitere Sachen zu Processing gibt es hier:
- Schöne Beispiele und viel zum Anschauen und Ausprobieren gibt es auf dieser Begleitseite zu einem Schulbuch: https://www.ningelgen.eu/index.html
- Ingo Bartling zeigt auf seinem Blog ganz viel zu Processing, hier sogar als Arbeitsheft
Schreibe einen Kommentar