ImageMagick v6 Examples --
Distorting Images

Index
ImageMagick Examples Preface and Index
General Distortion Techniques
Affine Matrixes, and DIY Distortions (separate sub-directory)
Generalized Distortion Operator

Having looked at the simple set of builtin image wraping and distortion operators IM has provided since its early days, here we go deeper and look at the internal mechnanics and more complex mathematical distortions of images.

From this deeper understanding, we then looks at a more generalize image distortion operator. From complex rotation, scaling and shearing, to perspective or 3D distortions, to warping to and from circular arcs, camera lens distortions, and finally to more general morph-like distortions.


General Distortion Techniques

Now that we have been introduce to the simple distortion operators that IM provides, lets take a step back and look at the nitty-gritty, and see how image distortions actually work, and how you can improve the way you use them.

Later we'll go forward to much more complex ways of distortion images, including methods that are not directly built into ImageMagick.

There are only a few basic ways an image processor can distort images.

The Simple Distortion operators for example are achieved by Pixel Swapping. That is, individual pixels or even whole rows and columns of pixels are just swapped around to Flip, Roll, Transpose and even do Rectangular Rotates of images. No color changes are made, and the number of pixels remains the same.

The next method of distorting images is by Shifting or Shearing the columns and rows of pixels either horizontally or vertically, such as what IM does with Image Shearing and the Wave Distortion above. The shears in turn providing one method to Rotate Images by any given angle, in a manner that should be quite fast.

However pixel shifting methods are limited to those basic distortions. It can not scale an image to a different size for example. You also have very little control over the handling of areas in the resulting image that was not covered by the original source image. In the above mentioned functions IM just sets the missing areas to the current background color.

To be able to distort images in a much more general way you need to use a more general distortion technique known as Reverse Pixel Mapping. For example this method is used by the more complex Circular Distortions such as Imploding and Swirling images.

Forward of Direct Pixel Mapping

The first thing prople think of when attempting to distort an image is to just take each pixel in the source image and move it directly to its new location in the destination image.

In fact this is sort of what actually happens for Simple Distorts, Image Cropping and even for distortin Vector Images. Each pixel (or coordinate) is is just moved to its new position in the final image.

Unfortunatally this has problems when you try to do this for anything but a simple distortion. For example here I take a Enumerated Pixel list, and just change the location of each pixel, so as to rotating it to its new location.

  # Rotate by 17 degrees -- get the Sine and Cosine of this angle
  sin=`convert xc: -format "%[fx:sin( 17 *pi/180)]" info:`
  cos=`convert xc: -format "%[fx:cos( 17 *pi/180)]" info:`

  # For each Pixel, rotate that pixels coordinates
  convert koala.gif  txt:- |  sed '2,$ s/,/:/' |\
    gawk -F: 'NR == 1 { print }
              NR > 1 {  x = $1-32;    y = $2-32;
                        nx = int( c*x - s*y + 32 );
                        ny = int( s*x + c*y + 32 );
                        printf( "%d,%d: %s\n", nx, ny, $3 );
              }' s=$sin c=$cos - |\
      convert -background black  txt:-   koala_rotated_direct.gif
[IM Output]

The distortion is a simple rotation of just 17 degrees, but the results are not very nice at all.

First of all each new pixel location is a floating point value, but pixels can only exist in an interger grid, so the above simply junks the non-interger fraction.

The second problem is that the result is full of holes where no pixel landed. In fact for every hole in the image, you will probable also find two pixels landed on the same coordinate. That is you have a third problem of overlapping pixels, which pixel value should you use?

In other words the resulting image is incomplete, where each pixel could have multiple results or no results at all. This is a serious problem, and one that can not be easilly solved when forward mapping pixels from the source image directly to the destination image. Basically it just does not work very well in the general case.

Reverse Pixel Mapping

Rather than trying to map pixels into the final image, you map the coordinate of each pixel in the destination image to the correspending location in the source image, and lookup the color that pixel should contain. This is known as a Reverse Pixel Mapping and is what just about every image distortion program does.

As each and every destination image pixel is processed, we can be sure that every pixel in the destination gets one and only one color. So as long as we can figure out the 'source' location for each destination pixel, we can distort a source image to the destination image using any mathematical formula you can imagine.

[photo]

In Summary, a distortion mapping (reverse mapping) does the following.
For each pixel (I,J) in the destination or output image
   Map the I,J pixel position to a X,Y pixel position in the original image
   Look up the Color of the original image at position X,Y
       Using color interpolation, work out the appropriate color.
       Or the virtual-pixel setting, if it misses the actual source image.
   Set the destination images color for pixel I,J

Note that I used the variable names 'i,j' and 'x,y' in the above as these variables map into the variables name that you would typically use in the FX DIY Operator.

For example here I simulate the same 17 degree rotation I attempted before, but this time use the "-fx" operator to look up the nearest pixel to that location in the source image.

  # Rotate by 17 degrees -- get the Sine and Cosine of this angle
  sin=`convert xc: -format "%[fx:sin( 17 *pi/180)]" info:`
  cos=`convert xc: -format "%[fx:cos( 17 *pi/180)]" info:`

  convert -size 75x75 xc:       koala.gif  \
          -virtual-pixel Black  -interpolate NearestNeighbor \
          -fx 'ii = i - 32;   jj = j - 32;
               xx =  '$cos'*ii +'$sin'*jj + 32;
               yy = -'$sin'*ii +'$cos'*jj + 32;
               v.p{xx,yy}' \
          koala_rotated_fx.gif
[IM Output]

You can get more detail about the the above DIY example in the sub-section on DIY Distortion Mapping.

As you can see we no longer have 'holes' in our image as a color is looked up for each and every pixel in the destination. It still does not look very good, but that is a matter of adjusting exactly what color should be placed into each pixel.

That is Reverse Pixel Mapping does not generate either holes, or overlapping pixels. Each pixel has a properly defined color producing a complete image.


The distinction between forward and reverse mapping is important as most mathematical transformations are defined as forward mappings, mapping a single source (X,Y) position to a destination (I,J) position. And indeed a 'forward mapping' works well for vector graphics, and drawing lines where you can just map the ends of the line and draw it. This is especially true for any linear transformation, such as rotations, where lines remain straight. It is in fact what is done for all vector based languages such as such as postscript and SVG.

But for a general raster image, you must use a 'reverse mapping' to distort the image, so that you can be certain that you 'fill in' all the pixels of the destination image.

For example if you look the mathematics that was used to map the coordinates in the above two cases, you will find they look almost exactly the same. The reverse mapping of a 'rotate' is another 'rotate', just in the oppisite direction. If you look closely you will see that the 'sin' constant is negated to the forward mapped version, and that is enough to reverse the direction of rotation. This detail is important and critical.

The problem is not all forward mapping transforms, work well as a reversed transform. Some forward mappings in fact have no simple direct solutions.

On the other hand some image transformations work very well when directly used as a reverse mapping, where they would fail badly if used on forward mapped vector images.


As an FYI here is the faster equivelent to the above using a General Distortion, SRT method to do the same rotation of the image (see below). Again the color lookup is restricted to just the color of the closest pixel to the mapped position. This means that no new colors are added to the image (other than when we 'missed' the source image), but you also get some sever aliasing effects.

  convert koala.gif  -virtual-pixel Black  -interpolate NearestNeighbor \
          -filter point    -distort SRT 17    koala_rotated_srt.gif
[IM Output]

What's in a name?

During my study I found that there is no real clear naming of this image processing method. The actual algorithmic process is known a 'Reverse Pixel Mapping', while the use of mathematical equations is known as a 'Geometric Transformation'. If the distortion is controlled by the movement of various control points, it often known a 'Image Warping' or 'Rubber Sheeting'. The process of defining specific points, usually to find equivelent points between two or more images is known as 'Image Registration'.

Images can also be subdivided into smaller simpler units which are individually distorted using a technique called 'Gridding' (quadrilaterals) and 'Triangular Meshing' (triangles). By using small incremental distortions with blending of a start and end image you generate 'Image Morphs' such as you see in movies and music videos.

In the 3d modeling, and in 3d computer games, the same techniques are also used to give some type of colored pattern to flat and curved surfaces in a method known as 'Texture Mapping'. This can involve sub-dividing images into grids and meshes that approach a single pixel.

All the above are very closely related, and all basically involve the look up of a pixels color color based on mapping a final destination coordinate, to the source image. In other words Reverse Pixel Mapping. What term should be used... Take your pick.

For an alternative discussion of distortion transforms, see Leptonica, Affine Implementation and specifically its discussion of 'point-wise' method. The other method, 'sequential', is essentially how IM implements its Rotate and Shear distortion operators (see above).

Pixel Color Lookup

There are still a few problems with the above Reverse Pixel Mapping technique. First of all is that when mapping a pixel from a fixed integer position in the destination, you can end up with a non-integer position in the source image. That is a location that falls somewhere between the individual pixels on the source image. To determine what color should be returned a process called Interpolation is used to determine the final color for that real position by mixing the colors of the surrounding pixels.

Then you have the problem of what to do when the mapped position 'misses' the source image completely. What color should be returned is determined by the Virtual Pixel setting, which can let you pick a color such as, the color of the nearest edge of the source image, pretend the source image is infinitely tiled (or mirror tiled), use some specific color such as 'white', 'black', or 'transparent' or the user defined background color.

There is also the possibility that there is no mathematically valid coordinate for a specific destination position being mapped. For example the pixel looks into the 'sky' of a perspective 'plane' (See Viewing Distant Horizons), and thus does not even see the 'plane' in which the source image lies. This should have a completely different response to just a normal 'miss' of the source image, as it never even 'hits' the source image space. That is the pixel is completely invalid!

The Interpolation setting will also handle the case when a part of a distorted image becomes 'stretched out' so that a single source pixel becomes smeared over a large area of the destination image. However the opposite is not handled very well by a simple interpolation method. And that requires other techniques which we will look at below.

For example here we again rotate our koala, but this time use a 'Mesh' interpolation to select better colors for the color lookup.

  convert koala.gif  -virtual-pixel Black  -interpolate Mesh \
          -filter point    -distort SRT 17    koala_rotated_mesh.gif
[IM Output]

As you can see but using a simple merger of neighboring colors surroding the lookup point, you can greatly improve the look of the distorted image.

Super Sampling, Improved Results

If part of the source image gets compressed into a much smaller area, the destination pixel at that point can represent a large area of the source image. That is a large area of the source image will be compressed in to a single destination pixel. When this happens a simple Pixel Lookup will fail, as it only looks up the color at a single 'point' in the source image (using the surrounding pixel neighbourhood).

This means that when the source image becomes compressed in the distorted image, you start to get isolated pixels of color, that does not seem connected to the other surrounding pixels. You also get Moire effects and 'staircase' edging along sharp color chnages. Thin lines also start to look more like dotted lines, or could disappear entirely. All these effects are known collectivally as Aliasing Artifacts (see image resizing).

One solution to this to take a lot more color readings from the source image, so as to try to determine a more correct color for each pixel in the destination image. The simplest technique to do this is generally know as super-sampling, or over-sampling. See the Wikipedia Entry on Super-Sampling.

Instead of taking just one color (sample) from the source image for each destination pixel that is generated, a larger number of samples, are taken over whole 'area' which is covered by an output pixel.

By taking more samples from the source image for each destination pixel, the final color of the individual pixel will become a more accurate representation of distorted image at that point. The more color samples you make, the more accurate the final color will be, and the results produce a smoother more realistic look.

Remember this technique only really improves the look of the destination in areas where the source image becomes compressed by more than 50%. In areas where the distortion magnifies the source image, or keeps it about the same scale, a Interpolated Lookup of the source image look up will generally produce a good result with just one single lookup.

In Imploding Images warping example, I touched briefly on the simplest method of 'super-sampling'. Enlarging the size of the output image (in this case by enlarging in the input image), and then performing the distortion. Afterward resizing the image back to normal again.

For example...

  convert -size 94x94 xc:red -bordercolor white -border 3 \
          -virtual-pixel tile                -implode 4 \
          implode_tiled_box.gif
  convert -size 94x94 xc:red -bordercolor white -border 3 \
          -virtual-pixel tile  -resize 400%  -implode 4 -resize 25% \
          implode_tiled_ss.gif
[IM Output]
Normal Implosion of a Box Image
[IM Output]
Super Sampled Implosion

Of course rather than enlarging the image, you could either use a higher quality (larger) source image, or generate one during your previous processing steps.

This is especially useful when rotating text, which often has very fine detail that needs to be uniformly preserved to ensure a good high quality looking final image. For examples of this see the Polaroid Transform.

As of IM v6.4.2-6, the General Distortion Operator, can directly generate an enlarged output image, which you can scale (or resize) back down so as to merge and super-sample the resulting pixels. See distortion scale setting, as well as the next example.

This is only one method of super sampling, there are however many other variations on this method. Eventually these methods may be implemented more directly in ImageMagick, but for now simple enlargment and scaling of images work quite well, without any additional coding need.

One final word of warning. Super-sampling is limited by the number of samples that was used for each pixel in the final image, and thus the amount of scaling used in the final resize. This determines the final 'quality' of the distorted image. But by using larger scaling factors, the distorted image will of course be much much slower to generate, but have even higher quality. Unfortuanatally even this has limits.

In the extreme, super-sampling will not handle image distortions that involves infinities (such as in the center of an imploded image). In such cases a completely different technique is needed, such as one that is provided by Area Resampling (see below).

In summary, super-sampling can improve the look of images with only minor distortions, such as rotations, and shears. But it has limits to the types of distortions that it can improve.

Adaptive Sampling

The super-sampling technique can be expanded further. Rather than just using a fixed number of color lookups for each pixel, a check is made on either the distance between the lookups in the source image, or on the colors returned, to see if we should make more samples for that specific pixel.

That is the amount of super-sampling could be made responsive to needs of the distortion, without knowing anything about the specifics of the distortion itself. This is known as Adaptive Super-Sampling.

This technique is actually very common in Ray Tracers, where it is next to impossible to determine just how complex the resulting image is at specific points. In this case it is often restricted to the use of 'color differences' to determine when more samples are needed.

IM does not currently support adaptive super-sampling at this time. Though it is now quite possible to add alternative sampling methods into the General Distortion Operator (see below). It will require some functional rearrangement of the code, so may not be added anytime soon.

Area Resampling, for better Distortions

The best alternative to super-sampling methods is Area Re-sampling.

Rather than distorting a larger image and averaging the results by resizing, Or just taking and averaging more samples from the image, we actually determine exactly how many pixels from the source image should be merged together (based on the 'scale' of the distortion at that point) to generate each specific output pixel. That is figure out a rough 'area' within the source image, each output pixel represents, and merge (filter) all those pixels together.

In fact this is exactly what the ImageMagick Resize Operator (in reality a very specific type of image distortion) does to generate such good results. However for resize, you only need to calculate the scale and area needed to be sampled, once for the whole image.

When area re-sampling a distorted image, the area of pixel being generate covers will change with position. Some pixel may only need to merge a few source image colors, or even just one single interpolated color lookup, while another pixel elsewhere in the image, it may need to sample a very large number of pixels to generate the correct final color.

Also the area that a destination pixel represents in the source image, may not be a simple square or circle, but may actually be a very weird awkward highly distorted shape, according to the distortion being used. Calculating and handling such awkward shapes can be very time consuming, or near impossible to achieve.

[Diagram] For example here is a diagram showing how a round pixel in the final image needs to use the colors from a larger, elliptical area in the source image.

Using an elliptical area of the source image to calculate colors for each destination pixel, is a method known as Elliptical Weighted Average (EWA) Re-sampling, and was outlined in the PDF research paper "Fundamentals of Texture Mapping and Image Warping" by Paul Heckbert. This was then used to define the new Generalized Distortion Operator (see below).

The results of this algorithm is especially good for extreme scale reductions such as produced by perspective distortions. For example here are all three re-sampling methods for an infinitely tiled perspective image. See Viewing Distant Horizons below for details.

  # input image:  special checkerboard with a gold outline.
  convert -size 90x90 pattern:checkerboard -normalize -fill none \
          -stroke gold -strokewidth 3 -draw 'rectangle 0,0 89,89' \
          -fill red        -draw 'color 20,20 floodfill' \
          -fill lime       -draw 'color 40,70 floodfill' \
          -fill dodgerblue -draw 'color 70,40 floodfill' \
          checks.png

  # Using Interpolated Lookup  (Perspective View)
  convert checks.png -filter point -interpolate Bicubic \
          -virtual-pixel tile -mattecolor DodgerBlue \
          -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          horizon_tile_point.png

  # Using Grid Super Sampling
  convert checks.png  -filter point  -set option:distort:scale 10 \
          -virtual-pixel tile -mattecolor DodgerBlue \
          -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          -scale 10%    horizon_tile_super.png

  # Using Area Resampling (default)
  convert checks.png       -virtual-pixel tile -mattecolor DodgerBlue \
          -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          horizon_tile.png
[IM Output]
Check Image
==> [IM Output]
Interpolated
Lookup
[IM Output]
Super Sampling
x10
[IM Output]
Elliptical Weighted Area
(EWA) Resampling

The last image was generated using the default EWA settings of the Generalized Distortion Operator (see below). It took 4.6 seconds to generate. Which is rather reasonable.

The first image however is exactly the same, except that EWA resampling has been turned off by using a "-filter point" setting. This forces it to use Direct Interpolated Lookup for each pixel. As such this image was generated extremely fast in comparison (.51 seconds).

The middle image is like the first image but with the image being enlarged, and all the coordinate settings being multiplied by ten. That is more than 100 pixels were used to Super Sampling each destination pixel. It is quite fast to generate (1.2 seconds), and while it improves the quality of the image in general, that improvement is limited, and depends on how much super-sampling was provided. The ×10 used in the above example is very heavy, far exceeding what is typically used in super-sampling.

The biggest difference between super-sampling and area-resampling, is that the former only does a general improvement in quality over the whole image. As the distortion gets more sever it starts to break down showing Resizing Artifacts in the middle ground, and a line of server moire effects just before the horizon, when the 10 samples across per pixel matches the checker board pattern itself.

On the other hand area resampling concentrates more on the problem pixels closer to the horizon (where it spends most of its time), than on foreground pixels, removing the artifacts. For a simple distortions, it is usually a lot faster than super-sampling, but will always be slower than simple interpolated lookup.

A simple ellipse used by EWA, may not be perfect for all distortions. For example the "DePolar" distortion actually requires a curved circular arc for its ideal area resampling. Because of this you may better off using Super Sampling for more extreme distortions.


Generalized Distortion Operator

With the generation of these examples, the ensuing discussions in the IM Forums, and multiple requests from users for easier and faster ways to do perspective and other distortions, a new operator was added to IM v6.3.5-1 to allow us to more easily add new types of image distortions.

This General Distortion Operator is called "-distort", and you can see what distortion methods it has available on your IM version using "-list Distort".

  convert -list distort

The "-distort" operator takes two arguments, one of the distortion methods as given by the above, and a second string argument consisting of comma or space separated list of floating point values.


  convert ... -distort  {method}  "{list_of_floating_point_values}" ...

The number floating point values given is however highly dependant on the distortion method being used, and their meanings also depend not only on the method chosen, but also can depend on the exact number of control points or attributes needed for a particular method.

This is especially the case for the 'Scale_Rotate_Translate' (or 'SRT' for short) distortion, which really combines three separate 'Affine' distortions into a single distortion.

About half the distortion methods take a list of pixel coordinate or control points, and typically these are given as pairs of coordinates which control how the distortion is to modify the image. These pairs of coordinates (four values) are details more fully later in Distortions Using Control Points.

Distort Options

Best Fit +Distort Flag

By default "-distort" will usually distort the source image(s) into a image that is the same size as the original image. There are exceptions to this, such as the 'Arc' distortion (a polar mapping variant) where the input source image size really does not have much meaning in the distorted form of the image (see Arc Distortion below for details).

The other form of the operator, "+distort" (Added to IM v6.3.5-7), will attempt resize the distorted image so it will contain the whole of the input image (if posible), much like what the older Rotate and Shearing operators do.

However this particular 'mode' of operation also goes further and also sets the Virtual Canvas Offset (page) of the resulting image. This way if you later Flatten this image onto another image the position of your control points, and origin will still be in the correct position (according to the distort), even though the positions within the actual image itself many not be easilly calculated.

Also (depending on the distortion method) a "+distort" will attempt to take into account any existing Virtual Canvas Offset that may be present in the source image, in the distortion process.

As such you may need to make judicious use of the "+repage" attribute setting operator to clear or adjust that offset when using the 'best-fit' "+distort" form of the General Distortion Operator. See also Removing Canvas/Page Geometry.

The normal "-distort" will just ignore any existing offset present in the source image in terms of the distortion itself, but will copy that offset unchanged to the distorted image.

In Summary... Use "-distort" to have results mapped into an image of the same size. And use "+distort" to resize the output to best-fit the distorted image, BUT also use and generate Virtual Canvas Offsets (page attributes).

Verbose Distortion Summery

By setting "-verbose" before running "-distort" (use "+verbose" to turn off again), distort will output to the standard error channel, information on the algorithm and internal coefficients it calculates, and uses when distorting the given image, in the given way.

You can use this information to look at and understand how the distortion works and is applied. It is also a debugging tool we can use to figure out what is going wrong, and as part of the implementation process for new distortions.

For example here is a verbose SRT 'no-op' distort that will not change the distorted image at all...

  convert koala.gif -verbose -distort SRT 0 +verbose  koala_noop.gif
[IM Output] ==>
[IM Text]
==> [IM Output]

The above output shows two alternative distortion methods for the given distortion. One is an 'AffineProjection' distortion, while the other shows a DIY FX Operator alternative detailing exactly what the distortion is doing.

Both give information on the distortion process and can be used to extract extra information for use in future distortions of the same type.

Viewport, Where Distort Looks

-set 'option:distort:viewport' WxH+X+Y
Was added in IM v6.3.6-1, which will set the size and location of the generated image in the 'distorted space'. The viewport is however limited to whole integers. It does not enlarge the distorted image itself, just the location and area being viewed, much like a 'window' or 'viewport'.

This can be used to create a destination image of a specific size, or shift the view to to a specific offset in the distorted image space. You can think of it as a type of 'Viewport Crop' of whole infinite distorted space.

For example, here we crop the output to just the koala's head.

  convert koala.gif  -set option:distort:viewport 44x44+15+0 \
          -distort SRT 0  +repage koala_viewport.gif
[IM Output]

And here we expand the view, to look at the extra space surrounding the distorted image, and showing the effects of any Virtual Pixel effects that distort sees...

  convert koala.gif  -set option:distort:viewport 125x125-25-25 \
          -distort SRT 0  +repage koala_viewport_2.gif
[IM Output]

The final "+repage" in both the previous examples is needed to remove the virtual canvas offset that "-distort" will leave behind when a viewport is used.

This option is particularly useful with a 'Tile' virtual pixel setting, letting you generate tiled (and distorted tiled) images of any size.

Output Scaling, Supersampling

-set 'option:distort:scale' N
Was added in IM v6.4.2-6, as a general output image scaling factor. This enlarges the output image by the factor and thus the "-distort" will need to generate N2 more distorted lookup 'samples'. The number is usally an integer, but can be a floating point enlargement factor.

Note that many distortions also allow you to 'scale' the size of resulting distorted image, however the resulting image size would be unaffected by that scaling (unless a 'best-fit' "+distort" was used). This 'scale' setting however does not change the contents of resulting image at all, just enlarges or shrinks it.

This can be used for example with an appropriate 'viewport' to produce a image that you can easily "-resize" to a specific size, allowing you generate a controlled 'zoom' into the distorted image, without loss of quality.

For example, we 'zoom' in on the head of the koala.

  convert koala.gif -set option:distort:scale 2.5 \
          -set option:distort:viewport 44x44+15+0 \
          -distort SRT 0  +repage koala_zoom.gif
[IM Output]

Note that while the viewport was requested to be 44x44 pixels, the actual output image has been scaled to 110x110 pixels.

More commonly, it is used as a simple means of 'Super Sampling' (see above) the distortion operation. For this a integer 'super-sampling' scale factor is used, and after distorting the image the image is scaled back to its original size, to merge the extra samples together, and produce a higher quality result.


  convert koala.gif -filter point -set option:distort:scale 10 \
          -distort SRT 0  -scale 10%   koala_super.gif
[IM Output]

Also as 'Area Re-Sampling' is not needed when using 'Super Sampling' for improving image quality (it only slows it down), it is typically turned off by using a "-filter point" option (see next section).

Image Filters and Color Determination

As discussed above in Reversed Pixel Mapping above, each point in the resulting image is determined by first looking mapping that pixels location, to the source image, according to teh method chosen. However the final color of the pixel is not so simple is is effected by a number of factors.


First the source image is regarded as being surrounded by 'infinite' or 'virtual' surface, the color of which is defined by the current "-virtual-pixel" setting. For details and examples of the effect of this setting see Virtual Pixel examples.

This can be very useful for generating distorted, or even undistorted tile patterns of the source image. Technique for this are shown in the Virtual Pixel section itself (undistorted) and in Affine Tiling and View Distant Horizons below.

Sometimes however a distort does not even 'hit' this 'infinite' plane on which the image sits. This generally happens when you distort the image in a 3-dimentional space and the pixel 'vector' does not hit that 3-d surface. Basically the point become 'undefined' mathematically. In that case the color will be determined from the "-mattecolor" setting.

For example when you see 'sky' in a Perspective Distortion (for example see View Distant Horizons).

Once the pixel has mapped to a location on that 'virtual plane', what IM does next depends on if the image is being enlarged or compressed at that point. This is important as different techniques work better for the two different situations.

Now unlike a simple Image Resize operation, which can be regarded as a special simple type of image distortion, a distorted image could be compressed in one spot and enlarged at another. That for each pixel the image may be 'scaled' differently. This makes a simple one fit solution difficult.

For areas of enlargement... A simple direct pixel lookup is performed according to the "-interpolate" setting. See Pixel Interpolation for details. This is what is normally used for the various simple Image Warping Operators, as well as the DIY FX Operator.

This is unscaled color lookup of the 'single point' that resulted from the mapping. It only uses a limited (and fixed) 'pixel neighbourhood' interpolation, making it very FAST, and works very well for 'stretched out' images.

Resize in fact does the same thing but uses the current resize filter setting pegged to the filter's support limit. Rather than using a dedicated unscaled Interpolation filter.

However a unscaled interpolated lookup will produce aliasing effects when used in areas where an image becomes compressed. Especially in cases of very heavy compression such as seen ing View Distant Horizons. So...

For areas of compression... the Area Resampling method EWA (Elliptical Weighted Average) is used to average out a larger area of the source image to work out the right color of for this pixel.

By default a 'Gaussian' filter is used as this is the default for EWA. It also works will as a cylindrical filter, but this is also known to be rather blury. You can change this using the "-filter" setting (See Resize Filters for details). These are full resize filters, and as such you can also modify that filter using the special Expert Filter Options. The bluriness of the default gaussian filter for example can be controled by the Filter Blur Option, though this setting is not recomended for non-gaussian-like filters.

Distort vs Resize Image Filtering... is actually very similar in many aspects. However as noted above, Resize uses the same "-filter" setting for BOTH compression re-sampling and enlargement interpolation. You can not directly specify a "-interpolate" setting, though many basic interpolation filters are defined a resize filter (using a different naming scheme). However in resize, the scaling factors are constant over all the pixels of the destination image, and as such is fixed to either compression or enlaregment, so this is not regarded a big problem.

Also as Resize operators do not have rotational components it can use a faster two pass system where it first compresses an image in one direction, then the other. This also means the filter handling is simpler as only a simple 1-dimentional filter (weighted averaging) is needed to determine the color at each step.

Distort however has to also deal with rotations, skewing, or worse, as part of the image transformation. As such it can not limit itself to a simple 2 pass orthogonal filtering technique, but must use a 2-dimentional 'cylindrical filter', (elliptical actually) for its re-sampling process. That basically means that distances between the actual 'lookup' point and the real pixel color in the source image, are determined by a radial distance from the lookup point, rather than two separate X and Y passes.

As such the averaging of the colors will be different, even if no rotations or other distortions are involved, just so that it will come out reasonably correct when they are involved, or heavily skewed distortions are in effect.

A number of extreme distortion methods, such as Depolar, are so distorted that even 'elliptical' (EWA) resampling will fail. Other distortions such as Shepards make the calculations of the 'scaling factors' needed, difficult to calulate (though a later improvement could make it posible).

Because of this those methods turn off EWA re-sampling for compressed areas (see below), and only use direct Interpolated Lookup. Using a Super Sampling is recomended for these distorts.

To Turn Off Image Filters... you can use "-filter point", at which point only a fast and simple Interpolation Setting used to determine the color of each pixel, for both compression as well and enlargegment.

Or you can use "-interpolate filter", to force distort to use a resize filter for enlargement as well as compression. This is a slow method (as no filter result caching takes place), and not recommended. It currently results in the "-filter" setting being used for color interpolated lookup.

Note however that the enlargement will be a orthogonal 2-pass filtering, while compression will continue to use a cylindrical filtering technqiue! That is while the same filter will be used for areas of enlargement and compression in the distorted image, they are used differently. In the future this may be changed to force the use of cylindrical EWA filter on enlargements, but that is not the case at this time.

I would definataly not recommend that you do use both settings at the same time! This basically tells both filtering methods to refer to each other, (regardless of if enlargement or compression was in effect) with a subsequent undefined result! Currently it do a use a unscaled orthogonal 'point' resize filter for color lookup, (equivelent to a very slow 'NearestNeighbor' interpolation filter), but that may change.

The filter support in "-distort" is currently limited to a fixed 2 unit radius. This means small filters like 'Gaussian' and the various Cubic Filters, and specifically a 'Mitchell' filter will work very well. However it also means that Windowed Sinc/Bessel Filters such as 'Lanczos currently do not work well.

This limitation is not simple to fix, and will require very careful re-coding of the generate filter cache in distort. Using Windowed Bessel filters with its large support would cause a large slowdown in EWA re-sampling! In otherwords it is complex to fix with little reward for the effort.


Scale-Rotate-Translate (SRT) Distortion

The simplest distortion, but probably one of the most versatile, is the 'SRT or 'Scale-Rotate-Translate' distortion. (SRT is just a quick short-hand)

You have already seen the 'no-op' example of this distortion in the above examples, where the image is processed without any distortion being applied at all. But this is only the start of what it can do.

This distortion is actually three separate, distortions in a single distortion method. All arguments, except the angle rotation, are optional and this makes the argument meaning highly variable in meaning, depending on exactly how many comma separated arguments you give, up to the maximum of 7 floating point numbers.

-distort SRT " 
                  Angle 
 "   -> centered rotate
        Scale     Angle
  -> centered scale and rotate
X,Y               Angle 
  -> rotate about given coordinate
X,Y     Scale     Angle 
  -> scale and rotate about coordinate
X,Y ScaleX,ScaleY Angle 
  -> ditto
X,Y     Scale     Angle  NewX,NewY
  -> scale, rotate and translate coord
X,Y ScaleX,ScaleY Angle  NewX,NewY
  -> ditto

What this does is take an image in which you have selected, and an optional control point. If no control point is given, the center of the input source image is used. Around that point the distortion will, in sequence... Scale the image, Rotate it, then Translate or move the selected control point to a new position. Hence the name of this distortion.

For example lets take our koala image, and just simply rotate it, in a similar way to the Rotate Operator, but without expanding the size of the input image...

  convert koala.gif  -background skyblue  -virtual-pixel background \
          -distort ScaleRotateTranslate -110 koala_srt_rotate.png
[IM Output]

Using the 'plus' form of "+distort", and a clean up of resulting virtual canvas offsets, we can generate something very simular to (often better than) the the normal Rotate Operator, but with a higher quality result.

  convert koala.gif  -background skyblue  -virtual-pixel background \
          +distort ScaleRotateTranslate -110 +repage koala_srt_rotate2.png
[IM Output]

Lets shrink it by 30% as well, but use a transparent background.

  convert koala.gif  -matte -virtual-pixel transparent \
          +distort ScaleRotateTranslate '.7,-110'  koala_srt_scale.png
[IM Output]

The next set of arguments will specify the 'center' around which the image is rotated and scaled. This point is called a 'control point' or 'handle' in the image which is a location used to control the distortion. As we are using a specific point for this distortion, lets not use the 'best-fit' mode to avoid the complications of 'virtual offsets'.

For example lets rotate and scale the koala around its 'nose', which is located at 28,24 in the source image. While we are at it lets distort the X and Y scales different.

  convert koala.gif  -background skyblue -virtual-pixel background \
          -distort ScaleRotateTranslate '28,24  .4,.8  -110' \
          koala_srt_center.png
[IM Output]

And as a final example, lets also move the 'nose' to near the bottom of the image, and set background to a matching white background.

  convert koala.gif  -virtual-pixel white \
          -distort ScaleRotateTranslate '28,24  .4,.8  -110  37.5,60' \
          koala_srt_trans.png
[IM Output]

Note that the final position is also a floating point value. In fact all the arguments can be floating point values and the distortion will do the right thing.

Remember each of the operations, Scale, Rotate, and Translate are performed in that order.

As you can see this distortion is very versatile, and while you can think of it as distorting the image using three different methods in sequence, in reality it is applying all three distortions simultaneously to produce the shown result. This makes it faster than doing multiple individual operators, and generally produces a better final result.

The above also demonstrates the use of different Virtual Pixel settings to define the color used for the areas referenced outside the actual source image. To see the effect of Interpolation on rotations see Interpolation of a Rotated Line and Edge.

This distortion specifically designed to take an image and generate an animation based on the movements and rotation of that object.

For example here I create a stylized space ship, which I then animate in a very rough way. The ship sits on its base at 20,75 (for the initial 'hunker-down' scaling) while the normal 'handle' for movement and rotations is the ships center which is located at 20,60 in the original image. These points represent control points by which the object can then be animated in simple terms.

  convert -size 80x80 xc:skyblue -fill yellow -stroke black \
          -draw 'path "M 15,75 20,45 25,75 Z  M 10,55 30,55" ' \
          spaceship.gif
  convert spaceship.gif \
          \( -clone 0  -distort SRT '20,75  1.0,0.6  0' \) \
          \( -clone 0  -distort SRT '20,60     1     0  20.5,49.5' \) \
          \( -clone 0  -distort SRT '20,60    0.9   20  27,35' \) \
          \( -clone 0  -distort SRT '20,60    0.8   45  40,23' \) \
          \( -clone 0  -distort SRT '20,60    0.5   70  55,15' \) \
          \( -clone 0  -distort SRT '20,60    0.3   75  72,11' \) \
          \( -clone 0  -distort SRT '20,60    0.1   80  100,8' \) \
          -set delay 50  -loop 0  spaceship_launch.gif
[IM Output] ==> [IM Output]

Of course it is a very rough example of how you can use a 'SRT' distortion to animated a static image, but you should get the idea. You can add more frames, and perhaps some flames and smoke to improve it further (submissions welcome and best result will be added here with your name).

Distortions Using Control Points

While the 'SRT' distortion method is defined by specifying rotation angles and scaling factors, most distortions are defined by moving 'points' on the source image, and moving them to a new position in the resulting image. This is a bit like the movement of the 'center' point when defining a 'SRT' translation.

These points are called control points, and are more usually defined by giving 4 floating point values (2 pairs of coordinates) for each single control point. So often a distortion is defined in terms of multiple sets of 4 values. For example....
X1,Y1 I1,J1     X2,Y2 I2,J2     X3,Y3 I3,J3     X4,Y4 I4,J4 . . . .
Where the control point Xi,Xi in the source image (relative it its virtual canvas), is mapped to Ii,Ji on the distorted destination image.

However as the Distort Operator is actually mapping destination coordinates to source coordinates, the internal use of the above is to map I,J coordinates to X,Y coordinates. The result however should be the same, just a different way of thinking.

Before IM version 6.3.6-0 when the Distort Operator operator was first introduced, the coordinate ordering for control points was defined as all the source coordinates, followed by all the destination coordinates. This however made it very hard to determine which source and destination coordinates corresponded to each other, and did not allow for the simple appending of more control points to further refine a distortion.

It is defined in this way so that the movement of each individual control point is kept together in the comma (or space) separated list of floating point values. It also allows for the future use of external 'control point files'.

The simplest distortion using control points is the 'Affine' distortion, though this as you will see later is usually defined in terms of three points, you can use just one or two control point movements. In actual fact 'SRT' is simply a two or one point sub-set of a 'Affine' distortion.

For example here we move the 'nose' of our koala image at '28,24' to the new position '45,40' (as indicated by the red arrow), which results in a simple 'translation' of the image location.

  convert koala.gif  -virtual-pixel white \
          -distort Affine '28,24 45,40'   koala_one_point.png
[IM Output] ==> [IM Output]

