Simple algorithms for adjusting image temperature and tint

I’ve already talked at length about converting a temperature (in Kelvin) to an RGB triplet. But what if you simply want to adjust an image’s temperature, without caring about the specifics of it?

Here’s how:

Given a temperature adjustment on the range -100 to 100,
 apply the following adjustment to each pixel in the image:

r = r + adjustmentValue
g = g
b = b - adjustmentValue

As with any additive RGB adjustment, you’ll need to manually clip the output values to the [0, 255] range.

Here’s a sample of the output, as implemented in the latest development build of my open-source photo editor. Note the temperature slider at the bottom of the screen. Because I find the -100 to 100 range to be a bit too strong, I actually divide the adjustment value by 5, thus limiting the actual adjustment value from -20 to 20:

temp_tint_base_image
Base image, courtesy of http://en.wikipedia.org/wiki/Great_wall
Temperature at max value (+20)
Temperature at max value (+20). Note the warmer tones.
Temperature at min value (-20)
Temperature at min value (-20). Note the cooler tones.

Pretty simple!

Because temperature and tint adjustments are usually provided together, here’s the code for basic tint adjustments. It’s even simper than temperature:

Given a tint adjustment on the range -100 to 100, 
 apply the following adjustment to each pixel in the image:

r = r 
g = g + adjustmentValue
b = b 

Sample output, using the same sample image from above (note the tint slider at the bottom):

Tint at max value (+20).  Hard to tell as the image is already very green, but the green has actually been ramped up further.
Tint at max value (+20). Hard to tell as the image is already very green, but the green has actually been ramped up further.
Tint at minimum value (-20).  Note the magenta color cast.
Tint at minimum value (-20). Note the magenta color cast.

This might be the simplest image processing algorithm I’ve ever posted… :)

PhotoDemon 6.2 beta live – testers welcome!

(Apologies for the dearth of blog posts over the past few months. The holidays always throw off my schedule, and it takes some time to get things back in order. I’ll try to make more time for articles!)

A new version of PhotoDemon includes an overhauled interface, lots of new tools (including cool things like Content-Aware resize), WebP and JPEG-XR support, and so much more.  Get it here.
The latest version of PhotoDemon includes an overhauled interface, cool new tools (like Content-Aware resize), WebP and JPEG-XR support, and so much more. Get it here.

A new version of PhotoDemon is about ready to release, and anyone who can help give it a final round of testing is most appreciated. Download the 6.2 beta here:

http://www.photodemon.org/downloads/PhotoDemon_6.2_beta.zip

The announcement article on photodemon.org contains a full run-down of all the new features, but let me mirror a few here for the curious:

Brand-new interface

PD_6-2-beta

The old MDI “floating-window” interface is gone for good. In its place is a sleek, lightweight tabbed interface.

Color-Managed Workflow

Color_Managed_Workflow

This was a huge project, and an exciting addition for both casual and professional users. PhotoDemon now provides a fully color-managed workflow across the entire program (including the main viewport, all tool dialogs, and even small things like color selection dialogs).

New color selector

PhotoDemon_Color_Selection

Major improvement over the stock Windows color selection dialog (which hasn’t changed in 20 years). Many thanks to the open-source photo editor GIMP, whose color selector served as the primary inspiration for this one.

Comprehensive Resize options

PhotoDemon_Resize_Dialog

In past versions, PhotoDemon always required you to resize images using pixel values – but no more! All resize dialogs (Image Resize, Canvas Resize, etc) now support resizing by pixels, percent, or physical units (inches, cm).

All-new JPEG export dialog

PhotoDemon_Export_JPEG_Final

PhotoDemon_Export_JPEG_Final_Metadata

As the most common image format, JPEGs deserve special consideration. PhotoDemon’s new JPEG export dialog provides many new features, including the ability to have the software automatically determine an appropriate quality setting for you.

WebP and JPEG-XR support (import and export)

I don’t know if anyone uses these formats, but they’re there if you need ’em!

New Autosave feature

PhotoDemon_Autosave_Dialog

New Content-Aware Resize tool

