Aus Linux-Magazin 09/2003

Gimp mit Perl automatisieren

Gimp, das GNU Image Manipulation Program, verbessert und manipuliert digitale Bilder mit anspruchsvollen Filterfunktionen. Statt immer wieder die gleichen Manöver mit der Maus auszuführen, lassen sich die Vorgänge mit Gimps Perl-Schnittstelle sogar automatisieren.

Kürzlich stolperte ich über die Webseite des Fotofachmanns Eric Jeschke, der auf[2] allerlei Tricks beschreibt, um digitale Fotos mit Gimp[1] zu verbessern. Das auf Linux frei erhältliche Fotobearbeitungsprogramm entfernt die roten Blitzaugen, reduziert eine zu starke Tiefenschärfe und Bildregionen werden nachbelichtet oder abgewedelt, wie man im Fotolaborjargon sagt, und was es der Gimmicks mehr gibt.

Perl für Gimp-Füchse

In[3] findet sich eine zwar kurze, aber einigermaßen gelungene Einführung in Gimps Perl-Schnittstelle. Marc Lehmann hat diese Erweiterung als Alternative zu Gimps Script-Fu geschrieben, sie ist ein Bestandteil der Source-Distribution. Damit kann ein Perl-Skript neue Menüs in Gimp einfügen, sonst per Maus ausgelöste Aktionen automatisieren und ungehemmt Bilddaten manipulieren.

Eines der Rezepte auf[2] erklärt, wie man ein Bild mit wenigen Handgriffen dramatischer oder traumartiger aussehen lässt. Die einzelnen Schritte sind:

  • Das Bild in Gimp laden und den »Layers«-Dialog
    öffnen.
  • Die erste Ebene in eine zweite kopieren (Duplizieren-Icon am
    unteren Rand des Dialogs) und den Modus »Overlay«
    wählen.
  • Die Farben mittels »Image | Colors | Levels« leicht
    aufhellen.
  • Die durch den Overlay entstandenen Ecken per »Filters |
    Blur | Gaussian Blur«-Unschärfe mit dem Radius 20
    abrunden.

Der Effekt ist erstaunlich, Abbildung 1 zeigt das Original und die Gimp-Fälschung[4]. Wendet man das Verfahren jedoch ein paarmal von Hand auf verschiedene Fotos an, ermüden schnell die Finger und eine innere Stimme ruft lautstark nach einem Skript.

Das im Folgenden vorgestellte Skript »dreamify« funktioniert sowohl als Gimp-Plugin, das Gimp mit einem neuen Menüpunkt anreichert und seine Spielchen mit dem gerade geladenen Bild treibt, als auch von der Kommandozeile aus: »dreamify test.jpg« lädt das Bild im JPEG-Format, führt die eben beschriebenen Manipulationen aus, lässt anschließend die beiden Ebenen durch »Layers | Flatten Image« kollabieren und schreibt das Ergebnis im PNG-Format nach »test.png«.

In Perl geschriebene Gimp-Plugins binden zwei Module ein:

use Gimp qw(:auto);
use Gimp::Fu;

Das erste Modul-Tag »:auto« ist optional, es importiert alle verfügbaren Gimp-Bibliotheksfunktionen praktischerweise in den Namensraum des Skripts und definiert nützliche Konstanten.

Die Plugin-Skripte müssen die »register()«-Funktion aufrufen, um das Plugin in Gimp zu registrieren. Die Funktion legt fest, unter welchem Menüpunkt es hängt, und definiert, was passiert, wenn der Anwender darauf drückt, und gibt zudem eine kurze Beschreibung für die Online-Hilfe. Das Plugin-Skript sollte am Ende mit »exit main;« wieder die Kontrolle an Gimp zurückgeben, das es während des Startvorgangs ausführt und sich die vorgenommenen Einstellungen zur weiteren Verwendung merkt.

Abbildung 1: Original und Fälschung: Links eine triste Straßenszene in San Franciscos Stadtviertel Soma (South of Market). Rechts die gefilterte Fassung, die dem Bild eine dramatischere Note verleihen soll. Sie besteht aus zwei Ebenen, die per Overlay-Technik kombiniert sind.

Abbildung 1: Original und Fälschung: Links eine triste Straßenszene in San Franciscos Stadtviertel Soma (South of Market). Rechts die gefilterte Fassung, die dem Bild eine dramatischere Note verleihen soll. Sie besteht aus zwei Ebenen, die per Overlay-Technik kombiniert sind.

Menü oder Kommando

Wenn Gimp das Plugin beim Hochfahren aufruft, gibt es ihm Parameter der Form

-gimp 9 8 -query 0

mit. Das nutzt Zeile 19 in »dreamify« (Listing 1) aus um festzustellen, ob der Aufruf von Gimp oder von der Kommandozeile kam. Im ersten Fall beginnt das Plugin damit, das aktuelle Bild zu bearbeiten. Von der Kommandozeile aus aufgerufen lädt das Skript hingegen erst die als Argument angegebene Datei im JPEG-Format und schreibt das Ergebnis nach getaner Arbeit als PNG-Bild auf die Festplatte.

Damit Gimp auf dem aktuellen Bild operiert, wenn der Benutzer den »Dreamify«-Eintrag im Menü auswählt, legt die Zeile 21 den String »”<Image>/Perl-Fu/Dreamify”« in der Variablen » ab. Diese Variable teilt später der »register«-Methode mit, wo sie den Eintrag aufhängen soll.

Im Falle eines Kommandozeilen-Aufrufs bleibt » auf dem in Zeile 16 gesetzten Wert »”<Toolbox>/Xtns/Mike/ Dreamify”«, denn obwohl Gimp in diesem Fall gar kein Menü anlegen muss, besteht es auf einem gültigen Eintrag. Der »<Image>«-Eintrag von oben würde hier nur eine wüste Fehlermeldung verursachen. Zeile 24 speichert den Namen der Bilddatei in ». Fehlt der Name, dann verzweigt Zeile 25 zur POD-Dokumentation und bricht das Skript ab.

Registrierung

Außer dem Menüpunkt, unter dem die neue Funktion liegt, teilt die »register()«-Funktion Gimp auch mit, welche Funktion es aufrufen soll (Zeile 40: »dreamify()«), welche Parameter es übergeben muss (keine), welche Rückgabeparameter Gimp erwarten kann (keine) und welche Teile der POD-Dokumentation in die verschiedenen Sektionen der Gimp-Laufzeit-Hilfe (siehe Abbildung 2) einfließen sollen.

Die »dreamify()«-Funktion wird von Gimp mit einem Handle zu dem aktuellen Image und Layer aufgerufen, wenn der Benutzer den neuen Menüpunkt anwählt (Abbildung 3). Und zwar nur, weil der Eintrag unter »<Image>« hängt – hinge er unter »<Toolbox>«, wäre das nicht der Fall, da dieses Menü nicht Image-spezifisch wäre.

Falls der Aufruf von der Kommandozeile aus erfolgte, sind » und » nicht gesetzt. In diesem Fall lädt die in Zeile 52 aufgerufene Gimp-Bibliotheksfunktion »file_jpeg_load« das Bild von der Platte, die Pfadangabe stammt vom Hauptprogramm und liegt in der Variablen ».

Abbildung 2: Die Manualseite der neuen Gimp-Erweiterung Dreamify ist im DB-Browser zu finden. Damit weiß der Benutzer, wie und wofür er dieses Skript einsetzen kann.

Abbildung 2: Die Manualseite der neuen Gimp-Erweiterung Dreamify ist im DB-Browser zu finden. Damit weiß der Benutzer, wie und wofür er dieses Skript einsetzen kann.

Praktische Dokumentation im DB-Browser

Parameter, die für interne Funktionen wie »file_jpeg_load« notwendig sind, dokumentiert Gimp praktischerweise in der Online-Hilfe. Man geht dazu in der Gimp-Toolbox ins Menü »Xtns | DB Browser«, sucht per Stichwort in Hunderten von Funktionen und bekommt deren Signatur mit Kurzhilfe angezeigt – praktisch! Auch das neue Plugin taucht nach der Installation dort auf, wie Abbildung 2 bestätigt.

Allerdings unterscheiden sich die Einträge im DB-Browser manchmal von den im »Gimp«-Perl-Modul verwendeten Signaturen: Als Faustregel gilt, dass man »run_mode« immer weglassen darf, weil das »Gimp«-Modul selbst weiß, ob der Aufruf interaktiv oder im Hintergrund erfolgte. Während die Dokumentation sowohl ein Image- als auch ein Layer-Handle fordert, genügt ein Layer-Handle, da das »Gimp«-Modul von diesem intern auf das Image schließt. Außerdem fungieren die Handles auch als Objektreferenzen, die folgenden drei Aufrufe sind daher identisch:

gimp_layer_copy($layer, $add_alpha);
$layer->layer_copy($add_alpha);
$layer->copy($add_alpha);

In Zeile 54 des Dreamify-Skripts ermittelt »image_get_active_layer()« die aktive (und normalerweise einzige) Ebene des ihr übergebenen Bildes. Zeile 58 kopiert den Layer in einen neuen, das Flag, das einen Alpha-Channel hinzufügt, bleibt auf »0« gesetzt.

Ebenen stapeln

Die darauf folgende Methode »layer_set_mode()« setzt den Modus der neuen Ebene auf »OVERLAY_MODE«, die Konstante hat das Skript wegen des »:auto«-Tags in Zeile 9 bequemerweise automatisch importiert. Diese Aktion entspricht der Wahl des Overlay-Modus im Layer-Dialog von Gimp. »image_add_layer()« in Zeile 60 fügt den neu erzeugten Layer zum Bild hinzu, der auf »-1« gesetzte Parameter stapelt ihn über die bestehende Basisebene.

Damit der Gimp-Benutzer bei lange dauernden Aktionen nicht die Nerven verliert, initialisiert »dreamify« mit »Gimp ->progress_init(” Titel“)« eine Fortschrittsanzeige und aktualisiert sie nach den einzelnen Schritten mit »Gimp ->progress_update( Bruchteil)«. Bruchteil ist ein Fließkommawert zwischen 0 und 1 und gibt die Position des Fortschrittsbalkens an.

Zeile 63 skaliert das Bild auf 800 mal 600 Pixel, hierzu ruft sie die ab Zeile 89 definierte Funktion »scale_image_down« auf. Diese erwartet ein Image-Handle sowie die gewünschte Maximalgröße des Bildes und verändert das Bild direkt.

Abbildung 3: Der neue Perl-gesteuerte Menüpunkt »Dreamify« erscheint in Gimps Kontextmenü: Nach dem Maus-Rechtsklick muss der Anwender in das »Perl-Fu«-Untermenü wechseln.

Abbildung 3: Der neue Perl-gesteuerte Menüpunkt »Dreamify« erscheint in Gimps Kontextmenü: Nach dem Maus-Rechtsklick muss der Anwender in das »Perl-Fu«-Untermenü wechseln.

Passend skalieren

Die Funktion berechnet zunächst, ob das Bild im Querformat statt – wie erwartet – im Hochformat vorliegt. In diesem Fall korrigiert sie die Maximalgröße auf 600 mal 800 Pixel. Überschreiten Breite und Höhe des Bildes ihren Maximalwert, wird das Bild mittels »image _scale« so verkleinert, dass die Bildproportionen erhalten bleiben.

Das Glätten des Bildes findet in Zeile 67 per »plug_in_gauss_iir« mit dem Gaussian-Blur-Algorithmus statt, der Unschärferadius beträgt 20. Die Funktion aktiviert sowohl horizontale also auch vertikale Unschärfe, dazu setzt das Skript die beiden letzten Parameter auf True. Zeile 72 schließlich hellt die Farben im Bild mit einem Gammafaktor von »1.5« etwas auf, Ein- und Ausgabebereiche bleiben mit 0 bis 255 konstant. Das entspricht einem etwas nach links verschobenen Intensity-Regler in Gimps »Image | Colors | Level«-Dialog. Anschließend fügt Zeile 75 die beiden Layer zu einem einzigen zusammen, Zeile 76 holt dessen Handle.

Listing 1:
Dreamify-Skript

001 #!/usr/bin/perl
002 ###########################################
003 # dreamify - Gimp Image Dreamifier
004 # Mike Schilli, 2003 (m@perlmeister.com)
005 ###########################################
006 use warnings;
007 use strict;
008 
009 use Gimp qw(:auto);
010 use Gimp::Fu;
011 use Pod::Usage;
012 
013 use Log::Log4perl qw(:easy);
014 Log::Log4perl->easy_init($DEBUG);
015 
016 my $menu = "<Toolbox>/Xtns/Mike/Dreamify";
017 my $jpg_file;
018 
019 if(grep /-gimp/, @ARGV) {
020     # Call from within Gimp
021   $menu = "<Image>/Perl-Fu/Dreamify";
022 } else {
023     # Call from the command line
024   $jpg_file = $ARGV[0];
025   pod2usage("No file")
026       unless defined $jpg_file;
027 }
028 
029 register(
030   "perl_fu_dreamify",    # Name
031   "Dreamify a Picture",  # Blurb
032   "=pod(HELP)",          # Help
033   "=pod(AUTHOR)",        # Author
034   "=pod(COPYRIGHT)",     # Copy
035   "=pod(DATE)",          # Date
036   $menu,                 # Menu
037   "",                    # Images accepted
038   [],                    # Parameters
039   [],                    # Return values
040   &dreamify             # Function
041 );
042 
043 ###########################################
044 sub dreamify {
045 ###########################################
046   my($img, $layer) = @_;
047 
048   Gimp->progress_init("Dreamifying ...");
049 
050   if(!$img) {
051     DEBUG "Loading $jpg_file";
052     $img = file_jpeg_load($jpg_file, "");
053     die "Can't load $jpg_file" unless $img;
054     $layer = image_get_active_layer($img);
055   }
056 
057   DEBUG "Copying layer";
058   my $new_layer = $layer->layer_copy(0);
059   $new_layer->layer_set_mode(OVERLAY_MODE);
060   $img->image_add_layer($new_layer, -1);
061   Gimp->progress_update(.1);
062 
063   scale_image_down($img, 800, 600);
064   Gimp->progress_update(.5);
065 
066   DEBUG "Blurring";
067   $img->plug_in_gauss_iir($new_layer,
068                           20.0, 1, 1);
069   Gimp->progress_update(.8);
070 
071   DEBUG "Adjusting Colors";
072   $new_layer->gimp_levels(0, 0, 255, 1.5,
073                           0, 255);
074 
075   $img->flatten();
076   $layer = $img->get_active_layer;
077   Gimp->progress_update(1);
078 
079   if($jpg_file) {
080       (my $png_file = $jpg_file) =~
081                            s/.jpg$/.png/g;
082       DEBUG "Saving to $png_file";
083       $layer->file_png_save($png_file, "",
084                             0, 6, (1) x 5);
085   }
086 }
087 
088 ###########################################
089 sub scale_image_down {
090 ###########################################
091   my($img, $x, $y) = @_;
092 
093   my $w = $img->image_width();
094   my $h = $img->image_height();
095 
096     # Switch x,y if portrait
097   ($x, $y) = ($y, $x) if $w < $h;
098 
099   DEBUG "Limits $x x $y";
100   DEBUG "Size   $w x $h";
101 
102   if($w > $x and $h > $y) {
103       my $new_h = int($h*$x/$w);
104       DEBUG "Resizing to $x x $new_h";
105       $img->image_scale($x, $new_h);
106   }
107 }
108 
109 exit main;
110 
111 __END__
112 
113 =head1 NAME
114 
115 Dreamify - Gimp Plugin for dreamy pictures
116 
117 =head1 SYNOPSIS
118 
119     dreamify file.jpg
120 
121 =head1 HELP
122 
123 Adds a second layer to a picture in
124 "Overlay" mode, lightens up the colors,
125 applies a Gaussian blur and rescales the
126 result to 800x600. Operates on the current
127 picture if called from within Gimp via
128 C<Rightclick/Perl-Fu/Dreamify>. If called
129 from the command line, it modifies the JPG
130 file specified, flattens the result and
131 writes it back as PNG.
132 
133 =head1 AUTHOR
134 
135 Copyright 2003 by Mike Schilli, all rights
136 reserved. This program is free software,
137 you can redistribute it and/or modify it
138 under the same terms as Perl itself.

Es geht auch ohne

Für den Fall, dass das Skript ohne Gimp läuft (es erkennt dies in Zeile 79 an der gesetzten »-Variable), speichert die »file_png_save«-Methode das Bild in der angegebenen PNG-Datei mit einem Kompressionsfaktor von »6«. Wie der DB-Browser zeigt, folgen noch fünf Integer-Parameter, die hier alle auf »1« gesetzt sind. Zeile 109 gibt die Kontrolle an Gimp zurück – fertig. Die angehängte POD-Dokumentation hilft dem Skript- und dem Gimp-Benutzer.

Ohne Gimp läuft das Skript auch ohne Fortschritts- und sonstige Anzeigen. Da es dennoch fast alle Gimp-Funktionen benötigt, bezahlt der Anwender mit einer langen Startphase. Um das zu umgehen, kann er aber auch eine Gimp-In- stanz starten und den Menüpunkt »Xtns | Perl | Server« anwählen. Er startet damit in Gimp einen Server, den sich das extern ablaufende Skript beim Start automatisch sucht und anschließend nutzt. Aber Vorsicht: Dem Skript übergebene relative Pfadangaben für Bilddateien müssen sich dann auf das Startverzeichnis von Gimp beziehen, nicht auf das des Skripts.

Installation

Das Skript benötigt die fünf Module »XML::Parser«, »XML::Writer«, »PDL«, »Log::Log4perl« und »Gtk«. Sie lassen sich reibungslos mit einer CPAN-Shell installieren. Falls es mit »Gtk-Perl-0.7009« Probleme gibt, klappt meist die manuelle Installation:

perl Makefile.PL --without-guessing
make
make install

Das Skript nutzt »Log::Log4perl« um anzuzeigen, was es gerade treibt. Wem das auf die Nerven geht, kann es einfach abstellen, indem er den Log-Level in Zeile 14 auf » hochsetzt.

Die Module »Gimp« und »Gimp::Fu« sind zusammen mit den Gimp-Sourcen erhältlich:

wget ftp://ftp.gimp.org/pub/gimp/v1.2/v1.2.5/gimp-1.2.5.tar.gz
tar xzvf gimp-1.2.5.tar.gz

Im Verzeichnis »gimp-1.2.5/plug-ins/perl/« ist danach das komplette Perl- Instrumentarium zu finden, installiert wird es wie üblich mit dem Dreisatz:

perl Makefile.PL
make
make install

Allerdings musste ich die Distribution korrigieren, indem ich eine Datei »po/ Makefile« einfügte. Sie enthält eine einzige Zeile, die mit »install:« ein leeres »install«-Target anlegte.

Um das Skript »dreamify« in Gimp zu verwenden, muss es ins Skriptverzeichnis, von wo es Gimp beim Programmstart einliest und die Registrierung vornimmt. Das Kommando

gimptool --install-bin dreamify

installiert das neue Plugin in das Verzeichnis »~/.gimp-1.2/plug-ins/« – fertig ist der Lack: Gimp starten und auf Knopfdruck Bilder verträumter dreinschauen lassen! (fjl)

Infos

[1] The Gimp: [http://www.gimp.org]

[2] Eric Jeschke, “Gimp Tutorials”: [http://redskiesatnight.com]

[3] Shawn Wallace, “Perl Graphics Programming”, O\’Reilly 2002

[4] Ein verträumtes Beispielbild: [http://www.perlmeister.com/rundbrief.archiv/20030526/images/pflaster.jpg] und zum Vergleich das Original: [http://www.perlmeister.com/rundbrief.archiv/20030526/images/porg.jpg]

[5] Listings zu diesem Artikel: [ftp://www.linux-magazin.de/pub/listings/magazin/2003/09/Perl]

Der
Autor

Michael Schilli arbeitet als Web-Engineer für AOL/Netscape in Mountain View, 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 ist [http://perlmeister.com].

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben