Sharpening using Image Magick

Summary: an explanation of Image Magick’s unsharp masking parameters, and how the filter compares to the unsharp mask filters in Photoshop and the GIMP.

Introduction

Image Magick is a set of software tools for processing digital images. The package is open-source, freely-available and may be freely distributed. It is a particularly powerful and flexible package for building custom image processing scripts because the command-line tools can be called from a script, or as library routines from special scripting versions of Perl, Python, and other high level programming languages. Many people, myself included, have scratched our collective heads trying to figure out Image Magick’s unsharp mask operator. The command has extremely sparsely documented parameters and (possibly) poor choices for default values, leading to a lot of frustration and confusion on the part of users of the package. This page is an attempt to shed some light on the parameters and operation of Image Magick’s unsharp mask and to compare it to a couple of other well-used unsharp mask filters in Photoshop and The GIMP.

If you need an introduction to the general concept of unsharp masking, you should look elsewhere for additional tutorials on the subject; I only briefly review the concepts in here. This tutorial is mostly for those who already understand the basics of unsharp masking and want to understand how to manipulate and interpret the parameters of the operator in Image Magick. I.M. does a very nice job with unsharp masking once you understand the parameters and how to use them effectively.

Invoking the unsharp mask

Most users will use the unsharp mask filter in the convert command. The option to invoke an unsharp mask looks like this

-unsharp <radius>{x<sigma>}{+<amount>}{+<threshold>}

