convert koala.gif -virtual-pixel white \
-distort Affine '30,11 15,15 48,29 60,15' koala_two_point.png
|
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
|
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
| |
|
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
|
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
|
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
|
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
| |
|
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
| |
|
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
| |
|
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
|
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' 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
|
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
|
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
| |
|
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
| |
|
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
| |
|
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
| |
|
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 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
|
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
| |
|
|
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
| |
|
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
| |
|
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 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
|
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
| |
|
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
| |
|
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 "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
| |
|
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
| |
|
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
| |
|
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
| |
|
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
| |
|
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
| |
|
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
| |
|
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
| |
|
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
| |
|
|
convert tree.gif -set option:distort:viewport 120x60-60-60 \
-virtual-pixel Dither +distort Arc '180 0 25 0' \
+repage arc_rays.gif
| |
|
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
| |
|
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
|
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
| |
|
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
|
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
|
(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
|
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
|
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
| |
|
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
|
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
| |
|
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
| |
|
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
| |
|
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
| |
|
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
| |
|
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
| |
|
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
|
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
|
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
|
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
| |
|
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
| |
|
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
| |
|
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
| |
|
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
|
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
| |
|
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
| |
|
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
| |
|
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
|
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
|
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.
|
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:
URL: http://www.imagemagick.org/Usage/distorts/
|