Script-fu example batch script (explained). For beginners

I’m posting this script because I’ve recently had to create a script so Gimp performs the same action to a group of files.

As I haven’t found any easy (for dummies) tutorial about script-fu, I’ve thought this little example would be of interest for begginers, so they could start to understand the structure and internals of a script using the script-fu engine.

I’m a newbie too, so maybe I will not be completely right while explaining each funtion and parameter, but in my computer it is running fine (either Linux Mint x64 or Windows 8.1 x64 with Gimp 2.9.5-CCE).

I have not created the script from scratch. Instead, I have joined parts of other scripts, and then I have added the function I needed. This example could be used as is if you need to batch process images sharpening them with the Wavelet sharpen plugin (every file sharpened the same).

In case you don’t have that plugin, you could download its source code in
Wavelet sharpen plugin (Gimp registry)

If you’re not confident about your compiling skills, you can download the plugin with these links

Windows (32 & 64 bits)
gimp - wavelet-sharpen-0.1.2_32bits-64bits_gimp-2.8_Win.zip (152.4 KB)

Linux (compiled in Linux Mint x64, working in Gimp 2.9.5-CCE Appimage, not tested in other versions or environments). If you launch it from the menu, Gimp will show a “message” warning about an error, but the plugin still works. I think it’s important to compile it with the version of Gimp you are using.
wavelet-sharpen.zip (13.2 KB)

To know where to find/place your scripts and plugins, see

The explanations are intended to stay in the script, so you will always have an explanation right next to the code. I don’t think the increased size of the script (because of the comments) will make it much slower.

