A Short Guide to GIMP Plugins

On more than one occasion, I have mentioned that you can get great benefits from processing and adjusting your reference photos before you start to paint from them.

In this blog post I want to introduce you to a sample automation script for GIMP (which I will hereafter refer to as a plugin) and show you how to create others like it that can perform incredibly powerful image manipulation with just a small amount of code. The benefits of this are twofold: you can do repetitive, complex image manipulation very quickly, and by having these operations in a script you can repeat them at any point in the future without having to remember or relearn anything.

Before we go further, I just want to quickly review the two main options for image manipulation software.

The most popular software, Adobe Photoshop, requires a subscription (there is also a limited version called Photoshop Elements that can be purchased outright). Photoshop has great workflow tools, the ability to record, edit and run automated scripts called actions, and has started to incorporate AI tools for image generation. It is the software of choice for most professional photographers.

The other major image manipulation software is called GIMP. It is free software which is developed and maintained by volunteers. I think it is fair to say that it falls far behind Photoshop in terms of workflow and integration with a wider suite of tools, however the core functionality is very strong. Its big advantage over Photoshop is that you can write plugins in different coding languages such as Python, Scheme and C.

In this article, we are going to limit ourselves to working in Python because it is much easier to learn than the other two: you can get started in no time, even if you have little experience with coding.

A GIMP Plugin Sample

By way of an example, here's a short GIMP plugin written in Python. It converts an image to a kind of Payne's Grey version of black and white and makes changes to the brightness and contrast in a constrained range, depending on the user's input:

#!/usr/bin/env python
# -*- coding: utf8 -*-

from gimpfu import *

def desat( img, draw, brightAdjust, contrastAdjust ):

	#clean start
	img.disable_undo()
	pdb.gimp_context_push()

	#set new foreground and background colors
	pdb.gimp_context_set_foreground((255, 255, 255))
	pdb.gimp_context_set_background((0, 0, 0))

	#copy the visible image into a new layer
	copyLayer1=pdb.gimp_layer_new_from_visible(img, img, "BaseCopy")
	pdb.gimp_image_insert_layer(img, copyLayer1, None, -1)

	#desaturate and colorize the new layer
	pdb.gimp_desaturate_full(copyLayer1, 1)
	pdb.gimp_colorize( copyLayer1, 215, 11, 0)

	#adjust brightness and contrast of the new layer
	pdb.gimp_drawable_brightness_contrast(copyLayer1, brightAdjust, contrastAdjust)

	#clean up	
	pdb.gimp_displays_flush()
	pdb.gimp_context_pop()
	img.enable_undo()

register( "gimp_desat",
  "Add b/w effect",
  "Add b/w effect",
  "Simon Bland",
  "(©) 2024 Simon Bland",
  "2024-01-30",
  "<Image>/Filters/Desat",
  'RGB*',
  [
	(PF_SLIDER, "brightAdjust","Brightness", 0.4, (-0.5, 0.5, 0.1)),
	(PF_SLIDER, "contrastAdjust", "Contrast", 0.3, (-0.5, 0.5, 0.1))
  ],
  '',
  desat)

main()

Although it looks complicated at first glance, there are only five lines of code in here that are doing image manipulation, the rest is boilerplate code that you might find in any other plugin. The coding is trivial.

Deconstructing a GIMP Plugin

Let's break it down into its constituent parts. There are five different sections that we will look at separately.

1.Opening

The first two lines are typical of all Python code. It tells the executable where to find Python and which coding is used for this script. You can likely use this unchanged for all your plugins. I would point out that the /usr/bin/env location is probably unnecessary and is definitely not used by Windows, but it won't hurt to leave it in.

#!/usr/bin/env python
# -*- coding: utf8 -*-

2. Imports

Next, we import the GIMP plugin API so that we can call GIMP functions from the plugin. All GIMP plugin scripts should contain this line.

If we need to import other Python modules (more advanced than we need to know at this point) we can also import them here.

from gimpfu import *

3. Define Functions

The main part of the plugin defines a set of functions and properties (here we just have a single function) that does something to our image.

You can see that we preface GIMP API calls with "pdb". That stands for procedural database which is the collection of all GIMP API functions, scripts and plugins. You can call other plugins from your plugin.

A good practice when creating a plugin is to make a copy of the visible image into a new layer and start editing the copy rather than the original image. Each new effect occurs on a new layer. After the plugin has run, we can fine tune the image effect by changing the opacity of each layer or even eliminate a layer in its entirely.

In this simple plugin we are just copying the visible image, adding it as a new layer above the old one, then colorizing it and changing the brightness and contrast. For the sake of simplicity, we have done performed both actions on the new layer.

The remaining code does things that might be thought of as administration.

We try not to leave a mess behind when we run a script because it can be incredibly annoying if we start overwriting settings and defaults, so we push the context when we start and pop the context when we finish.

In GIMP, context means the current collection of things we have as settings like brush size, brush type and gradient type. Push saves a copy of those settings and pop restores them. Foreground and background colors are also part of the context.

Flushing the display ensures that all pending image updates are completed before the plugin terminates.

Disabling undo on start allows the plugin to run faster than it otherwise would. If invoked, we always enable undo on completion.

def desat( img, draw, brightAdjust, contrastAdjust ):

	#clean start
	img.disable_undo()
	pdb.gimp_context_push()

	#set new foreground and background colors
	pdb.gimp_context_set_foreground((255, 255, 255))
	pdb.gimp_context_set_background((0, 0, 0))

	#copy the visible image into a new layer
	copyLayer1=pdb.gimp_layer_new_from_visible(img, img, "BaseCopy")
	pdb.gimp_image_insert_layer(img, copyLayer1, None, -1)

	#desaturate and colorize the new layer
	pdb.gimp_desaturate_full(copyLayer1, 1)
	pdb.gimp_colorize( copyLayer1, 215, 11, 0)

	#adjust brightness and contrast of the new layer
	pdb.gimp_drawable_brightness_contrast(copyLayer1, brightAdjust, contrastAdjust)

	#clean up	
	pdb.gimp_displays_flush()
	pdb.gimp_context_pop()
	img.enable_undo()

4. Call the Register

The next section calls a function that registers the plugin in the procedural database. We provide some general information about the plugin, where to place it in the GIMP menu, what image types it accepts, any input variables that the user must provide, and the name of the primary function that will be executed when the plugin is called.

register( "gimp_desat",
  "Add b/w effect",
  "Add b/w effect",
  "Simon Bland",
  "(©) 2024 Simon Bland",
  "2024-01-30",
  "<Image>/Filters/Desat",
  'RGB*',
  [
	(PF_SLIDER, "brightAdjust","Brightness", 0.4, (-0.5, 0.5, 0.1)),
	(PF_SLIDER, "contrastAdjust", "Contrast", 0.3, (-0.5, 0.5, 0.1))
  ],
  '',
  desat)

5. Load

Finally, we provide the name of the GIMP function that loads the plugin. This same line is used in all GIMP Python plugins.

main()

Where to Find More Information

You can find more information and documentation on the plugin API by browsing the Python-Fu console window in GIMP. It can be somewhat cumbersome to use, but it is up to date and contains thoroughly detailed information on each function and its options. If you search for information online, be aware that GIMP is now around 25 years old, and some information may have been superseded.

There are tons of resources online for learning Python, and it would make no sense to duplicate them here. If you start with https://www.w3schools.com/python/, you should be able to pick up the basics in no time at all.

It is worth noting that the current version on GIMP uses Python 2, not the more recent Python 3. There are some small syntax differences between the two versions, but nothing that should cause you significant problems when writing plugins. Future versions of GIMP will switch to Python 3 at some point.

Installing Your Plugin

Once you finally have a script ready it must be named with the Python extension type:

desat.py

Then copy it to one of the following hidden locations. If you have not already done do, you will need to make these hidden folders visible — you can do this from the menu bar in File Explorer.

Windows:

C:\user\<username>\AppData\Roaming\GIMP\2.10\plugins

Linux:

/usr/<username>/.config/GIMP/2.10/plugins

And that's all there is to it. Open GIMP and you're now ready to go with your new plugin.

The best way to create new plugins, of course, is to build on existing ones. Please feel free to use this and any of the other plugins in my repository as the starting point for your own work.

Happy image editing!


Stay in Touch

Get information about events and special offers delivered to your inbox.

Your Email

Enter your email address to receive occasional emails from Portraits by Simon Bland. You must be at least 18 years old to sign up for the newsletter. Your data will be managed in keeping with our Privacy Policy.