#!/opt/bin/perl use Gimp; use Gimp::Fu; # Dieses Plugin verändert Pixel effizient mit dem PDL-Modul (Perl # Data Language. Da dieses Modul nicht immer verfügbar ist, wird # mit dem Gimp::Feature-Modul auf dieses Feature getestet. Sollte keine # PDL-Unterstützung verfügbar sein, wird das Skript an dieser Stelle # abgebrochen und wird nicht bei der Gimp-Applikation registriert. use Gimp::Feature 'pdl'; # Nun, da es so aufwendig getestet und verlangt wurde, sollte man es # auch benutzen. PDL::LiteF ist eine abgespeckte PDL-Variante, die # dafür etwas schneller lädt. use PDL::LiteF; # Diese kleine Hilfsfunktion verwandelt Pixeldaten, die im RGB-Format # vorliegen, mit Hilfe der folgenden Formel in Graustufen: # # Grauwert = Rotwert * 0.21 + Grünwert * 0.72 + Blauwert * 0.07 # sub togrey { my $pdl = shift; # Als erstes werden die Pixeldaten, die als Bytes (also Werte 0-255) # vorliegen, 16-Bit-Werte, damit bei der folgenden Multiplikation # keine Überläufe stattfinden: $pdl->convert(ushort); # Hier findet die Umwandlung statt. Dies geschieht, indem die innere # Dimension (die Pixel werden als Vektor der Länge drei dargestellt) # mit dem Vektor [54 184 18] skalarmultipliziert wird (inner-Methode). # Dabei entsteht ein einzelner Wert. Dies geschieht mit allen Pixeln, # d.h. jeder 3-Vektor wird durch eine einzelne Zahl ersetzt, die den # Grauwert multipliziert mit 256 darstellt (obige Formel mit 256 # durchmultiplizieren). # # Das Ergebnis wird durch 256 geteilt, um wieder Pixelwerte im # Bereich 0-255 zu erhalten. $pdl = $pdl->inner (pdl [54,184,18]) / 256; # Abschließend werden die Pixel wieder in Bytes umgewandelt: $pdl->convert(byte); # Ich habe fertig! $pdl; } # Dieses Plugin nimmt ein Graustufenbild und weist jedem Grauwert eine # entsprechende Farbe aus einem Farbverlauf zu. Auch RGB-Bilder sind als # Eingabe erlaubt, sie werden dann automatisch in Graustufen umgewandelt. register "map_to_gradient", "map grayscale values to gradient", "Map all the pixels to the colours of a gradient according to their greyscale value.", "Marc Lehmann", "Marc Lehmann ", "19990802", N_"/Filters/Colors/Map To Gradient...", "RGB*, GRAY", [ # Einziger Parameter ist der Farbverlauf [PF_GRADIENT, "gradient", "The gradient to map to"], ], sub { my ($image, $drawable, $gradient) = @_; # Da der Vorgang etwas länger dauern kann, wird eine Verlaufsanzeige # erzeugt. Genaugenommen müsste diese übersetzt werden, aber der # Programmierer war wohl zu faul... Gimp->progress_init ("Mapping to '$gradient'", -1); # Das mit Abstand Schwierigste ist die Erzeugung eines Vektors aus # einem Farbverlauf. Die Methode "gradients_get_gradient_data" liefert # die Daten eines Farbverlaufs, jeweils vier Zahlen im Bereich 0-..1 # (RGBA), die einen Punkt auf dem Farbverlauf darstellen. Diese Punkte # werden gleichmäßig auf dem Farbverlauf verteilt. Da die Graustufen # eines Bildes 256 verschiedene Werte annehmen können, müssen # ebenso viele Farben aus dem Farbverlauf zur Verfügung stehen. # # Das map $_*255 sorgt dafür, daß die Zahlen im Bereich 0...1 auf # Byte-Werte im Bereich 0...255 skaliert werden. # my $grad = pdl byte, map $_*255, @{ (Gimp->gradients_get_gradient_data ($gradient, 256))[1] }; # $grad ist nun ein Vektor mit 1024 Zahlen. Diese müssen aber in # 256 Gruppen zu jeweils 4 Zahlen (RGBA) angeordnet werden. # Dies macht die reshape-Methode: $grad->reshape(4,256); # Alpha-Kanäle sind meistens etwas im Wege, sind aber für die # Bildverarbeitung sehr wichtig, so daß es sich lohnt, einige # Abfragen für die Behandlung von Alpha-Kanälen einzubauen: my $alpha = $drawable->has_alpha; # Wenn die Ebene keine Alpha-Kanäle besitzt, wird auch keiner erzeugt, # d.h. es reicht, wenn die Daten des Farbverlaufs aus 3 (RGB) statt # 4 (RGBA) Werten bestehen. Die slice-Methode schneidet hier den # letzten Wert ab. (Negative Werte werden von hinten gezählt, wie in # Perl). $grad = $grad->slice('0:-2') unless $alpha; # Falls die Ebene ein Graustufenbild ist, wird der Gradient # ebenfalls in Graustufen umgewandelt, da Graustufen erzeugt # werden müssen. # Da "togrey" eine Dimension abschneidet (aus 320x200x3 wird z.B. # 320x200), muß eine "dummy"-Dimension hinzugefügt werden # (320x200x1), damit die Form des Vektors kompatibel bleibt. $grad = togrey($grad)->dummy(0) unless $drawable->is_rgb; # Hier merkt sich das Skript die Länge der inneren Dimension, was # nichts anderes als die Anzahl der Komponenten eines Pixels ist. my $depth = ($grad->dims)[0]; # precalculcate # Der Gradient wird innerhalb der folgenden Schleife in der Form # 4x256 (oder 1x256...) statt 256x4 benötigt. Also werden hier # die beiden Dimensionen vertauscht. $grad = $grad->xchg(0,1); # Es folgt die "Standardschleife", mit der über alle Pixel # eines Bildes iteriert wird. Diese Schleife ist in fast allen # pixelverarbeitenden Skripten identisch. my @bounds = $drawable->bounds; { my $src = new PixelRgn ($drawable,@bounds,0,0); my $dst = new PixelRgn ($drawable,@bounds,1,1); # "$iter" ist ein sogenannter Iterator. Wiederholte # Aufrufe von Gimp->pixel_rgns_process liefern immer # weitere Teile des Bildes in $src->data. my $iter = Gimp->pixel_rgns_register($src,$dst); my $area = $bounds[2]*$bounds[3]; my $progress = 0; # Hier ist die eigentliche Schleife. Jeder Pixel (innerhalb der Auswahl) # wird innerhalb dieser Schleife umgewandelt. do { # Hole die Quelldaten (Ursprungspixel) my $data = $src->data; # Schneide, wie vorhin beim Gradienten, den Alpha-Kanal ab. Er # wird vom Algorithmus ignoriert. $data = $data->slice("0:-2") if $alpha; # Falls die Ebene mehr als zwei Komponenten pro Pixel besitzt (also # vom Typ RGB ist), werden die Pixel in Graustufen umgewandelt, # ansonsten wird die unterste Dimension (die dann nur die Länge "1" # haben kann, also GRAY) entfernt, was funktioniert, da Dimensionen # der Länge "1" einfach weggelassen werden können. $data = $src->bpp > 2 ? togrey($data) : $data->clump(2); # Nun haben die Pixeldaten das richtige Format. Bilde nun jede # Graustufe auf einen RGB-Wert ab, indem mit der index-Funktion # jeder Wert aus $data durch das jeweilige Element aus dem # $grad-Vektor ersetzt wird. # Da der $grad-Vektor mehrere Komponenten besitzen kann, wird # eine dummy-Dimension mit der richtigen Länge hinzugefügt. $data = index ($grad, $data->dummy (0, $depth)): # Und nun werden die erzeugten Pixeldaten in der Zielregion # gespeichert: $dst->data($data); # Schließlich wird noch die Verlaufsanzeige angepaßt: $progress += ($src->w*$src->h)/$area; Gimp->progress_update ($progress); } while (Gimp->pixel_rgns_process ($iter)); } Gimp->progress_update (1); # Diese Methode sorgt dafür, daß die Daten aus der Zielregion # wieder zurück in das Bild kopiert werden. $drawable->merge_shadow (1); # Und ohne einen Aufruf der update-Funktion wird die Gimp-GUI das # Bild zwar intern verändern, aber nicht richtig auf dem Bildschirm # anzeigen. $drawable->update (@bounds); (); }; exit main;