Thursday, January 28, 2010

Automated Image Editing for the Artistically Unskilled

GIMP is perhaps the poster child for bad open source usability. This may not be entirely fair; the UI is better than it used to be, and GIMP now has excellent docs, so a minute with Google will teach you how to do anything that the UI fails to make clear.

As a software developer, I firmly believe that anything worth doing more than once is worth automating, but scripting was one part of GIMP that remained opaque to me. GIMP's built-in scripting language, Scheme, is one that's unfamiliar to many programmers. And although GIMP extensions offer scripting support for Perl and Python, I've always had inordinate trouble getting those to work. (The Linux distros I've used often omit Perl support, for example, and getting GIMP's Python support to work on Windows requires manually installing several supporting Python libraries, of the correct versions, without any diagnostics if you get any of it wrong, and ideally without messing up existing installations of your Python libraries. YMMV.) I finally decided that all of this futzing with scripting extensions would be harder than just learning Scheme and that I should bite the bullet and learn to script GIMP natively. (Besides, I'm told that learning Scheme or Common Lisp will induce a "profound enlightenment experience", so it must be worth doing, right?)

Learning to Script

  • GIMP has extensive online documentation and tutorials. Its Batch Mode tutorial includes some scripting examples. There's even a Script-Fu Tutorial that provides a brief introduction to Scheme, for those of us who haven't yet worked our way through SICP.
  • GIMP also has a built-in Procedure Browser (under the Help menu) for looking up the GIMP-specific functions that you'll use in your Script-Fu scripts. This will be your primary reference after the above tutorials take care of the basics.

Unfortunately, none of the above references have much information on how to do non-image-processing tasks (such as making up filenames for your images) in Script-Fu. For that, you'll need one of the following sources:

  • GIMP uses TinyScheme as its scripting language. (Older versions of GIMP used SIOD, and there are still a few references to this on the web. Interestingly, GIMP doesn't use GUILE, even though GIMP and Guile are both GNU projects and Guile is the GNU Scheme interpreter.)
  • TinyScheme follows R5RS (the almost-current Scheme standard). Standard and builtin functions are documented in R5RS. Script-Fu extensions to TinyScheme are documented on the Tiny-Fu Wiki.

Running Your Script

The official GIMP documentation would have you place your script in a GIMP configuration directory, register it, and invoke it from the command line. This is unnecessarily complicated. Redirection from the command line is much simpler:

"c:\Program Files\GIMP-2.0\bin\gimp-console-2.6.exe" -c -b - < gimp-create-disabled-icons.scm

Ignore any wire errors that GIMP outputs at this point; those are harmless issues between GIMP and its various plugins and have nothing to do with your script.

Debugging Your Script

Error reporting from console batch mode is apparently nonexistent. This, combined with GIMP's slow startup times, makes debugging a frustrating experience. One approach is the tried-and-true debugging-via-printf approach using the gimp-message function ((gimp-message "Okay, I made it to line 5\n")).

A better approach is to use the Script-Fu console. To do so, go under the Filters menu, under Script-Fu, and choose Console. Then start typing in commands from your candidate script and seeing how they work out.

If you've poked around in the Procedure Browser at all, you've noticed a lot of procedures that take parameters of type IMAGE, LAYER, DRAWABLE, and so on. These are simply integers (presumably indexes into GIMP's data structures), so trying out these procedures is relatively easy. Consider the following Script-Fu Console session. (My commands are marked with > and comments are marked with > ;.)

> (gimp-image-list)
(1 #( 2 ))
> ; This tells me I have one image loaded, and its number is two.
> (gimp-image-get layers 2)
(1 #( 4 ))
> ; This tells me Image 2 has one layer, and its number is 4.
> ; Each LAYER is also a DRAWABLE, so I can test out whatever commands
> ; I want to now.
> (gimp-drawable-fill 4 WHITE-FILL)
> ; (#t) means this command has no particular return value.

Most procedures executed in the Script-Fu console are entered into undo history just like GUI operations, so it's easy to clean up after mistakes.

Additional Resources

  • The GIMP Plugin Registry has plenty of example code. (Keep in mind that GIMP scripts are probably not the best source for learning good Scheme programming style.)
  • Samuel A. Rebelsky and Ian Bone-Rundle have put together the "Glimmer Utilities for Gimp". Their float-array procedure is useful for Script-Fu procedures that take a FLOATARRAY parameter.

Putting It into Practice

Automating the creation of the various icon sizes required by Windows Vista and newer is a good idea. The iconify.scm script is an example of this.

Creating proper disabled icons for your desktop application can enhance its look. (The OpenOffice web site has good examples of what disabled icons should look like.) Automating the creation of disabled icons in GIMP involves converting the icon to monochrome then adjusting its shading and/or opacity to your liking.

No comments: