Mit ImageMagick skriptgesteuert zeichnen

Aus LinuxUser 08/2022

Mit ImageMagick skriptgesteuert zeichnen

© Yuri Konovalov / 123RF.com

Silhouette

ImageMagick kann mehr, als nur fertige Bilder zu bearbeiten. Die freie Software erstellt sogar automatisiert und skriptgesteuert einfache Zeichnungen.

Obwohl man zum Zeichnen und Malen in der Regel ein richtiges Grafikprogramm verwendet, gibt es durchaus Situationen, in denen es gilt, regelmäßige Formen in einem Bild in fest definierten Abständen wiederholt zu zeichnen, wie beispielsweise hier gezeigt beim Erstellen der Silhouette einer fiktiven Stadt (Abbildung 1). Dafür braucht es kein kostspieliges Grafikprogramm mit ausgefeilten Makro-Sprachen: Das kostenlose und freie Softwarepaket ImageMagick und eine Kommandozeile genügen für diese Aufgabe.

Für umfangreichere Bildkompositionen sollte man sich allerdings von einer Skriptsprache wie der Bash unterstützen lassen, die mithilfe von Schleifen und anderen Kontrollstrukturen Bildinhalte wiederholt in die Grafik einfügt. ImageMagick findet sich in den Paketquellen der meisten Linux-Distributionen, lässt sich zur Installation aber auch aus dem Download-Bereich der Projektseite herunterladen [1].

Abbildung 1: Bild einer mit ImageMagick erstellten Skyline. Die Anzahl der Fenster und Stockwerke übergeben Sie als Parameter.

Abbildung 1: Bild einer mit ImageMagick erstellten Skyline. Die Anzahl der Fenster und Stockwerke übergeben Sie als Parameter.

Malen mit Kommandos

Nach erfolgreicher Installation tippen Sie auf der Kommandozeile magick logo: logo.gif. Daraufhin legt ImageMagick im aktuellen Verzeichnis die Datei logo.gif an. Sie zeigt den in der oberen rechten Ecke von Abbildung 1 zu sehenden Zauberer, wie Sie durch Öffnen der Datei in einem geeigneten Bildbetrachter leicht überprüfen können. Bildnamen, die mit einem Doppelpunkt enden, stehen in ImageMagick für eingebaute Testbilder. Weitere Testbilder erhalten Sie mit den Optionen rose: und wizard: [2].

Den Beweis dafür, dass man mithilfe von ImageMagick auf der Kommandozeile zeichnen kann, liefert der Befehl aus Listing 1.

Listing 1

Rechteck

$ magick -size 500x300 \
  xc:skyblue -fill red \
  -draw 'roundrectangle \
  100,50,400,250,80,60' \
  bild.png

Er legt mittels des Parameters -size zunächst die Bildgröße fest. Anschließend füllt xc:skyblue den Hintergrund des Bilds mit himmelblauer Farbe. xc: steht dabei historisch gesehen für “X Constant Image”, steht heute aber für Leinwand oder Canvas. Die Anweisung erwartet eine Farbbezeichnung aus dem Fundus des X-Window-Systems – dessen Farbnamen hat ImageMagick übernommen [3].

Möglich wäre auch die Angabe eines Farbverlaufs, etwa durch gradient:blue statt xc:skyblue. Der eingebaute Gradient Image Generator würde dann für Sie einen blauen Farbverlauf als Hintergrundbild erzeugen. Einen Eindruck der Möglichkeiten liefert die Dokumentation von ImageMagick. Im Detail geht die Anleitung zum Beispiel auf Plasma-Leinwände [4] und Farbverläufe [5] ein.

Hinter dem Parameter -fill steht die Definition der Zeichenfarbe für den nachfolgenden mit -draw eingeleiteten und in Hochkommas eingeschlossenen eigentlichen Zeichenbefehl. Als letzter Parameter folgt der Name der zu erstellenden Bilddatei.

Als Ergebnis erhalten Sie ein rotes, an den Ecken abgerundetes Rechteck auf hellblauem Hintergrund. Komplexere Zeichungen erstellen Sie durch das Aneinanderreihen mehrerer Zeichenbefehle (Listing 2, Zeile 1 bis 3).

