Bildbasierte generative Grafiken in p5.js
Bildmanipulation zur Erstellung von Grafiken
Ziel der Übung
In dieser Übung lernst du, wie du generative Grafiken auf Basis von Fotos und Bildern erzeugst.
Editor öffnen
Als erstes starten wir den p5-Editor, welchen du hier öffnen kannst: https://editor.p5js.org/. Da Firefox eine wichtige Methode des Tutorials nicht unterstützt, empfehlen wir Chrome oder Edge als Browser zu benutzen. Die Auto-refresh-Option sollte man aus Performancegründen ausschalten. Im Editor wird dir folgender Code angezeigt:
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
}
In der Funktion setup()
wird deine Zeichenfläche erzeugt, welche später noch bearbeitet wird.
Die Funktion draw()
wird wiederholt ausgeführt. Da wir ein statisches Bild erzeugen, können wir die Funktion draw()
löschen.
Bild hochladen und einblenden
Als nächstes laden wir ein Bild in die Datenbank, welches wir bearbeiten möchten. Dazu legen wir einen Ordner an und nennen ihn beispielsweise „assets“. Hier laden wir die Bilddatei hoch und geben dieser Datei einen Namen, zum Beispiel „bild.jpg“.
Um die Bilddatei verwenden zu können, muss diese in den Code eingebunden werden. Dazu rufen wir eine neue Funktion preload()
auf. In der Klammer geben wir den Ordnerpfad der Bilddatei an. Achte hierbei auf Groß- und Kleinschreibung.
function preload(){
img = loadImage('assets/bild.jpg'); // Läd Bild aus angelegtem Ordner "assets"
}
Canvas bearbeiten
Die maximale Größe des Bildes legen wir auf 800 px fest, damit das Bild nicht zu groß eingebettet wird und der Browser sich nicht aufhängt. Das machen wir mit Hilfe einer if()-Schleife
und der Funktion resize()
. Dadurch wird jedes Bild, welches in der Höhe oder in der Breite größer ist als 800 px, im Verhältnis verkleinert. Die Canvasgröße von bisher 400 x 400 px passen wir der Bildgröße an, damit das Bild vollständig angezeigt wird. Zum Ausgeben des Bildes verwenden wir die image()
-Funktion.
function setup() {
if(img.width > 800 || img.height > 800){ // große Bilder werden für die Performance verkleinert
if(img.width > img.height ){
let d = 800/img.width;
img.resize(800,(img.height * d));
} else{
let d = 800/img.height;
img.resize((img.width * d),800);
}
}
createCanvas(img.width,img.height)
image(img, 0, 0);
}
Wenn du stattdessen hochauflösende Bilder verwenden willst, kannst du diesen Code weg lassen. Das solltest du allerdings erst am Ende deiner Bearbeitung machen, da sonst der Browser zu lange braucht dein Canvas zu erzeugen.
Raster erstellen und Punkte zeichnen
Mit einer for()
-Schleife rufen wir nun die einzelnen Pixel des Bildes ab, um sie dann verändern zu können. Hier geben wir beispielsweise an, dass jeder 10. Pixel erfasst werden soll. Mit der Funktion point()
wird jeder in der for()
-Schleife bestimmte Pixel als Punkt gezeichnet. Um die Punkte abbilden zu können, müssen wir ihnen mit stroke()
eine Farbe zuweisen. Hier weißen wir den Punkten die Farbe der Pixel zu. Dazu legen wir eine Variable c
an, welche die Farbinformationen der einzelnen Pixel mit Hilfe der Funktion get()
erfasst. Um die Größe der Punkte zu verändern, verwenden wir die Funktion strokeWeight()
.
for(let col = 0; col < img.width; col+=12){
for(let row = 0; row < img.height; row+=12){
let c = img.get(col,row); // Speichert die Farbinformation der Pixel in c
stroke(color(c)); // Farbinformation c wird Punkten zugewiesen
strokeWeight(20); // Legt die Größe der Punkte fest
point(col,row); // Zeichnet Punkte
}
}
Nun kannst du ausprobieren was passiert, wenn du die Parameter der Funktion strokeWeight()
, oder die Anzahl der abzurufenden Pixel änderst.
Modifizierung mit zufälligen Werten
Mit der Funktion random()
können die Punkte zufällig gesetzt. Dadurch variieren Größe und Abstand der Pixel. Dafür weisen wir der Funktion strokeWeigth()
die random()
-Funktion hinzu und geben hier als Parameter an, wie stark die Strichstärke der Punkte variieren soll.
Auch bei der point()
-Funktion kann die random()
-Funktion ergänzt werden. Hier bestimmen wir, wie stark sich die Position der Punkte vom Raster entfernen.
strokeWeight(random(20)); // Zufällige Strichstärke der Punkte
point(col+random(5),row+random(5)); // Zufälliger Abstand der Punkte zum ursprünglichen Raster
Wenn du möchtest, kannst du das Bild im Hintergrund ausblenden in dem du die bereits vorhandene Funktion image()
auskommentierst.
//image(img, 0, 0);
Rechtecke zeichnen
Statt Punkte können wir auch Rechtecke verwenden. Hierfür klammern wir die Funktionen point()
und strokeWeight()
aus, denn die brauchen wir hierfür nicht. Stattdessen ergänzen wir den Code mit der Funktion rect()
, um Rechtecke zu zeichnen. Die ersten 2 Parameter der rect()
-Funktion geben an, wie weit die Rechtecke vom Raster entfernt sein sollen. Mit den letzten 2 Parameter bestimmen wir die Größe der Rechtecke. Hier kannst du auch wieder verschiedene Parameter ausprobieren und auch die Funktion random()
einsetzten.
for(let col = 0; col < img.width; col+=5){
for(let row = 0; row < img.height; row+=5){
let c = img.get(col,row); // Speichert die Farbinformation der Pixel in c
stroke(color(c)); // Farbinformation c wird Punkten zugewiesen
//strokeWeight(random(40));
//point(col+random(20),row+random(20));
fill(color(c));
rect(col+ random(20),row+ random(20), random(20), random(20));
}
}
Kurven zeichnen
Um Kurven zu zeichnen musst du die Variablen xPos
und yPos
erstellen, die später die Position des Ursprungs der Kurve bestimmen. Diese werden direkt unter dem Kopf der for()
-Schleife inizialisiert.
for(let col = 0; col < img.width; col+=5){
for(let row = 0; row < img.height; row+=5){
let xPos = col;
let yPos = row;
let c = img.get(col,row);
Um die Bildpixel als Kurven darzustellen wird der Code, welcher die Kurve definiert, zwischen die push()
- und pop()
-Funktion geschrieben. Zuerst wird die erzeugte Kurve mit translate()
an die Position des dazugehörigen Pixels verschoben. Mit der Funktion rotate()
legen wir die Richtung fest, in welche die Kurve ausgerichtet werden soll. Durch noFill()
wird die Kurve als Linie und nicht als Fläche dargestellt. Mit strokeWeight()
und stroke()
werden die Strichstärke und Farbe der Linie festgelegt. Zu guter Letzt wird die Kurve dann mit den Parametern der curve()
-funktion beschrieben.
push();
translate(xPos, yPos);
rotate(radians(random(360)));
noFill();
strokeWeight(random(2));
stroke(color(c));
curve(xPos, yPos, sin(xPos) * random(100), cos(yPos) * sin(xPos) * 40, 0, 0, cos(yPos) * sin(xPos) * 140, cos(yPos) * sin(xPos) *50)
pop();
Modifizierung vereinfachen
Damit wir beim Ändern der Parameter nicht jedes Mal etwas ausklammern müssen, vereinfachen wir die Modifizierung und packen das Ganze in eine verschachtelte if-else()
-Schleife. Dazu vergeben wir neue Variabeln, die am Anfang des Codes und außerhalb der Funktion setup()
definiert werden. Wir wollen dort entscheiden können, ob wir Punkte, Rechtecke oder Kurven zeichnen. Außerdem ändern wir hier auch die Größe der Pixel und den Abstand zum Raster. Die Funktion random()
und den Hintergrund schalten wir mit einer booleschen Variable an und aus.
let art = "punkte"; // punkte, rechtecke oder kurven
let anzahl = 10; // Anzahl, wie viel Pixel erfasst werden sollen (1= Jeder Pixel)
let größe = 20; // pixelgröße von point oder rect
let abstand = 5; // Abstand vom Raster
let zufall = 1; // Zufall an =1, aus =0
let hintergrund = 1; // Hintergrund an =1, aus =0
Außerhalb der for()
-Schleife stellen wir mit einer if()
-Funktion die Bedingung, wann der Hintergrund eingeblendet werden soll. Bei der Rastererzeugung verwenden wir die Variable anzahl
um zu verändern, wie viele Pixel erfasst werden sollen. Nun erstellen wir eine if-else()
-Schleife um abzufragen ob Punkte, Rechecke oder Kurven gezeichnet werden sollen.
In der inneren for-()
Schleife fragen wir ab, ob die Variable zufall
an oder ausgeschaltet ist. Hier sagen wir, wenn der Zufall an ist, sollen Größe und Abstand der Pixel mit der Funktion random()
gezeichnet werden. Wenn der Zufall aus ist, sollen Größe und Abstand nicht zufällig sein, sondern eine festgelegte Pixelgröße haben und am Raster ausgerichtet sein.
createCanvas(img.width,img.height)
// Hintergrund
if(hintergrund == true){image(img, 0, 0);}
// Rastererzeugung
for(let col = 0; col < img.width; col+=anzahl){
for(let row = 0; row < img.height; row+=anzahl){
let c = img.get(col,row); // Speichert die Farbinformation der Pixel in c
stroke(color(c));
// Punkte
if (art == "punkte"){
if (zufall == true){
strokeWeight(random(größe));
point(col+random(abstand),row+random(abstand));
} else{
strokeWeight(größe);
point(col, row);
}
// Rechtecke
} else if (art == "rechtecke"){
fill(color(c));
if(zufall == true){
rect(col+ random(abstand), row+random(abstand), random(größe), random(größe));
} else {
rect(col+(abstand), row+(abstand),(größe),(größe));
}
// Kurven
} else if(art == "kurven"){
push();
translate(xPos, yPos);
rotate(radians(random(360)));
noFill();
strokeWeight(random(2));
stroke(color(c));
curve(xPos, yPos, sin(xPos) * random(100), cos(yPos) * sin(xPos) * 40, 0, 0, cos(yPos) * sin(xPos) * 140, cos(yPos) * sin(xPos) *50)
pop();
}
}
Jetzt kannst du die Variablen bequem anpassen. Probiere das Ganze doch mal mit unterschiedlichen Bildern aus und gestalte diese nach deinen Wünschen.
Du kannst dir den kompletten Code im p5.js-Editor anschauen.
Als Inspiration für dieses Tutorial diente dieses Video von Weidi.