Processing – Der Master-Raster

Herbert hat mich wieder einmal gebeten, etwas für ihn zu programmieren: Eine Anwendung, die ein beliebiges Bild rastert und siebdruck-artig in verschieden große Kreise zerlegt, die dann mit dem Laser aus Metall herausgeschnitten werden können. Als „Waffe der Wahl“ hab ich mich für Processing entschieden, eine quelloffene Software, mit der man sehr einfach und schnell visuelle Prototypen programmieren kann. Das hat zur Abwechslung wieder einmal echt Spaß gemacht, obwohl ich mich nicht beklagen kann, zu wenig vor dem Rechner zu sitzen. 😉

Processing hat seine Wurzeln in der Programmiersprache Java, in der es auch implementiert ist.  Es wurde 2001 von Ben Fry und Casey Reas vom MIT als Open-Source-Programmiersprache ins Leben gerufen und sieht sich als eine Art flexibler Software-Notizblock, mit dem man im visuellen Kontext lernen kann zu programmieren. Nichtsdestotrotz wird es auch von Profis verwendet, und erfreut sich weltweit großer Beliebtheit.

Ich hab es ausgewählt, weil es mir für die anstehende Aufgabe am geeignetsten erschien, und wurde nicht enttäuscht. Zwei Stunden später war der Prototyp ausgetüftelt – „quick and dirty“, ohne Fokus auf Performance und Eleganz. Ganz großspurig haben wir ihm den Namen Master-Raster gegeben. 😀
Hier die Eckpunkte:

Per Konfiguration kann man angeben, in wieviele Kreis-Zellen man das Bild rastern möchte, d.h. aus wievielen Spalten und Reihen der Laser später Kreise verschiedener Größe ausschneiden soll. Deshalb muss das Eingangsbild erst einmal in die entsprechenden Teile zerlegt werden, von denen man dann jeweils den Durchschnitts-Grauton berechnet. Ich hab das folgendermaßen bewerkstelligt:

// iterate over rasters
for (int actyRaster = 0; actyRaster < yRasters; actyRaster++) {
  for (int actxRaster = 0; actxRaster < xRasters; actxRaster++) {
     // get the image of the actual raster
     PImage rasterPart = inputPic.get(actxRaster * widthPerRaster, actyRaster * heightPerRaster, widthPerRaster, heightPerRaster);
     rasterPart.loadPixels();
...

Ich iteriere also über die einzelnen Rasterzeilen und -spalten und lade den betroffenen Bildteil des Originals in die Variable rasterPart.
Jeden dieser Teile nehm ich mir dann weiter vor, indem ich alle seine Pixel durchgehe und die entsprechenden RGB-Farbwerte aufsummiere.
Über den pixels-Array des Bildteils kann ich auf die Farbinformation zugreifen und die einzelnen Anteile (Rot / Grün / Blau) mit den Methoden red(), green() und blue() auslesen. Ein Wert von 255 steht dabei für den höchstmöglichen Farbanteil, 0 für den geringsten: rgb(255, 255, 255) entspricht Weiß, rgb (0, 0, 0) entspricht Schwarz.
Alle diese Werte werden erst einmal in der Variable allRasterPixels summiert:

     // sum up all color values of the rasterpart
     float allRasterPixels = 0f;
     for (int imgY = 0; imgY < heightPerRaster; imgY++) {
       for (int imgX = 0; imgX < widthPerRaster; imgX++) {
         color col = rasterPart.pixels[imgX + imgY * widthPerRaster];
         allRasterPixels += red(col) + green(col) + blue(col);
       }
     }

Um jetzt für diesen einen Raster-Bildteil einen durchschnittlichen Grauwert zu ermitteln, muss ich diese Summe natürlich wieder dividieren, und zwar durch die Gesamtzahl der Punkte (widthPerRaster, heightPerRaster => vorab berechnet) und durch drei, weil wir ja je drei Farbwerte in die Summe haben einfließen lassen. Ich weise den resultierenden Wert der Einfachheit halber der bestehenden Variable allRasterPixels  zu (bad style, ich weiß) und setze den so ermittelten Grauwert in das entsprechende Pixel des kleinen Ausgabebildes (in der Variable outputPic). Dessen Größe entspricht genau den konfigurierten Rasterzellen.

     // normalize the color value of the raster part and set it to the small greyscale image
     allRasterPixels = allRasterPixels / widthPerRaster / heightPerRaster / 3;
     outputPic.pixels[actxRaster + actyRaster * xRasters] = color(allRasterPixels);

Wir haben also beispielsweise aus diesem Bild

Leo Portrait

die folgende Grauton-Miniatur erzeugt:

Leo Grauton Mini

Mit diesen Grauwerten können wir nun weiterarbeiten um die Kreise in den verschiedenen Größen hochzurechnen:

// calculate the percentage of the grey tone and the size of the resulting circle
float colPercentage = 100f / 255f * allRasterPixels;
float circleDiameter = holeFactor / 100f * colPercentage;

Ich weiß ja, dass der maximale Farbton 255 entspricht, also berechne ich, wievielen Prozentpunkten der aktuell vorliegende Grauwert entspricht. Den Wert lege ich in der Variable colPercentage ab. Daraus berechne ich wiederum den Durchmesser des Kreises, wobei ich hier den Wert der Variable holeFactor konfigurierbar gemacht habe. Er bestimmt, um welchen Faktor jedes einzelne Pixel zum Kreis hochskaliert wird, z.B. bedeutet 10, dass der Grauwert eines Pixels dann auf einen Bereich von 10 x 10 Pixel projiziert wird.

Und den so ermittelten Kreis zeichne ich schließlich mit der ellipse()-Funktion in das resultierende Bild, das in der Variable holeGraphics  aufgebaut wird. Als Zielkoordinaten ermittle ich einfach die Mitte der zu beschreibenden Rasterzelle.

// finally draw the circle to the hole image
holeGraphics.ellipse(actxRaster * holeFactor + holeFactor / 2, actyRaster * holeFactor + holeFactor / 2, circleDiameter, circleDiameter);

Aus der Grauton-Miniatur wird nun folgende, aus lauter unterschiedlich großen Kreisen bestehende Grafik:

Leo gerastert

So weit, so gut. Die letzte Herausforderung war noch, das Ganze in ein Vektorformat zu bringen, das sich auch in diversen CAD-Programmen „weiterverwurschteln“ lässt. Hier hat sich wiederum SVG angeboten.
Processing kann das Zeichnen am Ausgabefenster im SVG-Format mitprotokollieren. Ich hab also parallel zum Erzeugen der Ausgabegrafiken die einzelnen Schritte aufgezeichnet:

beginRecord(SVG, "output.svg");
ellipseMode(CENTER);
noFill();
stroke(0);
...
ellipse(actxRaster * holeFactor + holeFactor / 2, actyRaster * holeFactor + holeFactor / 2, circleDiameter, circleDiameter);
...

endRecord();

Die so entstehende SVG-Datei enthält alle gezeichneten (ungefüllten) Kreise im Vektorformat und kann z.B. im Browser geöffnet werden (Herunterladen, entpacken, öffnen mit Firefox o.ä.):

SVG Output

Dieser kleine Exkurs hat wirklich Spaß gemacht. Ich hab mit Processing nun schon länger nichts gemacht, werde aber sicher die eine oder andere „dunkle Winterstunde“ über die Feiertage mit diesem genialen Tool verbringen.

Der Master-Raster wird bis dahin sicher noch weiterentwickelt und optimiert. Sobald es die ersten gelaserten Metall-Bilder gibt, wird das hier kundgetan. Und wenn jemand Interesse an der Software hat, bitte einfach kurz mailen.

Babsi@nline
Datenschutz-Übersicht

Diese Website verwendet Cookies. Mehr Informationen darüber findest Du in meiner Datenschutzerklärung.