Listing 2

Mehrere Elemente

$ magick -size 500x300 xc:skyblue -fill red -draw 'roundrectangle 100,150,400,225,55,10' \
  -draw 'roundrectangle 200,100,350,180,50,10' -fill black -draw 'circle 150,225 180,225' \
  -draw 'circle 350,225 380,225' auto.png
$ magick auto.png -fill red -draw 'polygon 175,150 200,110 200,150' auto-bevel.png
$ mogrify -fill red -draw 'polygon 175,150 200,110 200,150' auto.png

Drei-Schachtel-Prinzip

Das Ergebnis des ersten Aufrufs aus Listing 2 zeigt Abbildung 2: ein sehr stark vereinfachtes Symbol eines Autos mit Stufenheck nach dem Drei-Schachtel-Prinzip. Indem Sie den Zeichenbefehl aus Zeile 4 des Listings nachreichen, können Sie die Limousine noch ein wenig verschönern. Er schrägt die Fahrgastzelle im Frontbereich etwas ab.

Abbildung 2: Eine mit einfachen <code>draw</code>-Befehlen erstellte Stufenhecklimousine.

Abbildung 2: Eine mit einfachen draw-Befehlen erstellte Stufenhecklimousine.

Dabei dient die soeben erstellte Datei auto.png als Grundlage für die nachträgliche Abschrägung der Fahrgastzelle. Das Ergebnis schreibt das Kommando in die Datei auto-bevel.png. Möchten Sie das ständige Neuerstellen von Dateien vermeiden, verwenden Sie stattdessen das Kommando aus der letzten Zeile des Listings, das direkt die Datei auto.png modifiziert.

Obwohl beide Verfahren für die ersten Versuche beim Erlernen von ImageMagick zunächst durchaus Vorteile haben, wohnt ihnen doch ein entscheidender Nachteil inne: Sie arbeiten vergleichsweise langsam. Sie können sich sicher vorstellen, dass es sich kompliziert gestaltet, ein ganzes Hochhaus auf diese Weise zu zeichnen. Dazu müssten Sie etliche draw-Kommandos aneinanderreihen, schon für jedes einzelne Fenster wäre ein eigener Aufruf erforderlich. Darüber hinaus müssten Sie vorab die Position eines jeden Fensters manuell berechnen.

Schneller mit Skripten

Abhilfe schafft ein Skript, das die Bildgröße anhand der ihm übergebenen Angaben zur gewünschten Anzahl an Etagen und Fenstern pro Etage ermittelt. Es legt zusätzlich einen Farbverlauf für den Hintergrund fest und speichert das Bild zunächst ab. Anschließend berechnet es sämtliche Zeichenanweisungen für das eigentliche Gebäude sowie die Fenster und Türen und sammelt sie in einer XML-formatierten MSL-Datei. Das Kürzel MSL steht für Magick Scripting Language.

Abschließend verarbeitet das Skript mittels einer conjure-Anweisung die gesammelten Zeichenbefehle, was die Geschwindigkeit gegenüber einer schrittweisen Entwicklung des Bilds mittels einzelner mogrify-Anweisungen erheblich verbessert. Listing 3 zeigt eine MSL-Datei für ein kleines zweistöckiges Haus mit Fundament, fünf Fenstern und einer Haustür.

Listing 3

miniHaus.msl

<?xml version="1.0" encoding="UTF-8"?>
<image>
  <read filename='miniHaus.png' />
  <draw fill='sandybrown' primitive='rectangle 250, 1940, 810, 2500' />
  <draw fill='snow4' primitive='rectangle 0, 2500, 1060, 3000' />
  <draw fill='yellow' primitive='rectangle 270, 1970, 370, 2090' />
  <draw fill='yellow' primitive='rectangle 690, 1970, 790, 2090' />
  <draw fill='yellow' primitive='rectangle 440, 1970, 620, 2090' />
  <draw fill='yellow' primitive='rectangle 270, 2250, 370, 2370' />
  <draw fill='yellow' primitive='rectangle 690, 2250, 790, 2370' />
  <draw fill='yellow' primitive='rectangle 440, 2250, 620, 2500' />
  <write filename='miniHaus.png' />