With two points, the 'Affine' distortion can not only translate a image but scale and rotate it as well (the full range of a 'SRT' distortion.

For example here I map the 'ears' to the koala (the red line from '30,11' and '48,29'), to a larger horizontal position (a blue line from '15,15' to '60,15'), requiring the image to be scaled, rotated and translated so the control points are moved to this new position.

  convert koala.gif  -virtual-pixel white \
          -distort Affine '30,11 15,15  48,29 60,15'   koala_two_point.png
[IM Output] ==> [IM Output]

Of course a 'SRT' distortion could have reproduced the above two point 'Affine' distortion, except that here we defined the distortion in a different way. Which form you should use is up to you, and what you are trying to achieve.

Affine Distortion (a three point distort)

Both the 'SRT' distortion, and the one and two point forms of the 'Affine' distortion shown above are actually simplifications of a full 3 point form of the 'Affine' distortion. In fact if you study the "-verbose" output of any 'SRT' distortion (see verbose distort setting for an example) you will find that internally it really is a 'AffineProjection' distortion (see below).

The only distortion effect that the above methods could not handle fully was 'shears' similar to what the Shear Operator would provide. For that you need to use a three point affine distortion. You can think of a three point distortion, by imagining the first coordinate mapping as a 'origin' with the other two coordinate mappings as vectors from that origin.

For example here I draw some text, and overlay a red and blue 'vector' to define the three control points relative to that text. Now by moving the coordinates of those two lines, we can translate, rotate, scale and shear that text image, to fit the new location of those lines.

  convert -background lightblue -fill Gray -font Candice \
      -size 100x100 -gravity center label:Affine\! \
      -draw 'fill blue stroke blue path "M 3,60 32,60 M 27,58 27,62 32,60 Z"' \
      -draw 'fill red  stroke red  path "M 3,60  3,30 M  1,35  5,35  3,30 Z"' \
      label_axis.png
  convert label_axis.png \
          -distort Affine '3,60 3,60     32,60 32,60    3,30 30,25' \
          label_axis_distort_shear.png
  convert label_axis.png \
          -distort Affine '3,60 3,60     32,60 27,85    3,30 27,35' \
          label_axis_distort_rotate.png
  convert label_axis.png \
          -distort Affine '3,60 30,50    32,60 60,80    3,30 30,5' \
          label_axis_distort_affine.png
[IM Output] ==> [IM Output] [IM Output] [IM Output]

In the first example only the third coordinate (for the vertical red line) was modified causing the image to be sheared, and stretched along the Y axis. Of course it does not have to be limited to just the Y axis. Later examples make more radical changes to the image, including rotations.

Of course the Annotate Text operator can also skew actual text in this same way, though only with changes to the angle rather than any scaling, (see Annotate Argument Usage). Affine however distortion can do this for any image, and not just simple text.

Affine Least Squares Fit

If you supply more than 3 control points, to an 'Affine' distortion, ImageMagick will perform an least squares average over all those points to find the best representation for a 3 point, 'Affine Projection'.

This means if you are trying to match up one image with another image ('Image Registration'), you can define more than 3 points so that the result will be a more precise distortion to match up the images.

Of course if one or more of those points do not 'fit' an 'Affine Projection', then the result will be only a rough fit that best represents all the control points given.

FUTURE: example needed

Affine Projection Distortion

As I have already mentioned, the various arguments of an 'SRT' distortion and the control points of an 'Affine' distortion, are mathematically transformed into 6 special numbers which represent the 'coefficients' of an 'Affine Projection'.

The 6 floating point arguments are (in the order to be given)...
sx, rx, ry, sy, tx, ty

These in turn form the distortion expressions..
Xd sx*Xs + ry*Ys + tx   ,       Yd rx*Xs + sy*Ys + ty

Where "Xs,Ys" are source image coordinates and "Xd,Yd" are destination image coordinates. Internally ImageMagick Distort will reverse the above equations so as to do the appropriate Pixel Mapping to map "Xd,Yd" coordinates to lookup the color at "Xs,Ys" in the source image.

If you already have these coefficients pre-calculated (say extracted from the Verbose Output of distort, or calculated them yourself using other methods from other forms of input arguments, then you can directly supply them to IM to distort the image.

For example, here I 'shear' the image but using an angle to calculate the coefficients, rather than the movement of control points.

   angle=-20
   sine=`convert xc: -format "%[fx:sin( $angle *pi/180)]" info:`
   convert koala.gif -matte -virtual-pixel Transparent \
           +distort AffineProjection "1,$sine,0,1,0,0" +repage \
           koala_affine_proj.png
[IM Output]

The older way of doing this was to use the "-affine" and "-transform" operational pair. However as of IM v6.4.2-8 this is just a simple call to 'AffineProjection' using the 'plus' or 'bestfit' form of the Distort Operator.

Affine Distortion Examples

Affine Tiling

All three of the above affine-like distortion methods we have looked at so far, also provides interesting ways to generate various tiling patterns, based on a distorted image.


  convert checks.png    -matte    -virtual-pixel tile \
          -distort  ScaleRotateTranslate  '20,20  .5  30' \
          checks_srt_tile.png
  convert checks.png    -matte    -virtual-pixel tile \
          -distort  Affine  '0,0 10,10   0,89 10,50   89,0 50,0' \
          checks_affine_tile.png
  convert checks.png    -matte    -virtual-pixel tile \
          -distort  AffineProjection  '0.9,0.3,-0.2,0.7,20,15' \
          checks_amatrix_tile.png
[IM Output] ==> [IM Output] [IM Output] [IM Output]

Using a distortion mapping in this way is actually how 'texture mapping' works in 3D graphic libraries and games. The only difference is that they map 3 dimensional coordinates of surfaces, back to a two dimensional image.

Even the 'no-op' distortion ("-distort SRT 0"), with an appropriate Distort Viewport provides a useful way of tiling whole sequence of images such as Animated Glitter Tiles.

  convert glitter_blue.gif -virtual-pixel tile \
          -set option:distort:viewport 100x100 -distort SRT 0 \
          glitter_blue_tiled.gif
[IM Output] ==> [IM Output]

3d Cubes, using Affine Layering

The 'Affine' distortion, with its control points is ideal for generating Orthographic, and Isometric Cubes (see Wikipedia, Orthographic Projection and Isometric Projection for definitions), from three images. All that you need to do is figure out four control points on a destination image.

As we will be using a Image Layering Technique the points can even have negative values, and allows IM to adjust the final image size accordingly to the generated warped images.

For this example I'll choose the control points '0,0' for the center of the cube, and three points equally spaced around that central point, at '-87,-50', '87,-50', and '0,100'. All that I then need to to is map the appropriate corners of three (preferably square) images to these control points.

  convert -virtual-pixel transparent \
          \( lena_orig.png -matte \
             +distort Affine '0,511 0,0   0,0 -87,-50  511,511 87,-50' \) \
          \( mandrill_orig.png -matte \
             +distort Affine '511,0 0,0   0,0 -87,-50  511,511 0,100' \) \
          \( pagoda_sm.jpg -matte \
             +distort Affine '  0,0 0,0   0,319 0,100    319,0 87,-50' \) \
          -background black -layers merge +repage \
          -bordercolor black -border 10x5     isometric_cube.png
[IM Output]

3d Shadows, using Affine Shears

The same layering methods used above can also be used to generate cool 3-dimensional shadows of odd shapes. That add a shadow of any 'flat' shape that is standing upright.

For example lets create a shape with a flat base, so it could posibly stand upright.

  convert -background None -virtual-pixel Transparent -fill DodgerBlue \
          -pointsize 72 -font Ravie  label:A   -trim +repage \
          -gravity South -chop 0x5  standing_shape.png
[IM Output]

Note that the 'shape' has a flat base which is also the last row of the image. This is important as we will distort the shape along that row, so that the shadow will connect to standing shape alone that row.

Here is the command to generate the 3-D shadow from this 'standing shape'

  convert standing_shape.png   -flip +distort SRT '0,0 1,-1 0' \
          \( +clone -background Black -shadow 60x5+0+0 \
             -virtual-pixel Transparent \
             +distort Affine '0,0 0,0  100,0 100,0  0,100 100,50' \
          \) +swap -background white -layers merge \
          -fuzz 2% -trim +repage   standing_shadow.jpg
[IM Output]

The above does quite a few steps to achieve the result shown. The trickest however is that first line. This flips the image then does a 'distort flip' back again. The result of this is that the bottom row is now located so that it has a value of Y=0 on the virtual canvas. That is the whole image was given a negative offset to position it so that the bottom row passes through the origin of the the virtual canvas.

By doing this 'trick' we can use a very simple 'affine shear' on the extracted 'shadow' to distort it. We thus do not need to know the size of the shape image to distort the shadow, but still manage to keep everything 'lined up', as they all remain in-sync along the bottom (Y=0) row of the original image.

You can adjust the direction the shadow falls and its length simply by adjusting the final coordinate ('100,50') of the 'affine shear'. The first two 'coordinate pairs' should not be modified as these 'lock' the shadow to the original image along the bottom row.

Note however that right up until the last step all the images will contain negative virtual canvas offsets, so caution is advised if you plan to view or save the intermedite processing images.

The only problem with this shadowing effect is that it is a 'universal blur'. That is the shadow is not realistic. In reality the shadow should be sharp where it joins the 'standing shape' and getting more blurry as the shadow gets further way. This would require the use of a 'variable blur' to achieve, somethign not so easy to do in the current IM, but it is posible.

Improved 3D Shadow, with Variable Blur

Here is one way to add variable blur is to first distort the shadow using a Perspective Distortions, before blurring it and distorting it to its final 'Affine Shear' position.

  convert standing_shape.png   -flip +distort SRT '0,0 1,-1 0' \
          \( +clone   -virtual-pixel Transparent -mattecolor None \
             +distort Perspective \
                '0,0 0,0  100,0 100,0   0,-100 45,-100   100,-100 60,-100' \
             -fuzz 2% -trim   -background Black -shadow 60x3+0+0 \
             +distort Perspective \
                '0,0 0,0  100,0 100,0   45,-100 -100,-50   60,-100 0,-50' \
          \) +swap -background white -layers merge \
          -fuzz 2% -trim +repage     standing_shadow_var.jpg
[IM Output]

This is almost exactly the same as the original 3D Shadowing Example, but with some extra steps.

The original shape is first distorted into a trapezoid, then any excess space is trimmed to speed up the next step. We then extract a blurred shadow from the distorted shape. Once the shadow image has been created from the distorted image, the same control points are used to un-distort the shadow image, and move them to their correct positions for the desired Affine Shear.

The key is that the shadow blurring happens to a distorted image, which is then un-distorted (or in this case un-distorted, and Affine Sheared, at the same time). As a result the blur is also distorted so as to blur more around the top part of the shadow, and much less along the base line.

As a result of the perspective blurring, we get a variable blur that should peak at about 100 pixels away from the ground base-line. As defined by the initial perspective blur control points.


Perspective Distortion (a four point distort)

Probably the most common requested type of distortion, has been for a fast perspective distortion operation. This is a 4 point distortion, so requires at least 4 sets of control point pairs, or 16 floating point values.

For example, here I have a image building. From this image I manually discovered the location of 4 points (green). I also defined the final location to which I those points transformed to in the final image (blue), so as to 'straighten' or 'rectify' the face of the building.

  convert building.jpg \
          -draw 'fill none stroke green polygon 7,40 4,124, 85,122, 85,2' \
          building_before.jpg
  convert building.jpg \
          -draw 'fill none stroke blue polygon 4,30 4,123, 100,123, 100,30' \
          building_after.jpg
[IM Output] ==> [IM Output] [IM Output]

To do the actual image distortion, you only need to feed those coordinates into the 'perspective' method of "-distort".

  convert building.jpg -matte -virtual-pixel transparent \
         -distort Perspective \
              '7,40 4,30   4,124 4,123   85,122 100,123   85,2 100,30' \
          building_pers.png
[IM Output] ==> [IM Output]

Notice the blank area on the top right, where the distortion 'missed' the pixel data in the source image. What IM does in this situation is controlled by the "-virtual-pixel" setting (see Virtual Pixel).

What is less noticeable is that a small amount of the left-most edge of the original image is also 'lost' for the same reason.

As a matter of interest lets also reverse the distortion, by swapping the coordinates of each mapping pair. This lets us see just how much of the image is degraded by the distortion.

  convert building_pers.png  -matte -virtual-pixel transparent \
         -distort Perspective \
              '4,30 7,40   4,123 4,124   100,123 85,122   100,30 85,2' \
          building_pers_rev.png
[IM Output] ==> [IM Output] ==> [IM Output]

Not bad. A lot of 'fuzziness' is present, but that can't be helped. Notice that the 'fuzziness' is worse on the right side of the image where it was compressed the most. All distorts suffer from this compression problem, as such you should always try to distort from an original image, rather than distorting an already distorted image.

Here is another example, of using this transform, using the special checkerboard test image we created above, which we distort then reverse the distortion.

  convert checks.png        -matte    -virtual-pixel transparent \
          -distort Perspective '0,0,0,0  0,90,0,90  90,0,90,25  90,90,90,65' \
          checks_pers.png
  convert checks_pers.png   -matte    -virtual-pixel transparent \
          -distort Perspective '0,0,0,0  0,90,0,90  90,25,90,0  90,65,90,90' \
          checks_pers_rev.png
[IM Output] ==> [IM Output] ==> [IM Output]

You can see the slight fuzziness caused by image compression, but the image is basically restored.

What actually happens is that IM uses all the control point pairs given to calculate the appropriate coefficients for a 'Perspective Projection' (see next). If you include a Verbose setting, you can see both the coefficients, and the DIY FX Equivalent that is being used internally by IM to perform this distortion.

If only 3 or less control point pairs are provided, IM will automatically fall back to the simpler 'Affine' distortion. While more that 4 points (for 'Image Registration') will be least squares fitted to find the best fitting distortion for all the given control points.

FUTURE: Alternative. The four coordinates could also represent a triangle and center point. You can fix the triangle and move the center point, or fix that center and move the other three coordinates, to generate the perspective view.

If you like to see more detail in what IM actually does and the mathematics involved see DIY Perspective Distortion. You can also see a Postscript implementation that was presented in a PDF paper Perspective Rectification, by Gernot Hoffmann. Also have a look at Leptonica Affine and Perspective Transforms.


Viewing Distant Horizons

You can produce some very unusual effects using Perspective Distortions if you adjust the coordinates to produce a 'vanishing point' within the boundaries of the image.


  convert checks.png -mattecolor DodgerBlue \
          -virtual-pixel background -background Green \
          -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          checks_horizon.png
[IM Output]

Well we used 'green' for the surrounding virtual pixels of this image, which we enabled using Virtual Pixel Background Settings. But what is more interesting is the appearance of the 'blue' color that was defined using the "-mattecolor" setting.

This 'blue' color represents an area where the pixels generated by the distortion is invalid, and in such areas the "-distort" operator will just output the "-mattecolor" setting.

For a Perspective Distortion, any pixel ending up in the 'sky' of the resulting image will be classed as invalid. Also it defines the 'sky' as being the side of the 'horizon' on which the source image will not appear. It will only appear when images are highly foreshortened by the distortion.

If you don't want a 'sky' in your final image result then the best idea is to set both "-background" and "-mattecolor" to use the same color.

The Perspective Distortion gets more interesting when one of the special infinite tiling Virtual Pixel settings are used. For example here we used a 'tile' setting to generate a infinitely tiled plane.

  convert checks.png  -virtual-pixel tile -mattecolor DodgerBlue \
          -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          horizon_tile.png
[IM Output]

A word of warning about this image. Asking for an infinitely tiled image is very slow to generate. The larger the image the slower it gets. You can monitor the progress of the "-distort" (or any other slow image processing task) using the "-monitor" Operational Control Setting.

Basically for a single pixel that is close to the horizon, ImageMagick will need to average a huge number of pixels from the original image to figure out the appropriate color. This can take a very long time. ImageMagick does try to limit the amount of time it uses to handle these near-horizon pixels, bu caching information and in-built knowledge of various Virtual Pixel settings, but it can still take a long time.

For more details of this method see Area Resampling above.

Another infinitely tiled perspective image can be generated by using a Random Virtual Pixel Setting...

  convert checks.png  -virtual-pixel random -mattecolor DodgerBlue \
          -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
          horizon_random.png
[IM Output]

What is happening is that all virtual pixels surrounding the image are just random picks of any pixel within the image itself. The result is a ground consisting of random noise that gets smoother and more blurred as you look toward the horizon of the image. It gives a natural feeling of depth, without any specific repeating pattern.

Here I repeated the above but with a pure black and white source image. However I am not interested in the actual distorted image, only the Virtual Pixel 'random' pattern that was generated, so I changed what part of the 'distorted image space' I am looking at, by using a special '-set option:distort:viewport' setting. This setting overrides the normal size and location of the area of distorted space being viewed. In this case an area only containing virtual pixels, and not the distorted image.


  convert -size 90x90 pattern:gray50 -matte \
       -virtual-pixel random -mattecolor none \
       -set option:distort:viewport 120x120+100-15 \
       -distort Perspective '0,0 20,60  90,0 70,63  0,90 5,83  90,90 85,88' \
       +repage -size 120x50 gradient:dodgerblue-tomato \
       -compose DstOver -composite    sunset_horizon.png
[IM Output]

To complete the image I removed the viewport offset (using "+repage" ), and Underlaid a gradient of sunset colors into the transparent 'sky' (set using "-mattecolor") .

A very interesting image that could be used as a backdrop for some other image processing work. You can adjust the distortion parameters to adjust the height and slope of the horizon.

Here is a more traditional test of a tiled perspective distortion.

  convert pattern:checkerboard -scale 120x120 -normalize \
          -virtual-pixel tile  -distort Perspective \
             '0,0 10,61   119,0 60,60   0,119 5,114   119,119 125,110' \
          checkered_plain.gif
[IM Output]

In my studies I found the above test to be misleading, as it give no real indication of the quality of the area resampling technique for near unity scales of an image. That is typified by problems such as described in Resize Artifacts.


3d Boxes, Perspective Layering

The 'plus' form of "+distort" which ensures the whole distorted image is preserved in a correctly positioned layer (or 'virtual-canvas') is designed so that if the same 'control points' used to distort images, those point will line up in 'virtual-space'. This means that if the images are Layer Merged together, those images will also line-up accoding to the control points.

For example here we generate two images, a 'front' and a 'spine' image, so that two edge control points are lined with each other, to form the spine of a box.

  # Generate a Spine Image
  convert -size 200x40 xc:skyblue \
    -pointsize 20 -gravity north -annotate +5+0 'IM Examples' \
    -pointsize 10 -gravity south -annotate +0+0 'ImageMagick' \
    -stroke blue -strokewidth 2 -draw 'line 30,0 30,40' \
    -rotate -90 box_spine.jpg

  # generate the front cover
  convert -size 150x200 xc:skyblue \
    -fill black -pointsize 20 -gravity north -annotate +0+5 'IM Examples' \
    -fill blue -pointsize 15 -gravity northeast -annotate +5+28 'Box Set' \
    -fill black -pointsize 15 -gravity south -annotate +0+5 'ImageMagick' \
    -stroke blue -strokewidth 2 -draw 'line 0,169 150,169' \
    \( http://imagemagick.org/Usage/images/logo.gif -resize 100x100 \) \
    -gravity center -compose multiply -composite box_front.jpg

  # Distort both images and merge using common points.
  convert -virtual-pixel transparent \
    \( box_spine.jpg -matte  +distort Perspective \
           '0,0 -30,20   0,199 -30,179   39,199 0,199   39,0 0,0' \) \
    \( box_front.jpg -matte  +distort Perspective \
           '0,0 0,0   0,199 0,199   149,199 99,155   149,0 99,30' \) \
    -background black -layers merge  +repage \
    -bordercolor black -border 15x2    box_set.jpg
[IM Output] [IM Output] ==> [IM Output]

Examine the control points for the 'perspective' distortions carefully. You will notice that two destination image control points are common to both distorted images (at 0,0 and 0,199), positioning the generated images so they line up along the join.

That meant of course that I needed negative 'x' positions for the distorted image forming the "spine" of the box, which no problem when using the layers "+distort" version of the operator. The Layers Merge operator will handle the generated image containing such negative offsets without problems, 'stitching' the two images together cleanly. It also does not matter if the join is vertical (as above) or at some angle), the images will be positioned exactly by "+distort" in the generated 'layered' image.

Of course I still need to use a "+repage" to remove the negative offset from the final image, after they have been 'merged' together.

The above is example also available in the shell script "box_set_example" so that you can download and play with it more conveniently.

You can take this further to also add mirror images of the 'box' being reflected by surface on which it sits, though you may also like to recolor or dim that image in some way to make it more realistic.

(Contributed examples welcome)

Perspective Projection Distortion

Just as the 'Affine' distortion is handled by generating coefficients for a 'Affine Projection', so to 'Perspective' is handled by 8 coefficients of a 'Perspective Projection' distortion.

The 8 floating point arguments are (in the order given)...
sx, ry, tx,
rx, sy, ty,
px, py

These coefficent values in turn form the expression..
Xd sx*Xs + ry*Ys + tx   ,       Yd rx*Xs + sy*Ys + ty


 px*Xs + py*Ys + 1.0   px*Xs + py*Ys + 1.0 

Where "Xs,Ys" are source image coordinates and "Xd,Yd" are destination image coordinates. Internally ImageMagick Distort will reverse the above equations so as to do the appropriate Reverse Pixel Mapping to map "Xd,Yd" coordinates to lookup the color at "Xs,Ys" in the source image.

The first 6 values of the 'Perspective Projection' is in fact the same coefficients to that of the 'Affine Projection', though they are slightly reordered to be more logical (in 'matrix math' terms, the the first 6 elements have been diagonally transposed).

The extra two arguments px,py form a scaling divisor to the whole distortion which causes the image to look smaller in the specific direction according to the values given, and thus giving the distorted image the perspective 'distance' effect. If these to values are set to zero, the 'Perspective Projection' distortion becomes equivalent to a 'Affine Projection'


Arc Distortion (curving images into circular arcs)

The 'Arc' distortion (as of IM v6.3.5-5) is a simple variation of a much more complex, polar distortion (see below).

By default it will curve the given image into a perfectly circular arc over the angle given, and without other arguments it will try to preserve the scaling of both the horizontal center-line of the image, and the the image's aspect ratio, as much as possible.

To do this it takes up to four arguments.
arc_angle   rotate_angle   top_radius   bottom_radius
However only the "arc_angle" is required, the other arguments are optional, and can be added as needed, in the sequence given.

For example 'Arc' an image over an angle of 60 degrees...

  convert rose:  -virtual-pixel White -distort Arc 60  arc_rose.jpg
[IM Output]

Note that unlike the other image distortion operators, an 'Arc' distort will always set the size of the resulting image so that the complete source image is present. This includes any anti-aliasing edge pixels. As such the resulting image will rarely match the size of the input image.

Only a the Special Distort Options, will allow you to change the resulting image size for a specific distortion.

Adding the second argument "rotate_agle" allows you to rotate the image around the circle. For example rotate it by 90 degrees.

  convert rose:  -virtual-pixel White -distort Arc '60 90'  arc_rose_rot.jpg
[IM Output]

As no specific radius argument has be mentioned, the 'Arc' distortion method takes great pains to try to ensure the original images scale is preserved as much as possible. To do this the horizontal center line of the image is set to the 'ideal radius' for the width and the given "arc_angle" of the source image.

This means that if you arc the image over a larger "arc_angle", the radius of the center-line used will also shrink by the same factor. As such the radius of the center-line will be smaller and tighter.

  convert rose:  -virtual-pixel White -distort Arc 120  arc_rose_3.jpg
[IM Output]

Note how the image will now fit into a smaller circle, but that the bottom edge of the image is an even smaller circle still!

If you set an even larger angle over which to arc the image, the bottom edge will hit the center of the distortion, and beyond, which results in it disappearing into oblivion.

  convert rose: -virtual-pixel White -distort Arc 60   arc_rose_1.jpg
  convert rose: -virtual-pixel White -distort Arc 90   arc_rose_2.jpg
  convert rose: -virtual-pixel White -distort Arc 120  arc_rose_3.jpg
  convert rose: -virtual-pixel White -distort Arc 180  arc_rose_4.jpg
  convert rose: -virtual-pixel White -distort Arc 240  arc_rose_5.jpg
  convert rose: -virtual-pixel White -distort Arc 300  arc_rose_6.jpg
  convert rose: -virtual-pixel White -distort Arc 360  arc_rose_7.jpg
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output] [IM Output]

Longer images will 'Arc' distort a lot better over very large angles. For example you can wrap long images (like text messages) into rings. And just so you can truly see what is happening here I set a different Virtual Pixel background color, so you can see the boundary of the original image.

  convert -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Background  -background SkyBlue \
          -distort Arc 60     arc_circle_1.jpg
  convert -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Background  -background SkyBlue \
          -distort Arc 120    arc_circle_2.jpg
  convert -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Background  -background SkyBlue \
          -distort Arc 180    arc_circle_3.jpg
  convert -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Background  -background SkyBlue \
          -distort Arc 270    arc_circle_4.jpg
  convert -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Background  -background SkyBlue \
          -distort Arc 360    arc_circle_5.jpg
[IM Output] [IM Output] [IM Output] [IM Output] [IM Output]

And hey presto we have 'arc'ed the label image into a full circle.

If you look closely at the join of the full circle image you may see a small line of pixels, where the join is not quite complete. This is caused by the effect of the surrounding 'SkyBlue' Virtual Pixel background, as we are effectively joining two edges of an image.

When generating a full circle, you need to use a virtual pixel method that will 'join' these two edges correctly. This is generally done by using one of the tiling Virtual Pixel methods, such as Tile.

  convert -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Tile -background SkyBlue \
          -distort Arc 360   arc_circle_tile.jpg
[IM Output]

Unfortunately, as you can see, this not only joins the image together properly, but also generates duplicate lines of the image into and out-of the primary ring. Not good.

As of IM v6.4.2-6 a new Virtual Pixel method, HorizontalTile, solves this problem. This method joins the image sideways, so it creates a good join for our circled image, but fills the areas above and below the tiles with the background color, producing a perfect circle of text.

  convert -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel HorizontalTile -background SkyBlue \
          -distort Arc 360   arc_circle.jpg
[IM Output]

If before 'arc'ing an image you rotate the input image upside-down, you can place the original 'top' of the image on the inside edge of the circle. Of course you may like to 'rotate' the result back to normal again afterward, but that capability is already built into the 'Arc' distortion method.

  convert -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel Background -background SkyBlue \
          -rotate 180   -distort Arc '270 180'  arc_flip.jpg
[IM Output]

The third argument "top_radius" will override the 'ideal' center line radius that is calculated, so that the top of the image will fit in side a circle of the given radius.

  convert -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel HorizontalTile -background SkyBlue \
          -distort Arc '360 0 50'  arc_radius.jpg
[IM Output]

Note how the whole image was enlarged to match the new radius, by effectively scaling the whole image to fit this radius, and preserving the original images aspect ratio (height to width relation) as much as possible.

However if you provide the fourth "bottom_radius" argument, you can get complete control of the radial 'height' of the ring generated, and distort the radial scaling of the image, separate to the 'arc width' or angle.

  convert -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel HorizontalTile -background SkyBlue \
          -distort Arc '360 0 45 30'   arc_inner.jpg
[IM Output]

As a result of using all four parameters IM was given no leeway in attempting to preserve the aspect ratio of the original image.

You can even force it to completely fill the inside of the circle, wrapping the bottom edge of the input image at the center, or 'pole' of the distrotion.

  convert -font Candice -pointsize 20 label:' Around the World ' \
          -virtual-pixel HorizontalTile -background SkyBlue \
          -distort Arc '360 0 45 0'   arc_fill.jpg
[IM Output]

You can also try to generate interesting effects, for example arcing a long checkerboard pattern into the ring (using the Virtual Pixel setting 'HorizontalTile' produces...

  convert -size 210x30 pattern:checkerboard -matte \
          -virtual-pixel HorizontalTile -background SkyBlue \
          -distort Arc 360   arc_checks.png
[IM Output]

By using the default Virtual Pixel setting of 'Edge' you can produce a more interesting effect.

  convert -size 210x30 pattern:checkerboard  -virtual-pixel Edge \
          -distort Arc 360   arc_checks_edge.png
[IM Output]

If you look carefully at the last two images, you may notice that the the line from the center directly downward is very sharp when compared to the other radial lines in the image, such as the one going from the center upward. This sharp/fuzzy behaviour is especially apparent near the extreme outside edge.

This sharp change is caused by the color continuing beyond the arc limits, without any chance for color interpolation. It is the wrap around point and it is not handled very will by the 'Arc' distortion.

That is why it is important to use an appropriate tiling Virtual Pixel setting so that the join will be calculated correctly.

Of course a 'Tile' setting generated interesting 'radial' effects too, allowing you to generate a circular checkerboard pattern.

  convert -size 210x30 pattern:checkerboard  -virtual-pixel Tile \
          -distort Arc 360   arc_checks_tile.png
[IM Output]

Arc Distortion Examples

Here are some more 'Arc' distort examples. Play with them and try to figure out how they work. What can you come up with?

  convert -size 90x1 pattern:gray50 -scale 900x100 -normalize \
          -virtual-pixel Tile  -set option:distort:viewport 100x100-50-50 \
          -distort Arc 360  +repage  arc_radii.gif
[IM Output]

  convert -size 400x100 pattern:hs_diagcross \
          -virtual-pixel Tile  -set option:distort:viewport 100x100-50-50 \
          -distort Arc '360 0 80 0' +repage  arc_cross.gif
[IM Output]

  convert -size 360x80 xc: -draw "fill none stroke black line 0,5 360,80" \
          -virtual-pixel White  -distort Arc '360 0 50 0'  arc_spiral.gif
[IM Output]

  convert tree.gif -set option:distort:viewport 120x60-60-60 \
          -virtual-pixel Dither  +distort Arc '180 0 25 0' \
          +repage arc_rays.gif
[IM Output]
The 'rays' in this last example are a by-product of the pseudo-random 'Dither' setting, resulting in a dithering of the 'sun' color from one corner from the original image. You can achieve a similar and more controlled version of this effect by using a 'Edge' setting with a modified image. The dithering also produces the circular 'dots' surrounding the center 'tree' image.

Arc Center Point Placement

By default 'Arc' will completely ignore any Virtual Canvas offset the image may have or even not report the location of the 'center' around which the image was arc'ed. However knowing the location of the 'center point' can be very useful.

If instead of using "-distort" you use the special plus form, "+distort", the image will be given a Virtual Canvas, so that center is located at the virtual canvas origin. In other words the '0,0' point of the image is set to be the 'center' of the arc.

This is especially useful for positioning an arc'ed image with a smaller angle than the full circle, where the arc 'center' is not the center of the image.

For a example...

  convert logo: -resize x150 -gravity NorthEast -crop 100x100+10+0! \
          \( -background none label:'IM Examples' \
             -virtual-pixel Background +distort Arc '270 50 20' \
             -repage +75+21\! \)  -flatten  arc_overlay.jpg
[IM Output]

Here I create a text label 'Arc' distorted it into a incomplete circle using the plus "+distort" form of the operator. The 'center' of the arc was carefully preserved by IM using the images virtual canvas offset.

This means that by simply doing a relative adjustment of the offset using "-repage" with a '!' flag, we can position the resulting circle of text anywhere we want! Such as the point of the wizard hat, which is located at the pixel coordinated 75,21, in the above example. Though the positioning using this technique is limited to integer pixel sizes.


Polar Distortion (full circle distorts)

The 'Polar' distort (Added IM v6.4.2-6) is a more low level version of the 'Arc' distortion above. It will not automatically do 'bestfit', nor does it try to preserve the aspect ratios of images.

The 6 optional floating point arguments are...
Radius_Max Radius_Min Center_X,Center_Y Start_Angle,End_Angle

All the arguments are optional at the spaced positions.

By default the 'CenterX,Y' will default to the very middle of the input image area. Then a full circle polar image will be generated such that the whole top edge becomes the center, while the bottom edge is wrapped completely around the outside of the circle. The left and right edges meeting will meet at above the center point at angles '-180' to '+180' image.

As the 'Radius_Max' must be given, it should some positive value.   However if you give a value of '0' it will be set to the distance between the center and the closest edge, so that if the other values are not given (defaults), the whole input image is mapped into a circle in the middle of the image.

For example, lets convert a map of the world into a polar view, using all the defaults. Of course you should specify a Virtual Pixel setting of 'HorizontalTile' when producing a full circle polar mapping...

  convert worldmap_sm.jpg -virtual-pixel HorizontalTile  \
          -background Black   -distort Polar 0   polar_arctic.jpg
[IM Output] ==> [IM Output]

Of course this distorts the southern hemisphere severely, wrapping the Antarctica completely around the circumference of the 'diskworld'.

By rotating the source image, and cropping it so to just show the polar cap, we can generate a nice map of the Antarctic Continent. I also specified a larger output radius, to make more visible, and asked IM to 'fit' the output image to this size by using the 'plus' form of the Distort Operator.

  convert worldmap_md.jpg -rotate 180 -crop 100%x25%+0+0 +repage \
          -virtual-pixel HorizontalTile -background Black \
          +distort Polar 80   polar_antarctica.jpg
[IM Output]

Note that the above are not strictly correct views of the earth, as the Cartesian map is a representation of a sphere, and not an image in polar coordinates.

If you use a special 'Radius_Max' value of exactly '-1' the radius of the distorted image is set to the distance from the center to the furthest corner (diagonal). This is to provide an ideal 'reverse' for a full image 'Arc' distortion (see (De)Polar Tricks below).

Remember, unlike an 'Arc' distortion, 'Polar' (also known as a 'Cartesian to Polar' distortion) makes no attempt to preserve the 'ideal' aspect ratio of the source image. Caution is advised.

DePolar Distortion (Polar to Cartesian)

This is essentially the inverse of a 'Polar' distortion, and has exactly the same set of optional arguments.

The 6 optional floating point arguments are...
Radius_Max   Radius_Min   Center_X,Center_Y   Start_Angle,End_Angle
Again if 'Radius_Max' is set to '0' the distance the 'CenterX,Y' to the nearest edge is used which means anything in the largest whole circle, will be mapped to fit into an image the same size as the input image.

For example, lets reverse the previous 'diskworld' back into a Cartesian Map.

  convert polar_arctic.jpg  -distort DePolar 0  world_restored.jpg
[IM Output] ==> [IM Output]

As the input image size has been preserved all the way though the two distortions, the result of the above is basically exactly the same as the original map. Of course as the image was compressed both at the top 'pole' and in radius, the output is a lot fuzzier than you may expect.

Actually it is made worse in that the Area Resampling algorithm (EWA) can not sample pixels in a circular arc. As such Area Resampling is turned off for "DePolar" distortions. It is recommended that some form of Super-Sampling technique be used instead, such as shown in the next section.

If you allow IM to use 'bestfit' (using the "+distort" form of the operator), then it will resize the output image so as keep the 'Radius_Max' at unity scaling, and set the width to the circumference distance of the radius midway between 'Radius_Max' and 'Radius_Min'. This essentially tries to best preserve the Aspect Ratio of the polar image, though this may produce a longer thinner image than expected. For example.

  convert polar_arctic.jpg  +distort DePolar 0  world_restored_2.jpg
[IM Output] ==> [IM Output]

(De)Polar Cycle Tricks (radial/angular blurs)

As we saw above using a 'Radius_Max' of '0' will ensure that the whole image will be mapped into a circle when using a 'Polar' (Cartesian to Polar) distortion, and the same setting will map that circle back into a rectangular image by using 'DePolar' (Polar to Cartesian).

However this will not work very well if you what to 'DePolar' a rectangular image, and then reverse the distortion again using 'Polar'. For example lets take a flower image, de-polar, then restore it using the special 'Radius_Max' value of '0' (radius = nearest edge).

  convert flower_sm.jpg -virtual-pixel Black \
          -distort DePolar 0  flower_depolar.jpg
  convert flower_depolar.jpg \
          -virtual-pixel HorizontalTile -background black \
          -distort  Polar  0  flower_circle.jpg
[IM Output] ==> [IM Output] ==> [IM Output]

Now the image is not restored properly as it was clipped by the first 'DePolar' distortion. Even so this itself a useful technique, and can be used to generate perfect circular masks for an existing image sized in a way that is completely independent of the input image given.

To do this 'DePolar'-'Polar' cycle technique correctly we need to use a radius that is the distance from the center to the furthest corner. The special 'Radius_Max' value of '-1', will ask IM to calculate an use that radius for you.

  convert flower_sm.jpg  -virtual-pixel Black \
          -distort DePolar -1  flower_depolar-1.jpg
  convert flower_depolar-1.jpg \
          -virtual-pixel HorizontalTile -background black \
          -distort  Polar  -1  flower_restored.jpg
[IM Output] ==> [IM Output] ==> [IM Output]

The restored image is slightly blurry, which is caused by the compression of the radius needed to preserve the whole image during the 'DePolar' operation. That however can be fixed by using an appropriate Super-Sampling technique (see next set of examples).

But why would you want to convert an image into this form and back again? Well by applying other distortions on the intermediate 'DePolar' version of the image, you can generate very fancy radial or angular effects quite easily.

For example by rolling the intermediate image, you will rotate the output image, though you may get some clipping of the corners...

  convert flower_sm.jpg -virtual-pixel Black -distort DePolar -1 \
          -roll +15+0 \
          -virtual-pixel HorizontalTile -background Black \
          -distort  Polar  -1  flower_polar_rotate.jpg
[IM Output]

Note that the direction of the rotation is reversed from that of the Rotate Operator or the SRT Distortion.

Depolar-Polar Cycle problems

In the image rotation above you may have notice some 'stair case' like distortions along the edge of the rotated image. This is a well known problem and is caused by compressing the large circular circumfrence of the image into the smaller 'width' of the input image.

For example here I take the checker-board test image, and just run it though a normal Depolar-Polar cycle without making any changes.

  convert checks.png   -virtual-pixel Transparent \
          -distort DePolar -1   checks_depolar.png
  convert checks_depolar.png  -virtual-pixel HorizontalTile -background None \
          -distort  Polar  -1   checks_cycled.png
[IM Output] ==> [IM Output] ==> [IM Output]

You can clearly see the aliasing effects caused by image compression in the points of the intermedite image. It is also exasperated by the fact that normal Area Resampling is not used during that initial 'Depolar' conversion of the input image.

The best way to solve this problem is to use Distort Output Scaling to both enlarge the intermediate image, and then shrink the final image. This will provide a Super-Sampled result, that will remove the compression artifacts seen above.

For example, this is the better 'no-op' depolar-polar cycle, all in one command...

  convert checks.png -virtual-pixel Background -background None \
          -set option:distort:scale 4  -distort DePolar -1 \
          -noop \
          -virtual-pixel HorizontalTile -background None \
          -set option:distort:scale .25 -distort  Polar  -1 \
          checks_cycled_ss.png
[IM Output]

As you can see the horible aliasing effects has all but disappeared. However be warned that a very tall thin image could make the problem reappear. The best idea is to limit this to 'landscale' or wide images, with super-sampling as shown above.

All you need to do is replace the "-noop" operator with the appropriate command to generate the radial and rotational effect you are looking for.

Example Depolar-Polar Effects

So lets again show better Polar Rotation of the image, this time using super sampling. Note however that as the intermediate image is 4 times larger, the amount of Image Roll also needs to be 4 times larger.

  convert flower_sm.jpg   -virtual-pixel Black \
          -set option:distort:scale 4   -distort DePolar -1 \
          -roll +60+0   \
          -virtual-pixel HorizontalTile -background Black \
          -set option:distort:scale .25 -distort Polar -1 \
          flower_polar_rotate_ss.jpg
[IM Output]

As you can see the 'stair-case' effect along the edge has been removed, with a much higher quality image result.


Or you can apply simple linear blurring of the intermediate image (such achieved by by squeezing, and enlarging), you can produce a 'Rotational Blur' of the image. This is similar to but not quite the same as the the mis-named Radial Blur Operator).

  convert flower_sm.jpg -virtual-pixel Black \
          -set option:distort:scale 4   -distort DePolar -1 \
          -scale 10%x100%\! -filter Gaussian -resize 1000%x100%\! +filter \
          -virtual-pixel HorizontalTile -background Black \
          -set option:distort:scale .25 -distort Polar -1 \
          flower_angular_blur.jpg
[IM Output]

Note the use of a 'black' color in the various forms of Virtual Pixel Settings that was applied, will result in a slight darkening of the edges, but it isn't too bad in the above case.

One method to remove the 'black' edges effects, would be to use 'transparency' color instead, and then just turn-off the alpha/matte channel completely when finished so as to leave just the actual color that IM calculated.

Another is to use two 'edge' virtual pixel methods ('Edge' and 'HorizontalTileEdge'), which extends the edges of the image into the undefined virtual canvas space.

  convert flower_sm.jpg -virtual-pixel Edge \
          -set option:distort:scale 4   -distort DePolar -1 \
          -scale 10%x100%\! -filter Gaussian -resize 1000%x100%\! +filter \
          -virtual-pixel HorizontalTileEdge -background Black \
          -set option:distort:scale .25 -distort Polar -1 \
          flower_angular_blur_edge.jpg
[IM Output]

Which shows a much better result near the edges.


By blurring the polar version of the image vertically, such as by using the Motion Blur Operator you can generate a Radial Streaks that move outward from the center of the image...

  convert flower_sm.jpg   -virtual-pixel Black \
          -set option:distort:scale 4   -distort DePolar -1 \
          -virtual-pixel Edge   -motion-blur 0x28-90 \
          -virtual-pixel HorizontalTile -background Black \
          -set option:distort:scale .25 -distort Polar -1 \
          flower_radial_blur.jpg
[IM Output]

To make the blur only blur the highlights (the petals), you can compose this with the original image using Lighten, so only the petals get blurred into the background.

  convert flower_sm.jpg  flower_radial_blur.jpg \
          -compose Lighten -composite   flower_radial_blur_lighten.jpg
[IM Output]

See also Stars and Comets for another example of doing this, but directly generating the intermediate 'DePolar' image.

Special thanks goes to Fred Weinhaus for the special uses of a DePolar-Polar cycle, and for insisting that I ensure that these distortions were fully reversible for rectangular images. He puts this technique in good effect in a number of his ImageMagick Scripts, including "bump", "ripples", and "striations".


Barrel Distortion (correcting lens distortions)

The Barrel Distortion (added to IM v6.4.2-4) is designed specifically for correcting the spherical distortions caused by camera lenses in photos. That is distortions such as barrel and pincushion effects, which are effectively the reverse of each other.

The distort is implemented based on a set of 4 coefficient values, known as A, B, C, and D, as defined by Barrel Correction Distortion, by Helmut Dersch and as used by programs such as PTLens. The values basically form a distortion equation such that...
Rsrc = r * ( A*r3 + B*r2 + C*r + D )

Where "r" is the destination radius and "Rsrc" is the source pixel to get the pixel color from. They are also normalized so that radius = '1.0' for the half minimum width or height of the input image. This may seem reversed but that is because the Reverse Pixel Mapping technique is used to ensure complete coverage of the resulting image.

All four coefficients (A, B, C, and D) are fixed for any specific camera and lens combination. This is important as it means that once you have these values for your camera you can use them to remove the spherical lens distortion that is present in all the photos taken by that camera.

Currently IM does not provide a way to determine these 4 values for a specific camera. Yet. Sorry. But you can look them up for your camera using a tool like PTLens, or work them out from a photo of equally spaced lines using the tool PanoCoef. Note this program talks about 12 coefficients, 4 values for each of the red, green and blue channels.

Well enough introduction, lets look at the Distort Operator arguments needed for the 'Barrel' distort method. Generally you supply 3 or 4 values only...

A   B   C   [ D   [ X , Y ] ]

The optional X,Y arguments provide an optional 'center' for the radial distortion, otherwise it defaults to the exact center of the image given (regardless of its virtual offset).

The coefficients are designed so that if all four A to D values, add up to '1.0', the minimal width/height of the image will not change. For this reason if D (which controls the overall scaling of the image) is not supplied it will be set so all four values do add up to '1.0'.

Using the parameters '0.0 0.0 0.0' (equivalent to A=B=C=0.0 and D=1.0') will produce no change to the input image, and is the 'no-op' argument for this distortion.

Here is an example from Helmut Dersch web site, using the supplied coefficients for the camera used to take the photo.

  convert barrel_distorted.jpg -virtual-pixel black \
          -distort Barrel "0.0 0.0 -0.075 1.1" \
          barrel_distorted_fixed.jpg
[IM Output] ==> [IM Output]

Note how the distortion in the image was corrected making the pillars of the building straight. However as the 4 coefficients added up to a value that was greater than 1.0 the image was shrunk by a small amount, producing the small black areas at the middle top and bottom edges (according to the given Virtual Pixel Setting).

Here is the effect of adding 0.2 to each of the input coefficients, again the values add up greater than 1.0 so the resulting distorted image will be smaller.

  convert checks.png -virtual-pixel gray \
          -distort Barrel "0.2 0.0 0.0 1.0"   barrel_checks_A.png
  convert checks.png -virtual-pixel gray \
          -distort Barrel "0.0 0.2 0.0 1.0"   barrel_checks_B.png
  convert checks.png -virtual-pixel gray \
          -distort Barrel "0.0 0.0 0.2 1.0"   barrel_checks_C.png
  convert checks.png -virtual-pixel gray \
          -distort Barrel "0.0 0.0 0.0 1.2"   barrel_checks_D.png
[IM Output] [IM Output] [IM Output] [IM Output]

Subtracting 0.2 produces the opposite effect, though I offset the effect using a larger 'D' value (to shrink the image) so you can see the results better.

  convert checks.png -virtual-pixel gray \
          -distort Barrel "-0.2 0.0 0.0 1.3"   barrel_checks-A.png
  convert checks.png -virtual-pixel gray \
          -distort Barrel "0.0 -0.2 0.0 1.3"   barrel_checks-B.png
  convert checks.png -virtual-pixel gray \
          -distort Barrel "0.0 0.0 -0.2 1.3"   barrel_checks-C.png
  convert checks.png -virtual-pixel gray \
          -distort Barrel "0.0 0.0 0.0 1.3"    barrel_checks-D.png
[IM Output] [IM Output] [IM Output] [IM Output]

Note how the value of A produces a larger effect than B, and B a larger effect than C, while D provides an overall scaling of the result. This allows you to use each coefficient to adjust the image so that you can correct for one distortion around the outer edge, and another distortion toward the middle, be it pincushion for one, and barrel for the other. Very versatile.

The above coefficients (A, B, C, and D) are designed to work with a 'normalized' radius that is half the minimum width or height of the image (like the '0' radius setting for Polar Distort. That is they are image size independent. As such you can use the same set of values for any image that a specific camera generates, regardless of its quality size (camera setting), or if you resized the image smaller.

It is possible to adjust the coefficient values to use other 'normalized' radius values using the appropriate multipliers/divisors to each coefficient. Such as using half the maximum width/height, or using the diagonal radius.


You can also declare a different set of coefficients for the x and y axis, allowing you to generate some unusual distortions.
Ax Bx Cx Dx   Ay By Cy Dy   [ X , Y ]

The use of separate X and Y arguments was prototyped in Fred Weinhaus's pinbarrel script though his arguments are in the reverse order with D first and A last.

By using a positive C value, with appropriate D value for just the 'y' set of coefficients you can distort images so that they bulge vertically in the middle.

  convert rose: -matte -virtual-pixel transparent \
          -distort Barrel "0.0 0.0 0.0 1.0   0.0 0.0 0.5 0.5" \
          barrel_bulge.png
[IM Output]

Similarly using a negative C value you can 'pinch' an image in the middle.

  convert rose: -matte -virtual-pixel transparent \
          -distort Barrel "0.0 0.0 0.0 1.0   0.0 0.0 -0.5 1.9" \
          barrel_pinch.png
[IM Output]

Or by adding the opposite effect for the X coefficients, make it look like your squeezing the image between your fingers, making it bulge out the sides.

  convert rose: -matte -virtual-pixel transparent \
          -distort Barrel "0.0 0.0 0.5 0.5   0.0 0.0 -0.5 1.9" \
          barrel_pinch_2.png
[IM Output]


BarrelInverse Distortion (alternative barrel distortion)

The 'BarrelInverse' distortion method is very similar to the previous Barrel Distortion distortion method, and in fact takes the same set of arguments. However the formula that is applied is slightly different, with the main part of the equation dividing the radius. that is the Equation has been inverted.
Rsrc = r / ( A*r3 + B*r2 + C*r + D )

This equation does NOT produce the 'reverse' the 'Barrel' distortion. You can NOT use it to 'undo' the previous distortion.

The result of this is that you would use the 'negative' form of the A, B, C, with an equivalent adjustment in D to achieve a similar but slightly different result. Some sources such the research paper Method for Correcting Lens Distortion (PDF) suggest that that a better result can be achieved with a lens correction distortion of this form.

For example here is the equivalent of the last 'Pinch' example using this form of distortion.

  convert rose: -matte -virtual-pixel transparent \
          -distort BarrelInverse "0.0 0.0 -0.5 1.5   0.0 0.0 0.3 0.5" \
          barrel_inv_pinch.png
[IM Output]


Shepard's Distortion (taffy-like distorts)

Shepard's method (added to IM v6.4.2-4) uses the movement of the given control points to distort the image in terms of 'local' effects. You can think of this as equivalent to a thick block of 'taffy' representing the source image, having pins driven into it and then the pins moved around.

More technically it moves points in terms of a Inverse Squared Distance Interpolation.

If only one control point is used, naturally the whole image is moved (translated), just as you would get for a one point 'Affine' distortion. Not very interesting.

So lets try moving two control points. For example lets torture the 'koala' by pulling on his ears (at '30,11' and '48,29')...

  convert koala.gif -virtual-pixel Black \
          -distort Shepards '30,11 20,11  48,29 58,29' \
          koala_ear_pull.png
[IM Output] ==> [IM Output]

As you can see the parts of the image between the two control points were stretched out because of the control point movement. However all the other parts of the image was left pretty much intact, including the image close to the control point itself, the bottom of the image, and so on.

The area that lies in the middle between the control points were pulled and stretched out to ensure the control points are positioned where you requested. What may not be so noticable is that the parts on the far-side of the control points are also compressed, so that as you get further away, the control points have less influence on the result.

That is this distortion generates a 'localized' distortion.

Lets expand our view (using a Distortion Viewport) so we can see this better...

  convert koala.gif -virtual-pixel Black \
          -set option:distort:viewport 115x115-20-20 \
          -distort Shepards '30,11 15,11  48,29 53,29' \
          +repage koala_ear_pull_2.png
[IM Output]

As you can see the shape of the image was also distorted to accommodate the stretched 'head' of the koala.

To avoid this effect it is more typical to also 'pin' the corners and possibility some of the edges of the image, so that they don't move.

  convert koala.gif -virtual-pixel Black \
          -set option:distort:viewport 115x115-20-20 \
          -distort Shepards '30,11 15,11  48,29 53,29
              0,0 0,0  0,74 0,74   74,0 74,0  74,74 74,74' \
          +repage koala_ear_pull_3.png
[IM Output]

Even just moving one point, while pinning other points (just the corners in this case) can be useful. For example lets just move the koala's nose (at '28,24') into the middle of the image.

  convert koala.gif -virtual-pixel Black \
          -distort Shepards '28,24 37,37
              0,0 0,0  0,74 0,74   74,0 74,0  74,74 74,74' \
          +repage koala_move_nose.png
[IM Output]

This specific example is special as it is the distortion used by Fred Weinhaus for his single point 'animated morphing' script "shapemorph". However his original script used a slow DIY FX Operator, as 'Shepards' distortion had yet to be added to IM (I believe it has been updated now).

You can even move a whole sections of the image by moving a set of points around that section all together. For example lets move the koala's head sideways by using points around the head (red line), but also pinning the parts of the image we don't want to move (green line).

  convert koala.gif -virtual-pixel Black -distort Shepards \
            '19,8, 29,8   19,27 29,27   26,34 36,34
                 33,37 43,37   36,37 46,37   53,37 63,37   58,25 68,25
             13,20 13,20  17,28 17,28  25,36 25,36
                 35,39 35,39   46,40 46,40   50,43 50,43 ' \
          +repage koala_head_move.png
[IM Output] ==> [IM Output]

Note that while the head was moved, the edge of the head does get badly distorted. The reason is that the distort does not move areas, but points. If those edge marking points are too far apart, then the image will sort of drip, leak, or bend between those points.

Also if two control points are close together, but which move in different directions or amounts, could cause the image to locally swirl and bend around them, so those control points end up in the right locations. And that is what happening along the edge of the head.

How close should edge marking points be? Basically at least half the distance to the other points which are moving differently. So either add more edge points, or put some extra distance between the fixed points and moving points. By doing this you better define the space in which the image can be stretch and compressed.

Also note that the whole image in general also moved to the left, along with the head. Only the control points which were either fixed or moved to specific destinations are guaranteed to be placed correctly. Any parts of the image further away from any control points will also move based on a rough average of all the control point movements.

It is thus better to have a lot more 'fixed' points, spread throughout the image, or even some negative moved points just outside the image, to offset this general average movement. You can also duplicate or double up control points (list them twice) to give specific points influence or 'power' over the distortion.

Here is another version of the 'move head sideways', however this time I gave some extra separation between the moving and fixed points. I also added a lot more fixed points to reduce the average general movement of the distortion.

  convert koala.gif -virtual-pixel Black -distort Shepards \
            '15,15, 25,15   19,27 29,27   26,34 36,34
                33,37 43,37   36,37 46,37    53,37 63,37
             10,2 10,2   2,10 2,10   4,55 4,55   14,47 14,47
                25,47 25,47 45,51 45,51   55,45 55,45
                5,70 5,70  15,60 15,60   55,60 55,60   70,70 70,70' \
          +repage koala_head_move_2.png
[IM Output] ==> [IM Output]

As you can see this is a very versatile and free form method of distortion, limiting its distortions to areas marked by the movements, or non-movements of the given points. Its distortions are localized and restricted according to the distances between neighboring control-points, though all points still do have slight global effects, basied of an overall average movement of all the control points.

Just remember that this distortion is point driven, not line or area driven, so parts between the points can bulge, or swirl unexpectedly when differently moving control points are positioned too close together.

Internally this distort is equivalent to using the Shepards Sparse Color gradient generator to create two Relative Displacement Maps to distort the image.

Due to the complexity of calculations needed in using 'Shepards' distortion, IM does not provide any form of 'best-fit' functionality using the plus "+distort" form of the operator.

For the same reasons the Area Resampling is turned off. As such areas of extreme compression (more than a factor of 2) will likely show some aliasing effects (see the koala's hands in the above). Some form of Super-Sampling can be used to improve the final image quality.


Created: 14 January 2009 (distorts sub-division)
Updated: 2 February 2009
Author: Anthony Thyssen, <A.Thyssen@griffith.edu.au>
Examples Generated with: [version image]
URL: http://www.imagemagick.org/Usage/distorts/

a