Friday 25 November 2016

Scheme-ing with The Gimp: batch photo resizing

Gimp (a.k.a. The Gimp) is an image editor, very popular with Linux users and possibly a handful of Windows users.


It includes support for scripting tasks via Python (which most young programmers think is the bees-knees) and Scheme (which most Computer Science students think is the work of the devil).


Caught between the devil and the deep blue sea, this post focuses on Scheme.

No, its not Kung Fu


Script support in Gimp can be via Python-Fu (for Python) or Script-Fu for a lightweight implementation of Scheme: TinyScheme.

Considered by many to be "the world's most unportable programming language" Scheme is a functional programming language, floating in a sea of brackets.

Automating tasks using Python-Fu creates Gimp plugins, whereas Script-Fu creates Gimp scripts. As far as I know you can achieve the same results with scripts or plugins.

Script-Fu includes a very handy Procedure Browser which details each procedure/function including associated parameters.

To view in Gimp, goto Filters > Script-Fu > Console and click Browse...


Back in the 2000's I found I was resizing photos quite a bit in order to upload them to a simple website. I usually took a large image and reduced it twice; once as the main display photo, and again, even smaller as a thumbnail.

My early script


I discovered I could automate this in Gimp by creating a script, saving it to a file with a .scm extension, and placing the file in the Gimp "scripts" folder (e.g. /home/steve/.gimp-2.8/scripts). When Gimp is started/restarted it will pick up and register the new script.

This is my early script, saved in a file called web-images.scm:-

; The GIMP -- an image manipulation program
; Copyright (C) 1995 Spencer Kimball and Peter Mattis
;
; SD/WebImages script -- Rescale an image to create a thumbnail & web image.
;
; Copyright (C) 2008 Steve Davis
;
; $Revision: 1.0 $
;
; WebImages
(script-fu-register
    "script-fu-webImages"                ;function name
      "/Script-Fu/WebImages"
      "Rescale an image for web page use."
      "Creates thumbnail & main jpeg"
      "Steve Davis"
      "20 Dec 2008"
      "RGB, INDEXED"
      SF-IMAGE "inImage" 1
    SF-DRAWABLE "Drawable" 0
)
(define (script-fu-webImages inImage)
  (let*
    (
            (w 0)
            (h 0)
            (w1 800)    ;smallest dim for main photo is 800 pixels
            (h1 800)
            (w2 200)    ;smallest dim for thumb nail is 200 pixels
            (h2 200)
            (scaler 1)
            (drw 0)
            (filename " ")
            (noExt " ")
    )
   
        (set! filename (car (gimp-image-get-filename inImage)))  ;file name & path
        (set! noExt (car (strbreakup filename ".")))    ;remove extension
        (set! w (car(gimp-image-width inImage)))
        (set! h (car(gimp-image-height inImage)))
        (set! scaler (/ w h))
        ;create page image     
        (cond
            (
                (<= w h) (set! h1 (/ 800 scaler))
            )
            (
                (>  w h) (set! w1 (* 800 scaler))
            )
        )
        (gimp-message (number->string inImage))
        (gimp-image-scale inImage w1 h1)
        (set! drw (car (gimp-image-flatten inImage)))
        (file-jpeg-save 1 inImage drw
        (string-append noExt "_m.jpg")
        (string-append noExt "_m.jpg")
            .75 0 0 0
            "Auto reduced for main web photo by SteveDee's script"
            0 1 0 1)

        ;create thumbnail image     
        (cond
            (
                (<= w h) (set! h2 (/ 200 scaler))
            )
            (
                (>  w h) (set! w2 (* 200 scaler))
            )
        )
        (gimp-image-scale inImage w2 h2)
        (set! drw (car (gimp-image-flatten inImage)))
        (file-jpeg-save 1 inImage drw
            (string-append noExt "_x.jpg")
            (string-append noExt "_x.jpg")
            .75 0 0 0
            "Auto reduced to thumbnail image by SteveDee's script"
            0 1 0 1)

        (gimp-display-delete inImage)
        (gimp-quit FALSE)
  )
)


See what I mean about brackets? I find Scheme scripts easier to read if spread over many lines with plenty of indentation and white space.

In use, I would open an image in Gimp, and then select my script from the menu to create a medium sized and thumbnail sized images. The script finds the image shortest side (height or width) and scales the medium sized image so the shortest side is 800 pixels and the thumbnail is 200 pixels.

My requirements have changed over time, and I no longer require thumbnails. Usually I just shortlist a bunch of photos by copying them into a folder. Then I reduce them prior to uploading to the web (my internet upload speed is only about 1Mb/s).

My new batch script


So I decided to re-visit my earlier script and create a new one which would batch resize a bunch of photos.

When running Gimp from the Linux command line, you don't even need to open the gui. So my batch photo resize will run from a command line like this:-

gimp -i -b '(scale-batch ".jpg")' -b '(gimp-quit 0)'

...where -i means: run without user interface
 -b means: batch mode
 scale-batch is the name of my script
 .jpg is the file pattern (run script on all jpeg files)
 gimp-quit is the procedure to close Gimp

Here is the "scale-batch" script in a file called scale-batch.scm:-

(define (scale-batch pattern)
    (let*
        (
            (w 0)
            (h 0)
            (w1 800)    ;smallest dim for main photo is 800 pixels
            (h1 800)
            (scaler 1)
            (drw 0)
            (noExt " ")
        )
 
        (let* ((filelist (cadr (file-glob pattern 1))))
            (while (not (null? filelist))
                (let* (
                                (filename (car filelist))
                                (image (car (gimp-file-load RUN-NONINTERACTIVE filename filename)))
                                (drawable (car (gimp-image-get-active-layer image)))
                                (w1 800)
                                (h1 800)
                           )
            
                            (set! filename (car (gimp-image-get-filename image)))    ;get file name & path
                            (set! noExt (car (strbreakup filename ".")));remove ext
                            (set! w (car(gimp-image-width image)))
                            (set! h (car(gimp-image-height image)))
                            (set! scaler (/ w h))
                            ;create page image     
                            (cond
                                (
                                    (<= w h) (set! h1 (/ 800 scaler))
                                )
                                (
                                    (>  w h) (set! w1 (* 800 scaler))
                                )
                            )
                            (gimp-message (number->string image))
                            (gimp-image-scale image w1 h1)
                            (set! drw (car (gimp-image-flatten image)))

                            (file-jpeg-save 1 image drw
                                (string-append noExt "_s.jpg")
                                (string-append noExt "_s.jpg")
                                .75 0 0 0
                                "Auto reduced web photo by SteveDee's script"
                                0 1 0 1
                            )
                            (gimp-image-delete image)
                )
                (set! filelist (cdr filelist))
            )
        )
    )
)


Once again, this script finds the shortest side of the current image (height or width) and scales it to 800 pixels. It also sets the jpeg image quality to 75%. So I typically end up with an image file size of about 500k.

You may notice that "string-append..." appears twice as part of the find save process. This is because the filename parameter appears twice for many of the Script-Fu procedures (filename & raw-filename) which can be confusing.

To run this script in Linux I navigate to the folder containing the images in file manager (pcmanfm) then hit F4 to open terminal at this folder. Initially I then ran the command:-

gimp -i -b '(scale-batch "*.jpg")' -b '(gimp-quit 0)'

...but we need to remember that a .jpg file is not the same as a .JPG in Linux, and both extensions may be in use. There is an easy fix, just modify the command line to:-

 gimp -i -b '(scale-batch "*.[jJ][pP][gG]")' -b '(gimp-quit 0)'

The result is that a scaled copy of each jpeg file (with either a .jpg or .JPG extension) is added to the same folder with "_s" added to the name.

more user-friendly options...

 

...using the file manager


Obviously it is a bit tricky running the gimp command in terminal for a given directory. If you are using a file manager that supports script launching such as Nautilus, you may be able to write a simple script to run the Gimp command line via the right-click menu. This would be a good solution as you could navigate to the folder containing the images, then right-click to resize.

...using Gambas


I've chosen to write a simple Gambas program to run this script. All I have used is a form with a DirectoryView, a Button control and a label.



...and the code looks like this:-

Public Sub Button1_Click()
 Dim File As String
 Dim strExt As String
 Dim strCommand As String
 Dim index As Integer
 
  If IsDir(DirView1.Current) Then
    For Each File In Dir(DirView1.Current, "*.jpg")
      Inc index
    Next
  Endif
  lblStatus.Text = index & " image files found"
  If index > 0 Then
    'build command line: cd /home/steve/gimptest && gimp -i -b '(scale-batch "*.JPG")' -b '(gimp-quit 0)'
    strExt = Quote("*.[Jj][Pp][Gg]")
    strCommand = "cd " & DirView1.Current & " && gimp -i -b '(scale-batch " & strExt & ")' -b '(gimp-quit 0)'"
    lblStatus.Text = "Please wait..... " & index & " image files found"
    Wait
    Shell strCommand Wait
    lblStatus.Text = "Resizing complete"
  Endif
 
End



Off the shelf...


If you search the net you will find a number of ready-made Gimp scripts and plugins which will resize your images. The ones that I have tried batch resize using a percentage. This is fine if all of your source images are a similar size.

But with my method, I can resize a batch of dissimilar sized images to produce similar sized versions.

One of the most popular plugins is David's Batch Processor which is available as an rpm. For Debian/Buntu users, just down load, unzip and then copy the dbp executable to your plugin folder (e.g. /home/steve/.giml-2.8/plug-ins).

No comments:

Post a Comment