Using Script-Fu in the GNU Image Manipulation Program to Automate “Smart”

Benjamin Bucior
Summer Ventures 2007
Appalachian State University
The GNU Image Manipulation Program, commonly abbreviated as “the Gimp,” is a free,
open-source image editing program. Many photo restoration techniques exist to enhance
images by making them clearer. This project focused on automating a powerful, yet
complicated, method called “smart” sharpening. To accomplish this, a script was written
in the Gimp’s Script-Fu language. After completion of the script, “smart” sharpening
was compared to similar image sharpening procedures to evaluate its effectiveness.
Despite surprising results on some types of images, more research is needed to further
improve the script.
The GNU Image Manipulation Program, commonly abbreviated as “the Gimp,” is a free,
open-source image editing program similar to Adobe Photoshop. A default installation of
the Gimp includes filters for common photo restoration tasks, including edge detection,
matrix convolution, Gaussian blurs, and sharpening. The Gimp also has support for
channels and layers, allowing for advanced blending and delicate drawing on images.
Digital cameras have progressively become more advanced, but many still blur
photographs during capture and compression. To repair this damage, several algorithms
exist to sharpen images. The simplest of these, called convolution, uses a matrix to
replace the value of each pixel. The center of the matrix represents the modified point,
and surrounding pixels are multiplied by their corresponding factors in the matrix. These
products are added together to create the replacement value for the center pixel. Another
method to sharpen pictures, called unsharp masking, combines the original image and a
smoothed version to enhance edges [1].
“Smart” sharpening is a process that sharpens the image without increasing image
noise. Instead of applying a uniform filter across the image, it focuses on the edges of the
image and avoids changing its colors [2]. First, the procedure duplicates the image and
extracts the Value channel from the HSV (Hue Saturation Value) decomposition of
brightness levels. A sharpening layer is created on the original image by copying the
Value channel. An edge detection algorithm is passed over the duplicate image, which
is further filtered by modifying light levels and blurring the result. This becomes the
edge mask that is added to the sharpening layer. An unsharp mask on this sharpening
layer creates the final product, a clearer image than the original.
Most photo restoration techniques can be automated by scripting in the Gimp’s
Script-Fu programming language. The SOID (Scheme in One Defun) interpreter for
Script-Fu allows for cross-platform development of such tools by providing a simple
interface to the Gimp’s functions [3]. Automation of common tasks reduces repetition,
saves time, decreases the chance of human error, and simplifies processes by combining
steps into one central command. This project will focus on the development of a script to
automate the “smart” sharpening algorithm. After the program is completed, the “smart”
sharpening technique will be compared to similar methods of photo sharpening to
compare their effectiveness on enhancing images.