That is, a radius is expected (or a leading “x” if omitted), followed by optional parameters sigma, amount and threshold. [Note that there is also a -sharpen option to convert. This is not an alias for -unsharp, and (I'm pretty sure) employs a different sharpening routine internally.]

A typical call to convert might look something like the following:

$ convert ... -unsharp 1.5x1.2+1.0+0.10 <input file> <output file>

The “-unsharp” option should usually come last (after other options), since in Image Magick operations are carried out in the order that they appear on the command line, and sharpening is usually the last step that you want to take before image output. Missing parameters to the unsharp mask will take on default values, which may not be the ones you want. I strongly recommend that you always specify all four parameters to prevent ambiguity.

Notes on parameters and operation

The radius parameter

The radius parameter specifies (official documentation)

“the radius of the Gaussian, in pixels, not counting the center pixel”

Unsharp masking, like many other image-processing filters, is a convolution kernel operation. The filter processes the image pixel by pixel. For each pixel it examines a block of pixels surrounding it (the kernel) and does some calculations on them to render the output pixel value. The radius parameter determines which pixels surrounding the center pixel will be considered in the convolution kernel: (think of a circle) the larger the radius, the more pixels that will need to be processed for each individual pixel.

Image Magick’s radius is similar to the same parameter in Photoshop, GIMP and other image editors. In practical terms, it affects the size of the “halos” created to increase contrast along the edges in the image, increasing acutance and thus apparent sharpness.

How do you know how big of a radius to use? It depends on your output target resolution, for one thing. It also depends on your personal preferences, as well as the specific needs of the image at hand. As far as the resolution issue goes, the GIMP User Manual recommends that unsharp mask radius be set as follows:

    radius = (output ppi / 30) * 0.2

Which is very similar to another commonly found rule of thumb:

    radius = output ppi / 150

So for a monitor with 72 PPI resolution, you’d use a radius of approximately 0.5; if your targeting a printer at 300 PPI you’d use a value of 2.0. Use these as a starting point; different images have different sharpening requirements, and individual preference is also a consideration. [Aside: there are a few postings around the net (including some referenced in this article) that suggest that Image Magick accepts, but does not honor, fractional radii; that is, if you specify a radius of 0.5 or 1.2 it is rounded, or defaults to an integer, or is silently ignored, etc. This is not true, at least as of version 5.4.7, which is the one that I am using as I write this article. You can easily see for yourself by doing something like the following:

$ convert -unsharp 1.2x1.2+5+0 test.tif testo1.tif
$ convert -unsharp 1.4x1.4+5+0 test.tif testo2.tif
$ composite -compose difference test01.tif testo2.tif diff.tif
$ display diff.tif

you can also load them into the GIMP or Photoshop into different layers and change the blend mode to "Difference"; the resulting image is not black (you may need to look closely for a 0.2 difference in radius). No, this mistaken impression likely comes from the fact that there is a relationship between the radius and sigma parameters, and if you do not specify sigma properly in relation to the radius, the radius may indeed be changed, or at least not work as expected. Read on for more on this.]

Please note that the default radius (if you do not specify anything) is 0, a special value which tells the unsharp mask algorithm to “select an appropriate value for the radius”!.

The sigma parameter

The sigma parameter specifies (official documentation)

“the standard deviation of the Gaussian, in pixels”

This is the most confusing parameter of the four, probably because it is “invisible” in other implementations of unsharp masking, and it is most sparsely documented. The best explanation I have found for it came from a google search that unearthed an archived mailing list thread which had the following snippet:

>Comparing the results of
>
>    convert -unsharp 1.2x1+4+0 test test1.2x1+4+0
>
>and
>
>    convert -unsharp 30x1+4+0 test test30x1+4+0
>
>results in no significant differences but the latter takes approx. 50 times
>longer to complete.

That is not surprising.  A radius of 30 involves on the order of 61x61
input pixels in the convolution of each output pixel.  A radius of 1.2
involves 3x3 or 5x5 pixels.

>Please can anybody give me any hints, what 'sigma' means?

It describes the relative weight of pixels as a function of their distance
from the center of the convolution kernel.  For small sigma, the outer
pixels have little weight.

Another important clue comes from the documentation for the -unsharp option to convert (emphasis mine):

The -unsharp option sharpens an image. We convolve the image with a Gaussian operator of the given radius and standard deviation (sigma). For reasonable results, radius should be larger than sigma. Use a radius of 0 to have the method select a suitable radius.

Combining the two clues provides some good insight: sigma is a parameter that gives you some control over how the strength (given by the amount parameter) of the sharpening is “graduated” or lessened as you radiate away from a given pixel at the center of the convolution matrix to the limit defined by the radius. My testing confirms this inferred conclusion, namely that a bigger sigma causes more pronounced sharpening for a given radius. That is why the poster in the mailing list question (above) did not see any significant difference in the sharpening even though he was using an amount of 400% (!!) and a threshold of 0%; with a sigma of only 1.0, the strength of the filter falls off too rapidly to be noticed despite the large difference in radius between the two invocations. This is also why the man page says “for reasonable results, radius should be larger than sigma”; if it is not, then the sigma parameter does not have a graduated effect, as designed, to “soften” the halos toward their edges; instead it simply applies the amount evenly to the edge of the radius (which may be what you want in some circumstances). A general rule of thumb for choosing sigma might be:

if radius < 1, then sigma = radius
else sigma = sqrt(radius)

Summary: choose your radius first, then choose a sigma smaller than or equal to that. Experimentation will yield the best results. Please note that the default sigma (if you do not specify anything) is 1.0. This is the main culprit for why most people don’t see as much effect with Image Magick’s unsharp mask operator as they do with other implementations of unsharp mask if they are using a larger radius: unless you bump up this parameter you are not getting the full benefit of the larger radius!

[Aside: you might be wondering what happens if sigma is specifed larger than the radius. The answer, as the documentation states, is that the result may not be "reasonable". In my testing, the usual result is that the sharpening is extended at the specified amount to the edge of the specified radius, and larger values of sigma have little if any effect. In some cases (e.g. for radius < 0) specifying a larger sigma increased the effective radius (e.g. to 1); this may be the result of a "sanity check" on the parameters in the code. In any case, keep in mind that the algorithm is designed for sigma to be less than or equal to the radius, and results may be unexpected if used otherwise.]

The amount parameter

The amount parameter specifies (official documentation)

“the percentage of the difference between the original and the blur image that is added back into the original”

The amount parameter can be thought of as the “strength” of the sharpening: higher values increase contrast in the halos more than lower ones. Very large values may cause highlights on halos to blow out or shadows on them to block up. If this happens, consider using a lower amount with a higher radius instead.

amount should be specified as a decimal value indicating the percentage. So, for example, if in Photoshop you would use an amount of 170 (170%), in Image Magick you would use 1.7.

Please note that the default amount (if you do not specify anything) is 1.0 (i.e. 100%).

The threshold parameter

The threshold parameter specifies (official documentation)

“as a fraction of MaxRGB, needed to apply the difference amount”

The threshold specifies a minimum amount of difference between the center pixel vs. sourrounding pixels in the convolution kernel necessary to apply the local contrast enhancement. Increasing this value causes the algorithm to become less sensitive to differences that may define edges. Specifying a positive threshold is often used to avoid sharpening smooth areas that may contain noise (e.g. an area of blue sky). If you have a noisy image, strongly consider raising the threshold, or using some kind of smart sharpening technique instead.

The threshold parameter should be specified as a decimal value indicating this percentage. This is different than GIMP or Photoshop, which both specify the threshold in actual pixel levels between 0 and the maximum (for 8-bit images, 255).

Please note that the default threshold (if you do not specify anything) is 0.05 (i.e. 5%; this corresponds to a threshold of .05 * 255 = 12-13 in Photoshop). Photoshop uses a default threshold of 0 (i.e. no threshold) and the unsharp masking is applied evenly throughout the image. If that is what you want you will need to specify a 0.0 value for Image Magick’s threshold. This is undoubtedly another source of confusion regarding Image Magick’s sharpening algorithm.

Summary of differences in various unsharp mask filters

Program Radius Sigma Amount Threshold Notes
Photoshop specified in pixels N/A specified as % (% notation) specified as difference in pixel values (as integer)  
GIMP specified in pixels N/A specified as % (decimal notation) specified as difference in pixel values (as integer) Same results as Photoshop for same parameters.
Image Magick specified in pixels specified in pixels specified as % (decimal notation) specified as % difference in pixel values (decimal notation) Lower strength effect with same amount than with
GIMP or Photoshop, e.g. GIMP (r=4, a=0.75, t=0) seems equivalent to
IM (r=s=4, a=1.0, t=0). GIMP/Photoshop are thus quicker to blow
highlights on edges.

Examples

Program Radius Sigma Amount Threshold Notes
Photoshop 1.5 N/A 170 5  
GIMP 1.5 N/A 1.7 5  
Image Magick 1.5 1-1.5 1.7 0.02  

References

23 thoughts on “Sharpening using Image Magick

  1. Thanks a lot for providing this information. Until now I had been using Imagemagick with completely wrong parameters. Because I didn’t know what they actually meant (you’re right, the documentation on this is not clear), and with the evidently wrong parameters I got something of usable results. Until I upgraded to a newer version of IM, and suddenly the pictures remained muddy and fuzzy. When changing some of the (still evidently) wrong parameters, I was able again to get usuable results, but small deviations would either do nothing or ruin the picture. Thanks to your explanation I now have a good starting point again and I am able to enhance the pictures in a controlled way applying as much or as little sharpening as required.
    jlinkels

  2. Hello! an THANK YOU REALLY MUCH! :-D

    I had such hard time triying to understand this, and so good article are You made.

    I found it looking for info about using Digikam and Kipi batch plugin filter settings and found this, so I almost sure that there are the same settings for the unsharp mask option of such plugin because I believe that is also based on ImageMagick code.

    I wonder now if the simple sharp plugin that have ONLY TWO options that are radius and deviation use the same definition for their options ( I suppose this because some info in this link: http://urchin.earth.li/~twic/The_ImageMagick_Sharpen_Operation.html
    I hope that thsi could not bother too much but I would like to copy the info since may save this because in the web sometimes the info is lost.

    PLEASE, PLEASE excuse me if the the following bother and in this case please edit my messagu so it could be accepted for your page.

    Thanks again and best regards from Costa Rica!

  3. I came across this article while trying to figure out how to automate my (8-bit) by-hand Gimp workflow uisng (16-bit) ImageMagick. After a lot of tests, it seems to me that your analysis is a good explanation of the ideas behind the ImageMagick unsharp operator, but some of your comparisons with Gimp and Photoshop are misleading.

    In particular, the radius in ImageMagick does not behave like the radius in Gimp or Photoshop. In ImageMagick, radius simply seems to provide an outer bound for the operator’s calculations, whereas in Gimp or Photoshop it determines the visual effect of the filter.

    In ImageMagick, as long as the radius is sufficiently large (larger than sigma), there is no visible difference in output for different radius values. Compare “convert -unsharp 2×1.5+1.5+0.0″ with “convert -unsharp 200×1.5+1.5+0.0″. Now go to Gimp and compare radius=2;amount=1.5;threshold=0 with radius=200;amount=1.5;threshold=0. Very different results.

    A empirical comparison of the behaviors of ImageMagick and Gimp shows that ImageMagick’s sigma is the real equivalent to Gimp’s radius. The actual relationship is imSigma == gimpRadius + 1.0.

    Based on my tests, the following shell script will closely reproduce Gimp’s results in ImageMagick:

    #!/bin/sh
    # Usage: unsharp.sh
    convert “$1″ -unsharp 0x$(dc -e “4k 1 $2 +p”)+$3+$(dc -e “4k $4 255.0 /p”) “$5″

    I.e. Gimp’s unsharp at radius=2;amount=1.5;threshold=5 would result in
    convert infile.tif -unsharp 0×3+1.5+0.0196 outfile.tif

    Note that I let ImageMagick decide on radius here. To match Gimp’s results perfectly, one would have to read the source code of both programs to figure out a better value for radius.

  4. Thanks for this awesome technical note! Very well researched, experimented and compiled! Hope the IM homepage does the same for its tools :)

  5. A great article, thanks.

    I also found a page on the ImageMagick website, http://www.imagemagick.org/Usage/convolve/#sharpen. The radius and sigma values are explained in the Blurring section, with some good examples.

    Basically, they suggest always setting radius to 0, so IM picks the best value based on sigma. So to match other tools just choose a sigma of maybe half the intended radius, or as you suggest, sqrt.

  6. Thanks for this excellent Image Magick topic. This is the best explaination of how these parameters work that I’ve seen. Excellent work!

  7. Pingback: Urban Influence

  8. Pingback: Automated image sharpening- Nate Miller

  9. … there are a few postings around the net (including some referenced in this article) that suggest that Image Magick accepts, but does not honor, fractional radii; that is, if you specify a radius of 0.5 or 1.2 it is rounded … It is true, code for this looks like width = 2.0*ceil(radius)+1.0 where ceil rounds up “radius” to nearest integer bigger than “radius”, so if radius = 1.2 it is rounds up to 2 and “width” equals 5, and then through out the code only “width” is used.
    ————————————————-
    $ convert -unsharp 1.2×1.2+5+0 test.tif testo1.tif
    $ convert -unsharp 1.4×1.4+5+0 test.tif testo2.tif
    $ composite -compose difference test01.tif testo2.tif diff.tif
    $ display diff.tif
    ————————————————-
    should be:
    $ convert -unsharp 1.2×1.2+5+0 test.tif testo1.tif
    $ convert -unsharp 1.4×1.2+5+0 test.tif testo2.tif
    $ composite -compose difference test01.tif testo2.tif diff.tif
    $ display diff.tif

  10. Thanks for this very informative article. One query I have – in the formula for calculating the required radius (or is it sigma?) it seems to me that the ppi number should come from the photo, rather than the display device. For viewing a typical 12MP (4000×3000) photo on a 19 inch monitor this would be about 250ppi rather than the approx 85ppi of the device itself. So a radius/sigma around 1.5 or 2.0 would be suitable. In my experience a sigma in this range (and letting Imagemagick set the radius) gives an OK result.

    Apologies if I am misinterpreting your use of the ppi formula.

    • Nigel,
      It’s pretty standard advice to sharpen for the output. Take a scan, for example. One typically does some sharpening as the last step after resampling for some particular output (say printing). If you were going to print at 240 dpi, then the radius might be 240/150 = 1.6. The density of the print determines how big your sharpening halos need to be to be noticeable/effective. If you are showing your image on a 19in monitor with 1680×1050 resolution, then a good deal of your image will be thrown away–the image will be down sampled for display to around 1400×1050. For best results, down sample it yourself to this dimension, and finish it with an unsharpen operation. A 19in monitor with that resolution will be somewhere in the neighborhood of 16.56in (w) x 9.31in (h) (Pythagorean theorem, ymmv according to pixel pitch, cheat calculator here: http://cubic.bioc.columbia.edu/kernytsky/tools/aspect/aspect.php). The ppi of this output device is around 1400/16.56 = 85ppi, therefore I would use a radius of 85/150 = ~0.5. This concurs with the remarks in the main article. The ppi of monitors tends to go up as monitor size increases, with the ppi somewhere between 72-140 ppi. A radius of 0.5 is a pretty standard/safe choice for any monitor.

      Now, all that said, the above remarks assume that the image has had correct input sharpening applied to it. If you are not sharpening your images on input (e.g. a RAW file) or you are letting some image display program down sample it for display (without additional sharpening, because downsampling almost always requires additional sharpening), then you may need to increase the sharpening radius to account for the lack of two-pass (input/output) sharpening.

  11. ***
    *** ***
    ** **
    **
    ** **
    *** ***
    **** ****
    ***** ***** – extends to infinity on both sides ->

    The unsharp mask filter uses a gaussian blur filter internally. That means the image gets convolved with a gaussian distribution with a standard deviation of sigma. The gaussian distribution has its peak at the center, falling of in an s-curve to boths sides, but never reaching zero. Sigma tells you how broad the distribution is and thus, the amount of blur to be applied. But as the gaussian distribution extends from -infinity to +infinity you cannot calculate the convolution perfectly. This is where the radius parameter comes into play. The radius tells you, how big your kernel for the convolution should be and how much of the gaussian distribution function is actually used. Setting this to something like 2*sigma or higher should give you a reasonably good approximation. Radius is rounded to the next integer, because the kernel used for convolution is made up of whole pixels (as is you digitized image). A kernel size of 0.5 does not make sense.

    • Dirk,
      Thanks for a lucid and erudite explanation. It agrees mostly with what I understand about the algorithm. However, I’m still curious about the fractional radii. I understand that ultimately the number of pixels chosen is an integral number, but I’m wondering how early the value is rounded or truncated, and whether this is before or after the calculation of the bounding area of the convolution. I guess I really should grab the source code and take a look to understand this to my satisfaction.

  12. Ups, seems ascii art picture of a gaussian distribution collapsed to a bunch of stars.
    But you can simply google for “gaussian distribution”.

  13. thanks for article.

    sigma, however, is the most important parameter, as it defines the weight of each surrounding pixel. such weight is non zero for ALL pixels, but actually for pixels too distintant from the center, their value means almost nothing and counting them is just a loss of time. that’s what the parameter radous is for. it specifies a boundary to cut off and do not count pixels values. setting this parameter to zero is sesible, as imagemagick will take a value -according to sigma- which is studied to be small enough to reduce processing while obtaining very good (quasi optimal) results.

  14. quoting wikipedia:

    In theory, the Gaussian function at every point on the image will be non-zero, meaning that the entire image would need to be included in the calculations for each pixel. In practice, when computing a discrete approximation of the Gaussian function, pixels at a distance of more than 3σ are small enough to be considered effectively zero. Thus contributions from pixels outside that range can be ignored. Typically, an image processing program need only calculate a matrix with dimensions ceil(6σ) x ceil(6σ) to ensure a result sufficiently close to that obtained by the entire gaussian distribution.

    apart from that, what do you mean by “screen resolution” and why is it 72dpi? that’s pointless to consider. you may consider a formula to cumpute sharpen radius for printed pictures based on resolution, but, when it comes to screen display, resolution means nothing. it’s the zoom level which matters. for 100% zoom (that is, one picture pixel to one screen pixel), my prefered radius is 0.5. from that mark, simply multiply (i.e., for 50% zoom, use half radius). still, choosing radius depending on resolution is not a wise choise. it is better off set according to visual results.

  15. some lame idiot who couldnt even get IM unsharp right removed my valuable comments
    please fix your article. the radius parameter has nothing to do with ps radius.
    you don’t even seem to know clearly how convolution works at all. it’s sigma which gives weight of surrounding pixels, as it is the parameter of gauss mapping. radius is just used to discard values to speed up calculation. setting it to the same value of sigma makes little sense.

  16. I agree with the above commenter, the IM radius should not be used at all, best leave it zero. The value that in PS is called Radius, is the value in IM called Sigma. So to sharpen with radius of 0.55, amount 0.45 and threshold of 0.008 do this:

    -unsharp 0×0.55+0.55+0.008

    To resize and sharpen to 950px wide images, I use this batch command with superb results:

    for file in *.jpg; do convert $file -resize 950x -unsharp 0×0.55+0.55+0.008 -quality 95% small-$file; done

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 101 other followers

%d bloggers like this: