Shallow Thoughts : : gimp

Akkana's Musings on Open Source Computing and Technology, Science, and Nature.

Fri, 19 Feb 2016

GIMP ditty: change font size and face on every text layer

A silly little GIMP ditty:
I had a Google map page showing locations of lots of metal recycling places in Albuquerque. The Google map shows stars for each location, but to find out the name and location of each address, you have to mouse over each star. I wanted a printable version to carry in the car with me.

I made a screenshot in GIMP, then added text for the stars over the places that looked most promising. But I was doing this quickly, and as I added text for more locations, I realized that it was getting crowded and I wished I'd used a smaller font. How do you change the font size for ALL font layers in an image, all at once?

Of course GIMP has no built-in method for this -- it's not something that comes up very often, and there's no reason it would have a filter like that. But the GIMP PDB (Procedural DataBase, part of the GIMP API) lets you change font size and face, so it's an easy script to write.

In the past I would have written something like this in script-fu, but now that Python is available on all GIMP platforms, there's no reason not to use it for everything.

Changing font face is just as easy as changing size, so I added that as well.

I won't bother to break it down line by line, since it's so simple. Here's the script: changefont.py: Mass change font face and size in all GIMP text layers.

Tags: , ,
[ 11:11 Feb 19, 2016    More gimp | permalink to this entry | ]

Thu, 22 Oct 2015

Non-free software can mean unexpected surprises

I went to a night sky photography talk on Tuesday. The presenter talked a bit about tips on camera lenses, exposures; then showed a raw image and prepared to demonstrate how to process it to bring out the details.

His slides disappeared, the screen went blank, and then ... nothing. He wrestled with his laptop for a while. Finally he said "Looks like I'm going to need a network connection", left the podium and headed out the door to find someone to help him with that.

I'm not sure what the networking issue was: the nature center has open wi-fi, but you know how it is during talks: if anything can possibly go wrong with networking, it will, which is why a good speaker tries not to rely on it. And I'm not blaming this speaker, who had clearly done plenty of preparation and thought he had everything lined up.

Eventually they got the network connection, and he connected to Adobe. It turns out the problem was that Adobe Photoshop is now cloud-based. Even if you have a local copy of the software, it insists on checking in with Adobe at least every 30 days. At least, that's the theory. But he had used the software on that laptop earlier that same day, and thought he was safe. But that wasn't good enough, and Photoshop picked the worst possible time -- a talk in front of a large audience -- to decide it needed to check in before letting him do anything.

Someone sitting near me muttered "I'd been thinking about buying that, but now I don't think I will." Someone else told me afterward that all Photoshop is now cloud-based; older versions still work, but if you buy Photoshop now, your only option is this cloud version that may decide ... at the least opportune moment ... that you can't use your software any more.

I'm so glad I use Free software like GIMP. Not that things can't go wrong giving a GIMP talk, of course. Unexpected problems or bugs can arise with any software, and you take that risk any time you give a live demo.

But at least with Free, open source software like GIMP, you know you own the software and it's not suddenly going to refuse to run without a license check. That sort of freedom is what makes the difference between free as in beer, and Free as in speech.

You can practice your demo carefully before the talk to guard against most bugs and glitches; but all the practice in the world won't guard against software that won't start.

I talked to the club president afterward and offered to give a GIMP talk to the club some time soon, when their schedule allows.

Tags: , , ,
[ 10:24 Oct 22, 2015    More gimp | permalink to this entry | ]

Sun, 04 Oct 2015

Aligning images to make an animation (or an image stack)

For the animations I made from the lunar eclipse last week, the hard part was aligning all the images so the moon (or, in the case of the moonrise image, the hillside) was in the same position in every time.

This is a problem that comes up a lot with astrophotography, where multiple images are stacked for a variety of reasons: to increase contrast, to increase detail, or to take an average of a series of images, as well as animations like I was making this time. And of course animations can be fun in any context, not just astrophotography.

In the tutorial that follows, clicking on the images will show a full sized screenshot with more detail.

Load all the images as layers in a single GIMP image

The first thing I did was load up all the images as layers in a single image: File->Open as Layers..., then navigate to where the images are and use shift-click to select all the filenames I wanted.

[Upper layer 50% opaque to align two layers]

Work on two layers at once

By clicking on the "eyeball" icon in the Layers dialog, I could adjust which layers were visible. For each pair of layers, I made the top layer about 50% opaque by dragging the opacity slider (it's not important that it be exactly at 50%, as long as you can see both images).

Then use the Move tool to drag the top image on top of the bottom image.
[]

But it's hard to tell when they're exactly aligned

"Drag the top image on top of the bottom image": easy to say, hard to do. When the images are dim and red like that, and half of the image is nearly invisible, it's very hard to tell when they're exactly aligned.

[]

Use a Contrast display filter

What helped was a Contrast filter. View->Display Filters... and in the dialog that pops up, click on Contrast, and click on the right arrow to move it to Active Filters.

The Contrast filter changes the colors so that dim red moon is fully visible, and it's much easier to tell when the layers are approximately on top of each other.

[]

Use Difference mode for the final fine-tuning

Even with the Contrast filter, though, it's hard to see when the images are exactly on top of each other. When you have them within a few pixels, get rid of the contrast filter (you can keep the dialog up but disable the filter by un-checking its checkbox in Active Filters). Then, in the Layers dialog, slide the top layer's Opacity back to 100%, go to the Mode selector and set the layer's mode to Difference.

In Difference mode, you only see differences between the two layers. So if your alignment is off by a few pixels, it'll be much easier to see. Even in a case like an eclipse where the moon's appearance is changing from frame to frame as the earth's shadow moves across it, you can still get the best alignment by making the Difference between the two layers as small as you can.

Use the Move tool and the keyboard: left, right, up and down arrows move your layer by one pixel at a time. Pick a direction, hit the arrow key a couple of times and see how the difference changes. If it got bigger, use the opposite arrow key to go back the other way.

When you get to where there's almost no difference between the two layers, you're done. Change Mode back to Normal, make sure Opacity is at 100%, then move on to the next layer in the stack.

It's still a lot of work. I'd love to find a program that looks for circular or partially-circular shapes in successive images and does the alignment automatically. Someone on GIMP suggested I might be able to write something using OpenCV, which has circle-finding primitives (I've written briefly before about SimpleCV, a wrapper that makes OpenCV easy to use from Python). But doing the alignment by hand in GIMP, while somewhat tedious, didn't take as long as I expected once I got the hang of using the Contrast display filter along with Opacity and Difference mode.

Creating the animation

Once you have your layers, how do you turn them into an animation?

The obvious solution, which I originally intended to use, is to save as GIF and check the "animated" box. I tried that -- and discovered that the color errors you get when converting an image to indexed make a beautiful red lunar eclipse look absolutely awful.

So I threw together a Javascript script to animate images by loading a series of JPEGs. That meant that I needed to export all the layers from my GIMP image to separate JPG files.

GIMP doesn't have a built-in way to export all of an image's layers to separate new images. But that's an easy plug-in to write, and a web search found lots of plug-ins already written to do that job.

The one I ended up using was Lie Ryan's Python script in How to save different layers of a design in separate files; though a couple of others looked promising (I didn't try them), such as gimp-plugin-export-layers and save_all_layers.scm.

You can see the final animation here: Lunar eclipse of September 27, 2015: Animations.

Tags: , ,
[ 09:44 Oct 04, 2015    More gimp | permalink to this entry | ]

Sat, 07 Mar 2015

GIMP: Turn black to another color with Screen mode

[20x20 icon, magnified 8 times] I needed to turn some small black-on-white icons to blue-on-white. Simple task, right? Except, not really. If there are intermediate colors that are not pure white or pure black -- which you can see if you magnify the image a lot, like this 800% view of a 20x20 icon -- it gets trickier.

[Bucket fill doesn't work for this] You can't use anything like Color to Alpha or Bucket Fill, because all those grey antialiased pixels will stay grey, as you see in the image at left.

And the Hue-Saturation dialog, so handy for changing the hue of a sky, a car or a dress, does nothing at all -- because changing hue has no effect when saturation is zero, as for black, grey or white. So what can you do?

I fiddled with several options, but the best way I've found is the Screen layer mode. It works like this:

[Make a new layer] In the Layers dialog, click the New Layer button and accept the defaults. You'll get a new, empty layer.

[Set the foreground color] Set the foreground color to your chosen color.

[Set the foreground color] Drag the foreground color into the image, or do Edit->Fill with FG Color.

Now it looks like your whole image is the new color. But don't panic!

[Use screen mode] Use the menu at the top of the Layers dialog to change the top layer's mode to Screen.

Layer modes specify how to combine two layers. (For a lot more information, see my book, Beginning GIMP). Multiply mode, for example, multiplies each pixel in the two layers, which makes light colors a lot more intense while not changing dark colors very much. Screen mode is sort of the opposite of Multiply mode: GIMP inverts each of the layers, multiplies them together, then inverts them again. All those white pixels in the image, when inverted, are black (a value of zero), so multiplying them doesn't change anything. They'll still be white when they're inverted back. But black pixels, in Screen mode, take on the color of the other layer -- exactly what I needed here.

Intensify the effect with contrast

[Mars sketch, colorized orange] One place I use this Screen mode trick is with pencil sketches. For example, I've made a lot of sketches of Mars over the years, like this sketch of Lacus Solis, the "Eye of Mars". But it's always a little frustrating: Mars is all shades of reddish orange and brown, not grey like a graphite pencil.

Adding an orange layer in Screen mode helps, but it has another problem: it washes out the image. What I need is to intensify the image underneath: increase the contrast, make the lights lighter and the darks darker.

[Colorized Mars sketch, enhanced  with brightness/contrast] Fortunately, all you need to do is bump up the contrast of the sketch layer -- and you can do that while keeping the orange Screen layer in place.

Just click on the sketch layer in the Layers dialog, then run Colors->Brightness/Contrast...

This sketch needed the brightness reduced a lot, plus a little more contrast, but every image will be different. Experiment!

Tags: ,
[ 18:22 Mar 07, 2015    More gimp | permalink to this entry | ]

Thu, 06 Nov 2014

New GIMP Save/Export plug-in: Saver

The split between Save and Export that GIMP introduced in version 2.8 has been a matter of much controversy. It's been over two years now, and people are still complaining on the gimp-users list.

Early on, I wrote a simple Python plug-in called Save-Export Clean, which saved over an image's current save or export filename regardless of whether the filename was XCF (save) or a different format (export). The idea was that you could bind Ctrl-S to the plug-in and not be pestered by needing to remember whether it was XCF, JPG or what.

Save-Export Clean has been widely cited, and I hope it's helped some people who were bothered by the Save/Export split. But personally I didn't like it very much. It wasn't very flexible -- there was no way to change the filename, for one thing, and it was awfully easy to overwrite an original image without knowing that you'd done it. I went back to using GIMP's separate Save and Export, but in the back of my mind I was turning over ideas, trying to understand my workflow and what I really wanted out of a GIMP Save plug-in.

[Screenshot: GIMP Saver-as... plug-in] The result of that was a new Python plug-in called Saver. I first wrote it a year ago, but I've been tweaking it and using it since then, with Ctrl-S bound to Saverand Ctrl-Shift-S bound to Saver as...). I wanted to make sure that it was useful and working reliably ... and somehow I never got around to writing it up and announcing it formally ... until now.

Saver, like Save/Export Clean, will overwrite your chosen filename, whether XCF or another format, and will mark the image as saved so GIMP won't pester you when you exit.

What's different? Mainly, three things:

  1. A Saver as... option so you can change the filename or file type.
  2. Merges multiple layers so they'll show up properly in your JPG or PNG image.
  3. An option to save as .xcf or .xcf.gz and, at the same time, export a copy in another format, possibly scaled down. So you can maintain your multi-layer XCF image but also update the JPG copy that you're going to put on the web.

I've been using Saver for nearly all my saving for the past year. If I'm just making a quick edit of a JPEG camera image, Ctrl-S overwrites it without questioning me. If I'm editing an elaborate multi-layer GIMP project, Ctrl-S overwrites the .xcf.gz. If I'm planning to export that image for the web, I Ctrl-Shift-S to bring up the Saver As... dialog, make sure the main filename is .xcf.gz, set a name (ending in .jpg) for the exported copy; and from then on, Ctrl-S will save both the XCF and the JPG copy.

Saver is available on my github page, with installation instructions here: GIMP Saver and Save/Export Clean Plug-ins. I hope you find it useful.

Tags:
[ 12:57 Nov 06, 2014    More gimp | permalink to this entry | ]

Fri, 13 Sep 2013

GIMP menu placeholders

Someone on the gimp-developers list asked whether there was documentation of GIMP's menu hooks.

I wasn't sure what they meant by "hooks", but GIMP menus do have an interesting feature that plug-in writers should know about: placeholders.

Placeholders let you group similar types of actions together. For instance, iever notice that in the image window's File menu, all the things that Open images are grouped together? There's Open, Open as Layers..., Open Location... and Open Recent. And then there's a group of Save actions all grouped together -- Save, Save As..., Save a Copy... and so forth. That's because there's a placeholder for Open in the File menu, and another placeholder for Save.

When you write your own plug-ins, you can take advantage of these placeholders. For instance, I want my Save/Export clean plug-in to show up next to the other Save menu items, not somewhere else down near the bottom of the menu -- so when I register it, I pass menu = "<Image>/File/Save/" so GIMP knows to group it with the other Save actions, even though it's directly in the File menu, not a submenu called Save..

Pretty slick, huh? But how do you know what placeholders are available?

I took a look at the source. In the menus/ subdirectory are all the menu definitions in XML, and they're pretty straightforward. In image-menu.xml you'll see things like <placeholder name="Open">, <placeholder name="Save">

So to get a list of all the menu placeholders, you just need to find all the " grep '<placeholder' menus/*.xml

That's not actually so useful, though, because it doesn't tell you what submenu contains the placeholder. For instance, Acquire is a placeholder but you need to know that it's actually File->Create->Acquire. So let's be a little more clever.

We want to see <menu lines as well as <placeholders, but not <menuitem since those are just individual menu entries. egrep '<(placeholder|menu) will do that. Then pass it through some sed expressions to clean up the output, loop over all the XML files, and I ended up with:

for f in *.xml; do
  echo $f
  egrep '<(placeholder|menu) ' $f | sed -e 's_<placeholder *name="_** _' -e 's_<menu.*name="__' -e 's_"/*>__'
done

It isn't perfect: a few lines still show up that shouldn't -- but it'll get you the list you need. Fortunately the GIMP developers are very good about things like code formatting, so the identation of the file shows which placeholder is inside which submenu.

I only found placeholders in the image window menu, plus a single placeholder, "Outline", in the selection menu popup. I'm a little confused about that menu file: it seems to duplicate the existing Select menu in the image-menu.xml, except that the placeholder items in question -- feather, sharpen, shrink, grow, and border -- are in a placeholder called Outline in selection-menu.xml, but in a placeholder called Modify in image-menu.xml.

Anyway, here's the full list of placeholders, cleaned up for readability. Placeholders are in bold and followed with an asterisk *.

===== image-menu.xml =====
    File
      Create
        Acquire *
      Open *
      Open Recent
        Files *
      Debug
      Save *
      Export *
      Send *
      Info *
    Context
    Edit
      Undo *
      Cut *
      Copy *
      Paste *
      Paste as
      Buffer
      Clear *
      Fill *
      Stroke *
      Preferences *
    Select
      Modify *
    View
    Image
      New *
      Mode
        Color Profile *
      Precision
      Transform
        Flip *
        Rotate *
      Resize *
      Scale *
      Crop *
      Structure *
      Arrange *
      Guides
    Layer
      New *
      Structure *
      Text *
      Stack
        Select *
        Position *
      Mask
        Modify *
        Properties *
        Selection *
      Transparency
        Modify *
        Selection *
      Transform
        Flip *
        Rotate *
      Properties
        Opacity
        Layer Mode
      Resize *
      Scale *
      Crop *
    Colors
      Invert *
      Auto
      Components
      Desaturate
      Map
        Colormap *
      Info
      Modify *
    Tools
    Filters
      Recently Used
        Plug-Ins *
      Blur
        Motion *
      Enhance
      Distorts
      Light and Shadow
        Light *
        Shadow *
        Glass *
      Noise
      Edge-Detect
      Generic
      Combine
      Artistic
      Decor
      Map
      Render
        Clouds
        Nature
        Pattern
      Web
      Animation
        Animators *
      Menus *
      Languages *
      Extensions *
    Menus *
    Windows
      Recently Closed Docks
      Dockable Dialogs
      Images *
      Docks *
    Help
      Programming *

===== selection-menu.xml =====
    Outline *

Tags: ,
[ 19:06 Sep 13, 2013    More gimp | permalink to this entry | ]

Wed, 27 Feb 2013

Printing in Landscape orientation in GIMP (and some spinbutton tips)

I was printing out a map for a new trail we wanted to hike. I wanted to fill the paper with the map -- Google maps' print and Firefox's print, while fine for most map printing, weren't what I wanted.

So I took a screenshot of the maximized browser window with the map in it and imported it into GIMP. In the Crop tool, I constrained Aspect-ratio to 11:8.5 and cropped the image so it would just fit on a page, in landscape format -- wider in east-west than in north-south. Then I chose File->Print.

[GIMP printing dialog] Since printing defaults to Portrait orientation, I went to the Page Setup tab and changed Orientation to Landscape. (It's so nice to have that as part of the dialog. I'm forever amazed at how some apps, like Firefox, make you use a separate dialog first to change the print's orientation. How can anyone possibly see that as sensible UI design?)

Unfortunately, When I went to the Image Settings tab to check , I discovered that GIMP hadn't adjusted the image size when I changed to Landscape. It had Width listed as 8.500, Height as 6.567, and the image only took up part of the page.

I went to the Width field and replaced 8.500 with 11, and hit Tab. Whoops! The field reverted to 8.500. The same thing happened if I tried typing 8.5 into the Height field.

These fields aren't plain text entries -- they're "spin boxes", with a text entry plus up and down arrows. It turned out that under GIMP 2.8 and earlier, round-off errors sometimes prevent you from setting a spin box's maximum value. I could type 10.999 and it would work fine, but 11 or 11.0 failed.

Of course, 10.999 would have been fine -- I don't mind a little margin on a printed map. It's also trivial in GIMP to rotate a landscape photo 90 degrees and print it in portrait orientation. But by this point I was into stubborn mode -- by gosh, I wanted a way to fix this!

The best workaround, it turns out, is to use the spin box's up-arrow. Holding the mouse down over the up arrow will eventually get to the maximum value. But there's a faster way: right-clicking on the up-arrow goes straight to the maximum value. A nice trick!

The problem doesn't exist in GIMP 2.9 -- I reported it as bug 694477, and the awesome GIMP team fixed it very quickly. The spin boxes work beautifully now. (Thanks, Mitch!) But as long as 2.8 is around, or for any other app using spin boxes, I'm glad to know about right-clicking on the spin box arrows.

Tags: ,
[ 12:26 Feb 27, 2013    More gimp | permalink to this entry | ]

Sun, 02 Sep 2012

GIMP plug-in to export scaled-down versions of images

In a discussion on Google+arising from my Save/Export clean plug-in, someone said to the world in general

PLEASE provide an option to select the size of the export. Having to scale the XCF then export then throw out the result without saving is really awkward.

I thought, What a good idea! Suppose you're editing a large image, with layers and text and other jazz, saving in GIMP's native XCF format, but you want to export a smaller version for the web. Every time you make a significant change, you have to: Scale (remembering the scale size or percentage you're targeting); Save a Copy (or Export in GIMP 2.8); then Undo the Scale. If you forget the Undo, you're in deep trouble and might end up overwriting the XCF original with a scaled-down version.

If I had a plug-in that would export to another file type (such as JPG) with a scale factor, remembering that scale factor so I didn't have to, it would save me both effort and risk.

And that sounded pretty easy to write, using some of the tricks I'd learned from my Save/Export Clean and wallpaper scripts. So I wrote export-scaled.py

Update: Please consider using Saver instead. Saver integrates the various functions I used to have in different save/export plug-ins; it should do everything export-scaled.py does and more, and export-scaled.py is no longer maintained. If you need something export-scaled.py does that saver doesn't do as well, please let me know.

It's still brand new, so if anyone tries it, I'd appreciate knowing if it's useful or if you have any problems with it.

Geeky programming commentary

(Folks not interested in the programming details can stop reading now.)

Linked input fields

[screenshot: linked input fields] One fun project was writing a set of linked text entries for the dialog:
Scale to: Percentage 100 % Width: 640 Height: 480

Change any one of the three, and the other two change automatically. There's no chain link between width and height: It's assumed that if you're exporting a scaled copy, you won't want to change the image's aspect ratio, so any one of the three is enough.

That turned out to be surprisingly hard to do with GTK SpinBoxes: I had to read their values as strings and parse them, because the numeric values kept snapping back to their original values as soon as focus went to another field.

Image parasites

Another fun challenge was how to save the scale ratio, so the second time you call up the plug-in on the same image it uses whatever values you used the first time. If you're going to scale to 50%, you don't want to have to type that in every time. And of course, you want it to remember the exported file path, so you don't have to navigate there every time.

For that, I used GIMP parasites: little arbitrary pieces of data you can attach to any image. I've known about parasites for a long time, but I'd never had occasion to use them in a Python plug-in before. I was pleased to find that they were documented in the official GIMP Python documentation, and they worked just as documented. It was easy to test them, too: in the Python console (Filters->Python->Console...), type something like

img = gimp_image_list()[0]
img.parasite_list()
img.parasite_find(img.parasite_list()[0])
and so forth. Nice!

Not prompting for JPG settings

My plug-in was almost done. But when I ran it and told it to save to filenamecopy.jpg, it prompted me with that annoying JPEG settings dialog. Okay, being prompted once isn't so bad. But then when I exported a second time, it prompted me again, and didn't remember the values from before. So the question was, what controls whether the settings dialog is shown, and how could I prevent it?

Of course, I could prompt the user for JPEG quality, then call jpeg-save-file directly -- but what if you want to export to PNG or GIF or some other format? I needed something more general

Turns out, nobody really remembers how this works, and it's not documented anywhere. Some people thought that passing run_mode=RUN_WITH_LAST_VALS when I called pdb.gimp_file_save() would do the trick, but it didn't help.

So I guessed that there might be a parasite that was storing those settings: if the JPEG save plug-in sees the parasite, it uses those values and doesn't prompt. Using the Python console technique I just mentioned, I tried checking the parasites on a newly created image and on an image read in from an existing JPG file, then saving each one as JPG and checking the parasite list afterward.

Bingo! When you read in a JPG file, it has a parasite called 'jpeg-settings'. (The new image doesn't have this, naturally). But after you write a file to JPG from within GIMP, it has not only 'jpeg-settings' but also a second parasite, 'jpeg-save-options'.

So I made the plug-in check the scaled image after saving it, looking for any parasites with names ending in either -settings or -save-options; any such parasites are copied to the original image. Then, the next time you invoke Export Scaled, it does the same search, and copies those parasites to the scaled image before calling gimp-file-save.

That darned invisible JPG settings dialog

One niggling annoyance remained. The first time you get the JPG settings dialog, it pops up invisibly, under the Export dialog you're using. So if you didn't know to look for it by moving the dialog, you'd think the plug-in had frozen. GIMP 2.6 had a bug where that happened every time I saved, so I assumed there was nothing I can do about it.

GIMP 2.8 has fixed that bug -- yet it still happened when my plug-in called gimp_file_save: the JPG dialog popped up under the currently active dialog, at least under Openbox.

There isn't any way to pass window IDs through gimp_file_save so the JPG dialog pops up as transient to a particular window. But a few days after I wrote the export-scaled, I realized there was still something I could do: hide the dialog when the user clicks Save. Then make sure that I show it again if any errors occur during saving.

Of course, it wasn't quite that simple. Calling chooser.hide() by itself does nothing, because X is asynchronous and things don't happen in any predictable order. But it's possible to force X to sync the display: chooser.get_display().sync().

I'm not sure how robust this is going to be -- but it seems to work well in the testing I've done so far, and it's really nice to get that huge GTK file chooser dialog out of the way as soon as possible.

Tags: , ,
[ 18:34 Sep 02, 2012    More gimp | permalink to this entry | ]