Selbst Handykameras zeichnen heutzutage Bilder in Riesenformaten auf, aber derartige Bytefresser eignen sich kaum für den Blog oder zum Versenden durch enge Datenleitungen. Also skaliere ich Photos meist runter auf 2000x1000, schärfe vielleicht noch ein bisserl nach oder führe den Weißabgleich durch. Der getreue Gimp führt dies klaglos aus, allerdings wäre es schon schön, wenn er die immer gleichen Schritte statt von Hand gleich automatisch in einem Rutsch ausführen würde.

In einem Rutsch

Zum Glück lassen sich handgeschriebene Python-Skripts einfach und zügig in Gimp integrieren. Die Ubuntu-Installation des Pakets gimp enthält bereits alle Zutaten für hausgemachte Kommandos. Listing 1 initialisiert einen handgeschriebenen Plugin zum Skalieren und Nachschärfen eines Fotos. Ins Verzeichnis ~/.gimp-2.8/plug-ins/ überspielt und ausführbar gemacht (nicht vergessen!), findet Gimp die Datei beim Hochfahren und fügt den neuen Menüeintrag unter Filters->MyStuff->Sharpen and Scale ein (Abbildung 1). Letzteren zeigt Gimp sowohl im Kontext-Menü im Bild (rechter Mausklick) als auch vom entsprechenden Dropdown im Titelmenü an.

Abbildung 1: Der per Python-Skript neu eingerichtete Menüeintrag zum Skalieren und Nachschärfen.

Listing 1: sharpscaleplugin.py

    01 #!/usr/bin/env python
    02 from gimpfu import *
    03 from sharpscale import sharp_scale
    04 
    05 register(
    06   "python-fu-sharp-scale",
    07   "Sharpen and scale",
    08   "First sharpens, then scales",
    09   "Mike Schilli",
    10   "Mike Schilli",
    11   "2019",
    12   "_Sharpen and Scale",
    13   "RGB",
    14   [
    15     (PF_IMAGE, 
    16         "image", "Input image", None),
    17     (PF_DRAWABLE,
    18         "drawable", "Input drawable", None)
    19   ],
    20   [],
    21   sharp_scale,
    22   menu="<Image>/Filters/MyStuff"
    23 )
    24 
    25 main()

Die eigentliche Programmlogik zur Bildverarbeitung ruft Listing 1 in Zeile 21 mit der Funktion sharp_scale() auf. Diese Funktion hat es vorher in Zeile 3 mittels Import-Kommando aus der Datei sharpscale.py (Listing 2) importiert. Letztere findet Gimp ebenfalls in obigem Plugin-Verzeichnis, nachdem es der User dorthin installier hat, und im Gegensatz zu Listing 1 braucht sie keine Ausführungsrechte, da Listing 1 sie lediglich als Bibliothek hereinzieht.

Gimp-spezifische Python-Konstrukte wie die Funktion register() zum Einbinden des Plugins oder Konstanten wie PF_IMAGE holt Listing 1 in Zeile 2 aus dem Modul gimpfu, das Skripts in Gimps Plugin-Verzeichnis ohne Zutun finden. Als Parameter für den Aufruf der Plugin-Funktion definiert der Array ab Zeile 14 in Listing 1 einmal ein Image im Gimp-internen Format PF_IMAGE, sowie als Drawable den obersten Layer, den später die Funktion zum Nachschärfen benötigt.

Neues Menü

Klickt der User auf den neuen Menüpunkt in Abbildung 1, springt das Plugin-Framework die Funktion sharp_scale() ab Zeile 6 in Listing 2 an und übergibt ihr sowohl den Descriptor des angezeigten Bildes sowie eine Referenz auf das Drawable mit. Die Ausmaße des verkleinerten Bildes errechnet die Funktion scale_coords ab Zeile 30, die die aktuelle Breite und Höhe des Bildes aus Zeile 8 entgegennimmt, bestimmt, ob es im Quer- oder Hochformat vorliegt. Es errechnet den Skalierungsfaktor scale, indem es die Länge der längste Seite durch die in der Konstanten SIZE_MAX vorliegende Maximalgröße teilt. Die Dimensionen des skalierten Bildes ergeben sich dann durch Multiplikation von Breite und Höhe des ursprünglichen Bildes mit dem Skalierungsfaktor in Zeile 39.

Listing 2: sharpscale.py

    01 from gimpfu import *
    02 
    03 SIZE_MAX=2000
    04 SHARPEN=10
    05 
    06 def sharp_scale(img, layer):
    07   w, h = scale_coords(
    08     img.width, img.height)
    09 
    10   pdb.gimp_image_undo_group_start(img)
    11 
    12   pdb.gimp_progress_init(
    13     "Scaling Image to %dx%d ..." % (w, h),
    14     None)
    15 
    16   pdb.gimp_context_set_interpolation(
    17     INTERPOLATION_LANCZOS)
    18 
    19   pdb.gimp_image_scale(img, int(w), int(h))
    20 
    21   pdb.gimp_progress_init(
    22     "Sharpening Image with %d ..." %
    23       SHARPEN, None)
    24   pdb.plug_in_sharpen(img, layer, SHARPEN)
    25 
    26   pdb.gimp_levels_stretch(layer)
    27 
    28   pdb.gimp_image_undo_group_end(img)
    29 
    30 def scale_coords(w,h):
    31   if max(w,h) < SIZE_MAX:
    32     return w, h
    33 
    34   if w<=h:
    35     scale=SIZE_MAX/float(h)
    36   else:
    37     scale=SIZE_MAX/float(w)
    38 
    39   return int(w*scale), int(h*scale)

Da sich das Skalieren bei großen Bildern schon mal ein paar Sekunden hinziehen kann und der User nach seinem Mausklick wie in jedem guten User-Interface sofort eine Rückmeldung erwartet, wirft Zeile 12 mit gimp_progress_init eine Statusmeldung aus, die Gimp in einem Progressbar am unteren Ende anzeigt (Abbildung 2).

Abbildung 2: Dank gimp_progress_init() zeigt der Verkleinerungsprozess den Fortschritt unterhalb des Bildes an.

Beim Zusammenstauchen des Bildes muss Gimp mehrere Pixel in einem vereinen, was eine gewisse Glättung erfordert, damit das Bild hinterher noch natürlich aussieht. Zeile 17 stellt deswegen für die Interpolationsfunktion das sogenannte Lanczos resampling ([2]) ein.

Gut dokumentiert

Die Parameterwerte für den Aufruf interner Funktionen dokumentiert Gimp sehr schön direkt im Programm in der Dialogbox, die unter dem Menüpunkt Help->Procedure Browser erscheint. Dort sucht der Entwickler nach einem Stichwort wie "scale" oder "sharpen" und findet dann nach etwas Herumprobieren meist die richtige interne Funktion und ihre Parameter. Geht etwas schief, wie wenn etwa das Plugin-Skript auf einen Fehler läuft und abbricht, schreibt Gimp eine entsprechende Fehlermeldung auf die Console, es empfiehlt sich also, Gimp während der Debug-Phase von der Kommandozeile (und nicht etwa aus dem Desktop-Menü) zu starten, damit die Meldungen im Terminal Rückschlüsse auf etwaige Probleme erlauben. Nach Änderungen am Python-Code der Plugins sollten Entwickler übrigens Gimp neu starten, damit die Änderungen auch garantiert greifen.

Abbildung 3: Der Procedure-Browser enthüllt die Parameter gängiger Gimp-Funktionen.

Zum Nachschärfen nimmt sich die Funktion plug_in_sharpen() ab Zeile 24 des aktuellen Bildes an. Den gewünschten Schärfewert definiert die Konstante SHARPEN in Zeile 4 mit dem Wert 10, und Zeile 24 gibt ihn an den Sharpen-Plugin (ein Gimp bereits beigepackter Standard-Plugin) weiter. Die Funktion benötigt laut Manual neben dem Gimp-Image auch dessen Drawable, das, wie oben erwähnt, die Plugin-Registrierung dem Aufruf von sharp_scale() von Anfang an mitgegeben hat.

Farbe bekennen

Als dritte Korrektur von Rohbildern gleiche ich oft noch die White-Balance ab. Schöpft ein digitales Foto nicht für jeden der drei Kanäle Rot, Grün und Blau die gesamte Bandbreite der möglichen Pixelwerte von 0 bis 255 aus, wie in Abbildung 4, wirken Bilder oft unlebendig und matt. Wer knalligere Farben und höheren Kontrast bevorzugt, kann in Gimps Menüeintrag Colors->Auto->White Balance ein recht brauchbares Tool abfeuern, das die Pixelwerte der drei Kanäle so anpasst, dass sich deren Verteilung im Histogramm auf die gesamte Intensitätsbandbreite ausstreckt. Das sieht nicht immer gut aus, verleiht aber manchmal eher langweiligen Bildern den notwendigen Pep (Abbildung 5).

Allerdings verlief meine Suche nach der zugehörigen Gimp-Funktion im Procedure- und auch im Plugin-Browser erfolglos. Doch zum Glück ist Gimps Sourcecode auf Github ja für jedermann einsehbar (Abbildung 6), und eine kurze Suche nach dem String "Automatic white balance" ergab, dass die zugehörige Gimp-Prozedur drawable-levels-stretch heißt. Diese ist im Procedure-Browser dokumentiert, und nimmt als Parameter nur das "Drawable" des Bildes entgegen. Flugs war der Aufruf in Zeile 26 von Listing 2 eingebaut und damit Teil des Bildverarbeitungsprozesses.

Abbildung 4: Vor dem Weißabgleich zeigt das Histogramm eine enge Verteilung der Kanalwerte und das Bild ist matt.

Abbildung 5: Der Weißabgleich zieht das Histogramm in die Breite schöpft die Farbbreite voll aus, und das Bild wirkt lebendiger.

Abbildung 6: Ein Blick in den Gimp-Source-Code auf Github offenbart, dass die gesuchte Funktion für den Weißabgleich "drawable-levels-stretch" heißt.

Ein Schritt, vor und zurück

Die drei Aktionen zum Skalieren, Schärfen und der Farbkorrektur des Bildes behandelt Gimp von Natur aus aus drei getrennte Einheiten. Überlegt es sich der User hinterher anders und klickt den "Undo"-Menüeintrag, müsste er dies dreimal hintereinander tun, was nervt, denn schließlich hat der User mit seinem Mausklick eigentlich nur eine kombinierte Aktion eingeleitet. Abhilfe schafft der Aufruf von gimp_image_undo_group_start() in Zeile 10, sowie das korrespondierende ..._end() in Zeile 28. Beide definieren die dazwischenliegenden Aktionen als eine Undo-Einheit, sodass Gimp sie nur als Ganzes zurück- oder auch wieder vorrollt. Abbildung 6 zeigt, dass selbst Gimps "Edit"-Menü nun als letzte Aktion nicht mehr nur den (als letztes gelaufenen) Weißabgleich anzeigt, sondern "Sharpen and Scale", also den Namen der hausbackenen, kombinierten Aktion für die ganze Enchilada in einem Rutsch.

Abbildung 7: Dank undo_group_start/end() kann Gimp die Aktionen in einem Rutsch zurückfahren.

Die in dieser Ausgabe gezeigten Routinen haben gezeigt, dass Gimps Python-API Zugriff auf sämtliche internen Funktionen hat. Es ist nicht schwer, Arbeitsabläufe, die aus vielen kleinen Schritten bestehen, zusammen zu fassen, in ein Menü einzuhängen, und in einem Aufwasch ablaufen zu lassen. Das spart Zeit und eliminiert Benutzerfehler.

Infos

[1]

Listings zu diesem Artikel: http://www.linux-magazin.de/static/listings/magazin/2019/03/snapshot/

[2]

"Lanczos resampling", Wikipedia, "https://en.wikipedia.org/wiki/Lanczos_resampling

Michael Schilli

arbeitet als Software-Engineer in der San Francisco Bay Area in Kalifornien. In seiner seit 1997 laufenden Kolumne forscht er jeden Monat nach praktischen Anwendungen verschiedener Programmiersprachen. Unter mschilli@perlmeister.com beantwortet er gerne Ihre Fragen.

POD ERRORS

Hey! The above document had some coding errors, which are explained below:

Around line 4:

Unknown directive: =desc