Wednesday, March 14, 2018

Batch Enhance and Resize images using GIMP


GIMP is a free and open source replacement for Photoshop. A great tutorial for enhancing photos with GIMP is described in this link. It makes photos much brighter and vibrant.

steps involved are:

1. duplicate image layer twice
2. Desaturate (Colors -> Desaturate) topmost layer selecting luminosity option.
3. Invert the color of the layer from Colors -> Invert
4. Do Gaussian Blur (Filters ->Blur ->Gaussian Blur), with setting radii as 20 and blur method as RLE.
5. From the Layers panel set the opacity of top layer as 40.
6. Merge down the top layer to the middle layer
7. Change the blend mode of the middle layer (top layer now, after the merge) as 'Grain Merge'.
8. You can test the changes by toggling the visibility of the top layer.
9. Finally merge down the top layer.

That's a lot of steps, to remember. That's where scripting comes to use. (Photoshop has batch mode). GIMP provides 2 scripting languages 'Script-Fu' and 'Python-Fu'. Scripting is also useful to process a folder of photos in a single click, in batch mode.

Script-Fu is evolved from Scheme language, and is bit different in syntax from usual programming and scripting languages. Each statement is enclosed in parentheses, each statement begins with a function or an operator, functions are defined with 'define', variables assigned with 'set!' and variables declared with 'let*'. Edit the script using a text editor and store in '.gimp-2.8/scripts' folder in home folder (in Linux) as *.scm file. First I'll describe a script to resize individual photos and in batch mode from a folder.

(define (my_resize image drawable px)
  (let* ((cur-width  (car (gimp-image-width image)))
         (cur-height  (car (gimp-image-height image)))
         (height (/ (* cur-height px) cur-width))
        )
     (gimp-image-scale image px height)
  )
)

(define (batch_resize file-pattern new-width )
  (let* ( (file-glob-pattern (string-append file-pattern "/*.*"))
  (filelist (cadr (file-glob file-glob-pattern 1))))
   (while (not (null? filelist))
      (let* ( (cur-file  (car filelist))
      (image    (car (gimp-file-load RUN-NONINTERACTIVE cur-file "")))
      (drawable (car (gimp-image-active-drawable image)))
      )
    (my_resize image drawable new-width)
    (set! drawable (car (gimp-image-active-drawable image)))
    (gimp-file-save RUN-NONINTERACTIVE image drawable (string-append (car (strbreakup cur-file ".")) "_resized.jpg") "")
    (set! filelist (cdr filelist)))
    )
  )
)

(script-fu-register "my_resize"
  _"Resize Horizontal"
  _"Resizes horizontal images for photoblog."
  "PG"
  "PG"
  "2018"
  "*"
  SF-IMAGE       "image"          0
  SF-DRAWABLE    "drawable"       0
  SF-VALUE "horizontal pixels" "800"
)

(script-fu-menu-register "my_resize" "<Image>/Filters/Mine")
                       
(script-fu-register "batch_resize"
  "Batch Resize"
  "batch Resizes images."
  "PG"
  "PG"
  "2018"
  ""
  SF-DIRNAME "select directory of images to resize" "~/Pictures"
  SF-VALUE "horizontal pixels" "800"
)

(script-fu-menu-register "batch_resize" "<Image>/Filters/Mine")


In this I've defined two procedures my-resize and batch-resize and registered them using script-fu-register and script-fu-menu-register functions. To be able to use the batch procedure without opening an image, while registering , pass the 6th parameter as "" instead of any image format or as any format with "*" as we did with my-resize function.  To work with currently opened image, pass an image parameter and drawable parameter as first 2 parameters with 0 as default value. 'car' and 'cdr' functions are used to access the list members.

Save this script in the script folder and in Gimp select Filters->Script-Fu->Refresh Scripts. the script will appear in Filter->Mine menu.

Next, I'll write another script to enhance the image using the method I described above. I'll write a script to batch automate, which will first resize the image, and then enhance it, because enhancing will work faster in a smaller size image file.

(define (my_enhance image drawable)

(let* (
(layer2 0)
(layer3 0)
)
    (gimp-undo-push-group-start image)
            (set! layer2 (car (gimp-layer-copy drawable 1)))
    (gimp-image-insert-layer image layer2 0 -1)
    (gimp-layer-set-name layer2 "layer2")
    (set! layer3 (car (gimp-layer-copy drawable 1)))
    (gimp-image-insert-layer image layer3 0 -1)
    (gimp-layer-set-name layer3 "top-layer")

    (gimp-desaturate-full layer3 DESATURATE-LUMINOSITY)
    (gimp-invert layer3)
    (plug-in-gauss RUN-NONINTERACTIVE image layer3 20.0 20.0 1)
    (gimp-layer-set-opacity layer3 40.0)
    (set! layer2 (car(gimp-image-merge-down image layer3 0)))
    (gimp-layer-set-mode layer2 21)
    (gimp-image-merge-down image layer2 0) 

    (gimp-undo-push-group-end image)
    (gimp-displays-flush)
)  
)

(define (batch_enhance file-pattern )
  (let* ( (file-glob-pattern (string-append file-pattern "/*.*"))
  (filelist (cadr (file-glob file-glob-pattern 1))))
  (while (not (null? filelist))
           (let* ( (cur-file  (car filelist)) 
           (image (car (gimp-file-load RUN-NONINTERACTIVE cur-file "")))
             (drawable (car (gimp-image-active-drawable image)))
    )
          (my_enhance image drawable)
          (set! drawable (car (gimp-image-active-drawable image)))
  (gimp-file-save RUN-NONINTERACTIVE image drawable (string-append (car (strbreakup cur-file ".")) "_enhanced.jpg") "")
          (set! filelist (cdr filelist))
        )
    )
  )
)

(define (batch_resize_enhance file-pattern new-width)
  (let* ( (file-glob-pattern (string-append file-pattern "/*.*"))
  (filelist (cadr (file-glob file-glob-pattern 1))))
  (while (not (null? filelist))
          (let* ( (cur-file  (car filelist)) 
        (image (car (gimp-file-load RUN-NONINTERACTIVE cur-file "")))
          (drawable (car (gimp-image-active-drawable image)))
)
(my_resize image drawable new-width) 
        (my_enhance image drawable)
        (set! drawable (car (gimp-image-active-drawable image)))
(gimp-file-save RUN-NONINTERACTIVE image drawable (string-append (car (strbreakup cur-file ".")) "_enhanced.jpg") "")
        (set! filelist (cdr filelist))
      )
    )
  )
)

(script-fu-register "my_enhance"
  "My Enhance" "One touch enhance image" "PG" "PG" "2018" "*"
  SF-IMAGE       "image"          0
  SF-DRAWABLE    "drawable"       0
)

(script-fu-menu-register "my_enhance" "<Image>/Filters/Mine")
                         
(script-fu-register "batch_enhance" "Batch Enhance" "batch enhances images." "PG" "PG" "2018" ""
  SF-DIRNAME "select directory of images to resize" "~/Pictures"
)

(script-fu-menu-register "batch_enhance" "<Image>/Filters/Mine")
                         
(script-fu-register "batch_resize_enhance" "Batch Resize & Enhance"
  "batch resizes & enhances images." "PG" "PG" "2018"  ""
  SF-DIRNAME "select directory of images to optimize" "~/Pictures"
  SF-VALUE "horizontal pixels" "800"
)

(script-fu-menu-register "batch_resize_enhance" "<Image>/Filters/Mine")

As previous script, this adds 3 menu options with procedures to enhance a single open photo, to enhance a folder of photos and to do both resize and enhance a folder of photos in a single step.