Initial image, courtesy of Wikipedia (http://en.wikipedia.org/wiki/File:Broadway_tower_edit.jpg).
Initial image, courtesy of Wikipedia (http://en.wikipedia.org/wiki/File:Broadway_tower_edit.jpg).
Image resized using a standard resize algorithm.  Note the undesirable stretching.
Image resized using a standard resize algorithm. Note the undesirable stretching.
The same image, resized using a content-aware algorithm.  Uninteresting features (like sky and grass) have been shrunk preferentially, leaving the interesting features (person, castle) intact.
The same image, resized using a content-aware algorithm. Uninteresting features (like sky and grass) have been shrunk preferentially, leaving the interesting features (person, castle) intact.

Content-aware resize is a cutting-edge technique for resizing images without distorting their contents. It was recently added to PhotoShop in version CS4, and nearly all free photo editors lack a comparable tool – but not PhotoDemon!

Improved tool previews

updated vignetting tool

Tool dialogs now provide an option to toggle between “fit whole image on screen” and “show image at 100%”. When showing the image at 100%, the image can be click-dragged, so even large images can be fully inspected. Also, the preview tool now supports “click to set a center point” behavior when relevant.

Asynchronous image metadata handling

Parsing image metadata occupies a huge chunk of the image load process. To improve performance, metadata handling is now completely asynchronous, meaning it will operate in parallel with the rest of the image loading process.

Improved Screen Capture tool

PhotoDemon_Screenshot_Dialog

Individual program windows can now be selected from a list, with or without their window decorations (titlebar, borders, etc).

And lots more…

Check out the official beta announcement at photodemon.org for a full list of updates and improvements.

PhotoDemon 6.0 is now live at photodemon.org

The final splash screen for version 6.0.  Thank you again to all the talented contributors that made this release possible!
The final splash screen for version 6.0. Thank you again to all the talented contributors that made this release possible!

After a successful (and relatively bug-free!) beta testing session, PhotoDemon 6.0 has been deemed “ready for mass consumption”. Normally this is where I would put a download link, but let me instead refer you to the new home of all things PhotoDemon: photodemon.org

I thought it was finally time for PD to get its own online home, and photodemon.org is the result. The site still has a few pieces left to assemble (most notably the Features section, which is only about half done), but I didn’t want that to delay the availability of 6.0’s release.

You can download version 6.0 from the “Downloads” section of the new site, or from this direct link:

Download PhotoDemon 6.0 (.zip file, 6.3 mb)

Functionally, this release is very similar to the 6.0 beta that launched two weeks ago. The primary changes from the beta are miscellaneous bug fixes, performance improvements, and cosmetic adjustments.

If you are upgrading from version 5.4, you’re going to notice a lot of new things! You can get a full write-up of all the improvements here, or if you’re in a hurry, here is the abbreviated list of updates:

  • All tools now support save/load presets, reset to default, randomize, and automatic save/load of last-used settings.
  • Italian language support.
  • Vastly improved support for non-US locales and non-English users.
  • Advanced selection tools, including rectangular, elliptical, and line selections.
  • All selection types now support feathering, live move/resize, interior/exterior/border types, and antialiasing.
  • Selection actions are fully integrated into Undo/Redo, and selections can be saved to disk.
  • Full preservation of all types of image metadata (EXIF, XMP, IPTC, etc). Metadata can be browsed via a new metadata browser.
  • Official RAW image format support; more than 20 RAW filetypes are now supported.
  • Three new blur tools: motion, radial, and zoom blur. Also, big improvements to Gaussian and Box Blur.
  • A new chroma key (“green screen”) tool with performance comparable to professional tools, including full support for edge blending.
  • New Perspective tool, with support for forward and reverse transforms.
  • New Photo Filter tool, with support for 50 digital photo filters (Wratten-type).
  • New Curves tool. Supports adding, moving, and removing an unlimited number of nodes.
  • New Channel Mixer tool.
  • New Canvas Resize tool.
  • New Spherize tool (for wrapping images around spheres).
  • New Vibrance tool.
  • New Pan and Zoom, Poke, Shear, and Squish distortion tools.
  • A new Language Editor for translators.
  • New variable-strength Sharpen tool.
  • New Oil Painting tool.
  • Many improvements to the Batch Wizard, including dedicated options for batch resizing.
  • Minor improvements to many tools, including polar coordinate conversion, wave distort, ripple distort, figured glass, tile image, posterize, rotate, custom filters, histogram, resize.
  • Any tool with a “color” option now allows you to pick a color directly from the image by clicking the preview.
  • Much better support for high-DPI screens, including tablets.
  • Support for transparent clipboard images, allowing you to move layered images between GIMP and PhotoDemon.
  • Filters and other long-running actions can now be canceled mid-action by pressing ESC.

Acknowledgments

This 6.0 release represents six months of hard work from a variety of contributors. While I am very grateful to all of PhotoDemon’s talented contributors, a few deserve special mention. Thank you to:

  • Audioglider for contributing three new tools: Channel Mixer, Vibrance, and Exposure. Audioglider also reported a number of issues, and motivated me to implement preset support for every PD tool.
  • Frank Donckers for again providing the German, French, and Dutch translations, and for contributing many pieces of code to the new Language Editor, including the Google Translate interface. Amazing stuff.
  • GioRock for the Italian translation, and for detailed testing of many small translation items. It takes a ton of work to get all of PD’s text translating properly, and GioRock debugged many items for me, which benefits users of every language.
  • Kroc Camen for a new IDE-safe mouse interface class, derived from his own open-source VB project. Kroc also reviews many of PD’s individual commits, where he catches many small items I overlook.
  • Robert Rayment for helping me profile and optimize a number of PD’s more taxing functions, and for many suggestions on tweaks and improvements. Many of the performance improvements available in this new version are a result of Robert’s help. Please check out his own VB image editor if you can.

Enjoy!

I hope you enjoy version 6.0. If you have any feedback, please send me a message. I’d love to hear from you.

 

This site - and its many free downloads - are 100% funded by donations. Please consider a small contribution to fund server costs and to help me support my family. Even $1.00 helps. Thank you!

Blur Filter performance: PhotoDemon vs GIMP vs Paint.NET

(Note before I begin: the PhotoDemon 6.0 beta should be live by the end of this week. Sorry it took so long to prepare!)

See what kind of fun charts we get to discuss?  And here I thought the days of 17-minute photo editing actions died with the Pentium III...
See what kind of fun charts we get to discuss? And here I thought the days of 17-minute photo editing actions died with the Pentium III…

The latest nightly build of PhotoDemon (download it here) includes a bunch of new and improved blur filters. Blur filters are among the most computationally demanding filters in a photo editor, because for each pixel in an image, a bunch of other pixels must also be examined in order to calculate the blur. (Blurs generally work by averaging together groups of pixels. Motion blur averages pixels in a line, radial blur averages pixels in an arc, and normal blur averages pixels in a box or circle shape.)

As a simple example, consider a basic blur with a 200 pixel radius, applied to a 10 megapixel digital photo. For each pixel in the photo (all ten million of them), an area of 200 pixels in each direction must be averaged together. Using a simple box blur, that means a box of 200 pixels left, right, up and down must be tallied (for a net area of 400 * 400, or 160,000 pixel comparisons) in order to calculate the blur. Thus, such an algorithm would require:

10,000,000 pixels * 160,000 calculations per pixel = 1.6 trillion total calculations

Even on a modern processor, that’s an enormous undertaking. Fortunately, mathematicians and coders have developed many clever ways to optimize blur functions. Many of these optimizations appear in the newest PhotoDemon build, so I thought it would be fun to speed-test four of PhotoDemon’s blur tools against two other free photo editors: GIMP and Paint.NET. The results were surprising enough that I thought them worth sharing.

A brief overview of each photo editor:

  • PhotoDemon: open-source, written in VB6, nightly build 893 (6.0 beta)
  • GIMP: open-source, written primarily in C, v2.8.6
  • Paint.NET: closed-source, written primarily in C# (and the .Net framework, per the name), v3.5.11

As benchmarking goes, this was very informal. PhotoDemon reports timing automatically in nightly builds, but for GIMP and Paint.NET I had to resort to using a stopwatch. Normally this is a terrible idea, but the algorithms involved take a very long time to run, so a stopwatch was sufficient for broad timing. (10ths of a second don’t matter much when an algorithm takes twenty minutes to finish…)

All tests were done on Windows 7 (64-bit), on my Core i5 650 (3.2ghz) desktop PC with 8gb of RAM. My PC was middle-of-the-road when I bought it back in 2010, so I’d consider reasonably representative of an “average” PC. All the tools in question appear to be heavily CPU-bound anyway, so it’s doubtful newer processors or more cores would make a meaningful difference.

The test photo I used was a 10 megapixel photo, 3872×2592 specifically, in JPEG format:

10 megapixel test photo

With the exception of some very long timings (10+ minutes), all timings were checked twice to make sure results were representative. Very long ones were only checked once due to the wait involved, though I did initiate a second attempt just to make sure my PC wasn’t acting up. (It wasn’t.)

Here are the timing results for four separate blur types, with some notes on my implementation, and what I know or can potentially infer about GIMP and/or Paint.NET’s implementations.

(Due to the large size of the images involved, I saw no reason to upload the output images of each test. Anyone interested can easily reproduce this test on their own PC with images of their choosing.)

Gaussian Blur

Two notes - PhotoDemon used the "good" quality setting, which is a Gaussian estimation using a modified 3x box blur, and GIMP used the IIR method.
Two notes – PhotoDemon used the “good” quality setting, which is a Gaussian estimation using a modified 3x box blur, and GIMP used the IIR method.

Gaussian Blur provides an excellent starting point. Gaussian blur works by averaging a square chunk of pixels, and giving pixels close to the center more weight than pixels far away. It is the most common type of blur tool in photo editing software, probably because its results are aesthetically pleasing, and it is an easy blur function to optimize.

Instead of a naive approach, which would involve the 1.6 trillion calculations mentioned above, most photo editors implement Gaussian Blur using a separable implementation, which cuts the calculations to a much more pleasant 8 billion calculations. Unfortunately, 8 billion calculations is still a lot. (PhotoDemon’s “best quality” option on its Gaussian tool applies a pure Gaussian using separable kernels. On large images, it’s slow. Very slow.)

An even faster approach takes advantage of a neat mathematical relationship between box filters and Gaussian filters: if you keep applying a box filter to a set of data, the result will eventually approach a Gaussian distribution. (Excellent charts available here, courtesy of Nghia Ho.) The Central Limit Theorem shows that repeating a box blur three times results in a function that’s ~97% identical to a true Gaussian.

PhotoDemon uses this as the basis for its three quality settings for Gaussian blur (good, better, and best). Good is a 3x box blur approximation, Better is a 5x, and Best is a true Gaussian. For the chart above, I used the “good” setting because it is by far the fastest. (Note that there’s a bit more to it than just repeating a box blur – how you calculate the box blur size matters; I use a variation of the W3 recommendation available here.)

Take-home message: GIMP’s IIR implementation is excellent – very fast, and it produces a true Gaussian, no estimations. PhotoDemon is surprisingly competitive for a single-threaded VB6 app. Paint.NET’s Gaussian is quite poor both in quality and final result. Its resulting blur is muddier than a true Gaussian, and much slower than you’d expect for a box-blur approximation… so I honestly have no idea how they’ve implemented it.

Motion Blur

PhotoDemon used "Quality" mode instead of "Speed", meaning bilinear interpolation was applied to the rotated image.  No extra options are available for this tool in GIMP or Paint.NET.
PhotoDemon used “Quality” mode instead of “Speed”, meaning bilinear interpolation was applied to the rotated image. Also, “blur symmetrically” was checked. No extra options are available for this tool in GIMP or Paint.NET.

Motion blur is a bit more problematic than Gaussian blur, because it doesn’t work in a square pattern. A naive approach would have you use something like Bresenham’s algorithm on each pixel, tracing a line at the specified angle and averaging interpolated values as you go.

A much better approach is to simply rotate the image by the requested angle, apply a (very fast) horizontal blur, then rotate the image back into place. If you use a fast rotation algorithm (like the famous 3-shear technique), this can make motion blur very quick.

My PhotoDemon implementation does not use the fast 3-shear technique; it uses a naive, geometric rotation (reverse-mapped) with bilinear interpolation. I expected this to make it quite a bit slower than comparable tools in GIMP and Paint.NET, but I was surprised to discover that both software packages are… well, pretty damn terrible.

Based on a brief perusal of GIMP’s source code, they appear to use the naive Bresenham approach, which explains why it’s so slow.

Once again, Paint.NET’s execution time makes no sense to me. For a software package that claims: “extensive work has gone into making Paint.NET the fastest image editor available“, methinks they need a bit more “extensive work” on this particular tool…

Radial Blur

As before, PhotoDemon uses the "quality" setting for bilinear interpolation.  Paint.NET was applied at quality setting 2 out of 5, the default setting.  (This results in a noticeably lower-quality image than PhotoDemon or GIMP.)  GIMP does not provide any additional options for this tool.
As before, PhotoDemon uses the “quality” setting for bilinear interpolation. Paint.NET was applied at quality setting 2 out of 5, the default setting. GIMP does not provide any additional options for this tool.

And so we move to Radial Blur, where we find a surprising role reversal: Paint.NET gives a much better showing here, while GIMP turns in the worst performance yet. Again, a brief look at GIMP’s source code for this function shows a questionable nested-loop approach to the problem. Tracing an arc-like path for each pixel is a bad idea, and while bilinear interpolation is used to improve the output quality – same as PhotoDemon – the time required makes this tool pretty much unusable.

PhotoDemon’s implementation is nothing particularly special, which makes its relative performance so surprising. I use a well-known trick where I convert the image to polar coordinates, apply a horizontal blur, then convert the image back to Cartesian coordinates. A small amount of image quality is lost by the two coordinate conversions, but because we are blurring the image anyway, this doesn’t matter much. That said, for small angles (< 5 degrees), both GIMP and Paint.NET produce better-looking output. At larger radii, however, PhotoDemon's is much better. Both GIMP and Paint.NET produce Moire patterns, presumably from sampling at discrete intervals, while PhotoDemon’s output is clean and smooth. This can probably be fixed in Paint.NET by using a higher quality setting, but quality setting 2/5 was already slow enough!

The top-left corner of the image after PhotoDemon's radial blur.  Buttery smooth, and accurate edge handling.
The top-left corner of the image after PhotoDemon’s radial blur. Buttery smooth, and accurate edge handling.
Same corner, but from Paint.NET's radial blur.  Nasty Moire patterns, and problematic handling in the corner - from an algorithm that took 4x longer to run.
Same corner, but from Paint.NET’s radial blur. Nasty Moire patterns, and problematic handling in the corner – from an algorithm that took 4x longer to run.

Zoom Blur

No, that huge green bar is not an error.  GIMP took a whopping 17 minutes to render a 200px zoom blur.  PhotoDemon's "traditional" mode was used to provide comparable output.  Paint.NET does not offer any specialized options for this tool.
No, that huge green bar is not an error. GIMP took a whopping 17 minutes to render a 200px zoom blur. PhotoDemon’s “traditional” mode was used to provide comparable output. Paint.NET does not offer any specialized options for this tool.

Last up is Zoom Blur, and we have a surprising winner! Paint.NET’s zoom blur implementation is excellent – great quality, very fast, and overall a huge improvement from their other blur tools. I have no idea why Zoom Blur is significantly faster than their Gaussian Blur implementation at a comparable pixel size, so I can only assume that some kind of specialized optimizations have been added. Nice work, Paint.NET team!

GIMP… I don’t even know what to say. It’s possible that I triggered some sort of problem with GIMP’s tile-based processing system, because there is no good way to explain a 17-minute processing time for such a straightforward function. Even a naive implementation shouldn’t take anywhere near that long. Their implementation has loops nested five-deep (dear god), and while bilinear interpolation is used to improve output, that algorithm is so poorly written that I frankly think they should consider removing it completely. Even at very low distances, rendering takes forever. The original copyright date on the source file is 1997, so perhaps someone familiar with GIMP’s internals should give this one a second look.

PhotoDemon uses the same trick here as with radial blur. The image is converted to polar coordinates (with swapped x and y values compared to the radial blur conversion), a horizontal blur is applied, then the image is converted back. Again, there is quality loss at low values, and both Paint.NET and GIMP provide better-quality output at very small radii. To mitigate this, I provide a second style on that dialog, which uses an iterative image-sized alpha blend to generate a blur. One of the neat things about that approach is that the image can be zoomed-out as well as zoomed-in.

I doubt there is a legitimate use for zoom-blur-outward like this, but it wasn't any extra work to implement.  :)
I doubt there is a legitimate use for zoom-blur-outward like this, but it wasn’t any extra work to implement. :)

Conclusions

Blur algorithm performance is hugely variable in both GIMP and Paint.NET. I’ll admit – I find it a bit amusing that my little PhotoDemon project, written with a 15-year-old programming language and compiler, outperforms them so handily in multiple areas, despite my implementations being generally lazy, single-threaded, and heavily CPU-bound. I also call “bullshit” on Paint.NET’s claim about “extensive work going into making Paint.NET the fastest image editor available.” I think the Paint.NET team does great work, and their software is a wonderful improvement over many free and paid photo editors, but its performance is greatly lacking in a number of areas.

Then there is GIMP. While I am very grateful for their software, and have learned to love its many quirks, there’s no denying that whole swaths of its source code are in desperate need of a revamp. I imagine there is no point revisiting items like blur until they complete their migration to GEGL – perhaps then we will see big improvements in the performance of these various blur functions.

If there’s a take-home message to all this, it’s that algorithms will always be more important than programming languages. A well-written algorithm in a “slow” language will often outperform a poorly written algorithm in a “fast” language. VB6 may be forgotten and nearly dead, but I’m happy to see it staying competitive with the titans of the “free photo editor” world. :)

If you read the article all the way to here, I hope you’ll give PhotoDemon a look:

http://www.tannerhelland.com/photodemon/#download

For a free, open-source photo editor, it has a lot of nice features, and I can empirically state that it outperforms GIMP and Paint.NET in at least a few areas! (The current nightly build is pretty much how the next stable release (6.0) will look, minus a few minor bugfixes still to complete.)

PhotoDemon 5.4.1 is now live

PhotoDemon 5.4.1 provides fixes for several issues found in version 5.4 (released two days ago). If you have automatic updates enabled, you will automatically be notified up the update, or you can manually download it here.

5.4.1 fixes the following bugs:

  • FIXED: Some controls do not display text correctly on Windows XP
  • FIXED: Some dialogs load very slowly when a translation is active
  • FIXED: Some edge detection methods fail to initiate when a translation is active
  • FIXED: Left-hand toolbox text formatting looks poor when a translation is active
  • FIXED: Tooltips not showing for picture box objects when a translation is active
  • FIXED: When the last-used folder in the batch process tool contains many images (1000+), the batch tool loads slowly on subsequent uses
  • FIXED: Minor typographical errors in translation files

This update also includes a few minor modifications and additions:

  • MODIFIED: In the batch process tool, when a selected image is removed from the batch list, its preview is now erased to prevent confusion
  • ADDED: further optimizations to translation engine. Program performance should now be much better while translations are active.
  • ADDED: batch process tool now displays the number of images in the current batch list

Once again, you can download the update here.

PhotoDemon 5.4 is live – now with German, French, and Dutch language support

Summary

PhotoDemon 5.4 is complete. New features include language support (German, French, and Dutch), a full-featured batch processing wizard, shadow/highlight correction, nine new distort tools, vignetting, median noise removal, JPEG and PNG optimization, and more. Download it here.

Kaleidoscope is probably the least practical (but most fun!) new tool in 5.4.  :)
Kaleidoscope is probably the least practical (but most fun!) new tool in 5.4. Also, German!

Highlight feature: support for multiple languages!

This is the biggest addition in version 5.4, and I can only claim partial credit for it. Primary credit goes to Frank Donckers, a fellow VB programmer who prototyped the initial translation engine for me. As if that isn’t incredible enough, Frank also supplied the translations for French, German, and Dutch (Flemish), so I owe him an enormous debt of gratitude. Thank you, Frank!

One of the neatest aspects of this feature is the ability to change the language at run-time via the Language menu. Unlike every program I have ever used, no restart is required. PhotoDemon will dynamically change the program’s entire language immediately, and if you change your mind, you can switch to any other language at any time.

I hope these three languages are only the beginning. If you speak a language other than English, please consider contributing a new PhotoDemon translation! No programming knowledge is required, and you will receive full credit for your work. Contact me for more details.

Nine new Distort-style tools

Add and remove lens distortion. Swirl. Ripple. Pinch and whirl. Waves. Kaleidoscope. Polar conversion (both directions). Figured glass (dents).

The new Ripple tool.  All distort tools use resampling for improved image quality, and all provide real-time previews.
The new Ripple tool. All distort tools use resampling for improved image quality, and all provide real-time previews.
The new Figured Glass tool uses Perlin Noise to provide a warped glass look to images.
The new Figured Glass tool uses Perlin Noise to provide a warped glass look to images. (Note: the source image is a promotional photo for ABC’s Once Upon a Time.)

Vastly improved file format support

The new JPEG export dialog.  Optimization is a lossless way to reduce file size - very handy for JPEGs headed to the web.
The new JPEG export dialog. Optimization is a lossless way to reduce file size – very handy for JPEGs headed to the web.

JPEGs now support automatic EXIF rotation on import, and a variety of options on export (Huffman table optimization, progressive scan, thumbnail embedding, specific subsampling). TIFFs support CMYK encoding and a number of compression schemes (none, PackBits, LZW, CCITT 3 and 4, zLib, and more). PNG exporting supports variable compression strength, interlacing, and background color chunk preservation. PPMs can be exported with RAW or ASCII encoding. BMP and TGA files now support RLE encoding. And for icons, animated GIFs, and multipage TIFFs, all images inside a file can now be loaded (instead of just the first one).

These format settings can be accessed from the Tools -> Options menu, and the new Batch Process tool also provides direct access.

Revamped standard tools, including Box Blur, Gaussian Blur, Smart Blur, and Unsharp Masking.

Smart blur can be used to smooth out specific features, like skin, while leaving edges and fine details intact.  (Image of the lovely and talented Rashida Jones, via Glamour)
Smart blur can be used to smooth out specific features, like skin, while leaving edges and fine details intact. (Image of the lovely and talented Rashida Jones, via Glamour)

