Index
ImageMagick Examples Preface and Index
Affine Matrixes, and DIY Distortions (separate page)
Distortions are operators that warp or re-arrange the contents of the image.
In ways similar to fun-house mirrors you too can distort images in extreme
ways. The image itself remains intact, but the location of the various parts
of the image could be wildly displaced on the canvas.
Simple Distorts
Flipping, and Mirroring
![[IM Output]](koala.gif)
These simple distortions of the image doesn't change the actual images. They
don't even change the rectangular nature of the image, just re-arrange things.
For these examples lets use this cute looking koala image...
The simplest image distortion is to rearrange the pixels in the
image so as to "-flip" it
upside-down.
convert koala.gif -flip flip.gif
| |
|
Or by using "-flop" you
can generate a mirror image.
convert koala.gif -flop flop.gif
| |
|
Transpose and Transverse, Diagonally
The "
-transpose"
and "
-transverse"
image operations produce diagonal mirrors of the image.
The "-transpose"
mirrors the image along the image top-left to bottom-right diagonal.
convert koala.gif -transpose transpose.gif
| |
|
While "-transverse"
mirrors the image along the image bottom-left to top-right diagonal.
convert koala.gif -transverse transverse.gif
| |
|
Rectangular Rotates
All the above operations, will essentially produce a mirror image of the
original. The "
-rotate"
operator provides the non-mirrored versions of the image.
convert koala.gif -rotate 0 rotate_0.gif
convert koala.gif -rotate 90 rotate_90.gif
convert koala.gif -rotate 180 rotate_180.gif
convert koala.gif -rotate -90 rotate-90.gif
convert koala.gif -rotate 360 rotate_360.gif
|
Note that "
-rotate" is a
simple distort only if you use a rotation angle of a multiple of 90 degrees.
Any other angle will introduce other more complex pixel level distortions into
the image.
  |
You may notice that a positive angle of rotation is clock-wise, which seems
to be mathematically incorrect. Internally however, it is mathematically
correct and is caused by use a of negated Y-axis. That is the Y-axis goes
from 0 at the top and positive downward. Because of this the coordinate
system in reversed, and thus rotation is also reversed mathematically.
|
Rolling Images like a bad TV
You can "-roll" an image
horizontally (like a TV that is out of sync). The amount of the roll is given
in pixels.
convert koala.gif -roll +0+20 roll_horiz.gif
| |
|
Of course you can also roll the image sideways...
convert koala.gif -roll +30+0 roll_vert.gif
| |
|
Or by using a negative number of pixels, you can roll it in the opposite
direction.
convert koala.gif -roll +0-20 roll-horiz.gif
| |
|
Simple Distorts Summary
The most important aspect of all these operators is that you can add them all
together in many different ways such that the result will be
exactly as
if no operation was performed at all.
convert koala.gif -roll +25+0 -rotate 90 -flop \
-roll +0-25 -flip -rotate 90 original.gif
|
|
|
Rotating and Shearing
While all the above image distorting operators preserve the basic rectangular
format of the image, the next set does not. The results of these operators
do not fit in the original rectangle (or even the rotated rectangle). To
ensure the result contains the whole of the original image, the size of the
image is enlarged to accommodate.
Rotating Images -- Simple Image Rotation
As you saw above the "
-rotate" operator can perform simple, image preserving
distorts, when you rotate image in units of 90 degrees.
With other angles however, the rotated image will not fit nicely into a
rectangular image. Consequently to ensure that no image data is lost, the
size of the final image is enlarged just enough to accommodate the rotated
image.
convert koala.gif -rotate 30 rotate.jpg
|
|
|
Note that the direction of rotate is clock-wise. This may seem illogical
mathematically, until you realise that the image coordinate system is relative
to the top-left of the image instead of the mathematical norm of the
bottom-left. The result is the angle of rotation is also inverted. This is
important to keep in mind when dealing with any form of image rotation, vs a
diagrammatic or mathematical rotation.
The extra space added by ImageMagick is colored with the current "-background" color
setting. Allowing you to specify the color to fill into the corners.
convert koala.gif -background lightblue -rotate 30 rotate_color.png
|
|
|
Of course if you want to fill with the transparent color, you will need to
ensure the image can handle transparency (by using a "-matte" operator to add an
alpha channel), and is saved to an image format that can handle transparency.
convert koala.gif -matte -background none -rotate 30 rotate_trans.png
|
|
|
If the extra space comes out black, then your output image output format does
not allow the use of an alpha channel, (most likely the JPEG format), so the
transparency defaults to black.
  |
Before version 6.1.2, "-rotate" did not handle transparency correctly,
producing stripes of black and transparent in the corners of the rotated
image. The workaround for this problem was rather complex, involving
rotating the alpha channel separately to the colors.
|
But what if you don't what that extra space, wanting to preserve the images
original size? Well you can use a centered "
-crop" to return the image to its original size.
If you don't know what the original size was, you can use an alpha composition
trick (see the
'Src' Compose
Method) to restore the image back to its original size.
convert koala.gif -matte \( +clone -background none -rotate 30 \) \
-gravity center -compose Src -composite rotate_resized.png
|
|
|
The "
-rotate" operator
also understands two extra flags. If a '
>' symbol is added to
the rotate argument (before or after the number), then the image will only be
rotated if the image is wider than it is taller. The result is that this flag
will only rotate 'landscape' (wide) style images into 'portrait' (tall) style
images.
The other flag '
<' does the opposite, only rotate images that
are taller than it is wide.
Digital photos can also be rotated to match the camera orientation (based on
the "EXIF:Orientation" meta-data) by using the "
-auto-orient" operator.
However remember saving back to JPEG format may not be a good idea.
Because non-simple rotates are implemented as a sequence of
Shears there are some subsequent effects that result.
First areas added into the image corners are just directly filled in using the
"
-background" color.
You can not control this using
Virtual Pixel
Setting.
Also you can not control the mixing of colors during the
Image Rotation using the
Interpolation
Setting. Which means you can not preserve the original colors of the
image.
Rotating a Thin Line - rotation color blurring
For example here I rotated a simple vertical line of pixels by 17 degrees,
which I then scaled so as to show the effect of the individual pixels in the
resulting image.
convert -size 10x30 xc: -draw 'line 5,0 5,30' \
-background white -rotate 17 -scale 500% rotate_magnify.gif
|
Note how the line seems to phase in and out in the rotated image, as it
crosses pixel boundaries. Not only that, but as rotate is implements as
multiple shears (see note above), the line only phased in sharply every second
pixel column. That is the result is actually more blury than it needs to be.
|
|
This 'rotation blur' is especially noticable when you rotate small text and
labels, either attached to or part of a photo, by very small angles. Such
lines contain lots of fine detail that 'dissapeares' in a regular and highly
notice wave of blurring across the image.
The only direct solution to this problem is to use a
Pixel
Mapping technique, such as an
Affine Distortion or
Scale-Rotate-Translate (SRT). For examples of this see
Interpolation of a Rotated Line.
However a much easer, though indirect method is to use the
Super Sampling technique (see below). Basically we
rotate an image that is at least twice (or more) the size of the final image
size wanted. After rotating the resize will sharpen the resulting rotated
image producing very sharp and clean lines, edges, and fonts.
An example of this solution is given both in the
Super Sampling introducion below, and also in a more practical way, when
using the
Polaroid Transform, which is
especially vulnerable to small angle rotation blur, of the added text caption.
For a more deeper understanding of the various image rotation algorithms and
the issues involved see
Leptonica Rotation.
Shearing Images -- Linear displacement
The "
-shear" operator
takes each row (or column) of pixels and slides them along so that each row
(or column) is displaced by the same amount relative to the neighbouring row
(or column). Its two arguments are given in terms of angles.
Just as with "
-rotate"
the operation increases the size of the resulting image so as not to loose any
information.
However shear is more complex in that it is really a double operation.
convert koala.gif -shear 20 shear_rot.gif
convert koala.gif -shear 20x0 shear_x.gif
convert koala.gif -shear 0x50 shear_y.gif
convert koala.gif -shear 20x50 shear_xy.gif
convert koala.gif -shear 20x0 -shear 0x50 shear_xy2.gif
convert koala.gif -shear 0x50 -shear 20x0 shear_yx.gif
|
If you look at the results you will see that the double form of the "
-shear" operation is to do
the X shear first, followed by the Y shear (with an appropriate final image
size correction). This is fundamentally different to a Y shear followed by
the X shear.
If only one number is provided (without any '
x') then "
-shear" will apply it in
both the X and Y directions as a sort of poor mans rotate. For this reason
the units of shear are in degrees.
The "-background"
color setting is of course used as the color for the extra space added.
convert koala.gif -background none -shear 30 shear_trans.png
|
|
|
  |
Before IM version 6.1.2 "-shear" did not handle transparency. The workaround for
for this problem was rather complex, involving shearing the alpha channel
separately to the colors.
|
Note that using a "
-shear"
in this way a proper method of rotating an image.
To actually use shear to rotate an image properly, you would need to use a
need to perform
three shearing operations in the form of
"-shear {X}x{Y} -shear
{X}x0 -crop ... ", however working out the proper values for the
'
{X}', '
{Y}' and final crop requires some
trigonometry. The builtin "
Rotate Operator" (see
above) is actually implemented in this way.
  |
Note that shearing in the X direction will not affect an images height,
while shearing the the Y direction will not effect the images width. The
result is that the area covered by some object within the image will not
change (only the surrounding container holding the image).
|
Waving Images - Sine Wave Displacement
The "-wave" operator is
like "-shear" in that it
adds a 'linear displacement' to images. However this operator will only
displace columns of pixels vertically according to a sine wave function.
There are two arguments to the "-wave" operator. The first is the maximum height or
amplitude the pixels will be displace either up or down, while the
second is the wavelength of the sine function in pixels.
convert koala.gif -wave 10x64 wave.jpg
|
|
|
Note that because pixels can be displaced up to the given amplitude
that much extra space will always be added to both the top and bottom of the
image, even if that space is not actually needed.
For example by adjusting the arguments so that the wavelength is double
the width of the image, you can make the image into a arc.
convert koala.gif -wave 20x150 arched.jpg
|
|
|
In this sort of case the unused space can be removed using either a "-chop", "-shave", or possibly even a
"-trim" operation.
Lets clean up the previous example by using a negative amplitude to flip the
arc over, and use "-chop"
to remove the unused space the "-wave" operator added.
convert koala.gif -wave -20x150 -gravity South -chop 0x20 arched_2.jpg
|
|
|
Of course the "-background" color setting can be used to define the extra
space added to the image.
convert koala.gif -matte -background none -wave 10x75 wave_trans.png
|
|
|
As you can see from the above examples "-wave" only applies in the vertical or 'Y' direction. If
you want to add a wave in the X direction, you'll need to rotate the image
before and after you apply the wave.
convert koala.gif -rotate -90 -wave -10x75 -rotate 90 wave_y.jpg
|
|
|
The technique can be used to add a wave pattern or vibration to an image at
any angle. Examples of this is given in the
Vibrato Font and in the
Smoking Font.
One other limitation with "
-wave", is that the wave only starts at zero. That
is the left most column is not displaced, while the next few rows are
displaced downward (positive X direction), unless you give a negative
amplitude for an initial vertical offset.
Basically the "-wave"
operator does not (at this time) allow you to specify an offset for the start
of the sine function. This can be rectified however by adding, then removing,
an image offset using "-splice".
convert koala.gif -splice 19x0+0+0 -wave 10x75 \
-chop 19x0+0+0 wave_offset.jpg
|
|
|
While "
-wave" will not make
use of the current
Virtual Pixel Setting
to define the color of the added areas, it will look at the current
Interpolation Setting to map the colors from
the source to the image generated. This means wave will tend to blur pixels
slightly in vertical bands across the image...
EXAMPLE: wave a image of horizontal lines.
However you can use a special
Super Sampling
technique to improve the blurring of the results.
EXAMPLE: supersampled wave image
Circular Distortions
So far the image distortions have been rather mild, with very little
stretching, expanding or compressing of the image data. That is the data
remains pretty well unchanged.
These next few image operators are true image distortions, and can result in
a image that is so distorted, the original image can not be determined. The
colors being merged into a twisted blurry mess.
It also happens that they limit the distorting effects to a circular area with
little to no distortion of th eoriginal image at the edge of the image
rectangle. That means you can use these operators on a small area or
Image Region and the result will still blend into the
original image without it looking like it was: cut out, warped and pasted back
into place.
Imploding Images
The "-implode"
operator warps the image so as to pull all the pixels away from the edge
toward the center. Its sort of like sticking a vacuum, or 'black hole' in the
center of the image and sucking the pixels toward it.
Caution however is advised to only use very small values, to start with.
convert koala.gif -implode .6 implode.gif
|
|
|
Using increasingly large values will essentially suck all the pixels in a
circle around the center, into oblivion.
convert koala.gif -implode 5 implode_big.gif
|
|
|
However be warned that using any "
-implode" value larger than '
1.0' is also
effected by the
Virtual Pixel Setting,
as the algorithm starts to make color references beyond the boundaries of
actual image itself. As the default "
-virtual-pixel" setting is 'edge', the edge color or
surrounding frame on an image can have a major effect on the result.
For example these two images are the same except one had white border added to
it. this basically shows the area which is using colors looked up from beyond
the bounds of the image proper. The area normally defined by the "
-virtual-pixel"
setting.
convert rose: -gravity center -crop 46x46+0+0 +repage \
-implode 3 implode_rose.gif
convert rose: -gravity center -crop 44x44+0+0 +repage \
-bordercolor white -border 1 -implode 3 implode_rose_2.gif
|
|
|
Other "
-virtual-pixel" settings can result in highly distorted
copies of the image appearing withing the imploded region, here for example I
implode simple box image with a '
tile' setting.
convert -size 94x94 xc:red -bordercolor white -border 3 \
-virtual-pixel tile -implode 4 implode_tiled_box.gif
|
|
|
More "
-virtual-pixel" effects are explored on
Implosion Effects of Virtual Pixels.
As the number of pixels being merged into a small area increases, the results
start to get a distorted 'pixelated' look. To get a better more consistent
result, you can increase the number of pixels implode works with, using a
technique called
Super-Sampling.
Basically by using a larger image (enlarging the source image if necessary),
doing the distortion, then shrinking the result to its final size you will
produce a much better result.
convert -size 94x94 xc:red -bordercolor white -border 3 \
-virtual-pixel tile -resize 400% -implode 4 -resize 25% \
implode_tiled_ss.gif
|
|
|
As you can see you get a much smoother and more realistic result that shows
the internal detail of the distortion much better. We will look at this
technique a lot more later.
By using a larger "-border" around the image being imploded, and later removing
it again, you can also warp the edges of an image.
convert koala.gif -bordercolor blue -border 20x20 \
-implode .5 -shave 18x18 implode_border.jpg
|
|
|
As of IM version 6.2.1 you can also use a transparent border, or image with
transparency...
convert koala.gif -bordercolor none -border 20x20 \
-implode .5 -shave 18x18 implode_border_trans.png
|
|
|
Exploding Images
By using a negative value with the "-implode" operator, you can explode the image. This is
however more like magnifying the center of the image pushing all the
mid-radius pixels out toward the edge, rather than a true explosion.
convert koala.gif -implode -2 explode.jpg
|
|
|
Using larger value will essentially enlarge the center most pixels of the
image into a circle two-thirds the size of the smallest image dimension.
convert koala.gif -implode -30 explode_big.jpg
|
|
|
And here is a '
Super-Sampled', version.
convert koala.gif -resize 400% -implode -30 \
-resize 25% explode_big_ss.jpg
|
|
|
The central color of the internal 'explosion' is set by the color of the
center of the image (or region). This means that by changing the colors
around that point, before exploding your image, you can control the 'flash'
effect of the explosion. See
Distortion Animations
for an animated example of this color control.
Swirling Image Whirlpools
The "-swirl" operator
acts like a cake mixer. It will warp the image around in a circle the number
of degrees you give it as an argument.
convert koala.gif -swirl 180 swirl.jpg
|
|
|
By adding a border, and combining with "-implode" you can
give the look of a whirlpool sucking the image up to oblivion.
convert koala.gif -bordercolor white -border 20x20 \
-swirl 180 -implode .3 -shave 20x20 whirlpool.jpg
|
|
|
I have animated these swirling effects, which you can see below in
Distortion Animations.
Limiting the Distort with Regions
All of the distortion operators work on a full image. However some of the
distortion methods allow you to distort a smaller area or "
-region".
For example here we have a line of stripes.
convert -size 600x70 xc:darkred \
-fill white -draw 'roundrectangle 5,5 595,65 5,5' \
-fill black -draw 'rectangle 5,25 595,31' \
-fill red -draw 'rectangle 5,39 595,45' \
lines.gif
|
Now by defining regions we can distort the line in various ways in specific
places.
convert lines.gif \
-region 90x70+10+0 -swirl 400 \
-region 90x70+100+0 -swirl 400 \
-region 90x70+190+0 -swirl -400 \
-region 120x70+280+0 -implode 1.5 \
-region 100x70+380+0 -implode -7 \
-region 101x90+480-10 -wave 10x50 \
lines_regions.gif
|
Note that the two
Circular Distortion operators "
-implode" and "
-swirl", fit into the use of
regions very well, as they have the property that the outside edge of the
distorted image matches up to the rest of the image outside the defined
region.
Also note that we could even use a
Wave Distortion in the
above, but that to do so, I had to enlarge its defined "
-region" of operation.
This is very tricky, and only works in the special case when the region covers
the whole height of the image being processed.
How Regions Work
In reality the way regions work is they basically extract area of the image
that the "
-region"
covers. This image is then run through the distortion operator, and then it
is
Composed back on top of the original image.
This actually works for
any image operation (not just distorts).
For example here I define a region on the koala image, and
rotate it, with a 'blue' background 'corner fill'...
convert koala.gif -region 30x30+10+10 -background blue -rotate 30 \
koala_region_1.gif
| |
|
Even though this operation was not very useful in terms of results, you will
notice that only the defined region was modified.
The other thing that you should notice, is that as the resulting image was
enlarged by the rotation the enlarged image was still overlaid at the
original coordinates, overflowing the areas from which the image was
extracted.
The point this is that while regions can be used for interesting effects, or
for limit the effect of operators, it will break down in its intended use if
the size of the extracted image is changed. That is you need to be careful in
what operations you apply to a extracted region.
Now what if instead of using a 'blue' corner fill, we instead use a
transparent fill.
convert koala.gif -region 30x30+10+10 -matte \
-background none -rotate 30 koala_region_2.gif
| |
|
This shows that when a region is overlaid back onto the original image,
anything that is transparent will show the original image. That is the
extracted region is only a copy, and does not erase the original area.
Finally you can do multiple operations on a region. For example...
convert koala.gif -region 30x30+10+10 -matte \
-background blue -rotate 30 -background red -rotate 30 \
koala_region_3.gif
| |
|
Note that the second 'red' rotation was applied to the previously 'blue'
rotated reagion extracted image. That is the modified region was not restored
to the original image until after both rotations had been applied.
In other words, the "
-region" operator extracts an area of image to perform
operations in, and will only restore that area (using
Composite Over) when either the end of the
command is reached, or another "
-region" is requested.
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 to
Shifting or Shear the pixels
either horizontally or vertically, such as what IM does with
Image Shearing and the
Wave Distortion above. The
shears in turn providing a 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 new areas of the resulting image that is not
covered by the 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.
Reverse Pixel Mapping
If we try to use a direct mapping each pixel from the source image to
destination image, one pixel at a time, as is done for
Simple Distorts, we get lots of problems. A specific pixel may map to a
location that another pixel has already colored (overlaps), while a
destination position may not even get any color from the source image (holes).
The reason is that pixels require an integer location in both the source and
destination. But a general transformation generally produces a real, or
floating point value. That is a direct mapping of pixels from an old location
to a new position just does not work.
Reverse Pixel Mapping does the opposite.
For each and every pixel in the destination image, the color needed for that
pixel is looked up from the pixel array in the source image. As each and
every pixel is process, 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.
In Summary, a distortion 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 that you would use in
the "
-fx" or
DIY Operator.
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 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, to ensure you 'fill in' all the pixels of the destination image. The
problem is not all forward mapping transforms, work as a reversed transform.
On the other hand some image distortions work very well when used as a reverse
mapping, allowing you to produce interesting effects.
You can see the above in more detail in a DIY explanation in the sub-page
DIY Distortion Mapping, as well later in the
form of a more generalised technique of using
Distortion LUT
Maps.
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 is known a '
Image Warping' or
'
Rubber Sheeting'.
Images can also be subdivided into smaller simpler units (generally
triangles) using a process known as '
Image Registration', to map
similar points from one image to another. From the distortion of these
triangles, you can then perform '
Image Morphing' 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-divided images into regions that approach a single pixel.
All the above are very closely related, and all basically involve the exact
same thing, distorting images by looking up a pixel color basied on a specific
desination coordinate. 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).
|
Direct Interpolated Pixel Lookup
There are still a few problems with the above
Reverse
Pixel Mapping technique. First of all is that when mapping a pixel from
an integer position on the destination you can end up with a non-integer
position on the source. That is a location that falls between 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), or 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 source
position for the destination position being mapped. For example the pixel
looks into the 'sky' of a perspective 'plane' (See
Viewing
Distant Horizons). This probably should have a completely different
response to just a normal 'miss' of the source image.
The
Interpolation setting will also
handle the case that part of an 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 we'll look at shortly.
Super Sampling, Improving Distortion Results
If part of the source image gets compressed into a much smaller area, the
color of the destination image in that area should probably be a mix of a
number source pixels, not just the nearest pixels to the lookup point.
However only one single color lookup is performed by a simple reverse pixel
mapping.
The result is a bit like using "
-sample" instead of "
-resize" in generating thumbnails. Thin lines become
'dotty' and edges start showing 'aliasing' or 'staircase' effects.
Currently the only 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 the
destination image. The simplest technique is generally know as
super-sampling, or
over-sampling. See Wikipedia Entry on
Super-Sampling.
Normally only one color lookup (sample) is taken from the source image for
each destination pixel that is generated. But if the positions of
neighbouring destination pixels when mapped into the source image are far
apart (the source image is compressed into a smaller area), the individual
pixels can start to look very different to each other, producing a 'pixelated'
or almost random look.
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 enlarges 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.
In
Imploding Images (above), I touched briefly on how
you can use 'super-sampling' to improve the look of an image. I'll repeat
those two examples here so you can see the difference.
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
|
Normal Implosion of a Box Image |
Super Sampled Implosion |
This is the simplest way to
super sample an image you plan on
distorting. Increase the size of the image being distorted, distort it, then
shrink the result.
A larger destination means the distortion does more lookups of the source
image, and so creates more samples in a standard grid. A final resize then
sharpens the whole result to produce a much higher quality distorted image.
It is also a lot slower, but that is the trade-off.
Of course rather than enlarging the image, you could either generate a larger
image, or apply it to a larger source image, before you do your final resize.
This is especially useful when rotating photos, and text, the source of which
is usally much larger that the final result, and where the quality of the
resulting image can be highly noticeable. For examples of this see the
Polaroid Transform.
On final word of warning. Super-sampling is limited by the number of samples
that was used for each pixel in the final image. That is the amount of
scaling used in the final resize, determines the final 'quality' of the
distorted image. Dividing the image size by 4 produces a much better result
than only halving the distorted image, but it will also be 4 times slower.
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, one that is provided by
Area Resampling (see below).
In summery, super-sampling can improve the look of images with only minor
distortions, such as rotations, and shears. But as you will see below, 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
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 restricted to using '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 it as an alternative sampling method to use by the
General Distortion Operator (see below).
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 the 'area' within the source
image, each output pixel represents.
In fact this is exactly what the ImageMagick
Resize
Operator (a simpler form 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 color, while another pixel elsewhere in
the image may need to a very large number of pixels to generate the correct
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
distorted shape, according to the distortion being used. Calculating and
handling such awkward shapes can be very time consuming, or near impossible to
achieve. As such it may be better to only approximation of the area of the
source image needed.
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
next).
A simple ellipse may not be perfect for all distortions, but it works a lot
better than simple pixel lookup, or a fixed scale super-sampling technique.
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.
convert -size 90x90 pattern:checkerboard -normalize -fill none \
-stroke gold -strokewidth 3 -draw 'rectangle 0,0 89,89' \
checks.png
convert checks.png -virtual-pixel tile -mattecolor DodgerBlue -filter point \
-distort Perspective '0,0 20,60 90,0 70,63 0,90 5,83 90,90 85,88' \
horizon_tile_point.png
convert checks.png -resize 1000% \
-virtual-pixel tile -mattecolor DodgerBlue -filter point \
-distort Perspective \
'0,0 200,600 900,0 700,630 0,900 50,830 900,900 850,880' \
-resize 10% horizon_tile_super.png
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
|
Direct Reverse Pixel Interpolated Lookup |
Super Sampling x10 |
Elliptical Weighted Area Resampled Image |
The last image was generated using the default EWA settings of the
Generalized Distortion Operator (see below). It took
4.3 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 (.49 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 to generate (1.4 seconds), but does
not produce any real improvement in quality. What improvement there is is
actually restricted to a thin region in the middle ground of the image, and
for this distortion, and image, that improvement is hardly noticeable.
The biggest difference between super-sampling and area-resampling, is that the
former only does a general improvement in quality over the whole image. 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.
For simpler distortions it is usually faster than super-sampling.
Now are you glad that I spent months adding Area Resampling to IM :-).
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.
The
General Distortion Mapping operator is called "
-distort", and you can see
what distortion methods it has available on your IM version using "
-list" with a
'
Distort' argument.
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
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 values given.
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.
As discussed above in the
Reversed Pixel Mapping, the
resulting image (for magnification) is also effected by the
Pixel Interpolation setting "
-interpolate" to
define how to lookup pixels from the source image, (especially in areas that
become enlarged).
For minification of the source image the
Resize
Filter settings controls the merging of pixel colors in the
Area Resampling algorithm.
Finally the
Virtual Pixel setting
defines what the pixels surrounding the image should look like. while the
color setting "
-mattecolor" defining the color of pixels when the
distortion mapping is undefined (for example the sky in a perspective
distortion).
Phew. That is a lot of settings that you can use, but really you generally
don't need all of them. Just define them as you need to.
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 resize the distorted
image so it will contain the whole of the input image, much like what the
Rotate and Shearing operators do.
However this particular 'mode' of operation also goes further an 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, even though the positions within the actual
image itself many not be as you would expect.
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, if these offsets are not wanted, or you don't
want it to effect the outcome of the distortion operation. See also
Removing Canvas/Page Geometry.
The "
-distort" will
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 Summery... Use "
-distort" to have results mapped into an image the same
size. And use "
+distort" to size the output to best-fit the distorted
image, BUT also use and generate Virtual Canvas Offsets (page attributes).
There is one final special option. As of IM v6.3.6-1 you can specify the
setting
-set 'option:distort:viewport' WxH+X+Y
which will set the size and location of the resulting image in the distorted
image space. This can be used to enlarge the destination image to a specific
size, or shift it to a specific offset. This setting will override the normal
output size handling of the "
-distort" operator.
Well that is enough about the operator, lets look at some of the specific
distortions that it provides. Here is where the fun really begins...
Scale-Rotate-Translate (SRT) Distortion
The simplest distortion, but with probably the most versatile, is the
'
SRT or '
Scale-Rotate-Translate' distortion. (SRT
is just a quick short-hand)
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 a (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...
convert koala.gif -background skyblue -virtual-pixel background \
-distort ScaleRotateTranslate -110 koala_distort_rotate.png
| |
|
Using the 'plus' form of "
+distort", and a clean up of virtual offsets, we can make it
exactly like the
Rotate operator, but with better
results.
convert koala.gif -background skyblue -virtual-pixel background \
+distort ScaleRotateTranslate -110 +repage koala_distort_rotate2.png
| |
|
Lets shrink it by 30% as well, but use a transparent background.
convert koala.gif -matte -virtual-pixel transparent \
+distort ScaleRotateTranslate '.7,-110' koala_distort_scale.png
| |
|
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_distort_center.png
| |
|
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_distort_trans.png
| |
|
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.
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 only distorting the image
one single time 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 with Affine
Rotates.
This distortion specifically designed to take an image of some known object
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 user friendly 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 -virtual-pixel background \
\( -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
|
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).
Under Construction
Experiment: use a interpolated filter for pixel resampling, rather than
the default EWA filter (which is best for extreme scales).
convert spaceship.gif -virtual-pixel background \
-filter point -interpolate mesh \
\( -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_launch2.gif
|
Note that while a interpolated 'point' lookup removed the blur in the initial
small scale changes, it causes a pixelated look in the last couple of frames
(with high minimization), which is where a EWA filter works much better. This
is a known problem and will be fixed, when I next get back into a coding
cycle.
Affine Distortion (a three point distort)
The previous '
SRT' distortion method,
actually reproduces a general distortion known as a 'affine' or linear space
transformation. However the above can not reproduce all the effects of a true
affine distortion, as it is designed to avoid generating 'shear' distortions.
(See
Shearing Images above).
To make full use of an affine image transformation, the '
Affine'
distortion method is defined in terms of the mapping of 3 control points.
This is defined in terms of sets of coordinate pairs, the old pair then the
new pair, of four values each. Three sets of 4 values makes a total of 12
values. For example...
-distort Affine 'X1,Y1 I1,J1, X2,Y2 I2,J2 X3,Y3 I3,J3'
With the above the coordinate X1,Y1 in the source image will be mapped to
the position I1,J1 in the destination image. Simularly for the next two
control points. Remember you can use either commas or spaces to seperate
the values given.
  |
All the distortions which are defined in terms of a set of control points
define the pairs of coodinates in this way. That includes not only
'Affine', but also 'Perspective' and 'BiLinear' distortions.
|
  |
Before IM version 6.3.6-0 when the "-distort" operator was first interoduced, the corrdinate
ordering was defines as all the source coordinates, followed by all the
destination coordinates. This however make it hard to determine
which source and destination coordines corresponded to each other.
The change in ordering also allowed for the use of more than a minimal
number of control points for least squares fiting of the distortion to the
control points. That intern permitted the use of future 'image
registration' techniques for automatic determination of control points.
Finally the new system will also allow for the future reading of control
points directly from a file with one control point pair per line.
|
Another way you can think of this is the mapping of three corners of a
triangle.
FUTURE: Image with a obvious triangle of control points.
Alternativally you can thing of 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 to distort the image with.
convert -background lightblue -fill Gray -font Candice \
-size 100x100 -gravity center label:Affine\! \
-draw 'fill blue stroke blue path "M 2,60 32,60 M 27,58 27,62 32,60 Z"' \
-draw 'fill red stroke red path "M 2,60 2,30 M 0,35 4,35 2,30 Z"' \
label_axis.png
convert label_axis.png -virtual-pixel background \
-distort Affine '2,60 2,60 32,60 32,60 2,30 17,35' \
label_axis_distort_shear.png
convert label_axis.png -virtual-pixel background \
-distort Affine '2,60 2,60 32,60,27,85 2,30 27,35' \
label_axis_distort_rotate.png
convert label_axis.png -virtual-pixel background \
-distort Affine '2,60 30,50 32,60 60,80 2,30 30,5' \
label_axis_distort_affine.png
|
In the first example only the third coordinate (for the red vector) was
modified causing the image to be sheared. Later examples make more radical
changes to the image, including scaling, and rotations.
Of course the
Annotate Text operator can also
skew actual text in this same way more directly (also see
Annotate Argument Usage), but an Affine
distortion can do this for any image, not just text.
Affine Matrix Distortion
Under Construction
Using a raw affine matrix for the distortion
FUTURE
For now see the old
Affine Matrix Examples that uses
the "
-affine" and "
-transform" operational
pair for distorting images.
FUTURE: Look at +distort 'BestFit' and offset overlaying.
Affine Tiling
All three affine-like distortion methods we have looked at so far, also
provides interesting ways to generate various tiling patterns, based on a
distorted image.
For this example I created a special checkerboard image, with a extra border
to show the images normal bounds, when tiled.
convert -size 90x90 pattern:checkerboard -normalize -fill none \
-stroke gold -strokewidth 3 -draw 'rectangle 0,0 89,89' \
checks.png
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
|
Using a distortion mapping in this way is actually how 'texture mapping' works
in 3D graphic libraries and games. The only difference is that the
distortions are 3 dimensional coordinate mapping of surfaces, back to a two
dimensional image.
FUTURE: specifying alternative output image sizes or 'viewports'.
Perspective Distortion (a four point distort)
This is probably the most common requested type of distortion at the time of
this operators creation has been for a fast perspective distortion operation.
This is an implementation of a linear trapezoidal or keystone distortion, and
works by asking IM to move as set of four image coordinates from one position
to another position.
For example, here I have a image building. From this image I manually
discovered the location of 4 points (red). 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 red 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
|
The result is 2 sets of 4 pairs of image coordinates, a total of 16 numbers.
these are then re-ordered into 4 sets of mapping pairs, to produce the
arguments needed by the '
perspective' distortion method (source
coordinates is given first).
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
|
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' as the 'transform' would place outside the new
image.
As a matter of interest lets also reverse the distortion, by swaping 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
|
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 alway try to distort from an original image, rather than
distorting an already distorted image.
Internally the given coordinates (16 numbers) is converted into a mathematical
expression which maps a coordinate in the destination image (which is the same
size as the source image) to the source image. This in turn lets us look up
the colors to place on the destination image. See
Reverse
Pixel Mapping above.
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
|
You can see the slight fuzziness caused by the distortion, but the image
is basically restored.
FUTURE: Alternative. Coordinates represents 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.
Good Example needed.
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
| |
|
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 appearence 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 infiniteally
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
| |
|
A word of warning about this image. Asking for an infinitely tiled image is
very 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, but
it can still take a long time.
For more deatils 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
| |
|
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 the what part of the 'distorted image space'
I am looking at using a special 'distort:viewport' setting. This setting
overrides the normal size and location of the area of distorted space being
viewed.
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
| |
|
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
| |
|
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 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 'control points' used to distort the image, line-up in the
distorted output, the images will also line-up correctly, so they can be
Layered together.
For example here we generate two images, a 'front' and a 'spine' image, whcih
is then distorted and layered on top of each other, using two shared control
points at 0,0 and 0,199.
Producing the following
image...
# Generate a Spine Image
convert -size 200x40 xc:lightblue \
-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:lightblue \
-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,169 149,0 99,30' \
\) \
-background black -layers merge +repage \
-bordercolor black -border 15x2 box_set.jpg
|
The above is also available in the shell script "
box_set_example" so that you
can download and play with it.
You can take this further to also add mirror images of the 'box' being
reflected by surface on which it sits.
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 (still to be added).
By default it will curve the given image into a perfectly circular arc over
angle given, and without other arguments it will try to preserve the scaling
of horizontal center-line of the image, and the aspect ratio of the image as
much as possible.
To do this it takes up to four arguments.
arc_angle rotate 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
| |
|
  |
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.
|
Adding a second argument allows you to rotate the image around the circle.
For example make the right edge horizontal by rotating it 60 degrees.
convert rose: -virtual-pixel white -distort arc '60 60' arc_rose_rot.jpg
| |
|
As no specific radius argument has be mentioned, the '
Arc'
distortion method takes great pains to try to ensure the image is scaled as
minimally as possible. To do this the horizontal center line of the image is
set to the ideal radius for the width of the source image.
This means that if you arc the image over a larger angle, the radius of the
centerline 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
| |
|
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
|
Longer images '
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
|
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 white
Virtual
Pixel background.
The simplest solution to prevent the background color 'leaking' into the join
like this is to add a very very slight overlap in the joined image.
convert -font Candice -pointsize 20 label:' Around the World ' \
-virtual-pixel background -background SkyBlue \
-distort arc 362 arc_circle.jpg
| |
|
Overlapping the sides of the image like this however will 'chop' a few pixels
from the image where they join. It will also may cause a very sharp
color change along that same 'join' line. Well look at other ways
to fix by adjusting the
Virtual
Pixel setting later.
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
| |
|
The third argument will override the 'ideal' radius for the top of the image.
This has the effect of 'fitting' the distorted image so it exactly fits a
circle of the radius you give.
convert -font Candice -pointsize 20 label:' Around the World ' \
-virtual-pixel background -background SkyBlue \
-distort arc '362 0 50' arc_radius.jpg
| |
|
Note how the whole image was enlarged to match the new radius. This preserves
the original images aspect ratio (height to width relation) as much as
possible.
However if you provide the fourth "bottom edge 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 background -background SkyBlue \
-distort arc '362 0 45 30' arc_inner.jpg
| |
|
You can even force it to completely fill the inside of the circle.
convert -font Candice -pointsize 20 label:' Around the World ' \
-virtual-pixel background -background SkyBlue \
-distort arc '362 0 45 -1' arc_fill.jpg
| < |