GIMP Engrave plugin (script-fu)

This project was inspired by a beautiful photo (see fragment right) that my parents have. It's about 2x3 ft, and shows two ballet dansers (from the Royal Winnipeg Ballet). I have always been fascinated by the way the image was represented by lines of varying widths, and wanted to achieve a similar effect. I couldn't find what I wanted under GIMP, the existing plugin that does a similar thing was too ragged to my liking.

This plugin is an alternative to the existing Engrave plugin supplied with GIMP and found under
Filters -> Distorts -> Engrave.
This version registers itself under
Filters -> Artistic -> Engrave
to avoid collision with the existing one, and also because it seems a more appropriate place to me. The original plugin has a different approach which is perhaps more suited to smaller final images (more on this later). This plugin (I feel) works best on rather large final images (with 100 or more lines of varying width). This script is still somewhat work-in-progress, but I believe it is now usable.

Basically, the plugin transforms a grayscale image into a series of horizontal lines of varying widths. To get other orientations, just work on a rotated image, and rotate back at the end. Or edit the script...

Section 1 below explains how to use the plugin and the meaning of its parameters. Section 2 describes the process for those interested (with a bit on development history).

Version history:

1. Using the plugin

This section describes how to use the plugin. It works for me under GIMP 2.6.6, Windows binary. The plugin can be downloaded here (engrave.scm); it goes in your user script directory. Note that there is a bug under GIMP 2.6.1 which requires a hack (see the script). I don't know what the situation is for versions in between, or prior to 2.6.1.

Note also that the plugin overwrites the clipboard.

In the following, plugin parameters are normally quoted.

1.1 Checking that plugin works properly

Since changes to GIMP (esp. around scaling) could break this script, here's an easy way to check it: open the 5x5 test image [], process it with the script using all default parms, except that "Pre-processing -> Pre-scaled" needs to be selected, the output image should be close to the first below (five black cone-like patterns pointing in alternating directions):

Bad (need hack)
If you see something like the second image, there is still some hope. You are possibly seeing the same problem I was seeing during initial development (under 2.6.1) and for which I included a hack in the script. Find the line of code that contains "(set! hack 0)" in engrave.scm and comment it out by putting a semicolon (";") at the beginning of the line. Then do Filters -> Script-Fu -> Refresh scripts. Try the test again: you won't see exactly the "Good" image above, but if you see four black cone-like patterns pointing in alternating directions, you should be fine.

1.2 Preparing the initial image

Personally, I prefer to start from a small image, where each row of pixels will become a line of varying width (within a group of "Line width" rows in the final image). When working this way, the final image will scaled up by the chosen "Line width". So for example, if the initial image is 10x20 pixels, and the "Line width" is 15, the final image will be 150x300. However, by default, the plugin will work within the original image size (rounding to a multiple of "Line width" -- internally, it first reduces it, then expands it again).

As it works, the plugin will create a mask in addition to the final image, so it will need memory for two grayscale images of the final size. For example, a 300x300 image, with "Pre-processing -> Pre-scaled" selected and a "Line width" 25, would result in a final image of 7500x7500, about 57 Mbyte, so the plugin would need twice as much during processing.

Note that the image will be converted to grayscale as part of the process, but any image mode is accepted as input, with or without alpha channel.

1.3 Plugin parameters

Here's what the plugin's dialog looks like, with the default parameters.

The starting image I'll be using for the examples is the following (cropped to 76x59 pixels, grayscale mode):

Wilber is the work of Tuomas Kuosmanen ("Tigert") and is GPL licensed.

In the following discussion, the examples assume the default values unless otherwise noted (except that "Line width" will be 15 instead of 25 to keep the examples smaller, and that "Pre-processing -> Pre-scaled" is always selected). Note that some adjustments are still being made to gamma, and hence the examples aren't quite right in this area (most are a bit too dark); I am working on this. In the examples below, I show a section of the eye (an interesting area for this script).

The parameters of the plugin are:

And the final result is (with "Line width" at 15, "Gamma" at 1.6 -- the shadow does not come out nice here):

Fill your screen, stand back 20-30 feet...

1.4 Gamma

Selecting the proper gamma depends on how the resulting image is to be viewed.

If the final image is to be viewed from a distance, such that the lines are barely visible, then a "Gamma" close to the standard 2.2 should be chosen, so that the gray levels are correctly represented. At "Gamma" 2.2, the quantity of black and white should result in proper gray levels (assuming I got it right, which is still TBD). For example, 50% gray, corresponding to a pixel value of 185 (x0b9), would result in a line which is half white, half black.

If the final image is to be viewed from closer, or if graylevel accuracy isn't desirable (eg. for a logo), then such a high gamma will probably result in too much black. In such cases, a lower gamma should be chosen. With a "Gamma" of 1.0, the amount of black and white will be proportional to the pixel value (so 128 will be half black, half white).

1.9 should be a good compromise for images to be seen from a distance, but where the lines are still quite clearly visible (as it was in my parent's photo). Even at 1.9, it seems like the image (see links below) is too dark from a distance, which means I may have gotten some things not quite right wrt gamma and end effects. I think it works better with more lines (15 is few, I think 25 is a minimum for decent results); fewer lines seem to result in a darker image. The best is to experiment... (for now, a lower gamma seems necessary, still working on this).

Final guidelines for gamma-correct results in "Version 2" might be roughly something like:
- "Line width" 15, use "Gamma" 1.6
- "Line width" 25, use "Gamma" 1.9

In version 1, gamma was achieved by unequally spacing the values of the mask to get the desired effect. If "Version 2" is selected (default), inverse gamma is applied to the image at the beginning of the script such that equally spaced values can be used in the mask later. The advantage of this second approach is in smoothing when doing the levels and blur at the end: to do a levels with something else than black=0 and white=1, we need equally spaced values in the mask.

Not yet available: For comparison purposes, here are four images (you could print them on 5x7 paper, or view them at max size, one at a time, on your monitor). In both cases, stand back 20 feet or more:

1.5 Pre-processing -> Downscale vertical

The initial version of this script started from a small image, and expanded it
- vertically (by "Line width") to create the lines of varying widths,
- and horizontally (by same) using the chosen interpolation.
The result is an image scaled up by "Line width" in both directions. This approach is selected thru "Pre-processing -> Pre-scaled".

Then a pre-processing phase was added to the script so that one could work within the initial image size. This pre-processing reduces the image in both directions by "Line width" then the main part of the script does the expansion as described above. This results in loss of resolution in both the vertical axis (expected, since a group of rows is summarized in a line of varying width), but also in the horizontal axis (downscale, then upscale by chosen "Horizontal interpolation"). This is what I prefer, it results in the same resolution in both directions (both directions get "summarized", vertically by line width, horizontally thru smooth transitions in line widths). This approach is selected thru "Pre-processing -> Downscale both", and is the default.

However, the horizontal downscale followed by upscale can be seen as a waste of resolution. The choice "Pre-processing -> Downscale vertical" leaves the horizontal axis of the image alone. Processing is still done within the starting image size, but this results in much more detail in the horizontal axis.

This approach is very close to how the other GIMP Engrave plugin works. The GIMP 2.6 on-line documentation [from docs.gimp.org] has the following images:
[image from gimp.org]
Original image
   [image from gimp.org]
"Engrave" applied

With the new Engrave plugin described here, a very similar result can be obtained. This is what it looks like for this same image, varying "Pre-processing" option, with the following other parameters:
- "Line width" = 10
- "Gamma" = 1.1
- "Horizontal interpolation" = Cubic (default)
- "Line type" = Black on bottom
- "Version 2" unchecked
- "Blur radius" = 1

Downscale both
Downscale vertical
Notice how the last one is very similar to the image from the other GIMP Engrave plugin.

1.6 Various usage notes

2. The process implemented by the plugin

This (optional) section provides an overview of the steps of the process implemented by the plugin.

Brief note on development. I did a first plugin using gimp-drawable-set-pixel, but the result was horribly slow (hours for even a relatively small image). Such a direct approach would have to be done in C, but I didn't feel like getting into that: writing C is what I do all day, and my son kept telling me how great Lisp was (I had used Lisp 40 years ago, in university, now was my chance to get back to it).

Then I read Akkana Peck's excellent book, Beginning GIMP, From Novice to Professional, esp. the section on layer modes, and this was the start of this version.


Feedback, comments, &c., most welcome.

P.S. to get the result in the plugin registry, the steps are approximately:

Maintainer: Pierre Lewis
Last updated: Mar 26 2009
Feedback: leware at globetrotter dot net