AA_precision_bugdog_sxc_730329.jpg

© Bugdog, sxc.hu

Fix und fertig

Bilder verarbeiten mit der Magick Scripting Language

06.05.2011
,
Die Magic Srcipting Language hilft Ihnen, Bilder mit einem fest definierten Satz an Parametern zu bearbeiten. In Verbindung mit geschickter Shell-Programmierung nutzen Sie so die Ressourcen eines Systems optimal aus.

Als erstes Ergebnis beim Vereinfachen der Arbeit entstand im Teil 1 [1] ein Skript, das alle Parameter zum Verändern als Variablen enthielt. Das funktioniert sehr effizient, und es gelingt sehr leicht, die Modifikationen eindeutig einem ausgewählten Bildformat und Dateimuster zuzuordnen.

Möchten Sie hingegen mehrere Bilder individuell anzupassen, stoßen Sie so recht schnell an Grenzen und müssen sich eine brauchbare Alternative überlegen. In manchen Fällen hilft die Magick Scripting Language (MSL) weiter, und daher verknüpft dieser Workshop die Technik mit den Erkenntnissen aus dem Shell-Skripting aus dem Teil 1.

Serie Automatische Bildverarbeitung

Teil 1: Imagemagick, Graphicsmagick LU 03/2011, S. 84 http://www.linux-community.de/22947
Teil 2: Magick Scripting Language LU 06/2011, S. 84 http://www.linux-community.de/22948

Magische Sprache

MSL gehört zu den Imagemagick- und Graphicsmagick-Paketen. Es handelt sich dabei um einen XML-Dialekt [2] und als solcher folgt er daher den entsprechenden Konventionen. Bei MSL-Dateien handelt es sich um reine Textdateien mit einem Markup. Jeder Texteditor eignet sich zum Bearbeiten, und es existieren zudem Werkzeuge, die die Syntax auf Korrektheit prüfen (siehe Kasten "Validieren einer XML-Datei").

Validieren einer XML-Datei

Für komplexere XML-Dateien empfiehlt sich in jedem Fall das Überprüfen auf syntaktische Korrektheit. Dazu zählt beispielsweise, ob Sie jedes geöffnete Element in der richtigen Knotenebene wieder geschlossen, alle Attribute korrekt in Anführungszeichen gesetzt haben und ob Kommentare in sich abgeschlossen sind.

Das Standardisierungsgremium für Internetprotokolle W3C bietet dazu auf seiner Webseite [5] die Möglichkeit an, eigene XML-Dateien hochzuladen und sofort auf Korrektheit zu überprüfen. Das Ergebnis sieht dann analog zu Abbildung 1 aus.

Abbildung 1: Ergebnis der W3C-Validierung.

Auf der Kommandozeile hilft das Werkzeug Xmllint [6]. Es gehört zur Libxml, der XML-Bibliothek aus dem Gnome-Projekt. Für Debian und Ubuntu heißt das dazugehörige Paket Libxml2-utils. Sie verwenden Xmllint folgendermaßen:

$ xmllint --noout --valid bild.xml

Die Option --noout unterdrückt die Ausgabe der XML-Datei auf der Standardausgabe, --valid sorgt für das Überprüfen der angegebenen Datei auf Korrektheit ("XML-Konformität"). Gefundene Fehler gibt die Software am Ende auf der Standardausgabe aus.

In einer MSL-Datei legen Sie die Modifikationen fest, die Sie an einer Bilddatei vornehmen möchten. Diese einzelnen Anweisungen führt der Interpreter nacheinander aus. Für jedes Kommando gibt es in MSL ein eigenes Element, oft mit zusätzlichen Attributen. XML-Parser legen die Werte der Elemente und Attribute in einer Baumstruktur ab und haben so die Möglichkeit, die Daten effizient zu verarbeiten. Listing 1 zeigt eine einfache MSL-Datei.

Listing 1

<?xml version="1.0" encoding="UTF-8"?>
<image>
 <read filename="bild.png" />
 <resize geometry="640x480" />
 <write filename="bild-vorschau.png" />
</image>

In Zeile 1 steht der Kopf ("Header") der XML-Datei mit der verwendeten XML-Version (hier: 1.0) und der Bezeichnung für das Codierungsschema, in dem die einzelnen Zeichen in der XML-Datei abgespeichert sind (hier: UTF-8). Die Zeilen 2 und 6 markieren mit den Knoten <image> und </image> den Beginn und das Ende der Informationen zu einer Bilddatei.

Die Zeilen 3 bis 5 enthalten die Anweisungen, die ein Interpreter auf die Bilddatei anwendet. Zunächst erfährt der Konverter in Zeile 3 aus dem Element read mit dem Attribut filename den Name der angegebenen Bilddatei (bild.png) und liest diese ein. Anschließend skaliert er die Bilddaten auf die aus dem Attribut geometry des Elements resize gewonnene Breite (640 Pixel) und eine Höhe (480 Pixel). Das Element write gibt über das Attribut filename Aufschluss darüber, wie die Ausgabedatei heißt (bild-vorschau.png). Falls die angegebene Datei bereits existiert, überschreibt der Konverter diese ohne Vorwarnung mit dem neuen Bildinhalt.

Die MSL-Datei verarbeiten Sie mit dem Werkzeug Conjure (Teil von Imagemagick [3]) oder über die Option conjure von Graphicsmagick [4]. Die Aufrufe sehen für die beiden Werkzeuge wie folgt aus:

$ conjure bild-einfach.xml
$ gm conjure bild-einfach.xml

Beide interpretieren das übergebene MSL-Skript und arbeiten die Anweisungen wunschgemäß ab. Die beiden Programme verhalten sich konform zu traditionellen Unix-Tools – nur bei Fehlern erfolgt eine Nachricht an den Benutzer, ansonsten schweigen beide beharrlich.

Haben Sie mehr als ein einziges Bild zu bearbeiten, legen Sie für jede Bilddatei eine passende MSL-Datei an und erledigen danach mit Conjure das Umwandeln. Von Hand ist das aufwendig. Mit einem Shellskript wie in Listing 2 verkürzen Sie den Aufwand erheblich.

Listing 2

#!/bin/bash
for bilddatei in $(find . -type f -printf '%P\n');
do
        # Name und Dateityp ermitteln und prüfen
        fname=$(basename ${bilddatei})
        dateiname=${fname%.*}
        dateityp=${fname#*.}
        case "$dateityp" in
        png|PNG)
                msl_datei="${bilddatei%.*}.xml"
                echo "Lege an: ${msl_datei} für ${bilddatei}"
                cat > ${msl_datei} << EOF
<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="${fname}" />
<resize geometry="200x200" />
<write filename="${dateiname}-vorschau.png" />
</image>
EOF
                # Bild transformieren
                gm conjure "$msl_datei"
        esac
done

Das Skript analysiert zuerst den Inhalt des aktuellen Verzeichnisses und merkt sich alle Dateien in einer Liste (Zeile 2). In der For-Schleife überprüft es jeden Eintrag mittels File, ob es sich dabei um eine Bilddatei mit der passenden Dateiendung handelt (Zeile 9). Für jede PNG-Datei aus der Liste generiert es eine passende MSL-Datei und speichert diese ab (Zeilen 11 bis 20). In Zeile 11 setzt es den vollständigen Name der MSL-Datei aus dem Namen der Bilddatei und dem Suffix .xml zusammen.

Die Zeilen 14 bis 19 repräsentieren die Zeilen der MSL-Datei, wobei an den passenden Stellen die Inhalte der beiden Variablen fname und dateiname eingesetzt werden. Danach ruft das Skript in Zeile 22 Graphicsmagick mit der soeben erzeugten MSL-Datei als Parameter aufgerufen und nimmt die Bildtransformationen vor.

Um das Skript zu starten, rufen Sie es als ./msl.sh in einem Terminal auf. Einmal durchgelaufen liefert das Skript für jede PNG-Datei ein passendes Vorschaubild mit der maximalen Höhe beziehungsweise maximalen Breite von 200 Pixel.

Performance

Ein Kriterium für die Qualität dieses Ansatzes liegt in der Laufzeit des Skriptes. Wir erhalten diese, in dem wir die Zeit zwischen dem Start und dem Ende messen. Dabei kommt das Unix-Kommando Time zum Einsatz. Der Aufruf time ./msl.sh liefert als Ergebnis auf dem Testsystem eine Laufzeit von rund einer Minute für rund 100 Bilddateien. Jede Bilddatei hat eine Auflösung von 2048x1536 Pixel und ist etwa 1 MByte groß.

Um die Laufzeit zu verringern, hilft es, sich das Skript etwas genauer anzuschauen und Optimierungspunkte zu finden. Großes Potential zum Verbessern beinhaltet Zeile 22. In jedem Schleifendurchlauf startet ein GM-Prozess, und erst nach dessen Ende setzt das Skript seine Arbeit mit der nächsten Iteration der For-Schleife fort. Jeder GM-Aufruf ist unabhängig von einem anderen. Ergänzen Sie den Aufruf in Zeile 22 um ein Ampersand (&) am Ende der Zeile, läuft jeder Aufruf ab jetzt im Hintergrund ab und blockiert das Abarbeiten der Schleife nicht weiter.

Der Laufzeittest auf einem Rechner mit Doppelkernprozessor ergibt eine Zeitersparnis von 50 Prozent auf eine halbe Minute (siehe Tabelle "Laufzeittests"). Existieren weitere Prozessorkerne, verringert sich die Laufzeit insgesamt stärker, aber nicht mehr genau im gleichen Verhältnis wie bisher. Der Aufwand zum Verwalten der Prozesse fällt deutlich mit ins Gewicht. Trotzdem haben Sie damit eine Lösung, mit der Sie zunächst zufrieden sein können, da es die heutigen Mehrkernprozessoren deutlich besser ausnutzt.

Laufzeittests

Skript Laufzeit  
    35 Bilder 100 Bilder
Listing 2 (ohne Parallelisierung) 0:23 0:58
Listing 2 (mit Parallelisierung) 0:10 0:30
Listing 4 (ohne Parallelisierung) 0:19 1:00
Listing 4 (mit Parallelisierung) 0:07 0:29

Das Tool Htop [8] zeigt sehr schön die Last auf der Hardware (Abbildung 2). Beide Prozessoren kommen in etwa gleichmäßig zum Zuge und sorgen für unser erfreuliches Ergebnis.

Abbildung 2: Htop visualiert die Systemlast.

Die verwendete Methoden beim Programmieren hat aber Nachteile: Das Skript startet unabhängig von der Lastsituation des Gesamtsystems sehr schnell genauso viele Prozesse, wie es Bilder findet. 100 Bilddateien liegen noch im Rahmen, nimmt die Zahl der Dateien jedoch deutlich zu, entbrennt auf der Maschine ein Kampf um die Ressourcen. Der Festplattenzugriff und die RAM-Anforderungen treiben den Kernel zum Auslagern von Daten in die Swap-Partition.

Eigentlich sollte das Skript genau so viele Prozesse starten, wie CPU-Kerne vorhanden sind. Auf jedem Kern arbeitet der Rechner einen Prozess ab, ohne sich Rechenkapazität mit einem anderen Prozess zu teilen. Die Information zur Anzahl der CPU-Kerne steht im Proc-Filesystem [9]. Sie ermitteln sie zur Laufzeit mit dem folgenden Shell-Befehl:

$ cat /proc/cpuinfo | grep "cpu cores"

Abbildung 3 zeigt die Ausgabe des Kommandos auf einem Quadcore-System mit zwei Kernen pro CPU.

Abbildung 3: Die Ausgabe von cpuinfo liefert Informationen über die Anzahl der CPU-Kerne.

Solche Informationen im Skript auf die Anzahl der gestarteten Prozesse abzubilden, wäre Thema eines Artikels über optimierten Ressourceneinsatz und sprengt den Rahmen dieses Beitrags. Das Grundprinzip lautet: Nur soviel Arbeit starten, wie auch Ressourcen bereit stehen.

Über diese Maßnahmen hinaus gerät ein weiterer Flaschenhals in den Blick – den genutzten Datenträger. Mit dem bisherigen Skript steigt nicht nur die Menge der Lese- und Schreibzugriffe auf dem Datenträger, es erzeugt auch etliche MSL-Dateien mit einer Größe von etwa 300 Byte. Diese belegen vergleichsweise viel Plattenplatz – meist einen ganzen Block pro Datei. Ein Block hat je nach Dateisystem eine Größe ab 512 Byte aufwärts. Da die MSL-Dateien recht klein sind, verschwendet das Skript mit dieser Methode eigentlich enorm viel Platz.

Weiterhin ist die Anzahl der I-Nodes pro Dateisystem begrenzt. Was bedeutet, sie dürfen nicht beliebig viele Dateien anlegen. Es besteht die Gefahr, dass das Skript die Eintragsliste auf dem Datenträger mit vielen kleinen Dateien füllt und Sie den Speicherplatz nicht vollständig nutzen können.

Um den beschriebenen Nachteil aufzuheben, besteht der nächste Schritt konsequenterweise darin, möglichst nur eine einzige MSL-Datei für alle Bilddateien zu benutzen. Dazu legen Sie ein MSL-Skript mit Variablen an, welches für alle Bilddateien passt. Listing 3 zeigt ein Beispiel für so ein Skript.

Listing 3

<?xml version="1.0" encoding="UTF-8"?>
<image>
 <read filename="%[basename].png" />
 <resize geometry="%[dimensions]" />
 <write filename="%[basename]-vorschau.png" />
</image>

Darin kommen zwei Variablen zum Einsatz – basename und dimensions (Zeile 3, 4 und 5). Ins MSL-Skript fügen Sie die Variablen in eckige Klammern ein und stellen diesen ein Prozentzeichen voran. Die erste Variable (basename) beinhaltet den Namen der Bilddatei ohne Erweiterung, die zweite die Bildgröße, auf die Sie das Bild skalieren wollen. Für beiden Variablen übergeben Sie die Werte an Graphicsmagick als Parameter.

Der folgende Aufruf skaliert die Datei bild.png auf eine maximale Breite von 200 Pixel und speichert das Ergebnis in der Datei bild-vorschau.png ab:

$ gm conjure -basename bild -dimensions 200x200 png.xml

Um nun auch alle PNG-Dateien zu bearbeiten, modifizieren wir noch unser Shellskript entsprechend. Wir passen die Variablennamen im Aufruf von Graphicsmagick an und übergeben das MSL-Skript aus Listing 3 als Parameter.

Listing 4

#!/bin/bash
for bilddatei in $(find . -type f -printf '%P\n');
do
        # Name und Dateityp ermitteln und prüfen
        fname=$(basename ${bilddatei})
        dateiname=${fname%.*}
        dateityp=${fname#*.}
        case "$dateityp" in
        png|PNG)
                msl_datei="${bilddatei%.*}.xml"
                echo "Bearbeite: ${bilddatei}"
                msl_datei="png.xml"
                dimension="200x200"
                # Bild transformieren
                gm conjure -basename "$dateiname" -dimensions "$dimension" "$msl_datei"
        esac
done

Bei Laufzeittests analog zu Listing 2 ergibt sich ein ähnliches Bild: Für rund 100 Bilddateien benötigt das Skript rund 1 Minute, um Sie zu verkleinern. Ergänzen Sie den Aufruf von Graphicsmagic in Zeile 17 am Ende um ein "&", laufen die Prozesse wiederum parallel ab und die Laufzeit verringert sich um 50 Prozent auf etwa eine halbe Minute.

Im Vergleich zu Listing 2 erreichen Sie damit ein ähnliches Zeitverhalten, aber mit deutlich geringeren Anforderungen an die erforderlichen Ressourcen. Sie benötigen jedoch nur noch eine einzige MSL-Datei für alle Bilddateien, anstatt diese dynamisch zur Laufzeit zu erzeugen.

LinuxCommunity kaufen

Einzelne Ausgabe
 
Abonnements
 

