Statt digitale Bilder mit den immer gleichen Schritten im Gimp zu bearbeiten, bieten Perlskripts die Möglichkeit, den Ablauf zu automatisieren.
Bevor ich Bilder von meiner digitalen Kamera aufs Netz stelle, laufen seit Jahren immer die gleichen Schritte im Gimp ab: Erst wird die Bilddatei auf 1000 Pixel Breite verkleinert, denn die von meiner Nikon D70 gelieferte Auflösung ist eigentlich schon fast zuviel für's Web und das Hochladen dauert nur unnötig lange. Zweitens versucht fast jeder ambitionierte Fotograf, den Kontrast zu verbessern und eventuelle Farbstiche zu zu korrigieren. Und drittens verbessert die Funktion ``Sharpen'' im Gimp die Bildschärfe, üblicherweise stelle ich den Wert 20 ein.
Abbildung 1: Das Originalbild hat einen leichten Farbstich und ist vom Kontrast her verbesserungswürdig. |
Abbildung 2: Das mit picfix korrigierte Bild zeigt natürlichere Farben und einen besseren Kontrast. |
Dabei bietet der Gimp, wie schon einmal vor fünf Jahren im Snapshot
vorgestellt, eine komfortable Skript-Schnittstelle, um diese immer
wiederkehrenden Aktionen zu automatisieren und sogar ohne GUI von der
Kommandzeile aus ablaufen zu lassen. Allerdings haben die
Gimp-Entwickler seit dem die ganze API durcheinander gewirbelt und nichts
funktioniert mehr wie früher. Aber zum Glück dokumentiert der Gimp
unter dem Menüpunkt Xtns->Procedure Browser
sämtliche Funktionen
vollständig und pinibel genau. Ein dickes Bravo an das Gimp-Team!
Die Standardschnittstelle ist in der verrückten Professorsprache
Scheme geschrieben,
aber Gott sei Dank wird auch ein Perl-Modul angeboten. Zur Installation
unter Ubuntu waren ein paar Tricks notwendig, im Abschnitt
``Trickreiche Installation'' am Ende dazu mehr. Listing picfix
wird
dann einfach von der Kommandozeile aus mit
picfix file.jpg
aufgerufen, worauf es intern den Gimp aufruft (allerdings ohne dass sich
dieser auf der Oberfläche zeigt) und mit register()
die Funktion
picfix()
registriert. Die Gimp-Schnittstelle besteht seltsamerweise
darauf, dass auch ein Skript, das nur auf der Kommandozeile läuft, unter
einem genau vorgegebenen Menüpunkt nutzloserweise einen Eintrag
einstellt, also tut picfix
ihr den Gefallen. In Zeile 43 ruft
das Skript dann die Funktion main()
auf, die in den Gimp verzweigt
und erst nach getaner Arbeit wieder von dort zurückkehrt. Das
vorangestellte exit()
stellt dann sicher, dass sich das Skript mit
dem Returncode von main()
ebenfalls verabschiedet.
Abbildung 1 zeigt das Originalbild, eine Aufnahme von meinem Balkon in
San Francisco. Das Abendlicht kam von hinten und leuchtete die
Stadt schön aus. Trotzdem waren die Farben im Original nicht ganz richtig.
Nachdem Aufruf von picfix
kam als Ergebnis das Bild in Abbildung 2
zum Vorschein. Der Kontrast ist deutlich besser und auch die Farben
entsprechen ziemlich genau der Realität.
In der ab Zeile 46 definierten Funktion picfix()
, die der Gimp
als Callback aufruft, lädt die Funktion gimp_file_load()
das
digitale Bild unter dem vorher festgelegten Dateinamen von der Festplatte.
Es wird dann mit image_scale()
auf 1000 Pixel Breite skaliert.
Die Funktion gimp_levels_stretch()
simuliert dann den ``Auto''-Knopf
in Gimps ``Levels''-Dialog und maximiert damit den Kontrast, indem es
die tatsächlich verwendeten Pixelwerte über die ganze verfügbare Bandbreite
ausdehnt.
Die Methode plug_in_sharpen()
mit
dem Parameter 20 verbessert anschließend die Bildschärfe und
gimp_file_save()
speichert die Datei am Ende unter dem
Pfad file-1.jpg
im
ursprünglich verwendeten
Format ab. Es ist also egal, ob das Bild als JPG oder PNG vorliegt,
die load/save-Methoden geben intern die Kontrolle an die
auf das jeweilige Bildformat spezialisierten Gimp-Routinen ab.
Das Laden der Module mit
use Gimp qw(:auto)
und
use Gimp::Fu
importiert alle Gimp-Funktionen praktischerweise
in den Namensraum des Skripts. Auch Gimp-Konstanten wie
RUN_NONINTERACTIVE schleust das :auto
-Tag zur Freude des
Programmierers mit ein.
001 #!/usr/bin/perl 002 use warnings; 003 use strict; 004 005 use Gimp qw(:auto); 006 use Gimp::Fu; 007 use ColorCast; 008 use Getopt::Std; 009 use Log::Log4perl qw(:easy); 010 011 my $viewer = "eog"; 012 013 Log::Log4perl->easy_init($DEBUG); 014 015 getopts("xl:c:a:s:u:", \my %opts); 016 017 $opts{a} ||= "green"; # color adjust 018 $opts{s} ||= "1000"; # size 019 $opts{l} ||= 1; # autolevel 020 021 DEBUG "Starting up"; 022 023 my $menu = 024 "<Toolbox>/Xtns/Perl-Fu/Picfix"; 025 026 my $file = $ARGV[0]; 027 die "No file" 028 unless defined $file; 029 030 register( 031 "perl_fu_picfix", # Name 032 "Fix Colors and More", # Explain 033 "", # Help 034 "", # Author 035 "", # Copyright 036 "", # Date 037 $menu, # Menu 038 "*", # Images accepted 039 [ undef ], # No parameters 040 \&picfix # Function 041 ); 042 043 exit main(); 044 045 ########################################### 046 sub picfix { 047 ########################################### 048 049 my $img = gimp_file_load( 050 RUN_NONINTERACTIVE, $file, $file); 051 052 die "Can't load $file" unless $img; 053 054 my $layer = image_get_active_layer($img); 055 056 scale_image_down($img, $opts{s}); 057 058 $layer = $img->get_active_layer(); 059 if($opts{l}) { 060 DEBUG "Autolevel [$file]"; 061 gimp_levels_stretch($layer); 062 } 063 064 if($opts{c}) { 065 my $colorcast = ColorCast->new( 066 yml_file => $opts{c}, 067 drawable => $layer, 068 ); 069 $colorcast->load(); 070 $colorcast->adjust_to($opts{a}); 071 } 072 073 if($opts{u}) { 074 my $unsharp = { 075 people => [1, 1.5, 10], 076 urban => [3, 0.65, 2], 077 general => [1, 0.85, 4], 078 }; 079 if($opts{u} =~ /([\d.]+):([\d.]+):([\d.]+)/) { 080 $unsharp->{ $opts{u} } = [$1, $2, $3]; 081 } elsif(! exists $unsharp->{$opts{u}}) { 082 LOGDIE "Unknown unsharp value (use ", 083 join(', ', keys %$unsharp), ")"; 084 } 085 086 DEBUG sprintf "Unsharp mask radius=%.2f amount=%.2f threshold=%.2f", 087 @{ $unsharp->{$opts{u} } }; 088 $img->plug_in_unsharp_mask($layer, @{ $unsharp->{$opts{u}} }); 089 } else { 090 DEBUG "Sharpening $file"; 091 $img->plug_in_sharpen($layer, 20); 092 } 093 094 $file =~ s/\./-1./g; 095 $file =~ s/\.nef$/.png/g; 096 097 DEBUG "Saving $file"; 098 gimp_file_save( 099 RUN_NONINTERACTIVE, 100 $img, 101 $layer, 102 $file, 103 $file); 104 105 system("$viewer $file") if $opts{x}; 106 return $img; 107 } 108 109 ########################################### 110 sub scale_image_down { 111 ########################################### 112 my($img, $size) = @_; 113 114 my $w = $img->image_width(); 115 my $h = $img->image_height(); 116 117 if($w >= $h) { 118 if($w > $size) { 119 $h = int($h * $size/$w); 120 $w = $size; 121 } else { 122 return 1; 123 } 124 } else { 125 if($h > $size) { 126 $w = int($w * $size/$h); 127 $h = $size; 128 } else { 129 return 1; 130 } 131 } 132 133 DEBUG "Resizing to $w x $h"; 134 $img->image_scale($w, $h); 135 }
Die Bildskalierung ist auf 1000 Pixel voreingestellt, aber mit
der Kommandozeilenoption -s
(size) sind beliebige Werte verfügbar.
Der Aufruf picfix -s 500 file.jpg
reduziert die maximale Bildbreite
zum Beispiel auf 500 Pixel. Die maximale Größe bezieht sich bei
querformatigen Bildern auf die Breite, bei hochformatigen aber auf die
Höhe. Die Funktion scale_image_down()
ab Zeile 92 enthält die
dazu notwendige if/else-Logik.
Wer nach getaner Arbeit das Bild gleich
ansehen möchte, kann mit der Option -x
(für X-Windows) das Bild
gleich in den in Zeile 11 eingestellten Viewer (Eye of Gnome, eog)
ansehen. Soll die nicht immer zufriedenstellende Ergebnisse liefernde
Autolevel-Funktion unterbleiben, stellt dies die Option -l 0
ein.
Ist ein Bild zu grün, rot oder blau, stimmt etwas mit der Farbenbalance nicht. Ein weißes Objekt in der fotografierten Szene sollte auch im Foto knallweiß und ohne Farbeinschlag zu sehen sein. Analoges gilt für graue oder pechschwarze Objekte. Wurde die White-Balance der Kamera aber nicht eingestellt, wie das die Profis machen, sind öfter mal unnatürliche Farbanteile zu sehen. Solche Fehler lassen sich nach einem in [2] beschriebenen Verfahren auch noch hinterher am digitalen Bild korrigieren. Die Möglichkeiten sind zwar bei dem von vielen preiswerten Kameras verwendeten JPEG-Format beschränkt, aber etwas lässt sich üblicherweise doch noch rausholen.
Abbildung 3: Ein Satz weißer, grauer und schwarzer Kärtchen erleichtert das Finden der Kontrollpunkte. |
Zunächst stellt sich das Problem, dass sich nicht in jedem Bild Elemente bar jedes Farbtupfers befinden. Es gibt aber im Fotofachhandel genormte Plastikkarten zu kaufen (scherzhafterweise auch ``die teuersten Plastikkarten der Welt'' genannt), die man einfach ins Bild hält, eine Testaufnahme macht und die Messwerte dann für alle folgenden Aufnahmen der gleichen Szene verwendet. Ändern sich allerdings die Lichtverhältnisse und kommt nur die Sonne hinter einer Wolke hervor, muss eine neue Kontrollaufnahme mit den Karten gemacht werden.
In Abbildung 3 wurde mit Gimps ``Color Picker'' der Pixelwert einer Stelle auf der grauen Karte gemessen und das Ergebnis war Rot:122, Grün:127 und Blau:123. Wäre die Aufnahme perfekt, hätten alle drei Farbkanäle denselben Wert gespeichert. Ähnliches gilt für die weiße und die schwarze Karte, dort wurden Werte von 227/235/228 und 16/10/17 gemessen.
Die Datei colorcast.yml
(Abbildung 4) zeigt die mit Gimp eingefangenen
Messwerte im YAML-Format. Diese einmal dort abgelegten Werte nimmt
das Skript picfix
auf der Kommandozeile mit
picfix -c colorcast.yml file.jpg
entgegen und korrigiert damit beliebig viele Bilder der selben Szene. Um das Bild nun zu korrigieren, gilt es, alle im Bild verwendeten Farben so zu transformieren, dass die die Farbanteile der farblosen Bildelemente verschwinden. Man definiert hierzu einen Übergangsfunktion, deren Graph sich an den bekannten Messpunkten entlangschlängelt und für alle übrigen Werte Spline-ähnliche Interpolationen bereithält.
Abbildung 4: Die Datei colorcast.yml speichert die gemessenen Farbwerte für die weiße, die graue und die schwarze Karte. |
Im Gimp lässt sich dies im ``Curves''-Dialog aus dem Menü ``Tools->Color Tools'' bewerkstelligen. In der Auswahlbox oben im Dialogfenster stellt man hierzu die zu korrigierende Farbe ein (Red/Green/Blue) und verbeult dann die anfangs gerade Kurve, bis sie ermittelten Kontrollwerte berührt.
Abbildung 5: Im "Curves"-Dialog lässt sich ein Farbstich korrigieren. Hier wird die Kurve des roten Kanals mit drei Kontrollpunkten angepasst. |
Das Verfahren ist recht simpel: Wenn zum Beispiel die graue Karte die Werte Rot:122, Grün:127 und Blau:123 ergibt, werden sowohl der rote als auch der blaue Kanal auf den grünen Wert eingestellt, sodass sich Rot:127, Grün:127 und Blau:127 und damit Farblosigkeit ergibt. Hierzu öffnet man den roten Kanal im ``Curves''-Dialog und zieht die sich geschmeidig anpassende Kurve am Punkt 122/122 (auf der Geraden) zum Punkt 122/127 (Beule). Gimp zeigt während des Verschiebens links oben die aktuellen Koordinaten an. Im blauen Kanal wird entsprechende der Punkt 123/123 auf den Punkt 123/127 gezogen. So entstehen im roten und blauen Kanal leicht verbeulte Kurven wie in Abbildung 5 gezeigt.
Wurde das Verfahren auch für die schwarze und die weiße Karte wiederholt,
befinden sich sowohl im roten als auch im blauen Kanal jeweils drei
Kontrollpunkte, die die Kurve verbiegen. Das Ganze lässt sich natürlich
auch skripten. Listing ColorCast.pm zeigt, dass der Konstruktor zwei
Werte erwartet, yml_file
(die YAML-Datei mit den Messwerten) und
drawable
, den Gimp-Layer, auf dem die Transformation durchgeführt
wird.
01 ########################################### 02 package ColorCast; 03 # Mike Schilli, 2008 (m@perlmeister.com) 04 ########################################### 05 use strict; 06 use warnings; 07 08 use YAML qw(LoadFile DumpFile); 09 use Gimp qw(:auto); 10 use Log::Log4perl qw(:easy); 11 12 my %channels = ( 13 red => HISTOGRAM_RED, 14 blue => HISTOGRAM_BLUE, 15 green => HISTOGRAM_GREEN, 16 ); 17 18 ########################################### 19 sub new { 20 ########################################### 21 my($class, %options) = @_; 22 23 my $self = { 24 yml_file => undef, 25 drawable => undef, 26 ctrls => undef, 27 %options, 28 }; 29 30 bless $self, $class; 31 } 32 33 ########################################### 34 sub save { 35 ########################################### 36 my($self) = @_; 37 38 DumpFile $self->{yml_file}, 39 $self->{ctrls}; 40 } 41 42 ########################################### 43 sub load { 44 ########################################### 45 my($self) = @_; 46 47 $self->{ctrls} = 48 LoadFile $self->{yml_file}; 49 } 50 51 ########################################### 52 sub adjust_to { 53 ########################################### 54 my($self, $ref_channel) = @_; 55 56 DEBUG "Adjusting to $ref_channel"; 57 58 for my $channel (keys %channels) { 59 60 next if $ref_channel eq $channel; 61 62 my $ctrls = $self->{ctrls}; 63 64 my @points = (0, 0, 255, 255); 65 66 for my $ctrl (keys %$ctrls) { 67 push @points, 68 $ctrls->{$ctrl}->{$channel}, 69 $ctrls->{$ctrl}->{$ref_channel}; 70 } 71 72 gimp_curves_spline( 73 $self->{drawable}, 74 $channels{ $channel }, 75 \@points); 76 } 77 } 78 79 1;
Die Methode load()
liest dann die YAML-Werte ein, die anschließend in
einem Hash von Hashes liegen. Ab Zeile 52 erwartet dann adjust_to()
einen Kanal (z.B. ``green''), worauf es die Werte der
restlichen Kanäle (``red'', ``blue'')
an die grünen Werte anpasst. Dies geschieht mit der Gimp-Funktion
gimp_curves_spline()
, die als Parameter den aktiven Layer des zu
modifizierenden Bildes, den zu modifizierenden Kanal und eine Reihe
von Kontrollpunkten erwartet. Zusätzlich zu den Messpunkten kommen in
adjust_to()
immer noch (0,0) und (255,255) hinzu, damit die
Kurve wie in Abbildung 5 gezeigt links unten anfängt und rechts oben
aufhört.
Ohne spezielle Angaben führt das Skript picfix
keine Farbkorrektur durch.
Mit der Option -c
gibt man eine eventuell vorher
erstellte YAML-Datei an und
die Option -a (adjust) nimmt einen Kanalnamen an, auf den die beiden
anderen Kanäle ausgerichtet werden. Voreingestellt ist ``green''.
Eigentlich sollte ein einfaches
sudo apt-get install libgimp-perl
genügen, um die ganze Perl-Gimp-Enchilada installieren, aber leider
flippt der aktuelle
Ubuntu-Release (auch der Perlmeister ist mittlerweile Ubuntu verfallen)
dabei aus. Synaptic treibt's noch bunter und
will gar den Gimp und sogar den Ubuntu-Desktop entfernen,
falls man libgimp-perl
zu installieren begehrt. Grund für dieses
Tohuwabohu scheint ein kaputtes Gimp-Paket zu sein, das zwar einige
Dateien der libgimp-perl-Distribution enthält, aber nicht die notwendigen
Perlmodule. Und natürlich definiert es auch noch einen ``Conflict'' und ein
``Replaces'' auf die aktuelle Version von libgimp-perl
, damit der
Package-Manager mit den überlappenden Dateien nicht durcheinander kommt.
Man kann allerdings mit den im Kasten ``Ubuntu ausgetrickst'' gezeigten Kommandos die libgimp-perl-Sourcen herunterladen, neu kompilieren, eine Version mit einer um 1 höheren Nummer erzeugen (aus ...dfsg-2 wird ...dfsg-3) und installieren. Ganz sauber ist die Lösung nicht, der nächste Gimp-Update wird wahrscheinlcih dazwischenfunken.
---------------------------------------------------- Kasten: "Ubuntu ausgetrickst"
sudo apt-get install devscripts sudo apt-get source libgimp-perl sudo apt-get build-dep libgimp-perl cd libgimp-perl-2.0.dfsg+2.2pre1.dfsg sudo dch --newversion=2.0.dfsg+2.2pre1.dfsg-3 -- Version Bump sudo dpkg-buildpackage -uc -us cd .. sudo dpkg --install --force-overwrite libgimp-perl_2.0.dfsg+2.2pre1.dfsg-3_i386.deb ----------------------------------------------------
Das Modul ColorCast.pm sollte irgendwo installiert werden, wo
picfix
es findet, alternativ kann man picfix
im Code mit
usr lib "verzeichnis";
auf das Installationsverzeichnis von ColorCast.pm hinweisen.
Abbildung 6: Behält man den voreingestellten Loglevel bei, loggt das Skript detailliert mit, was es gerade treibt. |
Die zusätzlich verwendeten Perl-Module Log::Log4perl und YAML liegen
auf dem CPAN und lassen sich einfach entweder mit einer CPAN-Shell
oder, komfortabler in Ubuntu, mit apt-get install libyaml-perl
bzw.
liblog-log4perl-perl
installieren.
Zeile 13 stellt mit dem Logging-Level $DEBUG detailliertes Logging ein,
wen diese Geschwätzigkeit mit der Zeit nervt, setzt es auf $ERROR hoch.
Digitale SLR-Kameras wie die Nikon D70 speichern aufgenommene Bilder auf
Verlangen auch zusätzlich im Rohformat ab. Allerdings verbraucht dies
massiv Speicherplatz und erfordert die Installation des Ubuntu-Pakets
gimp-ufraw
, damit Gimp die rohen Daten erfasst. Allerdings kann Gimp
die Daten im rohen .nef-Format zwar lesen, aber nicht mehr zurückschreiben.
Deswegen wandelt picfix
die Dateiendung kurz vor dem Schreibbefehl
in .png
um, worauf Gimp das Ergebnis im png-Format abspeichert.
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. |