Online tutorials were helpful in gathering background knowledge about photo restoration
techniques. The “smart” sharpening technique created by Eric Jeschke was chosen for
this project because of its complexity and remarkable results [2]. The Gimp was chosen
for image processing because of its cross-platform compatibility, functionality, and
support for scripting. At first, the “smart” sharpening technique was going to be
programmed in Perl using the Gimp::Fu module. However, limitations against using Perl
in Windows XP encouraged development in Scheme. Only Linux builds of the Perl
module were available. In addition, installation of the “smart” sharpening script was
easier without the prerequisite of the Perl interpreter. Although the syntax of Scheme
was challenging at first, programming in Script-Fu did not require knowledge of all of
Scheme’s functions. The Procedure Browser in the Gimp aided development by listing
every function with a brief description and the required parameters.
Besides the Gimp, the only other program required for creating the script was a
basic text editor. This project used Notepad++ Portable in Windows and the KDE
Advanced Text Editor (Kate) in Linux to write the script because of their syntax-
highlighting support. However, no specific text editor was required to program in Script-
Fu, provided that it could save with the .scm extension. While coding, it was helpful to
keep the Gimp open to debug each line and ensure that it preformed its intended function.
For testing and installation, the script was saved as smart-sharpen.scm in the Gimp’s
scripts folder. The location of this scripts folder varied but could always be found by
going to File>Preferences>Folders>Scripts in the Gimp.
Some peculiarities of the Gimp’s Script-Fu language made coding more difficult.
Gimp plug-ins written in C and the Procedure Browser listed functions with underscores,
but Script-Fu required them to have dashes instead. Gimp functions always returned
values as single-item lists, so the car function was required when setting variables [4].
Additionally, using Scheme’s prefix notation inside of nested parenthesis [5] caused
problems when commands were coded as func(arg1) instead of (func arg1). Several
parameters [6] were added to the script-fu-register procedure to create user interactivity
with the script. In addition, a list after each variable’s description set the default values
and checked the validity of parameters passed to the script [7].
The script’s final source code is as follows:
(define (script-fu-smart-sharpen img1
;Start script with define and enclose everything as local variable
(gimp-image-undo-group-start img1)
(let* (
;Duplicate img1 to img2
(img2 (car (gimp-image-duplicate img1)))
;;(display2 (car (gimp-display-new img2)))
;Get var name of layer on img2, for later usage in edge detection
(draw2 (car (gimp-image-get-active-drawable img2)))

;Decompose original image to value channel of HSV
(img3 (car (plug-in-decompose 1 img1 draw1 "Value" 1)))
;;(display3 (car (gimp-display-new img3)))

;;(OLD)Create new layer called sharpening, replaced with layer-copy
;;(sharpening (car (gimp-layer-new img1 (car (gimp-image-width img1))
(car (gimp-image-height img1)) 0 "sharpening" 100 0)))
;Duplicate original layer to "sharpening" layer
(sharpening (car (gimp-layer-copy draw1 0)))

;Now all var declarations must be called with (set! varname value)
(gimp-image-add-layer img1 sharpening 0)
(gimp-drawable-set-name sharpening "Sharpening")
;Add alpha channel to Sharpening layer for mask later
(gimp-layer-add-alpha sharpening)

;Copy and anchor Value image to Sharpening Layer
(gimp-image-set-active-layer img1 sharpening)
(gimp-selection-all img3)
(gimp-edit-copy-visible img3)
(set! value_float (car (gimp-edit-paste sharpening 0)))
(gimp-floating-sel-anchor value_float)
;Clear the selected area on the Values image
(gimp-selection-none img3)

;Set Sharpening blending mode to "Value"
(gimp-layer-set-mode sharpening 14)

;Now to create the edge mask :)
;Edge Detection and conversion to grayscale
(plug-in-edge 1 img2 draw2 edge_amt 1 0)
(gimp-image-convert-grayscale img2)

;Adjust mask by filtering insignificant edges
;Change black/white points, apply gaussian blur, and redo change levels
(gimp-levels draw2 0 gmin1 gmax1 1 0 255)
(plug-in-gauss 1 img2 draw2 blur_amt blur_amt 0)
(gimp-levels draw2 0 gmin2 gmax2 1 0 255)

;Add white mask to Sharpening layer
(set! sh_mask (car (gimp-layer-create-mask sharpening 0)))
(gimp-layer-add-mask sharpening sh_mask)

;Add and anchor edge mask image to Sharpening layer
;Yay for reusable code!
(gimp-selection-all img2)
(gimp-edit-copy-visible img2)
(set! value_float (car (gimp-edit-paste sh_mask 0)))
(gimp-floating-sel-anchor value_float)
(gimp-selection-none img2)

;Apply final unsharp mask
;;(gimp-image-set-active-channel img1 sh_mask)
(plug-in-unsharp-mask 1 img1 sharpening umask_radius umask_amt

;Remove file close confirmation for temp images, refresh displays,
complete undo group
(gimp-image-clean-all img2)
(gimp-image-clean-all img3)
(gimp-image-undo-group-end img1)

;Variable Parenthesis after name:
;(default, min, max, small_inc, big_inc, precision, slider(0)/entry(1))
(script-fu-register "script-fu-smart-sharpen"
_"Smart _Sharpening..."
"Sharpens an image without increasing film grain.\nBased on the
tutorial at\n"
"Benjamin Bucior <>"
"Benjamin Bucior"
SF-IMAGE "Image" 0
SF-DRAWABLE "Drawable" 0
SF-ADJUSTMENT _"Amount of Edge Detection" '(6 1 10 .1 1 1 0)
SF-ADJUSTMENT _"Mask's Grayscale Min Before Blur" '(20 1 100 1 10 0 0)
SF-ADJUSTMENT _"Mask's Grayscale Max Before Blur" '(235 155 255 1 10 0
SF-ADJUSTMENT _"Mask's Gaussian Blur Amount" '(5 1 15 1 5 0 0)
SF-ADJUSTMENT _"Mask's Grayscale Min After Blur" '(10 1 100 1 10 0 0)
SF-ADJUSTMENT _"Mask's Grayscale Max After Blur" '(235 155 255 1 10 0 0)
SF-ADJUSTMENT _"Unsharp Mask Radius" '(1 .1 120 .1 1 1 0)
SF-ADJUSTMENT _"Unsharp Mask Amount" '(2 0 5 .01 .1 2 0)
SF-ADJUSTMENT _"Unsharp Mask Threshold" '(0 0 255 1 10 0 0)

(script-fu-menu-register "script-fu-smart-sharpen" "<Image>/Script-
The effectiveness of the technique was tested by comparing the results of the
filters on the same image. The original testing image was an unmodified photo from a
digital camera. Convolution filtering was tested by using Filters>Generic>Convolution
Matrix with a standard matrix [Figure 1]; red, green, and blue channels enabled; and an
extended border [8]. Unsharp masking was evaluated by using Filters>Enhance>Unsharp
Mask with the default radius of 5.0, amount of 0.5, and threshold of 1. “Smart”
sharpening was given the default parameters in the script to maintain consistency among

[Figure 1: Tested convolution matrix]

Zoomed sections of each enhanced image were analyzed to determine the effectiveness of
each filter. The original image [Figure 2] without enhancement was more blurred than
the others and had the softest colors. The convolution matrix filtering [Figure 3]
sharpened the photo too broadly, causing noise in the picture to become worse. The
unsharp mask [Figure 4] enhanced edges in the image without modifying the color of the
image’s objects. “Smart” sharpening [Figure 5] enhanced the photo while increasing
contrast between colors, but many soft edges became more jagged. The “smart”
sharpening script greatly decreased processing time on the images. Instead of taking over
20 minutes to manually complete the tutorial, the script sharpened the image in less than
30 seconds.
[Figure 2: Original Image] [Figure 3: Convolution Matrix]

[Figure 4: Unsharp Mask] [Figure 5: “Smart” Sharpening]

Of the three tested sharpening filters, “smart” sharpening and unsharp mask filtering
enhanced digital photos the best. “Smart” sharpening seemed to increase contrast in the
image since it applied filters to light levels, but some of these changes to object color in
the image made the image appear more artificial. The edge detection used in “smart”
sharpening made many lines in the final image appear jagged. This problem is
commonly solved by anti-aliasing, a process that would make the image less sharp.
“Smart” sharpening best enhanced photographs at plants. The technique also fixed small
amounts of blur but failed to remove the heavy blurring commonly caused by taking
photographs in darker environments.
Script-Fu was a simple programming language to learn. The entire process of
creating the script, from first learning Scheme’s syntax to finishing the final product, took
less than 5 days. Scripting significantly reduced image processing time and could be
used to automate other common tasks, such as red-eye removal, image noise reduction,
and blurring correction.
Tweaking the parameters passed to each filter also changed the effectiveness of
image enhancement. Convolution matrices could have been used to do many types of
filtering, including sharpening, blurring, embossing, and edge detection [8]. The unsharp
mask performed best with large radius values and smaller strengths. Results from the
“smart” sharpening filter varied based on the image. Each image required different
parameters to produce an optimized product because of the complexity of the “smart”
sharpening algorithm.
In the future, the script could be improved upon and expanded. Since image
processing is mainly based on human preference, no predetermined algorithm could ever
produce a perfected image. However, several extensions could be added to the script to
make it more useful. In addition to the current automatic mode, the script could include a
more guided approach to apply the “smart” sharpening filter. Based on the appearance of
the image after every step, the user would increase or decrease the amount of sharpening
required. The script could also be given a command line mode to allow batch processing
of images. In order to be a more effective filter, the script should also have better default
values. Currently, parameters passed to the script are estimated figures, but averaging
optimized values from several photos would produce better default parameters. More
testing with the script could also eventually lead to the creation of a larger, generalized
photo restoration script that would do several filters, including red-eye removal, before
creating the finalized result.
[1] HIPR2,
[2] Eric R. Jeschke,
[3] Simon Budig,
[4] Dov Grobgeld,
[5] John Beppu,
[6] Michael Terry,
[7] Simon Budig,
[8] The Gimp Documentation Team,