And now the script (you just have to copy the text, create an empty text file, paste the text inside, and save the file as “yourdesiredname.scm” (“scm” extension is needed).
.

    (script-fu-register                       ;Here we start registering our script in the
                                              ;Gimp's procedural database (so it will appear
                                              ;in the Help>>Procedure Browser) and any other
                                              ;script will be able to run it
                                           ;Next you have to set a few required parameters:
         "script-fu-wavelets-batch"        ;you give a name to your script, anything
                                           ;you want, but better if it begins with "script-fu".
                                           ;It will be used along the script
         "Batch wavelet sharpen"           ;the name of the script shown in the submenu
                                           ;(this script is about applying a sharpen plugin
                                           ;to a group of images, hence the name)
         "Batch process to sharpen images\
           using Wavelet sharpen plugin."  ;a description of the script shown as a tooltip
                                           ;(when you mouse over the submenu name)
         "Javier Bartol"                   ;author
         "CC-BY-SA-3.0"                    ;copyright
         "April 02, 2018"                  ;creation date
         "*"                               ;types of images the script is intended to work
                                           ;with (in this case, any type, but it could be
                                           ;RGB, RGBA, GRAY, GRAYA, INDEXED or INDEXEDA)

                                                      ;Now you need to give the kind of data
                                                      ;each function parameter accepts:
                                                      ;look at the parameters defined in the
                                                      ;main function, and set them
                                                      ;in THE SAME ORDER
          SF-STRING    "Images"     "~/Images/*.tif"  ;the first parameter has to be a
                                                      ;string of characters (SF-STRING)
                                                      ;when running the script a dialog will show, and
                                                      ;this first parameter will be labeled as "Images"
                                                      ;and it will have a default content "~/Images/*.tif"
                                                      ;(this will be the path to the files, and the type of
                                                      ;images processed)
          SF-VALUE     "Amount"     "0"               ;the second parameter relates to the
                                                      ;information needed by the wavelet
                                                      ;sharpen plugin. It has to be a number (SF-VALUE),
                                                      ;and the default value is "0"
                                                      ;(this will be the amount of sharpening applied)
          SF-VALUE     "Radius"     "0.6"             ;the third parameter has to be a number,
                                                      ;being "0.6" the default value (it will be the radius
                                                      ;of sharpening)
          SF-TOGGLE    "Luminance"   1                ;the last parameter must answer a YES/NO
                                                      ;question: it will ask if you will sharpen luminance
                                                      ;channel only (well, in fact, you will only see a label
                                                      ;saying "Luminance", and you will have to guess it is
                                                      ;asking if you want to sharpen luminance only)
                                                      ;default value is YES, or "1"
    )

    (script-fu-menu-register "script-fu-wavelets-batch" "<Image>/Filters/Enhance")
               ;Now you are registering the script in the Gimp menus, using the name you gave
               ;it, so you will find your script in Filters>>Enhance>>Batch wavelet sharpen
               ;(apparently <Image> is the root of every menu)



    (define (script-fu-wavelets-batch ask-fileglob ask-amount ask-radius ask-luminance)
              ;Declaration of the main function (the part of the script that does the work)
              ;you tell Gimp the name of your script (script-fu-wavelets-batch), and which
              ;parameters has to ask the user when launching it. Those parameters are just
              ;variables, and it's better if they have meaningful names to you.
              ;Here I add "ask-" to each variable to remind me Gimp will ask for their values
    (let*                                  ;Here it is: the engine of the script
        ((thefiles (cadr (file-glob ask-fileglob 0))))  ;Let's go from the inner to the outer part
                                                        ;remember that "ask-fileglob" is the first parameter of
                                                        ;your function, and is defined as a string of characters,
                                                        ;with a default value "~/Images/*.tif"
                                                        ;"file-glob" will search the path given by "ask-fileglob"
                                                        ;and will return a number followed by a list of strings of
                                                        ;characters. Each string of characters will be the full
                                                        ;path and filename of each image
                                                        ;so we will get: a number, the full path/filename of the
                                                        ;first image, the full path/filename of the second image,
                                                        ;and so on
                                                        ;with "cadr" we will remove the number from the list (read
                                                        ;https://www.gimp.org/tutorials/Basic_Scheme/, chapter 3.1),
                                                        ;although I'm not pretty sure why one has to use "cadr"
                                                        ;instead of "cdr"
                                                        ;so, the variable "thefiles" will hold a list returned by
                                                        ;"file-glob", removing the first member of the list (the number)
                                                        ;the trailing "0" means that the filenames will be coded in UTF-8

        (while (not (null? thefiles))
                               ;While the list inside "thefiles" is not empty, the script will
                               ;perform the actions inside this loop
          (let*
            ((thefilename (car thefiles)) 
                               ;the variable "thefilename" will hold the first item (car)
                               ;from the "thefiles" list
             (image (car (gimp-file-load RUN-NONINTERACTIVE thefilename thefilename)))
                               ;then the image with the path present in "thefilename" is loaded
             (drawable (car (gimp-image-get-active-layer image)))
                               ;as a last step, the active layer is made the modifiable (drawable)
                               ;part of the image (this has to do with Gimp internals, and may be
                               ;used in advanced scripts, but here is the same as the full image)
            )
            (gimp-image-undo-disable image)
                               ;the undo cache is disabled (this is not really necessary in this simple script)
            (plug-in-wavelet-sharpen RUN-NONINTERACTIVE image drawable ask-amount ask-radius ask-luminance)
                               ;the plugin Wavelet sharpen is launched
                               ;to know which parameters the plugin needs, open the Procedure
                               ;Database and it will be listed as "plug-in-wavelet-sharpen"
                               ;"RUN-NONINTERACTIVE" is set so the plugin doesn't ask for user
                               ;input on each image
                               ;"image" is the image to work with
                               ;"drawable" is the editable part of the image to work with
                               ;(in our case, it's the full image)
                               ;"ask-amount" is the second parameter the script asks in the dialog
                               ;It's a number, and it will be the amount of sharpening the plugin will apply
                               ;"ask-radius" is the third parameter of the function. It's a number (as
                               ;defined at the beggining, when registering the script), and will be the
                               ;radius used by the plugin
                               ;"ask-luminance" is the last parameter of the function. It will be a tick box,
                               ;and will be ticked by default, meaning the plugin will sharpen only the
                               ;luminance channel
            (gimp-image-undo-enable image)
                               ;the undo cache is enabled again (this is not really necessary and you can
                               ;remove both disabling and enabling of undo cache)
            (gimp-file-save RUN-NONINTERACTIVE image drawable thefilename thefilename)
                              ;saves/overwrites the image stored in "thefilename". BE CAREFUL WITH THIS!
                              ;You may lose your original images!
                              ;If you want to save files with different names, maybe you will find a solution in
                              ; http://www.gimptalk.com/index.php?/topic/34672-script-fu-how-to-increment-filenames
                              ;and http://it-nonwhizzos.blogspot.com.es/2014/10/gimp-script-scheme-to-scale-multiple.html
            (gimp-image-delete image)
                              ;close the image
          )
          (set! thefiles (cdr thefiles))
                              ;modifies the content of "thefiles", giving it the same list as before, but removing
                              ;the first item (the previously first image of the list)
        )                     ;here the loop is closed and returns back to checking if "thefiles" is not an empty list
    )
)
6 Likes

Awesome! We collect gimp scripts here if you’d like to make a pull request!

if you’d like to make a pull request!

Of course, I would!

But I will wait a bit, just in case somebody finds a mistake that I have to correct.

And in the meantime I will try to figure out how to make pull requests :wink:

1 Like

This is very helpful. I am trying to learn how to write a script. I have tried the script on the GIMP batch page, but it doesn’t run. Is there a way of debugging scripts? Can you issue a marker/notification?

Hi Paul, and sorry for the late reply.

Do you mean running it from command line? I think you can’t run my script from command line, as it is interactive (should ask proper parameters), and maybe it has to run from a ‘live’ Gimp. I’m not pretty sure, and I don’t know how to try it myself, as I run Gimp from an Appimage.

But first things first:

  • have you compiled the Wavelet sharpen plugin for your current gimp installation?
  • Is it properly installed, and Gimp loads it into the Procedural Database (do you see the plugin in Filters>>Enhance)?
  • have you tried running the plugin from withing Gimp (to see if there’s some sort of errors)?
  • if the plugin is working, have you tried running the script from the menu (Filters>>Enhance)?

Perhaps the most important part is making sure the plugin works in your own version of Gimp (mine is 2.9.5)

Hope this helps

For various reasons, I am trying to run GIMP in batch. I did get some success. I’m pretty sure that the problem is that the Win-10 batch processor is not working. I can build a small simple .scm file - basically it simply copies an image from one file to another. I can run this successfully from the console in GIMP. Using the MSYS-64 linux emulator in Win-10, again it runs successfully. Using an attempt to run through a console window in Win-10 (and running with --verbose), I get all kinds of statements printed (suggesting that the process is accessing and running GIMP), but it does not complete. So it seems something to do with GIMP batch on Win-10.

One question that I am still trying to resolve: When you call gimp-2.x from the command line (from console), you have an option -batch-interpreter= something. I do not know what the options are here. Is this listed somewhere? The documentation on this functionality is not really very good. I raise this question because it is not clear that the engine which runs the script-fu script in the console and the engine which runs the script in the Win-10 console are the same engine

As far as I know, there are two options: “script-fu-eval” (the default interpreter for the scripts, written in tiny-scheme), and “python-fu-eval” (if you want to run a script written in python).

But I’m a newbie, so maybe if somebody with better knowledge could answer your question…

I was informed in another thread that the problem was my use of single quotes. Indeed, using double quotes fixed the failure to successfully complete the script .

I have updated the comments in the code to (hopefully) better explain what’s happening in the script.

The script itself hasn’t changed and does exactly the same.

Here is the new code:

; GIMP Script-fu script first published in
; https://discuss.pixls.us/t/script-fu-example-batch-script-explained-for-beginners/7341

(script-fu-register           ; Here we start registering our script in the GIMP's
                              ; procedural database (so it will appear in the
                              ; Help>>Procedure Browser) and any other script will
                              ; be able to run it
                              ; If we compare a script with a recipe, we will find
                              ; out that we need some fixed ingredients (salt, water,
                              ; flour, ...), plus some others, specific for your own
                              ; recipe
                              ; Here, we must set a few required parameters (common to
                              ; every script) and some others specific to our own script

                              ; The REQUIRED PARAMETERS are:

     "script-fu-wavelets-batch"        ; give a name to your script, anything you
                                       ; want, but better if it begins with "script-fu".
                                       ; It will be used along the script
     "Batch wavelet sharpen"           ; the name of the script shown in the submenu
                                       ; (this script is about applying a sharpen
                                       ; plugin to a group of images, hence the name)
     "Batch process to sharpen images\
       using Wavelet sharpen plugin."  ; a description of the script shown as a tooltip
                                       ; (when you mouse-over the submenu name)
     "Javier Bartol"                   ; author
     "CC-BY-SA-3.0"                    ; copyright
     "April 02, 2018"                  ; creation date
     "*"                               ; types of images the script is intended to work
                                       ; with (in this case, any type, but it could be
                                       ; RGB, RGBA, GRAY, GRAYA, INDEXED or INDEXEDA)


                              ; And then, OUR OWN PARAMETERS (the ones needed by our function)
                              ; You need to give the kind of data each function parameter
                              ; accepts: look at the parameters defined in the main
                              ; function, and set them IN THE SAME ORDER

      SF-STRING    "Images"     "~/Images/*.tif"  ; the first of our parameters has to
                                                  ; be a string of characters ("SF-STRING").
                                                  ; When running the script a dialog will
                                                  ; show, and this first parameter will
                                                  ; be labeled as "Images", with a
                                                  ; default content "~/Images/*.tif" (this
                                                  ; will be the path to the files, and
                                                  ; the file format of images processed)
      SF-VALUE     "Amount"     "0"               ; the second parameter relates to the
                                                  ; information needed by the wavelet
                                                  ; sharpen plugin. It has to be a number
                                                  ; ("SF-VALUE"), and the default value
                                                  ; is "0" (this will be the amount of
                                                  ; sharpening applied)
      SF-VALUE     "Radius"     "0.6"             ; the third parameter has to be a number,
                                                  ; being "0.6" the default value (it will
                                                  ; be the radius of sharpening)
      SF-TOGGLE    "Luminance"   1                ; the last parameter must answer a YES/NO
                                                  ; question: it will ask if you will sharpen
                                                  ; luminance channel only (well, in fact,
                                                  ; you will only see a label saying
                                                  ; "Luminance", and you will have to guess
                                                  ; it is asking if you want to sharpen
                                                  ; luminance only). Default value is YES, or "1"
)

(script-fu-menu-register "script-fu-wavelets-batch" "<Image>/Filters/Enhance")
                              ; Here you are registering the script in the GIMP menus,
                              ; using the name you gave it, so you will find your script
                              ; as "Filters>>Enhance>>Batch wavelet sharpen"
                              ; (apparently "<Image>" is the root of every menu)



(define (script-fu-wavelets-batch ask-fileglob ask-amount ask-radius ask-luminance)
                              ; Declaration of the main function (the part of the script
                              ; that does the work).
                              ; You tell GIMP the name of your script ("script-fu-wavelets-batch"),
                              ; and which parameters has to ask the user when launching it.
                              ; Those parameters are just variables, and it's better if they
                              ; have meaningful names to you.
                              ; Here I add "ask-" to each variable to remind me GIMP will
                              ; ask for their values

(let*                         ; Here it is: the engine of the script

    (                         ; definition of the VARIABLES used in the function
        (thefiles (cadr (file-glob ask-fileglob 0)))
    )                         ; End of definition of VARIABLES

                              ; Let's look into the previous code from the inner to the
                              ; outer parts. Remember that "ask-fileglob" is the first
                              ; parameter of your function, and is defined as a string
                              ; of characters, with a default value: "~/Images/*.tif"

                              ; Now, "file-glob" will search the path given by "ask-fileglob"
                              ; and will return a list consisting of 2 elements: the first
                              ; element is a number (the number of images found), and the
                              ; second element is a list of strings of characters. Something
                              ; like "(number ("string1" "string2" "string3"))". That is,
                              ; the second element is a list inside the main list.

                              ; Each string of characters will be the full path and filename
                              ; of each image

                              ; Then, we apply a "cadr", that is nothing but a "car"
                              ; after a "cdr"
                              ; (read "https://www.gimp.org/tutorials/Basic_Scheme/", chapter 3.1)

                              ; The "cdr" part will remove the number from the list, and
                              ; we will get a list with just one element, that is indeed
                              ; a list (something like "(("string1" "string2" "string3"))").
                              ; So we need to be able to access the contents of the nested
                              ; list (the list of filenames). By using "car", we get the
                              ; first element of the list, and as a result we get the list
                              ; we were searching for (as an explanation "for dummies":
                              ; with "car" we remove the first pair of brackets)

                              ; In the end, the variable "thefiles" will contain a list
                              ; returned by "file-glob", after removing the first member
                              ; of the list (the number)

                              ; The trailing "0" means that the filenames will be
                              ; encoded in UTF-8

                              ; Starting a LOOP

    (while (not (null? thefiles))
                              ; as long as the list inside "thefiles" is not empty,
                              ; the script will perform the actions inside this loop
      (let*
        (                     ; definition of the VARIABLES used in the loop
            (thefilename (car thefiles))
                              ; the variable "thefilename" will contain the first
                              ; item ("car") from the "thefiles" list
            (image (car (gimp-file-load RUN-NONINTERACTIVE thefilename thefilename)))
                              ; then, the image with the path present in "thefilename"
                              ; is loaded and set into the variable "image"
            (drawable (car (gimp-image-get-active-layer image)))
                              ; as a last step, the active layer of the loaded image
                              ; is set into the variable "drawable"

        )                     ; End of definition of VARIABLES used in the loop

        (gimp-image-undo-disable image)
                              ; the undo cache is disabled
                              ; (this is not really necessary in this simple script)
        (plug-in-wavelet-sharpen RUN-NONINTERACTIVE image drawable ask-amount ask-radius ask-luminance)
                              ; the plugin "Wavelet sharpen" is launched.
                              ; To know which parameters this plugin needs, open
                              ; the Procedure Database and it will be listed as
                              ; "plug-in-wavelet-sharpen".

                              ; "RUN-NONINTERACTIVE" is set so the plugin doesn't
                              ; ask for user input on each image

                              ; "image" is the image to work with, present in the
                              ; variable, and different on each iteration of the loop

                              ; "drawable" is the editable part of the image to work
                              ; with (in our case, it's the full active layer of the image)

                              ; "ask-amount" is the second parameter the script asks
                              ; in the dialog. It's a number, and it will be the amount
                              ; of sharpening the plugin will apply

                              ; "ask-radius" is the third parameter of the function.
                              ; It's a number too (as defined at the beggining, when
                              ; registering the script), and will be the radius used
                              ; by the plugin

                              ; "ask-luminance" is the last parameter of the function.
                              ; It will be a tick box, that will be ticked by default,
                              ; meaning the plugin will sharpen only the luminance channel
        (gimp-image-undo-enable image)
                              ; the undo cache is enabled again (this is not really
                              ; necessary and you can remove both disabling and enabling
                              ; of undo cache)
        (gimp-file-save RUN-NONINTERACTIVE image drawable thefilename thefilename)
                              ; saves/overwrites the image stored in "thefilename".
                              ; BE CAREFUL WITH THIS!
                              ; You may lose your original images!
                              ; If you want to save files with different names, maybe
                              ; you will find a solution in
                              ; "http://www.gimptalk.com/index.php?/topic/34672-script-fu-how-to-increment-filenames"
                                    ; (bear in mind that "aref" is deprecated, and
                                    ; you may wish to use "vector-ref" instead)
                              ; and "http://it-nonwhizzos.blogspot.com.es/2014/10/gimp-script-scheme-to-scale-multiple.html"
        (gimp-image-delete image)
                              ; closes the image
      )                       ; End of "let*"
      (set! thefiles (cdr thefiles))
                              ; modifies the content of "thefiles", giving it the same
                              ; list as before, but removing the first item (the
                              ; previously first image of the list), so in the next
                              ; iteration of the loop, the function will work with
                              ; the next image of our folder

    )                         ; the loop is closed and returns back to checking if
                              ; "thefiles" is not an empty list. The moment "thefiles"
                              ; is empty, the loop will end

)                             ; End of "let*"
)                             ; End of function

Hope it’s easier to understand now.

1 Like

Hey thanks for script, i was looking for some examples and this was very helpful.
In my experience gimp did not recognize ~ in file path so i had to use full path to folder, other than that it works like charm.
Its interesting how i could not find any documentation for gimp fu script.
Cheers

1 Like

A few remarks:

  • If it is used for batch, you don’t need to “register” the script, normally. Defining the function is enough since the batch invocation calls the function directly.
  • The Gimp registry has been dead for quite a while so your URL doesn’t work. Its contents have been copied to a github project (but file lists being limited to 1000 names, it’s hard ot get the w* files directly, it can be faster to git clone the whole thing.
  • Gimp 2.10 comes standard with a wavelet decomposition , so maybe the same thing can be dome more simply now.

Well, given that the original post was written a whole 5 years ago, I wouldn’t be surprised that the script doesn’t even work at all.

In spite of this, I believe the comments included are still useful for some new user trying to write script-fu scripts.

Yes. The current (as of May '23) link to download the scripts is

Or you can just click the green button (labeled Clone), select Download ZIP and within the compressed file, inside the registry.gimp.org/files folder you will find all the archived scm scripts (for those who have no clue about how git works…).

And about the function included, as I said it was what I needed at that moment, and it could be easily swaped by any other function.