Related content

  • Bilddaten automatisiert bearbeiten
    Viele Bilder manuell zu bearbeiten erweist sich schnell als Geduldspiel. Mit pfiffigen kleinen Werkzeugen automatisieren Sie solche Arbeiten mühelos.
  • Bildverarbeitung mit den Skriptsprachen Perl und Python
    Mit nur wenigen Zahlen Code korrigieren Sie das Format digitaler Bilder, passen die Metadaten an oder beschriften die Fotos für den Upload in ein Online-Album.
  • Bildbearbeitung mit ImageMagick
    Wer nicht 1000 Urlaubsbilder von Hand skalieren, normieren, beschneiden oder mit Wasserzeichen versehen möchte, für den führt kein Weg an der Werkzeugsammlung ImageMagick vorbei.
  • Grafiken schnell passend gemacht
    Sei es für die Verwendung innerhalb von Web-Seiten oder andere Zwecke: Häufig müssen Grafiken von einem Format in ein anderes konvertiert und dabei noch in Größe und Qualität verändert werden. Mit convertaus dem ImageMagick-Paket machen Sie das schnell auf der Kommandozeile.
  • convert
    Zum ImageMagick-Paket gehören eine ganze Reihe von Tools zur Bildbearbeitung. Bei convert ist der Name Programm: Es konvertiert auf der Kommandozeile zwischen verschiedenen Grafikformaten. Darüber hinaus kann convert PostScript-Dateien erstellen, Bilder beschriften, Filter verwenden und vieles mehr.
Kommentare

Infos zur Publikation

title_2014_08

Digitale Ausgabe: Preis € 5,95
(inkl. 19% MwSt.)

Mit der Zeitschrift LinuxUser sind Sie als Power-User, Shell-Guru oder Administrator im kleinen Unternehmen monatlich auf dem aktuelle Stand in Sachen Linux und Open Source.

Sie sind sich nicht sicher, ob die Themen Ihnen liegen? Im Probeabo erhalten Sie drei Ausgaben zum reduzierten Preis. Einzelhefte, Abonnements sowie digitale Ausgaben erwerben Sie ganz einfach in unserem Online-Shop.

NEU: DIGITALE AUSGABEN FÜR TABLET & SMARTPHONE

HINWEIS ZU PAYPAL: Die Zahlung ist auch ohne eigenes Paypal-Konto ganz einfach per Kreditkarte oder Lastschrift möglich!       

Tipp der Woche

Schnell Multi-Boot-Medien mit MultiCD erstellen
Schnell Multi-Boot-Medien mit MultiCD erstellen
Tim Schürmann, 24.06.2014 12:40, 0 Kommentare

Wer mehrere nützliche Live-Systeme auf eine DVD brennen möchte, kommt mit den Startmedienerstellern der Distributionen nicht besonders weit: Diese ...

Aktuelle Fragen

Server antwortet mit falschem Namen
oin notna, 21.07.2014 19:13, 1 Antworten
Hallo liebe Community, Ich habe mit Apache einen Server aufgesetzt. Soweit, so gut. Im Heimnet...
o2 surfstick software für ubuntu?
daniel soltek, 15.07.2014 18:27, 1 Antworten
hallo zusammen, habe mir einen o2 surfstick huawei bestellt und gerade festgestellt, das der nic...
Öhm - wozu Benutzername, wenn man dann hier mit Klarnamen angezeigt wird?
Thomas Kallay, 03.07.2014 20:30, 1 Antworten
Hallo Team von Linux-Community, kleine Zwischenfrage: warum muß man beim Registrieren einen Us...
openSUSE 13.1 - Login-Problem wg. Fehler im Intel-Grafiktreiber?
Thomas Kallay, 03.07.2014 20:26, 8 Antworten
Hallo Linux-Community, habe hier ein sogenanntes Hybrid-Notebook laufen, mit einer Intel-HD460...
Fernwartung für Linux?
Alfred Böllmann, 20.06.2014 15:30, 7 Antworten
Hi liebe Linux-Freunde, bin beim klassischen Probleme googeln auf www.expertiger.de gestoßen, ei...