</image>

Das diesem Workshop zugrunde liegende Shell-Skript baueHaus rufen Sie wie in Listing 4 gezeigt auf. Seinen Inhalt übernehmen Sie unverändert aus Listing 5. Der Einfachheit halber verzichtet das Skript auf eine umfangreiche Prüfung der übergebenen Parameter. Darüber hinaus kommen ausschließlich Positionsparameter zum Einsatz, bei denen die Aufrufreihenfolge in der Kommandozeile über die Zuordnung im Skript entscheidet. Möchten Sie das Skript verbessern, sollten Sie sich mit den Prüfmöglichkeiten der Shell auseinandersetzen sowie über den ergänzenden Einsatz von getopts im Skript nachdenken.

Listing 4

Beispiel-Syntax

$ ./baueHaus Datei Etagen FensterProEtage

Listing 5

baueHaus

#/bin/bash
if [ -z "$1" ]; then
  echo "Bitte mindestens den Namen der Ausgabedatei ohne Dateiendung als Parameter angeben, optional noch die Anzahl der Etagen und der Fenster pro Etage."
  exit
fi
zeilen=5
spalten=10
ox=250  # X-Offset des Hauses
oy=2500 # Y-Offset des Hauses
if [ $# -ge 3 ]; then
  zeilen=`expr $2`
  spalten=`expr $3`
  if [ $# -ge 4 ]; then
    ox=`expr $4`
  fi
fi
rh=250 # Raumhoehe
fh=120 # Fensterhoehe
fb=100 # Fensterbreite
dh=30  # Deckenhoehe
fa=20  # Fensterabstand
ma=300 # Mittelabstand/breite Treppenhauserker
mslfile="$1.msl"
hausbreite=`expr $spalten \* \( $fa + $fb \) + $fa + $ma`
haushoehe=`expr $zeilen \* \( $rh + $dh \)`
t="$ox, `expr $oy - $haushoehe`, `expr $hausbreite + $ox`, $oy"
mitte=`expr $hausbreite / 2 + $ox`
mahalb=`expr $ma / 2`
mittelinks=`expr $mitte - $mahalb + \( 3 \* $fa \)`
mitterechts=`expr $mitte + $mahalb - \( 3 \* $fa \)`
magick -size "`expr $ox + $hausbreite + $ox`"x3000 gradient:#0000ff-#ffffff -draw "rectangle $t" $1.png
str="0, 2500, `expr $ox + $hausbreite + $ox`, 3000"
echo '<?xml version="1.0" encoding="UTF-8"?>' > $mslfile
echo '<image>' >> $mslfile
eingabefile="<read filename=\"$1.png\" />"
echo $eingabefile >> $mslfile
echo "  <draw fill='sandybrown' primitive='rectangle $t' />" >> $mslfile
echo "  <draw fill='snow4' primitive='rectangle $str' />" >> $mslfile
oben=`expr $oy - $haushoehe + $dh`
links=$ox
for ((i=0; i < zeilen; i++)) ; do
  links=`expr $links + $fa`
  for ((j=0; j < spalten; j++)) ; do
    varInt=`expr $spalten / 2`
    if [ $j -eq $varInt ]; then
      links=`expr $links + $ma`
    fi
    t="$links, $oben, `expr $links + $fb`, `expr $oben + $fh`"
    echo "  <draw fill='yellow' primitive='rectangle $t' />" >> $mslfile
    links=`expr $links + $fb + $fa`
  done
  if [ $i -ne `expr $zeilen - 1` ]; then
    tm="$mittelinks, $oben, $mitterechts, `expr $oben + $fh`"
    echo "  <draw fill='yellow' primitive='rectangle $tm' />" >> $mslfile
  else
    tm="$mittelinks, $oben, $mitterechts, `expr $oben + $rh`"
    echo "  <draw fill='yellow' primitive='rectangle $tm' />" >> $mslfile
  fi
  links=$ox
  oben=`expr $oben + $rh + $dh`
done
ausgabefile="<write filename=\"$1.png\" />"
echo $ausgabefile >> $mslfile
echo '</image>' >> $mslfile
conjure msl:$mslfile

Beim Aufruf des Skripts geben Sie den Dateinamen ohne Dateiendung an. Die Endungen .msl sowie .png ergänzt der Code automatisch. Lassen Sie die Option für die Etagenanzahl und die Anzahl an Fenstern pro Etage leer, dann nutzt das Skript vorgegebene Standardwerte. Den Dateinamen dagegen müssen Sie immer angeben, anderenfalls bricht das Programm ab. Die Anzahl an Etagen und Fenstern pro Etage müssen Sie immer gemeinsam anhängen, da das Skript die Parameter sonst nicht komplett angegeben wertet und dann beide durch Standardwerte ersetzt.

Haben Sie alle Angaben gemacht, dürfen Sie optional noch einen vierten Parameter für die Korrektur des Grenzabstands zum Nachbargrundstück angeben, falls Sie wie in Listing 6 gezeigt die Mustersiedlung aus Abbildung 1 nachbauen möchten. Die Tabelle “Kommandozeilenwerkzeuge des ImageMagick-Pakets” erläutert unter anderem die Bedeutung der Werkzeuge montage und composite.

Listing 6

Bau einer Skyline

$ ./baueHaus haus1 8 8
$ ./baueHaus haus2 4 10 0
$ ./baueHaus haus3 5 10 0
$ ./baueHaus haus4 1 8 250
$ magick montage haus1.png haus2.png haus3.png haus4.png -geometry +0 -tile x1 strasse.png
$ magick composite logo: strasse.png -gravity NorthEast strasse-magick.png

Werkzeugname

Funktion

convert

Standardwerkzeug des ImageMagick-Paketes. Es kann Dateiformate konvertieren und Bilder skalieren, weichzeichnen, beschneiden, entrauschen, dithern, drehen, spiegeln und vieles mehr.

identify

Gibt eine Beschreibung des Formats und der Charakteristika einer oder mehrerer Grafikdateien aus.

mogrify

Bietet dieselben Funktionen wie convert, überschreibt jedoch im Gegensatz zu diesem die Ursprungsdatei.

composite

Überlappt zwei Bilder.

montage

Setzt mehrere Bilder zu einem zusammen.

compare

Zeigt die Unterschiede zwischen zwei Grafiken an (als Bericht einer mathematischen Analyse und visuell).

stream

Kopiert einzelne oder mehrere Pixel-Komponenten eines Bilds in ein anderes Format. Hauptsächlich für sehr große Bilddateien gedacht.

display

Zeigt ein Bild oder eine Bildersequenz über einen X-Server an.

import

Erstellt unter X11 Bildschirmfotos. Die Funktion sichert wahlweise den gesamten Bildschirmbereich, den Bereich eines Fensters oder ein definiertes Rechteck.

conjure

Interpretiert Skripte in der Magick Scripting Language (MSL) und führt sie aus.

Fazit

Wie Sie gesehen haben, mausert sich ImageMagick in Verbindung mit selbst geschriebenen Shell-Skripten zu einem sehr mächtigen Werkzeug, mit dem Sie nicht nur Bilder bearbeiten können, sondern bei Bedarf sogar automatisiert neue Grafiken erstellen. Es lohnt sich daher, sich mit diesem Thema einmal zu beschäftigen. (cla)

Der Autor

Ralf Kirschner arbeitet als Visual-Basic-Programmierer in einem Software- und Systemhaus. Der ausgebildete Systemadministrator erteilt auch freiberuflich Computerschulungen.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDF
LinuxUser 08/2022 KAUFEN
EINZELNE AUSGABE
ABONNEMENTS
TABLET & SMARTPHONE APPS
E-Mail Benachrichtigung
Benachrichtige mich zu:

Hinweis: Dieser Artikel ist älter als ein Jahr, enthaltene Informationen sind möglicherweise veraltet.

0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben