Become a Gimp Script-Fu Master: with Guile (aka Scheme)

I’ve been using GIMP for around a year, and have started to script it. The tutorials out there were kind of dated, and confusing to me, so this is an attempt to address this situation. This document is still very rough. The examples are plugins that help enhance the quality of photos taken with the JamCam toy camera.

  1. Reference Materials
  2. Creating a Plug-In
  3. Automating a Single Filter
  4. Running a Sequence of Filters
  5. Doing it on Multiple Windows

Reference Materials

You will probably want to read some other script-fu examples.
The application’s default scripts are kept in /usr/share/gimp/2.0/scripts/.
The user’s scripts are kept in ~/.gimp-2.2/scripts/.

The definitive book about Scheme is The Structure and Interpretation of Computer Programs by Abelson, Sussman, and Sussman.

Some cool scripts:

  • http://www.porpoisehead.net/mysw/index.php?pgid=gimp_svg

Creating a Plug-In

Script-fu is used to create plug-ins. A plug-in is some code that performs an action in GIMP, with additional code that registers the plug-in into the application’s menus. Plug-ins are read from the shared script directory (above) and the user’s scripts directory in .gimp-2.2. They are named with the .scm extension.

The following is code for a plug-in that does nothing. Save it to demo.scm in ~/.gimp-2.2/scripts

(define (script-fu-demo image
            drawable)
        1)

(script-fu-register "script-fu-demo"
            "_Demo..."
            "Demo Plugin."
            "John Kawakami "
            "John Kawakami"
            "8/28/2006"
            "RGB*"
            SF-IMAGE       "Image"          0
            SF-DRAWABLE    "Drawable"       0)

(script-fu-menu-register "script-fu-demo"
             _"/Script-Fu/Demo")
@riceball.com>

This is a framework that holds code that operates on the image being edited. (You do things a little differently if you’re generating a new image from scratch.)

Let’s go through the code line by line. The first three lines are the code that does nothing. When you write your script, you’ll put the guts of your program in this function.

(define (script-fu-demo image
            drawable)
        1)

All plugins that operate on the current image take two arguments, image and drawable. There’s a complicated explanation for this, and I’ll link to it later. For now, just know the first two arguments, and know you’ll pass the to your functions.

All functions return a value, and this one returns 1.

The second part registers our function, script-fu-demo, with the global registry of functions. This registry makes it possible for other functions to call your plug ins. There’s a tool to browse the registry under the GIMP menu Xtns->Procedure Browser. You should probably check it out.

(script-fu-register "script-fu-demo"
            _"_Demo..."
            "Demo Plugin."
            "John Kawakami "
            "John Kawakami"
            "8/28/2006"
            "RGB*"
            SF-IMAGE       "Image"          0
            SF-DRAWABLE    "Drawable"       0)
@riceball.com>

The first seven arguments are strings that describe your function. The subsequent arguments are used to construct the argument list that’s going to be passed to your function. The first is the image and the second is the drawable, just like in the function definition. (I bet you could switch them… but there’s no point to doing that.)

Note that they come in triplets (so there are six arguments to define two parameters).

Later, these arguments will be used to display a simple dialog box to enable user input to the plugin.

The last section creates a menu item for this plugin.

(script-fu-menu-register "script-fu-demo"
             _"/Script-Fu/Demo")

Now you can save the file, and install it. To install it, go to the GIMP menu Xtns->Script Fu->Refresh Scripts. Then, go to an image window and look under the Script-Fu menu. You should see the new menu item there. Clicking on that item will run the script (which does nothing).

The _”_AAAAAA” string is used in menus. The letter after the underscore within the string, “A”, will be used as the accelerator key.

Automating a Single Filter

Here’s the code for a plugin that sharpens an image. I have a cheap toy camera that always requires some sharpening to make it look okay. This plugin saves me a few clicks.

(define (script-fu-jamcam image
              drawable)
        (begin
            (plug-in-sharpen 1 image drawable 40)
            (gimp-displays-flush) ))

(script-fu-register "script-fu-jamcam"
            _"_JamCam..."
            "Fixes up JamCam pictures."
            "John Kawakami "
            "John Kawakami"
            "8/28/2006"
            "RGB*"
            SF-IMAGE       "Image"          0
            SF-DRAWABLE    "Drawable"       0)

(script-fu-menu-register "script-fu-jamcam"
             _"/Script-Fu/JamCam")
@riceball.com>

The new code, below, runs the plug-in-sharpen filter, and then calls gimp-displays-flush to refresh the window’s contents. If you don’t call gimp-displays-flush, the old image stays in the window.

        (begin
            (plug-in-sharpen 1 image drawable 40)
            (gimp-displays-flush) ))

The (begin … ) construction executes a sequence of functions, and returns the value of the last function.

To see more information about the functions, use the Procedure Browser and search for the functions. Simple documentation is provided.

Running a Sequence of Filters

To get the picture just right, I always do some adjustments to the saturation and contrast before sharpening. The code is as follows:

        (begin
            (gimp-hue-saturation drawable 0 0 0 5)
            (plug-in-sharpen 1 image drawable 40)
            (gimp-brightness-contrast drawable 0 20)
            (gimp-displays-flush) ))

Doing it on Multiple Windows

Doing the filtering on all the open windows should be simple, but it’s kind of hard.

I’m going to lose all the programming newbies and Scheme/SIOD newbies here. (If you are a noob, do a search for a Script-Fu tutorial on the gimp.org site. Study the tutorial. That will explain some of this.)

The difficulty is due to the return value of gimp-image-list. gimp-image-list returns a list, the first member is the number of images, and the second member is an array of image IDs. An array is not a list. To get the elements out of an array, you use (aref array index). So, you have to use an index and loop over the array. More on this later. For now, here’s the code.

Save this script to jamcamall.scm:

(define (script-fu-jamcam-all)
    (let* ( 
        (image-num  (car (gimp-image-list)) )
        (image-ids  (cadr (gimp-image-list)) ) 
        (counter 0)
          )
        (while (< counter image-num)
            (let* (
                (image (aref image-ids counter)) 
                  )
                (script-fu-jamcam image (car (gimp-image-get-active-drawable image)))
                (set! counter (+ counter 1))
            )
        )
    )
)

(script-fu-register "script-fu-jamcam-all"
            _"_JamCam All Windows..."
            "Fixes up JamCam pictures."
            "John Kawakami "
            "John Kawakami"
            "8/28/2006"
            "RGB*")

(script-fu-menu-register "script-fu-jamcam-all"
             _"/Xtns/Script-Fu/JamCam")
@riceball.com>

This plugin is installed into The GIMP window, under Xtns/Script-Fu/JamCam.

The function has a lot of new ideas in it. We’ll go over these one by one.

Scheme math is all prefix. To get the value of “x – 1” you type “(- x 1)”. To get the value of “5 * n” you type “(* 5 n)”. The operator is typed first, then the arguments follow. Comparison operators work the same way. “n > -1” becomes “(> n -1)”. For more information about this, read Section 3 of the GIMP tutorial.

(let* ( …assignments… ) (func …) (func …) … ) – this assigns values to names, and then executes a sequence of functions. The first block assigns values:

(define (script-fu-jamcam-all)
    (let* ( 
        (image-num  (car (gimp-image-list)) )
        (image-ids  (cadr (gimp-image-list)) ) 
        (counter 0)
          )

car and cadr are list functions. For information about car, cdr, cadr, cddr, and other functions, read Section 3.3 of the GIMP tutorial.

The (while (…cond…) …sequence…) construction executes the sequence repeatedly, as long as cond is true.

        (while (< counter image-num)
            (let* (
                (image (aref image-ids counter)) 
                  )
                (script-fu-jamcam image (car (gimp-image-get-active-drawable image)))
                (set! counter (+ counter 1))
            )
        )
    )
)

Again, in the above, we see another (let* … …).

(aref …array… …index…) extracts values from an array. An array is not a list. It’s a fixed data structure that’s returned by some functions. The data is from deep in the guts of GIMP.

After extracting the value of an image from image-ids, we use it to call script-fu-jamcam, which was defined in the other plugin.

The final new function we see is (set! …name… …value…). set! assigns a value to a name. It’s not like let*, because the values assigned in let* exist only within let*’s block of code. set! assigns a new value to an existing name.

(The real Scheme’y way to do this would be to recursively apply the script-fu-jamcam function to a list of image IDs. Unfortunatey, we don’t get the data in a format that makes life so easy.)

Attachment Size
jamcam.scm.txt 566 bytes
jamcamall.scm.txt 654 bytes