[Hinweis für Nicht-Amerikaner: Anspielung auf ``Pimp Daddy'', hier sind ein paar Ideen für den Grafiker: http://pimpdaddy.com/sightings/index.php :) ] Mit Gimp lassen sich selbst anspruchsvollste Verbesserungen an digitalen Bildern vornehmen. Statt aber immer wieder die gleichen Manöver per Maus auszuführen, automatisieren wir die Vorgänge heute mit Gimps Perl-Schnittstelle.
Kürzlich stolperte ich über die Webseite des Foto-Fachmanns Eric Jeschke, der auf [2] allerlei Tricks beschreibt, um digitale Fotos mit dem frei auf Linux erhältlichen Fotobearbeitungsprogramm Gimp zu verbessern. Da verschwinden ruckzuck rote Blitzaugen, Bildregionen werden 'nachbelichtet' oder 'abgewedelt', wie man im Fotolaborjargon sagt, exzessive Tiefenschärfe reduziert, und was es der Gimmicks mehr gibt.
In [3] fand ich dann eine zwar kurze, aber einigermaßen gelungene Einführung in Gimps Perl-Schnittstelle, eine von Marc Lehmann geschriebene Gimp-Erweiterung, die ein fester Bestandteil der Gimp-Source-Distribution ist. Damit kann man neue Menüs in den Gimp einfügen, sonst üblicherweise per Mausklick ausgelöste Aktionen im Skript ausführen und ungehemmt auf den Bilddaten herumorgeln.
Eines der Rezepte auf [2] beschrieb, wie man ein Bild dramatischer, ja traumartiger macht. Das geht so: Man lädt das Bild in den Gimp, lässt den Layers-Dialog erscheinen, kopiert den ersten Layer in einen zweiten und wählt dafür den Modus ``Overlay''. Dann hellt man die Farben mittels des ``Image->Colors->Levels''-Dialogs leicht auf und rundet die durch den ``Overlay'' entstandenen Ecken durch eine ``Gaussian Blur''-Unschärfe mit Radius 20 ab. Der Effekt ist erstaunlich, [4] zeigt Originalbild und Gimp-Fälschung. Wendet man das Verfahren allerdings ein paarmal von Hand auf verschiedene Bilder an, ermüden schnell die Finger und eine innere Stimme beginnt, lautstark nach einem Skript zu schreien.
Das heute vorgestellte Skript dreamify
funktioniert sowohl als Gimp-Plugin, das
den 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 angegebene Bild im JPG-Format, führt die gerade beschriebenen
Manipulationen aus, lässt dann die zwei Layer durch 'Flatten' kollabieren
und schreibt das Ergebnis im PNG-Format nach test.png
.
In Perl geschriebene Gimp-Plugins ziehen einfach
use Gimp qw(:auto); use Gimp::Fu;
herein. :auto
ist optional, importiert aber alle verfügbaren
Gimp-Bibliotheksfunktionen praktischerweise in den Namensraum des
Skripts und definiert nützliche Konstanten. Die Plugin-Skripts müssen
die register()
-Funktion aufrufen, die den Plugin in Gimp registriert. Sie
legt fest, unter welchem Menüpunkt der Plugin hängt, gibt
eine kurze Beschreibung für die Online-Hilfe und definiert, was passiert,
wenn der Anwender darauf drückt. Das Gimp-Plugin-Skript sollte am Ende mit
exit main;
die Kontrolle an den Gimp zurückgeben, der das Skript während des Startvorgangs ausführt und sich die vorgenommenen Einstellungen merkt.
Wenn der Gimp den Plugin beim Hochfahren aufruft, gibt er ihm Parameter der Form
-gimp 9 8 -query 0
mit. Das nutzt Zeile 19 in dreamify
aus, um festzustellen, ob der Aufruf vom Gimp
oder von der Kommandozeile kam. Im ersten Fall beginnt der Plugin,
auf dem aktuellen Bild herumzuorgeln. Von der Kommandozeile aus
aufgerufen, lädt das Skript zunächst die als Argument angegebene
Datei als JPG-Bild und schreibt das ausgeflachte Ergebnis
nach getaner Arbeit in ein PNG-Bild zurück auf die Festplatte.
Damit Gimp auf dem aktuellen Bild operiert, wenn der Benutzer den den ``Dreamify''-Eintrag im Menü auswählt, legt Zeile 21 den String
"<Image>/Perl-Fu/Dreamify"
in der Variablen $menu
ab, die später der register
-Methode mitteilt,
wo der Eintrag aufzuhängen ist. Im Falle eines Kommandozeilenaufrufs ist
$menu
auf
"<Toolbox>/Xtns/Mike/Dreamify"
gesetzt, denn obwohl der Gimp in diesem Fall gar kein Menu anlegen muss, besteht
er auf einem gültigen Eintrag und der <Image>
-Eintrag oben
verursacht eine wüste Fehlermeldung.
Wird das Skript von der Kommandozeile aus ohne Angabe der zu bearbeitenden Bilddatei aufgerufen, verzweigt Zeile 25 zur weiter unten stehenden POD-Dokumentation und bricht das Skript ab.
Außer dem Menüpunkt, unter dem die neue Funktion liegt, teilt
die register()
-Funktion dem Gimp auch noch mit, welche Funktion
aufzurufen ist (dreamify()
), welche Parameter ihr zu übergeben sind
(keine), welche Rückgabeparameter der Gimp erwarten kann (keine)
und welche Teile der POD-Dokumentation in die verschiedenen
Sektionen der Gimp-Laufzeit-Hilfe einfließen sollen.
Die dreamify()
-Funktion wird vom Gimp mit einem Handle zum aktuellen
Image und Layer aufgerufen, wenn der Benutzer den neuen Menüpunkt anwählt
(Abbildung 2).
Und das nur, weil der Menüeintrag für die Funktion unter
<Image>
hängt -- hinge er hingegen
unter <Toolbox>
, wäre
das nicht der Fall, da das Menü nicht Image-spezifisch ist.
Falls der Aufruf von der Kommandozeile aus erfolgte, sind $img
und
$layer
nicht gesetzt und die in Zeile 52 aufgerufene
Gimp-Bibliotheksfunktion
file_jpeg_load
lädt das Bild von der Platte, dessen Pfadangabe
vom Hauptprogramm her noch in der Variablen $jpg_file
liegt.
Die für interne Funktionen wie file_jpeg_load notwendigen Parameter zeigt der
Gimp übrigens praktischerweise in seiner Online-Hilfe an. Man geht
dazu ins Menü Xtns/DB_Browser
, sucht per Stichwort in Hunderten
von Funktionen und bekommt deren Signatur mit Kurzhilfe angezeigt -
praktisch!
Und auch der neue Plugin taucht gleich nach der Installation dort auf, wie
Abbildung 1 zeigt. Abbildung 2 zeigt den neuen Menüpunkt in Aktion
im rauhen Gimp-Alltag.
Abbildung 1: Manualseite der neuen Gimp-Erweiterung im DB-Browser |
Abbildung 2: Der neue Perl-gesteuerte Menüpunkt erscheint im Gimp |
Allerdings unterscheiden sich
die Einträge dort manchmal von den im Gimp
-Perl-Modul
verwendeten Signaturen: Als Faustregel gilt, dass man run_mode
immer weglassen darf, weil das Gimp
-Modul selbst weiss, ob der
Aufruf interaktiv oder im Hintergrund erfolgte. Und wenn 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
sind die Handles Objektreferenzen, so dass man statt
gimp_layer_copy($layer, $add_alpha);
einfach
$layer->layer_copy($add_alpha);
oder sogar
$layer->copy($add_alpha);
sagen kann. Zurück zu dreamify()
: In Zeile 54 gibt
image_get_active_layer()
den aktiven (und normalerweise einzigen) Layer des
übergebenen Bildes zurück. Zeile 58 kopiert den Layer in einen neuen,
das Flag, das einen Alpha-Channel hinzufügt, bleibt auf 0 gesetzt.
Die nachfolgende Methode layer_set_mode()
setzt den Modus des neuen
Layers auf die Konstante OVERLAY_MODE
, die wegen des :auto
-Tags
nach use Gimp
bequemerweise ins Skript importiert wurde.
Diese Aktion entspricht der
Auswahl des ``Overlay''-Modus im Layer-Dialog des Gimp.
image_add_layer()
in Zeile 60
fügt den neu erzeugten Layer zum Bild hinzu, der auf
-1 gesetzte Parameter setzt ihn auf den Kopf des schon bestehenden
Basislayers.
Damit der Gimp-Benutzer bei langdauernden Aktionen nicht die Nerven
verliert sondern bei Laune bleibt, initialisiert dreamify
mit
Gimp->progress_init("Titel")
eine Fortschrittsanzeige und
aktualisiert diese nach den einzelnen Schritten jeweils mit
Gimp->progress_update($fraction)
. $fraction
ist hierbei
ein Fließkommawert zwischen 0 und 1 und gibt die Position des
Fortschrittsbalkens an.
Zeile 63 skaliert das Bild auf 800x600 Pixel, Hierzu ruft es die ab Zeile
89 definierte Funktion scale_image_down
auf, die ein Image-Handle
und die gewünschte Maximalgroße des Bildes erwartet und die Veränderung
direkt am Bild vornimmt. Hierzu findet sie zunächst heraus, ob das
vorliegende Bild statt im Quer- im Hochformat vorliegt und deswegen die
Maximalgröße auf 600x800 korrigiert werden muss.
Sind Breite und Höhe des Bildes über den
vorgegebenen Maximalwerten, wird das Bild mittels image_scale
so
verkleinert, dass die Bildproportionen erhalten bleiben.
plug_in_gauss_iir
in Zeile 67 führt die Glättung des Bildes mit dem
``Gaussian Blur''-Algorithmus aus und setzt den Unschärferadius auf 20.0
.
Sie aktiviert sowohl
horizontale also auch vertikale Unschärfe mit jeweils wahren Werten
für die letzten beiden Parameter.
Zeile 72 schließlich hellt die Farben im Bild mit einem Gamma-Faktor von
1.5 etwas auf. Ein- und Ausgabebereiche bleiben mit 0-255 konstant.
Dies entspricht einem etwas nach links verschobenen ``Intensity''-Regler
in Gimps Image/Colors/Level
-Dialog.
Anschließend patscht Zeile 75 die beiden Layer zu einem
einzigen zusammen, und Zeile 76 holt dessen Handle.
Für den Fall, dass das Skript ohne Gimp läuft (erkennbar an der
gesetzten $jpg_file
-Variable), sichert die file_png_save
-Methode das
Bild in der angegebenen PNG-Datei mit einem Kompressionsfaktor von 6.
Wie aus dem DB-Browser ersichtlich folgen noch 5 weitere Integer-Parameter,
die wir alle auf 1 setzen.
Zeile 109 gibt die Kontrolle an den Gimp zurück -- fertig. Die angehängte POD-Dokumentation hilft sowohl dem Skript- als auch dem Gimpbenutzer.
Ohne Gimp läuft das Skript ohne Fortschritts- und sonstige Anzeigen.
Da es letztendlich aber doch alle Funktionen des Gimp benötigt,
bezahlt man mit einer langen Startphase. Um das zu umgehen, kann man
eine Gimp-Instanz auf dem Rechner starten und den Menüpunkt
Xtns/Perl/Server
anwählen. Dies startet im Gimp einen Server, den
sich das extern gestartete
Skript beim Start automatisch sucht und anschließend den
bereits laufenden Gimp nutzt. Aber Vorsicht: Dem Skript übergebene
relative Pfadangaben für Bilddateien müssen sich dann auf das
Startverzeichnis des Gimp beziehen, nicht auf das des Skripts.
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 [ 039 [PF_INT, "size", "Img size", 100], 040 [PF_COLOR, "color", "Img color", [255,127,0]] 041 ], 042 043 #"", # Images accepted 044 #[], # Parameters 045 #[], # Return values 046 \&dreamify # Function 047 ); 048 049 ########################################### 050 sub dreamify { 051 ########################################### 052 my($img, $layer) = @_; 053 054 Gimp->progress_init("Dreamifying ..."); 055 056 if(!$img) { 057 DEBUG "Loading $jpg_file"; 058 eval { $img = file_jpeg_load($jpg_file, "") }; 059 $img = file_png_load($jpg_file, "") unless $img; 060 die "Can't load $jpg_file" unless $img; 061 $layer = image_get_active_layer($img); 062 } 063 064 DEBUG "Copying layer"; 065 my $new_layer = $layer->layer_copy(0); 066 $new_layer->layer_set_mode(OVERLAY_MODE); 067 $img->image_add_layer($new_layer, -1); 068 Gimp->progress_update(.1); 069 070 scale_image_down($img, 1000, 800); 071 Gimp->progress_update(.5); 072 073 DEBUG "Blurring"; 074 $img->plug_in_gauss_iir($new_layer, 075 20.0, 1, 1); 076 Gimp->progress_update(.8); 077 078 DEBUG "Adjusting Colors"; 079 $new_layer->gimp_levels(0, 0, 255, 1.5, 080 0, 255); 081 082 $img->flatten(); 083 $layer = $img->get_active_layer; 084 Gimp->progress_update(1); 085 086 if($jpg_file) { 087 (my $png_file = $jpg_file) =~ 088 s/\.jpg$/.png/g; 089 DEBUG "Saving to $png_file"; 090 $layer->file_png_save($png_file, "", 091 0, 6, (1) x 5); 092 } 093 } 094 095 ########################################### 096 sub scale_image_down { 097 ########################################### 098 my($img, $x, $y) = @_; 099 100 my $w = $img->image_width(); 101 my $h = $img->image_height(); 102 103 # Switch x,y if portrait 104 ($x, $y) = ($y, $x) if $w < $h; 105 106 DEBUG "Limits $x x $y"; 107 DEBUG "Size $w x $h"; 108 109 if($w > $x and $h > $y) { 110 my $new_h = int($h*$x/$w); 111 DEBUG "Resizing to $x x $new_h"; 112 $img->image_scale($x, $new_h); 113 } 114 } 115 116 exit main; 117 118 __END__ 119 120 =head1 NAME 121 122 Dreamify - Gimp Plugin for dreamy pictures 123 124 =head1 SYNOPSIS 125 126 dreamify file.jpg 127 128 =head1 HELP 129 130 Adds a second layer to a picture in 131 "Overlay" mode, lightens up the colors, 132 applies a Gaussian blur and rescales the 133 result to 800x600. Operates on the current 134 picture if called from within Gimp via 135 C<Rightclick/Perl-Fu/Dreamify>. If called 136 from the command line, it modifies the JPG 137 file specified, flattens the result and 138 writes it back as PNG. 139 140 =head1 AUTHOR 141 142 Copyright 2003 by Mike Schilli, all rights 143 reserved. This program is free software, 144 you can redistribute it and/or modify it 145 under the same terms as Perl itself.
Für das Skript brauchen wir die Module XML::Parser, XML::Writer, PDL, Log::Log4perl und Gtk, die sich reibungslos mit einer CPAN-Shell installieren lassen. Falls es mit Gtk-Perl-0.7009 Probleme gibt, sollte man die manuelle Installation mit
perl Makefile.PL --without-guessing make make install
versuchen, das sollte klappen. Auch nutzt das Skript Log::Log4perl
,
um anzuzeigen, was es gerade treibt. Wem das auf die Nerven geht, der
kann's einfach abstellen, indem er den Log-Level in Zeile 14 auf
$ERROR
hochsetzt.
Die Module Gimp
und Gimp::Fu
erhält man zusammen mit der
Gimp-Source über
wget ftp://ftp.gimp.org/pub/gimp/v1.2/v1.2.5/gimp-1.2.5.tar.gz tar zxfv gimp-1.2.5.tar.gz
herein. Im Verzeichnis
cd gimp-1.2.5/plug-ins/perl
ist dann endlich das Perl-Instrumentarium, das wie üblich mit
perl Makefile.PL make make install
installiert wird. Allerdings musste ich die Distribution korrigieren,
indem ich eine Datei po/Makefile
einfügte, mit einer einzigen
Zeile, die mit install:
eine leere install
-Target anlegte.
Um das Skript dreamify
im Gimp zu verwenden, muss es ins
Skriptverzeichnis, von wo es der Gimp beim Programmstart einliest und
die Registrierung vornimmt. Das Kommando
gimptool --install-bin dreamify
installiert den neuen Plugin in ~/.gimp-1.2/plug-ins/
und fertig
ist der Lack: Gimp starten und auf Knopfdruck Bilder verträumter
dreinschauen lassen!
Michael Schilliarbeitet 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. |