Bilder-Trickser (Linux-Magazin, März 1999)

Das Modul Image::Magick konvertiert Bilder von einem Format ins andere, schneidet Teile aus, flickt Text hinein, montiert Einzelbilder zu kombinierten Darstellungen, erzeugt animierte GIFs und morpht sogar Übergänge -- Sackzement!

ImageMagick dürfte Linux-Hardlinern wohlbekannt sein -- diese Distribution stellt einige Utility-Programme zur Verfügung, die allerhand mit verschiedensten Graphikformaten anstellen. Ist die Perl-Schnittstelle Image::Magick von Kyle Shorter erst einmal installiert (siehe Abschnitt Installation), eröffnet sie den programmierten Zugriff und vereinfacht die Automatisierung von Bilderkonvertierungsaufgaben.

Manipulieren

Der Zugriff ist objektorientiert, ein neues Bild entsteht mit dem Konstruktor

    $image = Image::Magick->new();

und mit Daten wird es mittels der Read-Methode gefüllt, die ein Bild von der Platte einliest:

    $image->Read("test.gif") && die "Cannot read!";

Image::Magick folgt leider nicht der Perl-Konvention, im Erfolgsfall einen wahren und im Fehlerfall einen falschen Wert zurückzuliefern, sondern es gibt die Anzahl erfolgreich eingelesener Dateien zurück -- im Erfolgsfall 1, und dann kommt der über eine And-Anweisung gekoppelte die-Befehl nicht zum Einsatz.

Zurückgeschrieben wird auf die gleiche Weise:

    $image->Write("test.jpg") && die "Cannot write!";

Weist der Name der Ausgabedatei eine andere Endung als das eingelesene Bild auf, konvertiert Image::Magick alles automatisch ins richtige Format, im vorliegenden Fall von GIF nach JPG. Weiter werden PNG, BMP, TIFF und viele andere unterstützt.

Ein Objekt vom Typ Image::Magick kann dabei nicht nur ein Bild aufnehmen, sondern eine ganze Reihe, jeder weitere Aufruf der Read-Methode hängt ein weiteres an. Listing tojpg.pl zeigt eine Anwendung: Die foreach-Schleife liest alle Dateien, deren Namen über die Kommandozeile hereinkommen, der Reihe nach ein und hängt deren Inhalt immer wieder an das Objekt, das sich hinter $images verbirgt, an.

Über alle Bilder im Objekt kann man nun iterieren, indem man die Objektreferenz $images in einen Array konvertiert und so über die Elemente von @$images eiert.

Diese sind wiederum Image::Magick-Objekte und verstehen daher neben den bereits besprochenen Methoden Read und Write auch Get (Attribute abfragen) und Set (Attribute setzen). Einige der wichtigsten Attribute:

    base_columns   Breite des Original-Bildes
    base_rows      Höhe des Original-Bildes
    base_filename  Name der Eingabedatei
    filesize       Größe der Eingabedatei in Bytes
    total_colors   Anzahl der Farben
    x-resolution   Auflösung in X-Richtung
    y-resolution   Auflösung in Y-Richtung

tojpg.pl macht nun aus jedem hereinkommenden Dateinamen einen mit der Endung *.jpg, ruft die Write-Methode auf und konvertiert so alle angegebenen Dateien ins JPG-Format. Damit's spannend anzusehen ist, ermittelt es mittels des filesize-Attributs auch noch die ursprüngliche Dateigröße, und läßt anschließend den -s-Operator auf das frischgeschriebene File los, der anstandslos dessen Größe zurückliefert. Die commify-Funktion setzt Punkte in die zurückkommenden Zahlen, um sie übersichtlich in Dreiergruppen zu strukturieren.

Ruft man also tojpg.pl mittels

    tojpg.pl images/*.bmp

auf, lautet die Ausgabe etwa

    images/lion.bmp->images/lion.jpg (717.606 -> 73.241 Bytes)
    images/pieks.bmp->images/pieks.jpg (688.018 -> 53.743 Bytes)
    images/schuh.bmp->images/schuh.jpg (708.302 -> 50.450 Bytes)

und für alle *.bmp-Dateien entstehen platzsparende JPEGs.

Listing tojpg.pl

    01 #!/usr/bin/perl
    02 ##################################################
    03 # Michael Schilli, mschilli@perlmeister.com 1999
    04 # Dateien nach JPG konvertieren
    05 ##################################################
    06 use Image::Magick;
    07 
    08 $images = Image::Magick->new();
    09 
    10 foreach $file (@ARGV) {
    11     $images->Read(filename => $file) && 
    12        die "Read failed";
    13 }
    14 
    15 foreach $image (@$images) {
    16 
    17     $oldsize = $image->Get('filesize');
    18 
    19     ($newname = $image->Get('base_filename'))
    20          =~ s/\.[^.]*$/.jpg/;
    21 
    22     $image->Write($newname) && die "Write failed";
    23 
    24     printf "%s->%s (%s -> %s Bytes)\n", 
    25           $image->Get('base_filename'),
    26           $newname,
    27           commify($oldsize),
    28           commify(-s $newname); 
    29 }
    30 
    31 ##################################################
    32 sub commify {    # Punkte in große Zahlen einfügen
    33 ##################################################
    34     my $number = shift;
    35     while($number =~ s/(\d)(\d{3})\b/\1.\2/) { }
    36     return $number;
    37 }

Manipulieren

Von den dutzenden von Methoden, die Image::Magick anbietet, um am Bildinhalt herumzuschustern, kann ich nur einen kleinen Teil besprechen: Crop zum Ausschneiden von Teilen, Scale zum Strecken und Stauchen, Frame zum Rahmenziehen und Annotate zum Hineinschreiben von Text. Für den Rest sei auf die ausführliche Dokumentation [2] verwiesen.

In Listing manip.pl nimmt die Crop-Methode ein geometry-Attribut entgegegen, das wie in X-Windows üblich im Format WxH+X+Y aufgebaut ist. Crop ersetzt das ursprüngliche Bild durch den Ausschnitt. So schneidet es aus dem dritten Bild aus Abbildung 2 die abgebildete Tafel aus und streckt das Ergebnis anschließend mit der Scale-Methode von 100 auf 200 Pixels. Dies hat zur Folge, daß sich das Bild etwas verzerrt. Einen Rahmen zeichnet dann die Frame-Methode, und zwar mit einer Innenkante von 2 Pixels, einer Außenkante von 2 Pixels und einer oberen und unteren Rahmenbreite von 20 Pixels. Die Annotate-Methode schließlich schreibt den Text Woohoo! in Rot 55 Pixels vom linken und 30 Pixels vom oberen Rand entfernt ins Bild. Als Font wird ``Helvetica'' gewählt. Das Ergebnis, das die Write-Methode wegschreibt, zeigt Abbildung 1.

Abb.2: Croppen, Ausziehen, Beschriften, Rahmen zeichnen

Listing manip.pl

    01 #!/usr/bin/perl
    02 ##################################################
    03 # Michael Schilli, mschilli@perlmeister.com 1999
    04 # Ein Bild manipulieren
    05 ##################################################
    06 use Image::Magick;
    07 
    08 $image = Image::Magick->new;
    09 
    10 $image->Read("images/lion.jpg") && 
    11     die "Cannot read";
    12 
    13 $image->Crop(geom=>'155x300+110+100');
    14 
    15 $image->Scale(width => 200);
    16 
    17 $image->Frame(inner => 2, outer => 2, 
    18               width => 20, height => 20);
    19 
    20 $image->Annotate(text => "Woohoo!", 
    21                  geometry => "+55+30",
    22                  pen => "red", font => 'helvetica',
    23                  pointsize => 30);
    24 
    25 $image->Write("../fig/manip.gif") && 
    26     die "Cannot write";

Montagen

Um Sammlungen von Bildern in Übersichtstafeln als kleine ``Thumbnails'' darzustellen, eignet sich die Montage-Methode. Listing montage.pl zeigt, wie Montage alle im Objekt $images gespeicherten Bilder entsprechend den gesetzten Attributen zusammenfaßt und eine Referenz auf ein neues Objekt, $montage, zurückliefert.

Das tile-Attribut gibt in der vorgestellten Form die Anzahl der Spalten an, in die Montage die Bildchen aufreiht, in der Form 3x4 ließe sich dazu auch noch die Anzahl der Zeilen begrenzen. Das auf True gesetzte Shadow-Attribut läßt rechts und unterhalb jedes Bildes einen kleinen Schatten erscheinen, der eine Tiefenwirkung vermittelt. Das label-Attribut schließlich gibt an, was als Beschriftung unter jeden Bild stehen wird, und neben normalem Text dürfen auch einige Makros stehen, hier die wichtigsten:

    %b   Dateigröße
    %f   Dateiname
    %h   Höhe
    %m   Typ (GIF, JPG)
    %w   Breite

Abb.2: Montierte Bilder

Listing montage.pl

    01 #!/usr/bin/perl
    02 ##################################################
    03 # Michael Schilli, mschilli@perlmeister.com 1999
    04 # Montage von Bildern
    05 ##################################################
    06 
    07 use Image::Magick;
    08 
    09 $images = Image::Magick->new();
    10 
    11 foreach $file (@ARGV) {
    12     $images->Read($file) && die "Read failed";
    13 }
    14 
    15 $montage = $images->Montage(label  => "%f", 
    16                             shadow => "True",
    17                             tile   => "2");
    18 
    19 $montage->Write("../fig/montage.gif") && 
    20     die "Write failed";

Morphen

Und schließlich kann Image::Magick auch noch animierte GIFs schreiben -- und die Animation sogar noch durch Morphen zwischen mehreren Bildern selbst erzeugen.

Listing morph.pl zeigt wie's geht: Für ein animiertes GIF, das langsam zwischen zwei Darstellungen wechselt und die Übergänge fließend herstellt, brauchen wir zwei statische GIFs, im Listing sind das book1.gif und book2.gif.

Damit das animierte GIF, wenn es sich bis zum zweiten Bild durchgemorpht hat, nicht ruckartig zum ersten Bild zurückspringt, sondern auch ordentlich abmorpht (dieses Wort gefällt mir), speichert morph.pl die Dreierfolge book1.gif-book2.gif-book1.gif in $images ab und fordert die Morph-Methode auf, jeweils 10 Übergangsbilder (Frames-Attribut) zwischen den Eckpunkten zu erzeugen.

Vor dem Schreiben des animierten GIFs mit der Write-Methode setzt morph.pl noch schnell die Attributwerte für delay und loop -- die Anzahl von Hunderstelsekunden zwischen den einzelnen Frames und die Gesamtzahl der Schleifendurchläufe bis das animierte GIF stehenbleibt.

Listing morph.pl

Abbildung 3: Bildübergänge durch Morphen

    01 #!/usr/bin/perl
    02 ##################################################
    03 # Michael Schilli, 1998 (mschilli@perlmeister.com)
    04 ##################################################
    05 
    06 use Image::Magick;
    07 
    08 $images = Image::Magick->new();
    09 
    10 foreach $file (qw(book1.gif book2.gif book1.gif)) {
    11     $images->Read(filename => $file) && die "Read failed";
    12 }
    13 
    14 $morph = $images->Morph(frames => 10);
    15 
    16 $morph->Set(delay => 20, loop => 10);
    17 
    18 $morph->Write("../fig/morph.gif") && 
    19     die "Cannot write";

Installation

Ausgehend von einer Redhat-5.2-Installation, die bereits über die Bibliotheken libpng, libtiff, libjpeg und libz verfügt, fehlen noch drei Sachen (Fehlt mehr, hilft README.txt der ImageMagick-Distribution weiter):

Die ImageMagick-Distribution, die in Version 4.1.8 vorliegt, holt man von der Original-Site

   ftp://ftp.wizards.dupont.com/pub/ImageMagick

oder vom niederländischen Spiegel

   ftp://ftp.oce.nl/pub/Internet/audio+video/ImageMagick

als komprimierte Tar-Datei ImageMagick-4.1.8.tar.gz. Bevor's ans Auspacken geht, brauchen wir noch die Freetype-Bibliothek libttf, die unter

    ftp://ftp.freetype.org/pub/freetype/freetype-1.2.tar.gz

liegt. Weiter braucht's noch die BZLIB, die's unter

    http://www.muraroa.demon.co.uk/bzip2-0.9.0c.tar.gz

gibt. Sind alle drei Distributionen in einem Verzeichnis, kann die Installiererei losgehen:

    
    tar zxfv ImageMagick-4.1.8.tar.gz
    cd ImageMagick-4.1.8
    
    tar zxfv ../bzip2-0.9.0c.tar.gz
    mv bzip2-0.9.0c bzlib
    cd bzlib 
    make
    cd ..
    
    tar zxfv ../freetype-1.2.tar.gz
    mv freetype-1.2 ttf
    cd ttf 
    ./configure
    make
    cd ..
    
    ./configure
    make
    make install
    
    cd PerlMagick
    make install
    cd ..
    
    cd ttf
    make install
    cd ..

Die Perl-Bibliothek PerlMagick liegt dem Ganzen als Version 1.60 schon bei und wird mit installiert. Falls übrigens die Environment-Variable LD_LIBRARY_PATH nicht den Pfad /usr/local/lib enthält, bitte nachziehen, sonst funktionieren die Binaries, die der ImageMagick-Distribution beiliegen (wie z.B. das handliche Programm convert) nicht.

Sodann sollte ein einfaches

    #!/usr/bin/perl -w
    use Image::Magick;

funktionieren. Uff!

Fröhliches Abmorphen, bis zum nächsten Mal -- wenn es wieder heißt: News aus Perl-Land!

Literatur

[1]
PerlMagick-Dokumentation: http://www.wizards.dupont.com/cristy/www/perl.html

[2]
ImageMagick User Guide: ftp://ftp.yggdrasil.com/mirrors/site/ftp.wizards.dupont.com/pub/ImageMagick/ImageMagick.pdf

Michael Schilli

arbeitet als Software-Engineer bei Yahoo! in Sunnyvale, Kalifornien. Er hat "Goto Perl 5" (deutsch) und "Perl Power" (englisch) für Addison-Wesley geschrieben und ist unter mschilli@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com.