PhotoDemon is now a much better photo editor, thanks to the revamp of its core convolution filters. Larger tool dialogs make it easier to see the result of your actions. Better performance means real-time previews, even at enormous radii (up to 200px for all filters, plus 500px for box blur!). And all convolution algorithms now use specialized edge handling code to make sure every part of the image – from center to border – is handled correctly.

Also, the program’s Gaussian Blur is now a true gaussian blur. There are no shortcuts, no estimations, and it’s still fast enough to preview in real-time.

New advanced color tools, including Shadow/Midtone/Highlight adjustments, color balancing, and monochrome-to-grayscale recovery

Shadow / Midtone / Highlight correction allows for detailed recovery of light and dark parts of an image.  Thanks to deviantart user deviantsnark for the sample image.
Shadow / Midtone / Highlight correction allows for detailed recovery of light and dark sections of an image. Thanks to dA user deviantsnark for the Borderlands wallpaper.
Color balance provides a per-color way to adjust the hue of an image (versus hue / saturation adjustments, which apply equally to all colors).  Thanks to dA user LadyGT for the beautiful artwork.
Color balance provides a per-color way to adjust the hue of an image (versus hue / saturation adjustments, which apply equally to all colors). Thanks to dA user LadyGT for the beautiful Tomb Raider artwork.

New stylize tools, including Film Grain, Vignetting, Modern Art, Trace Contour, Film Noir, and Comic Book

Vignetting refers to the rounded halo around the edges of the image.  The new tool allows you to add halos of any size, softness (how blurry the edges are), transparency, and color, and it can automatically fit the effect to any aspect ratio.  Thanks to dA user chrismickens for the great Mad Men artwork.
Vignetting refers to the rounded halo around the edges of the image. The new tool allows you to add halos of any size, softness (how blurry the edges are), transparency, and color, and it can automatically fit the effect to any aspect ratio. Thanks to dA user chrismickens for the great Mad Men artwork.
PhotoDemon now allows you to add artificial film grain to any image.  This effect was famously used in the Mass Effect trilogy of games to create a more gritty, realistic look.
PhotoDemon now allows you to add artificial film grain to any image. This effect was famously used in the Mass Effect trilogy to create a more gritty, realistic look.
Contour tracing uses a stack of unique algorithms to "paint" the edges of an image.  It is also a useful edge detection tool.
Contour tracing uses a unique stack of algorithms to “paint” the main features of an image. It is also a useful edge detection tool.

Noise removal via Median Filtering

Median filtering serves two main purposes: removal of image noise (unwanted pixel variance), and recovery of damaged images.  The severely damaged image above is courtesy Wikipedia; the after image is pure PhotoDemon (note that it recovers better than the Wikipedia example!).
Median filtering serves two main purposes: removal of image noise (unwanted pixel variance), and recovery of damaged images. The severely damaged image above is courtesy Wikipedia; the after image is PhotoDemon’s correction (note that it recovers more than the Wikipedia example!)

Automatic image cropping

If an image has empty space around the edges - like this Firefox wallpaper - Autocrop can automatically crop it for you.  The feature supports thresholding, so it works equally well on lossy formats like JPEG.
If an image has empty space around the edges – like this Firefox wallpaper – Autocrop can automatically remove it for you. Autocrop supports thresholding, so it works just fine on JPEGs.

New Batch Process Wizard

If I had to pick a personal “favorite” new feature in this release, it would be the brand-new batch processing wizard. This tool is a highlight of PhotoDemon’s emphasis on usability, and I researched more than a dozen other image batch processing tools while building it. I could be biased, but I believe PhotoDemon is now the best general-purpose image batch processor available on the web.

The first page of the new Batch Process wizard.  This step is by far the most intricate, and a ton of work went into exposing full functionality without overwhelming the user.  To my knowledge, PhotoDemon is the only batch processor that allows you to create your own batch list from any number of source directories spread across any number of drives.
The first page of the new Batch Process wizard. This step is by far the most intricate, and a ton of work went into exposing full functionality without overwhelming the user. To my knowledge, PhotoDemon is the only batch processor that allows you to create your own batch list from any number of source directories spread across any number of drives.

Drag-and-drop is now supported when building the list of images to be processed – not only from within the dialog, by dragging between list boxes, but also from Windows Explorer. Live previews make it much easier to find the images you want, while helpful instructions on the left-hand side expose some of the more nuanced functionality.

Once a list of images has been created, you can optionally choose to apply photo editing actions to each image.  Unlike other batch processors, PhotoDemon allows you to use any photo editing actions provided by the program.
Once a list of images has been created, you can optionally choose to apply photo editing actions to each image. Unlike other batch processors, PhotoDemon allows you to use any photo editing actions provided by the program – not just a tiny subset.

Page 2 is the barest page of the new wizard. The current version allows you to skip photo editing actions (if you want to just do a batch rename or format conversion, for example), or you can apply any recorded macro. In the next release, I will add a set of “one-click” presets for common actions, like resizing, or optimizing images for the web.

Once you've created a list of images and chosen any photo editing actions, an output image format can be set.  New to this version, PhotoDemon can retain original image formats - allowing you to apply actions to mixed PNG/JPEG collections, for example.  Alternatively, you can select a single output format, with access to the program's full range of detailed format settings.
Once you’ve created a list of images and chosen any photo editing actions, an output image format can be set. New to this version, PhotoDemon can retain original image formats – allowing you to apply actions to mixed PNG/JPEG collections, for example.

Page 3 asks you to choose an output format. If you want to retain original image formats, that’s cool too – PhotoDemon now supports this! Alternatively, you can select a single output format, with access to the program’s full range of detailed format settings. In the example above, you can see all the options available for JPEGs, including new support for optimization (lossless file size reduction), thumbnails, progressive encoding, and specific subsampling.

The last step of the wizard asks you to choose a location to save all the processed files.  If desired, a number of rename options are also available.
The last step of the wizard asks you to choose a location to save all the processed files. If desired, a number of rename options are also available.

The final page asks you to select an output folder where PhotoDemon can save the processed images. New to this release is a wide range of renaming options – things like adding custom text to each filename, removing text from each filename, changing case, and replacing spaces with underscores for web-bound images. Additionally, original filenames can be retained, or PhotoDemon can just use ascending numbers.

So that’s the new batch wizard! I’d love feedback from power users, as there are a lot of moving parts to the batch tool, and while I have been very thorough in my own testing, it’s impossible to test every combination of variables. So if you find anything that doesn’t work, please let me know.

Improved features: Gamma Correction, Dilate, Erode, Monochrome Conversion, Histogram and Printing

As is usual with each PhotoDemon update, a number of existing tools received redesigns or new features. Gamma correction now displays live gamma curves, and each color component (red, green, and blue) can be adjusted individually. Dilate and Erode use a new algorithm that’s significantly more optimized, meaning sizes up to 200px radius can be previewed in real-time. Monochrome conversion supports any two color (not just black and white), while the printing and histogram dialogs were completely overhauled to make them more user-friendly.

The new gamma correction dialog.  The old dialog forced users to correct only one channel at a time.  The new one allows for correcting all three, with a live preview of the new curves.  Thanks to dA user Kouken for the Persona fan art.
The new gamma correction dialog. The old dialog forced users to correct only one channel at a time. The new one allows for correcting all three, with a live preview of the new curves. Thanks to dA user Kouken for the Persona fan art.

Universal color depth support at import and export time

PhotoDemon can now write 1, 4, 8, 24, and 32bpp variations of every supported file format. By default, when saving images, color depth detection is completely automated – the program will count the number of colors in an image and automatically select the most appropriate color depth for the output file. Alternatively, you can set a preference to manually specify color depth at save time. This also works for grayscale images; for example, the JPEG encoder will now detect grayscale images and write out 8bpp JPEGs accordingly. Alpha thresholding is also available when saving 32bpp images to 8bpp (e.g. PNG to GIF).

When saving a 32bpp image with a complex alpha channel to a simple format like GIF, the program has to reduce the alpha channel to binary values.  A new threshold dialog helps you find the perfect value.
When saving a 32bpp image with a complex alpha channel to a simple format like GIF, the program has to reduce the alpha channel to binary values. A new threshold dialog helps you find the perfect value.

This feature was a nightmare to implement, as PhotoDemon supports a huge variety of file formats, and each one has a detailed list of color depths it does or does not support. Full support for transparency adds a whole other layer of complexity. But now that the feature is completely implemented and rigorously tested, I can’t imagine it any other way. Color depth is not something users should have to worry about, and automatic handling should be a feature of every photo editor (rather than pestering you for color depth every time you save… *cough* GIMP *cough*).

New feature: pngnq-s9 plugin for optimizing PNG files

At the request of a good friend, PhotoDemon now provides integrated support for the pngnq-s9 variety of the famous pngnq library. For the uninitiated, pngnq provides a way to reduce 32bpp PNG files to 8bpp while still preserving complex alpha channels, allowing for file size reductions of up to 75%. Pngnq provides superior results over other tools by using a neural network to reduce image colors, unlike the brute-force median cut algorithm used by software like pngquant. See here for a gallery of sample images if you’re curious.

