[QUIZ#7] Matthias Reitinger (Script-Fu [GIMP])


#1
Hallo,

und noch eine etwas andere Lösung von mir.

Zuerst der Quelltext (game-of-life.scm):
[lisp](define (script-fu-game-of-life image)
(let* ((active-layer (car (gimp-image-get-active-layer image)))
(survivor-layer (car (gimp-layer-copy active-layer FALSE)))
(newborn-layer)
(result-layer)
(convmatrix (cons-array 25 'double))
(channels (cons-array 5 'long))
(survivor-curve (cons-array 12 'byte))
(newborn-curve (cons-array 12 'byte)))

; Arrays initialisieren
(for-each (lambda (i) (aset convmatrix i 1.0)) '(6 7 8 11 13 16 17 18))
(for-each (lambda (i) (aset channels i 1)) '(0 1 2 3))

(aset survivor-curve 0 0) (aset survivor-curve 1 0)
(aset survivor-curve 2 61) (aset survivor-curve 3 0)
(aset survivor-curve 4 62) (aset survivor-curve 5 255)
(aset survivor-curve 6 96) (aset survivor-curve 7 255)
(aset survivor-curve 8 97) (aset survivor-curve 9 0)
(aset survivor-curve 10 255) (aset survivor-curve 11 0)

(aset newborn-curve 0 0) (aset newborn-curve 1 0)
(aset newborn-curve 2 93) (aset newborn-curve 3 0)
(aset newborn-curve 4 94) (aset newborn-curve 5 255)
(aset newborn-curve 6 96) (aset newborn-curve 7 255)
(aset newborn-curve 8 97) (aset newborn-curve 9 0)
(aset newborn-curve 10 255) (aset newborn-curve 11 0)

; Undo-Gruppe starten
(gimp-undo-push-group-start image)

; Kopie des aktiven Layers hinzufügen
(gimp-image-add-layer image survivor-layer -1)

; Konvolutionsmatrix anwenden
(plug-in-convmatrix RUN-NONINTERACTIVE
image
survivor-layer
25
convmatrix
FALSE
8
0
5
channels
2)

; Eine Kopie des konvolutierten Layers anlegen
(set! newborn-layer (car (gimp-layer-copy survivor-layer FALSE)))
(gimp-image-add-layer image newborn-layer -1)

; Kurven anwenden
(gimp-curves-spline survivor-layer HISTOGRAM-VALUE 12 survivor-curve)
(gimp-curves-spline newborn-layer HISTOGRAM-VALUE 12 newborn-curve)

; Ebenenmodi setzen
(gimp-layer-set-mode survivor-layer MULTIPLY-MODE)
(gimp-layer-set-mode newborn-layer ADDITION-MODE)

; Ebenen vereinen
(gimp-image-merge-down image survivor-layer 0)
(gimp-image-merge-down image newborn-layer 0)

; Undo-Gruppe beenden
(gimp-undo-push-group-end image)

; Anzeige updaten
(gimp-displays-flush)))

(script-fu-register "script-fu-game-of-life"
"Game of Life"
"Plays Conway's Game of Life"
"Matthias Reitinger"
"Matthias Reitinger"
"2008-12-07"
"RGB*, GRAY*"
SF-IMAGE "Input Image" 0)
(script-fu-menu-register "script-fu-game-of-life" "<Image>/Script-Fu")
[/lisp]

Diese Datei (gibt's auch als Anhang ganz unten) speichert man einfach unter ~/.gimp-2.6/scripts (fragt mich nicht wo der entsprechende Ordner unter Windows liegt). Dann startet man GIMP 2.6. Es sollte ein neuer Menüpunkt „Script-Fu“ sichtbar sein. Nun legt man ein neues Bild mit schwarzem Hintergrund an. Die Hefezellen setzt man, indem man weiße Pixel an die entsprechenden Stellen malt (alles in einer Ebene). Dann wählt man im Menü Script-Fu -> Game of Life und schon erhält man die nächste Generation. Mit Strg+F kann man den Schritt beliebig oft wiederholen.

Wie funktioniert das ganze? Hier die Anleitung und Erklärung zum nachmachen: Zunächst wird die aktuelle Ebene dupliziert (nennen wir sie mal „Überlebende“). Dann wird auf das Duplikat eine Faltungsmatrix angewandt (Filter -> Allgemein -> Faltungsmatrix…). Dieser Filter betrachtet für jeden Bildpixel eine 5x5-Umgebung und addiert die Farbwerte dieser Pixel auf. Man kann dabei festlegen, wie die einzelnen Pixel gewichtet werden. In unserem Fall soll die Matrix so aussehen:
0 0 0 0 0
0 1 1 1 0
0 1 0 1 0
0 1 1 1 0
0 0 0 0 0

Wir addieren also gerade die Werte der benachbarten Zellen auf (der aktuelle Pixel liegt in der Mitte der Faltungsmatrix). Das Ergebnis ist der neue Wert des aktuell betrachteten Pixels. Diesen teilen wir vorher noch durch 8 (in das Feld „Divisor“ eine 8 eintragen), damit wir Werte zwischen 0 und 1 erhalten. Wir haben nun also eine Ebene, in der die Intensität eines jeden Pixels der Anzahl der lebenden Nachbarn entspricht (Intensität 0 -> kein Nachbar, Intensität 1/8 -> 1 Nachbar, Intensität 2/8 -> 2 Nachbarn…). Diese Ebene duplizieren wir nun nochmal und nennen das Duplikat „Neugeborene“.
Da eine Hefezelle genau dann überlebt, wenn sie zwei oder drei Nachbarn besitzt, möchten wir die „Überlebende“-Ebene so modifizieren, dass die Werte 2/8 und 3/8 auf 1 gezogen werden und alle anderen zu 0 werden. Dies erreicht man z.B. über Farben -> Kurven…. Setzt man Punkte bei (0,0), (61,0), (62,255), (96,255), (97,0), (255,0), dann entspricht das genau dieser Abbildung. Analog wollen wir in der „Neugeborene“-Ebene den Wert 3/8 (bei 3 Nachbarn wird eine neue Zelle geboren) auf 1 ziehen und alles andere auf 0. Dazu kann man als Kurvenpunkte (0,0), (93,0), (94,255), (96,255), (97,0), (255,0) verwenden.
Jetzt müssen wir die Ebenen nur noch passend verheiraten. Die weißen Pixel in der „Überlebende“-Ebene sollen im Ergebnis nur dann weiß sein, wenn in der ursprünglichen Ebene auch schon ein weißer Pixel gesetzt war. Das erreichen wir, indem wir den Ebenenmodus „Multiplikation“ auswählen. Die „Neugeborene“-Ebene trägt einfach neue weiße Pixel zum Ergebnis bei, also wählen wir hier den Ebenenmodus „Addition“. Und schon haben wir die nächste Generation berechnet :)

Das Script-Fu-Skript macht nun nichts anderes als genau diese Schritte auszuführen (und die Ebenen am Schluss zu vereinen).

Das ganze sollte nach demselben Schema auch z.B. in Photoshop durchführbar sein – da könnte man dann auch recht einfach ein Makro draus machen, wenn mich nicht alles täuscht. Erfahrungsberichte werden gern gelesen :)

Wenn noch Fragen offen sind, dann stellt sie ruhig. Ich glaub ich hab das ganze nämlich wieder mal komplizierter erklärt als es eigentlich ist…

Grüße,
Matthias
 

Anhänge

Zuletzt bearbeitet:

RedWing

Erfahrenes Mitglied
#2
Bin immer wieder überrascht was man alles so entdecken kann, dabei hab ich gimp bis dato immer nur zum Bilder retuschieren verwendet :)

Gruß,
RedWing