Pngnq-s9 is a further improvement over stock pngnq, including cool features like YUV color space matching for better results, and the ability to preserve alpha values of 0 and 255. When saving 32bpp PNG files to 8bpp, PhotoDemon will now lean on pngnq-s9 to do the heavy lifting.

In the next version of PhotoDemon, pngnq-s9 support will be integrated into the batch process wizard as a new “optimize for web” option. For now, if you want to test out the feature, head to Tools -> Options -> Saving, and change the “set outgoing color depth” option to “ask me what color depth I want to use”. Then save a 32bpp PNG image to 8bpp and compare the file size.

New plugin manager and plugin downloader

Sometimes it makes sense for PhotoDemon to use an existing open-source project instead of me writing a new feature from scratch. These support libraries are included as “plugins”, and there are four of them in current version. Each one provides indispensable features (like scanner support) at a fraction of the cost involved to write such a feature from scratch.

Some of these plugins expose additional functionality, but it has always been a challenge for PhotoDemon to expose these additional features to the user. So the program now has a detailed plugin manager, where advanced users can change settings on a per-plugin basis, including activating or deactivating plugins as necessary. The manager also tracks availability and version numbers of each plugin.

It is now much, much easier for the program to keep its plugins up-to-date.  Advanced users may also find it useful to enable or disable plugins while testing various features.  All changes happen in real-time - no restart required.
It is now much, much easier for the program to keep its plugins up-to-date. Advanced users may also find it useful to enable or disable plugins while testing various features. All changes happen in real-time – no restart required.
The pngnq-s9 page of the plugin manager.  Advanced or esoteric plugin features can be adjusted here, which keeps the program's main preferences dialog uncluttered.
The pngnq-s9 page of the plugin manager. Advanced or esoteric plugin features can be adjusted here, which helps keep the main “Options” dialog uncluttered.

Many canvas and interface improvements

Larger effect and tool previews. Persistent zoom-in/zoom-out buttons. Image URLs and files can now be directly pasted as new images. Improved drag/drop support, including drag/drop from common dialogs. New “Safe” save behavior to avoid overwriting original files. New Close All Images menu. New algorithms for auto-zoom when images are loaded, meaning much better results at all screen sizes. Tool and file panels can now be hidden. Higher-quality dynamic icons for the program, taskbar, child windows, and Recent Images list. Improved support for low screen resolutions.

Program-wide performance improvements

More aggressive memory management means lower resource usage. Program loading has been heavily streamlined, and now happens in less than a second on modern hardware. Image loading is much faster and more robust, including better support for damaged or incomplete image files.

More robust and comprehensive error handling

When loading multiple images, the program will now suppress warnings and failures (such as invalid files) until all images have been loaded. Many subclassing issues have been resolved – so no more surprise crashes! Overall this release should be extremely stable.

Many miscellaneous bug fixes and improvements

This article is already way too long, so I won’t bore you with a list of all the minor fixes and improvements. For a full list, see the commit log at https://github.com/tannerhelland/PhotoDemon/commits/master

In Conclusion…

This release was a lot bigger than I’d like future releases to be. The biggest delay came from adding language support, as that affected every piece of text in every part of the program (nearly 10,000 words in total!). Now that language support is complete, I foresee future releases being much tidier and quicker.

A developer’s work is never done, and a roadmap for version 5.6 is already being worked on. Some features that didn’t make the cut for 5.4 – like improvements to the selection tool, or a “smart resize” option – were cut at the last minute, and they will be among the first features added to 5.6. The batch process wizard will see a number of additions, and I’d love to add some advanced multilanguage features, like a way for casual users to fix or adjust translations on-the-fly. I also think I’m finally ready to tackle the monumental task of writing a user manual… should be fun!

As always, the best way to stay abreast of PhotoDemon development is the official code repository at https://github.com/tannerhelland/PhotoDemon

But for now, I hope you enjoy all the new features in 5.4, and please remember to donate if you find the software useful.

A simple algorithm for correcting lens distortion

One of the new features in the development branch of my open-source photo editor is a simple tool for correcting lens distortion. I thought I’d share the algorithm I use, in case others find it useful. (There are very few useful examples of lens correction on the Internet – most articles simply refer to existing software packages, rather than explaining how the software works.)

Lens distortion is a complex beast, and a lot of approaches have been developed to deal with it. Some professional software packages address the problem by providing a comprehensive list of cameras and lenses – then the user just picks their equipment from the list, and the software applies a correction algorithm using a table of hard-coded values. This approach requires way more resources than a small developer like myself could handle, so I chose a simpler solution: a universal algorithm that allows the user to apply their own correction, with two tunable parameters for controlling the strength of the correction.

This is what PhotoDemon's new lens correction tool looks like.
PhotoDemon’s new lens correction tool in action.

The key part of the algorithm is less than ten lines of code, so there’s not much work involved. The effect is also fast enough to preview in real-time.

Before sharing the algorithm, let me demonstrate its output. Here is a sample photo that suffers from typical spherical distortion:

This lovely demonstration photo comes from Wikipedia, courtesy of Ashley Pomeroy
This lovely demonstration photo comes from Wikipedia, courtesy of Ashley Pomeroy

Pay special attention to the lines on the floor and the glass panels on the right.

Here’s the same image, as corrected by the algorithm in this article:

Note the straight lines on both the floor and the glass panels on the right.  Not bad, eh?
Note the straight lines on both the floor and the glass panels on the right. Not bad, eh?

My use of simple bilinear resampling blurs the output slightly; a more sophisticated resampling technique would produce clearer results.

A key feature of the algorithm is that it works at any aspect ratio – rectangular images, like the one above, are handled just fine, as are perfectly square images.

Anyway, here is the required code, as pseudocode:


input:
    strength as floating point >= 0.  0 = no change, high numbers equal stronger correction.
    zoom as floating point >= 1.  (1 = no change in zoom)

algorithm:

    set halfWidth = imageWidth / 2
    set halfHeight = imageHeight / 2
    
    if strength = 0 then strength = 0.00001
    set correctionRadius = squareroot(imageWidth ^ 2 + imageHeight ^ 2) / strength

    for each pixel (x,y) in destinationImage
        set newX = x - halfWidth
        set newY = y - halfHeight

        set distance = squareroot(newX ^ 2 + newY ^ 2)
        set r = distance / correctionRadius
        
        if r = 0 then
            set theta = 1
        else
            set theta = arctangent(r) / r

        set sourceX = halfWidth + theta * newX * zoom
        set sourceY = halfHeight + theta * newY * zoom

        set color of pixel (x, y) to color of source image pixel at (sourceX, sourceY)

That’s all there is to it. Note that you’ll need to do some bounds checking, as sourceX and sourceY may lie outside the bounds of the original image. Note also that sourceX and sourceY will be floating-point values – so for best results, you’ll want to interpolate the color used instead of just clamping sourceX and sourceY to integer values.

I should mention that the algorithm works just fine without the zoom parameter. I added the zoom parameter after some experimentation; specifically, I find zoom useful in two ways:

  • On images with only minor lens distortion, zooming out reduces stretching artifacts at the edges of the corrected image
  • On images with severe distortion, such as true fish-eye photos, zooming-out retains more of the source material

As there is not a universally “correct” solution to these two scenarios, I recommend providing zoom as a tunable parameter. To give a specific example of the second circumstance, consider this fish-eye photo from Wikipedia, courtesy of Josef F. Stuefer:

Severe distortion like this is difficult to correct completely.
Severe distortion like this is difficult to fully correct.

If we attempt to correct the image without applying any zoom, the image must be stretched so far that much of the edges are lost completely:

This is hardly the same photo.  Note also the visible stretching at the edges.
This is hardly the same photo. The pier at the bottom has been completely erased!

By utilizing a zoom parameter, it is possible to include more of the image in the finished result:

Much more of the photo can be preserved by adding a simple zoom parameter to the algorithm.
Use of a zoom parameter allows us to preserve much more of the photo. When correcting severe distortion like this, you might want to apply a sharpening algorithm to the final image. (This sample image has no sharpening applied.)

Again, I only use a simple resampling technique; a more sophisticated one would produce clearer results at the edges.

If you’d like to see my actual source code, check out this GitHub link. The fun begins at line 194. I also include an optional radius parameter, which allows the user to correct only a subset of the image (rather than the entire thing), but other than that the code is identical to what you see above.

Enjoy!

P.S. For a great discussion of fish-eye distortion from a pro photographer’s perspective, check out http://photo.net/learn/fisheye/

Image Dithering: Eleven Algorithms and Source Code

Dithering: An Overview

Today’s graphics programming topic – dithering – is one I receive a lot of emails about, which some may find surprising. You might think that dithering is something programmers shouldn’t have to deal with in 2012. Doesn’t dithering belong in the annals of technology history, a relic of times when “16 million color displays” were something programmers and users could only dream of? In an age when cheap mobile phones operate in full 32bpp glory, why am I writing an article about dithering?

Actually, dithering is still a surprisingly applicable technique, not just for practical reasons (such as preparing a full-color image for output on a non-color printer), but for artistic reasons as well. Dithering also has applications in web design, where it is a useful technique for reducing images with high color counts to lower color counts, reducing file size (and bandwidth) without harming quality. It also has uses when reducing 48 or 64bpp RAW-format digital photos to 24bpp RGB for editing.

And these are just image dithering uses – dithering still has extremely crucial roles to play in audio, but I’m afraid I won’t be discussing audio dithering here. Just image dithering.

In this article, I’m going to focus on three things:

  • a basic discussion of how image dithering works
  • eleven specific two-dimensional dithering formulas, including famous ones like “Floyd-Steinberg”
  • how to write a general-purpose dithering engine

Update 11 June 2016: some of the sample images in this article have been updated to better reflect the various dithering algorithms. Thank you to commenters who noted problems with the previous images!

Dithering: Some Examples

Consider the following full-color image, a wallpaper of the famous “companion cube” from Portal:

This will be our demonstration image for this article.  I chose it because it has a nice mixture of soft gradients and hard edges.
This will be our demonstration image for this article. I chose it because it has a nice mixture of soft gradients and hard edges.

On a modern LCD or LED screen – be it your computer monitor, smartphone, or TV – this full-color image can be displayed without any problems. But consider an older PC, one that only supports a limited palette. If we attempt to display the image on such a PC, it might look something like this:

This is the same image as above, but restricted to a websafe palette.
This is the same image as above, but restricted to a websafe palette.

Pretty nasty, isn’t it? Consider an even more dramatic example, where we want to print the cube image on a black-and-white printer. Then we’re left with something like this:

At this point, the image is barely recognizable.
At this point, the image is barely recognizable.

Problems arise any time an image is displayed on a device that supports less colors than the image contains. Subtle gradients in the original image may be replaced with blobs of uniform color, and depending on the restrictions of the device, the original image may become unrecognizable.

Dithering is an attempt to solve this problem. Dithering works by approximating unavailable colors with available colors, by mixing and matching available colors in a way that mimicks unavailable ones. As an example, here is the cube image once again reduced to the colors of a theoretical old PC – only this time, dithering has been applied:

A big improvement over the non-dithered version!
A big improvement over the non-dithered version!

If you look closely, you can see that this image uses the same colors as its non-dithered counterpart – but those few colors are arranged in a way that makes it seem like many more colors are present.

As another example, here is a black-and-white version of the image with similar dithering applied:

The specific algorithm used on this image is "2-row Sierra" dithering.
The specific algorithm used on this image is “2-row Sierra” dithering.

Despite only black and white being used, we can still make out the shape of the cube, right down to the hearts on either side. Dithering is an extremely powerful technique, and it can be used in ANY situation where data has to be represented at a lower resolution than it was originally created for. This article will focus specifically on images, but the same techniques can be applied to any 2-dimensional data (or 1-dimensional data, which is even simpler!).

The Basic Concept Behind Dithering

Boiled down to its simplest form, dithering is fundamentally about error diffusion.

Error diffusion works as follows: let’s pretend to reduce a grayscale photograph to black and white, so we can print it on a printer that only supports pure black (ink) or pure white (no ink). The first pixel in the image is dark gray, with a value of 96 on a scale from 0 to 255, with zero being pure black and 255 being pure white.

Here is an example of the RGB values in the example.
Here is a visualization of the RGB values in our example.

When converting such a pixel to black or white, we use a simple formula – is the color value closer to 0 (black) or 255 (white)? 96 is closer to 0 than to 255, so we make the pixel black.

At this point, a standard approach would simply move to the next pixel and perform the same comparison. But a problem arises if we have a bunch of “96 gray” pixels – they all get turned to black, and we’re left with a huge chunk of empty black pixels, which doesn’t represent the original gray color very well at all.

Error diffusion takes a smarter approach to the problem. As you might have inferred, error diffusion works by “diffusing” – or spreading – the error of each calculation to neighboring pixels. If it finds a pixel of 96 gray, it too determines that 96 is closer to 0 than to 255 – and so it makes the pixel black. But then the algorithm makes note of the “error” in its conversion – specifically, that the gray pixel we have forced to black was actually 96 steps away from black.

When it moves to the next pixel, the error diffusion algorithm adds the error of the previous pixel to the current pixel. If the next pixel is also 96 gray, instead of simply forcing that to black as well, the algorithm adds the error of 96 from the previous pixel. This results in a value of 192, which is actually closer to 255 – and thus closer to white! So it makes this particular pixel white, and it again makes note of the error – in this case, the error is -63, because 192 is 63 less than 255, which is the value this pixel was forced to.

As the algorithm proceeds, the “diffused error” results in an alternating pattern of black and white pixels, which does a pretty good job of mimicking the “96 gray” of the section – much better just forcing the color to black over and over again. Typically, when we finish processing a line of the image, we discard the error value we’ve been tracking and start over again at an error of “0” with the next line of the image.

Here is an example of the cube image from above with this exact algorithm applied – specifically, each pixel is converted to black or white, the error of the conversion is noted, and it is passed to the next pixel on the right:

This is the simplest possible application of error diffusion dithering.
This is the simplest possible application of error diffusion dithering.

Unfortunately, error diffusion dithering has problems of its own. For better or worse, dithering always leads to a spotted or stippled appearance. This is an inevitable side-effect of working with a small number of available colors – those colors are going to be repeated over and over again, because there are only so many of them.

In the simple error diffusion example above, another problem is evident – if you have a block of very similar colors, and you only push the error to the right, all the “dots” end up in the same place! This leads to funny lines of dots, which is nearly as distracting as the original, non-dithered version.

The problem is that we’re only using a one-dimensional error diffusion. By only pushing the error in one direction (right), we don’t distribute it very well. Since an image has two dimensions – horizontal and vertical – why not push the error in multiple directions? This will spread it out more evenly, which in turn will avoid the funny “lines of speckles” seen in the error diffusion example above.

Two-Dimensional Error Diffusion Dithering

There are many ways to diffuse an error in two dimensions. For example, we can spread the error to one or more pixels on the right, one or more pixels on the left, one or more pixels up, and one or more pixels down.

For simplicity of computation, all standard dithering formulas push the error forward, never backward. If you loop through an image one pixel at a time, starting at the top-left and moving right, you never want to push errors backward (e.g. left and/or up). The reason for this is obvious – if you push the error backward, you have to revisit pixels you’ve already processed, which leads to more errors being pushed backward, and you end up with an infinite cycle of error diffusion.

So for standard loop behavior (starting at the top-left of the image and moving right), we only want to push pixels right and down.

Apologies for the crappy image - but I hope it helps illustrate the gist of proper error diffusion.
Apologies for the crappy image – but I hope it helps illustrate the gist of proper error diffusion.

As for how specifically to propagate the error, a great number of individuals smarter than I have tackled this problem head-on. Let me share their formulas with you.

(Note: these dithering formulas are available multiple places online, but the best, most comprehensive reference I have found is this one.)

Floyd-Steinberg Dithering

The first – and arguably most famous – 2D error diffusion formula was published by Robert Floyd and Louis Steinberg in 1976. It diffuses errors in the following pattern:


       X   7
   3   5   1

     (1/16)

In the notation above, “X” refers to the current pixel. The fraction at the bottom represents the divisor for the error. Said another way, the Floyd-Steinberg formula could be written as:


           X    7/16
   3/16  5/16   1/16

But that notation is long and messy, so I’ll stick with the original.

To use our original example of converting a pixel of value “96” to 0 (black) or 255 (white), if we force the pixel to black, the resulting error is 96. We then propagate that error to the surrounding pixels by dividing 96 by 16 ( = 6), then multiplying it by the appropriate values, e.g.:


           X     +42
   +18    +30    +6

By spreading the error to multiple pixels, each with a different value, we minimize any distracting bands of speckles like the original error diffusion example. Here is the cube image with Floyd-Steinberg dithering applied:

Floyd-Steinberg dithering
Floyd-Steinberg dithering

Not bad, eh?

Floyd-Steinberg dithering is easily the most well-known error diffusion algorithm. It provides reasonably good quality, while only requiring a single forward array (a one-dimensional array the width of the image, which stores the error values pushed to the next row). Additionally, because its divisor is 16, bit-shifting can be used in place of division – making it quite fast, even on old hardware.

As for the 1/3/5/7 values used to distribute the error – those were chosen specifically because they create an even checkerboard pattern for perfectly gray images. Clever!

One warning regarding “Floyd-Steinberg” dithering – some software may use other, simpler dithering formulas and call them “Floyd-Steinberg”, hoping people won’t know the difference. This excellent dithering article describes one such “False Floyd-Steinberg” algorithm:


   X   3
   3   2

   (1/8)

This simplification of the original Floyd-Steinberg algorithm not only produces markedly worse output – but it does so without any conceivable advantage in terms of speed (or memory, as a forward-array to store error values for the next line is still required).

But if you’re curious, here’s the cube image after a “False Floyd-Steinberg” application:

Much more speckling than the legit Floyd-Steinberg algorithm - so don't use this formula!
Much more speckling than the legit Floyd-Steinberg algorithm – so don’t use this formula!

Jarvis, Judice, and Ninke Dithering

In the same year that Floyd and Steinberg published their famous dithering algorithm, a lesser-known – but much more powerful – algorithm was also published. The Jarvis, Judice, and Ninke filter is significantly more complex than Floyd-Steinberg:


             X   7   5 
     3   5   7   5   3
     1   3   5   3   1

           (1/48)

With this algorithm, the error is distributed to three times as many pixels as in Floyd-Steinberg, leading to much smoother – and more subtle – output. Unfortunately, the divisor of 48 is not a power of two, so bit-shifting can no longer be used – but only values of 1/48, 3/48, 5/48, and 7/48 are used, so these values can each be calculated but once, then propagated multiple times for a small speed gain.

Another downside of the JJN filter is that it pushes the error down not just one row, but two rows. This means we have to keep two forward arrays – one for the next row, and another for the row after that. This was a problem at the time the algorithm was first published, but on modern PCs or smartphones this extra requirement makes no difference. Frankly, you may be better off using a single error array the size of the image, rather than erasing the two single-row arrays over and over again.

Jarvis, Judice, Ninke dithering
Jarvis, Judice, Ninke dithering

Stucki Dithering

Five years after Jarvis, Judice, and Ninke published their dithering formula, Peter Stucki published an adjusted version of it, with slight changes made to improve processing time:


             X   8   4 
     2   4   8   4   2
     1   2   4   2   1

           (1/42)

The divisor of 42 is still not a power of two, but all the error propagation values are – so once the error is divided by 42, bit-shifting can be used to derive the specific values to propagate.

For most images, there will be minimal difference between the output of Stucki and JJN algorithms, so Stucki is often used because of its slight speed increase.

Stucki dithering
Stucki dithering

Atkinson Dithering

During the mid-1980’s, dithering became increasingly popular as computer hardware advanced to support more powerful video drivers and displays. One of the best dithering algorithms from this era was developed by Bill Atkinson, a Apple employee who worked on everything from MacPaint (which he wrote from scratch for the original Macintosh) to HyperCard and QuickDraw.

Atkinson’s formula is a bit different from others in this list, because it only propagates a fraction of the error instead of the full amount. This technique is sometimes offered by modern graphics applications as a “reduced color bleed” option. By only propagating part of the error, speckling is reduced, but contiguous dark or bright sections of an image may become washed out.


         X   1   1 
     1   1   1
         1

       (1/8)

Atkinson dithering
Atkinson dithering

Burkes Dithering

Seven years after Stucki published his improvement to Jarvis, Judice, Ninke dithering, Daniel Burkes suggested a further improvement:


             X   8   4 
     2   4   8   4   2

           (1/32)

Burkes’s suggestion was to drop the bottom row of Stucki’s matrix. Not only did this remove the need for two forward arrays, but it also resulted in a divisor that was once again a multiple of 2. This change meant that all math involved in the error calculation could be accomplished by simple bit-shifting, with only a minor hit to quality.

Burkes dithering
Burkes dithering

Sierra Dithering

The final three dithering algorithms come from Frankie Sierra, who published the following matrices in 1989 and 1990:


             X   5   3
     2   4   5   4   2
         2   3   2
           (1/32)


             X   4   3
     1   2   3   2   1
           (1/16)


         X   2
     1   1
       (1/4)

These three filters are commonly referred to as “Sierra”, “Two-Row Sierra”, and “Sierra Lite”. Their output on the sample cube image is as follows:

Sierra (sometimes called Sierra-3)
Sierra (sometimes called Sierra-3)
Two-row Sierra
Two-row Sierra
Sierra Lite
Sierra Lite

Other dithering considerations

If you compare the images above to the dithering results of another program, you may find slight differences. This is to be expected. There are a surprising number of variables that can affect the precise output of a dithering algorithm, including:

  • Integer or floating point tracking of errors. Integer-only methods lose some resolution due to quantization errors.
  • Color bleed reduction. Some software reduces the error by a set value – maybe 50% or 75% – to reduce the amount of “bleed” to neighboring pixels.
  • The threshold cut-off for black or white. 127 or 128 are common, but on some images it may be helpful to use other values.
  • For color images, how luminance is calculated can make a big difference. I use the HSL luminance formula ( [max(R,G,B) + min(R,G,B)] / 2). Others use ([r+g+b] / 3) or one of the ITU formulas. YUV or CIELAB will offer even better results.
  • Gamma correction or other pre-processing modifications. It is often beneficial to normalize an image before converting it to black and white, and whichever technique you use for this will obviously affect the output.
  • Loop direction. I’ve discussed a standard “left-to-right, top-to-bottom” approach, but some clever dithering algorithms will follow a serpentine path, where left-to-right directionality is reversed each line. This can reduce spots of uniform speckling and give a more varied appearance, but it’s more complicated to implement.

For the demonstration images in this article, I have not performed any pre-processing to the original image. All color matching is done in the RGB space with a cut-off of 127 (values <= 127 are set to 0). Loop direction is standard left-to-right, top-to-bottom.

Which specific techniques you may want to use will vary according to your programming language, processing constraints, and desired output.

I count 9 algorithms, but you promised 11! Where are the other two?

So far I’ve focused purely on error-diffusion dithering, because it offers better results than static, non-diffusion dithering.

But for sake of completeness, here are demonstrations of two standard “ordered dither” techniques. Ordered dithering leads to far more speckling (and worse results) than error-diffusion dithering, but they require no forward arrays and are very fast to apply. For more information on ordered dithering, check out the relevant Wikipedia article.

Ordered dither using a 4x4 Bayer matrix
Ordered dither using a 4×4 Bayer matrix
Ordered dither using an 8x8 Bayer matrix
Ordered dither using an 8×8 Bayer matrix

With these, the article has now covered a total of 11 different dithering algorithms.

Writing your own general-purpose dithering algorithm

Earlier this year, I wrote a fully functional, general-purpose dithering engine for PhotoDemon (an open-source photo editor). Rather than post the entirety of the code here, let me refer you to the relevant page on GitHub. The black and white conversion engine starts at line 350. If you have any questions about the code – which covers all the algorithms described on this page – please let me know and I’ll post additional explanations.

That engine works by allowing you to specify any dithering matrix in advance, just like the ones on this page. Then you hand that matrix over to the dithering engine and it takes care of the rest.

The engine is designed around monochrome conversion, but it could easily be modified to work on color palettes as well. The biggest difference with a color palette is that you must track separate errors for red, green, and blue, rather than a single luminance error. Otherwise, all the math is identical.

 

This site - and its many free downloads - are 100% funded by donations. Please consider a small contribution to fund server costs and to help me support my family. Even $1.00 helps. Thank you!

Announcing PhotoDemon 5.2 – Selections, HSL, Rotation, HDR, and More

Summary

PhotoDemon v5.2 is now available. New features include selection tools, arbitrary rotation, HSL adjustments, CMYK support, new user preferences, multiple monitor support, and more. Download the update here.

PhotoDemon 5.2
Version 5.2 includes many new tools and features, including PhotoDemon’s first on-canvas tool – “Selections”.

New Feature: Selection Tool

Selections have been one of the top-requested PhotoDemon features since it first released, so I’m glad to finally be able to offer them. A lot of work went into making selections as user-friendly and powerful as possible.

Three render modes are provided. On-canvas resizing and moving are fully supported, as are adjustments by textbox (see screenshot above). Everything in the Color and Filter menus will operate on a selection if available, as well as the Edit -> Copy command.

(Note: as of this v5.2, selections are not yet tied into Undo/Redo, and selections will not be recorded as part of a Macro. These features will be added in the next release.)

New Feature: Crop to Selection

Finally!

New Feature: HSL Adjustments

PhotoShop and GIMP users should be happy about this tool.

New Feature: Arbitrary (Free) Rotation

Arbitrary rotation comes courtesy of the FreeImage library. A 3-shear method is used: very fast, very high quality.

New Feature: CMY/K Rechanneling

Both CMY and CMYK rechanneling are now available.

New Feature: Sepia (W3C formula)

Here’s the sepia version of the photo from the Rechannel screenshot. I still prefer PhotoDemon’s “Antique” filter for most photos, but this sepia formula (from the W3C spec) provides a pleasant, flat alternative.

New Feature: Preferences Dialog (rewritten from scratch)

Preferences, preferences, and more preferences. The old Preferences dialog was pretty lame, so it was due for an overhaul. Tons of new settings have been added, and they are now organized by category.

New preferences include:

Interface:

  • Render drop shadows between images and canvas (similar to Paint.NET)
  • Full or compact file paths for image windows and Recent File shortcuts
  • Improved font rendering on Vista, Windows 7, and Windows 8 (via Segoe UI)
  • Remember the main window’s location between sessions

Loading and Saving:

  • Tone map imported HDR and RAW images
  • Options for importing all frames or pages of multi-image files (animated GIFs, multipage TIFFs)

Tools:

  • Automatically clear selections after “Crop to Selection” is used

Transparency handling:

  • Pick your own transparency checkerboard colors
  • Pick from three transparency checkerboard sizes (4×4, 8×8, 16×16)
  • Allow PhotoDemon to automatically remove empty alpha channels from imported images

All preferences from v5.0 remain present, and there is now an option to reset all preferences to their default state – so experiment away!

New Feature: Recent File Previews (Vista, Windows 7, Windows 8 only)

Now that recent file previews are available, I honestly can’t use any software that *doesn’t* provide the feature. It makes locating the right file significantly easier – especially with digital camera filenames like IMG_0366.jpg.

New Feature: Multi-Image File Support (animated GIFs, multipage TIFFs)

PhotoDemon will now recognize when you try to load image files that are actually composed of multiple images. You are given the option to import every image, or just the first one (which is what most other software does). The default behavior can be changed in the Edit -> Preferences menu.

New Feature: Waaaay better transparency handling, including adding/removing alpha channels

It’s hard to overstate how much better transparency support is in v5.2 compared to v5.0. Images with alpha-channels are now rendered as alpha in all viewport, filter, and tool screens. When printing, saving as 24bpp, or copying to the clipboard, transparent images are automatically composited against a white background. As mentioned previously, user preferences have been added for transparency checkerboard color and sizes.

PhotoDemon also allows you to add or remove alpha channels entirely. Here’s an example of an image with an alpha channel, and the associated “Image Mode” setting:

Note how the top-level “Mode” icon has changed to match the current mode – this saves you from having to go to the sub-menu to check. I’m a big fan of small touches like this.

And here it is again, after clicking the “Mode -> Photo (RGB | 24bpp | no transparency)” option:

No more alpha!

Finally, PhotoDemon now validates all incoming alpha channels. If an image has a blank or irrelevant alpha channel, PhotoDemon will automatically remove it for you. This frees up RAM, improves performance, and leads to a much smaller file size upon saving. (Note: this feature can be disabled from the Edit -> Preferences menu if you want to maintain blank alpha channels for some reason.)

New Feature: Custom “Confirm Unsaved Image(s)” Prompt

This is the new “unsaved images” prompt in PhotoDemon. A preview is now provided – again, very important for digital photos with obscure names – and the options have been reworked to make them as crystal-clear as possible. Also handy is the “Repeat this action for all unsaved images” option, which will either save or not save all unsaved images per your request.

Improved Feature: Edge Detection

Edge detection now allows for on-black or on-white processing. Generally speaking, on-white is used for artistic purposes, while on-black is used for technical and research ones. (Thanks to Yvonne Strahovski, who appears in the sample image above.)

New Feature: Thermograph Filter

This Wikipedia article describes thermography in great detail. PhotoDemon’s thermography filter works by correlating luminance with heat, and analyzing the image accordingly. Here’s a sample, using a picture of the lovely Alison Brie, of Mad Men and Community fame:

New Feature: JPEG 2000 (JP2/J2K), Industrial Light and Magic (EXR), High-Dynamic Range (HDR) and Digital Fax (G3) image support

PhotoDemon now supports importing the four image types mentioned above, and it also supports JPEG 2000 exporting.

Other New and Improved Features:

  • Much faster resize operations, thanks to an updated FreeImage library (v3.15.4)
  • Multiple monitor support during screen captures (File -> Import -> Screen Capture)
  • Many miscellaneous interface improvements, including generally larger command buttons, text boxes, labels, and more uniform form layouts.
  • Many new and improved menu icons.
  • Heavily optimized viewport rendering. PhotoDemon now uses a triple-buffer rendering pipeline to speed up actions like zooming, scrolling, and using on-canvas tools like the new Selection Tool. Even when working with 32bpp images, all actions render in real-time.
  • Bilinear interpolation is now used during Isometric Conversion. This results in a much higher-quality transform. Hard edges are still left along the image border to make mask generation easy for game designers.
  • Vastly improved image previewing when importing from VB binary files.
  • Better text validation throughout the software. Invalid values are now handled much more elegantly.
  • More accelerator hotkey support, including changes to match Windows standards (such as Ctrl+Y for Redo, instead of the previous Ctrl+Alt+Z).
  • Update checks are now performed every ten days (instead of every time the program is run).
  • All extra program data – including plugins, preferences, saved filters and macros – have been moved to a single /Data subfolder. If you run PhotoDemon on your desktop, this should make things much cleaner for you.
  • PhotoDemon’s current and max memory usage is now displayed in the Preferences -> Advanced panel.
  • Tons of miscellaneous bug fixes, tweaks, and optimizations. For a full list of changes, visit https://github.com/tannerhelland/PhotoDemon/commits/master

In Conclusion…

Not bad for two months work, eh? I hope you enjoy all the new features in 5.2., and please remember to donate if you find the software useful!

Announcing PhotoDemon 5.2 Beta 1 – Testers Needed!

PhotoDemon 5.2 beta 1 screenshot
It’s time for another PhotoDemon update. This update includes many new tools, including PhotoDemon’s first on-canvas tool – “Selections”.
  1. Summary
  2. Download
  3. List of what’s new and improved

Summary

PhotoDemon 5.2 is nearing completion, and I need help testing it. Version 5.2 provides a bunch of new features, including selections, cropping, HSL adjustment, CMY/CMYK rechanneling, a new Sepia filter (based off the W3C standard), an overhauled preferences engine and interface, and more. Please download the beta and help me make sure everything is working properly.

Download

The PhotoDemon 5.2 beta comes in two flavors:

Remember – if you are an advanced user, you can always download the most recent development build of PhotoDemon’s source code from its GitHub page.

PhotoDemon is funded by donations from users like you.
Please consider a small donation to fund development and to help me support my family.
Even $1.00 helps. Thank you!

List of what’s new and improved in v5.2 (so far)

  • Selection tool! It’s a hell of a tool, and a lot of work went into making it as user-friendly and powerful as possible. Three render modes are provided. On-canvas resizing and moving are fully supported as well. Everything in the Color and Filter menus will operate on a selection if available, as well as the Edit -> Copy command. (Note: as of this beta, selections are not yet tied into Undo/Redo, and selections will not be recorded as part of a Macro.)
  • PhotoDemon Selection Tool
    Here’s an example of the selection tool in action. Note that the HSL adjustment tool is only operating on the selected area.
  • Image cropping is now possible via the Crop-to-Selection option (in the Image menu).
  • New HSL adjustment tool. (See above screenshot for sample.)
  • New Rechannel tool. Live previews, CMY, and CMYK color spaces were added.
  • PhotoDemon’s new and improved Rechannel tool.
  • New Sepia filter based off the official W3C formula (available here). I still prefer PhotoDemon’s “Filters -> Antique” effect, but felt it was worthwhile to make both available.
  • Here’s the sepia version of the photo from the Rechannel screenshot. I took this photo during a hike up Spanish Fork Canyon several weeks ago; the fall colors were stunning.
  • Vast improvements to PhotoDemon’s support for images with transparency. Images with alpha-channels will now be rendered as alpha in all filter and tool screens. When printing, saving as 24bpp, or copying to the clipboard, the image will be composited against a white background. User preferences were also added for transparency checkerboard color and sizes.
  • All-new User Preferences dialog. Many new options were added, and the Preferences interface is now sorted by category.
  • Interface-related options in the new Preferences dialog.
    As another example, here are the afore-mentioned transparency handling options.
  • Improved font rendering is now available for users on Windows Vista, Windows 7, and Windows 8.
  • A drop-shadow can now be rendered between the image and the canvas (similar to Paint.NET).
  • PhotoDemon is now capable of remembering its window size and position between sessions.
  • Multiple monitors are now supported by the Import -> Screen Capture tool.
  • Many miscellaneous interface improvements. Additionally, I am testing a new layout in the Color -> Grayscale tool. This layout style is intended to help users make sense of PhotoDemon’s many options. Let me know what you think, because if this style is popular, I will redo the other tool dialogs to match.
  • Heavily optimized viewport rendering. PhotoDemon now uses a triple-buffer rendering pipeline to speed up actions like zooming, scrolling, and using on-canvas tools like the new Selection Tool. Even when working with 32bpp images, all actions should render in real-time on any modern system.
  • Bilinear interpolation is now used in “Convert to Isometric Image”. This results in a much higher-quality transform. Hard edges are still left along the image border to make mask generation easy for game designers.
  • Many bug fixes and miscellaneous improvements. For complete details, please visit the commit log at https://github.com/tannerhelland/PhotoDemon/commits/master