- Index
ImageMagick Examples Preface and Index
Introduction to Image Mapping
Color Lookup Tables
Dithering Patterns and Threshold Maps
Variable Blur Mapping
Distorting Images using Image Mapping
Absolute Distortion Lookup Maps
Relative Displacement Lookup Maps
2 Dimensional Displacement Maps
Distorting or modifying image using some sort of secondary 'mapping' image
that controls the process. Be it replacing colors, variably bluring the image,
or distorting images by specifying source coordinates absolutely or
relativally.
Introduction
As you have seen in the previous sections on
Composition,
Simple Warping, and
Distorting, you can modify images in many different
ways. However they all are limited to the specific methods that have been
built into ImageMagick.
You can of course 'roll your own' image composition, or distortion using the
'FX' DIY Operator, or directly modify the
values of an image using operators such as
Evaluate or
Function, or even the
various
Level operators.
However all these techniques only work by globally modifying the whole image,
(or a smaller
Region of the image). None of
them lets you modify one area in one way, and modify another area in another
way. They all work globally.
Image Mapping is different. It uses a second 'mapping' image to
control what parts of an image is to be modified, and by how much. It does
not have to modify the whole image, nor does it have to modify the image in
some pre-defined pre-programmed way.
You can create a 'map' that can modify an image in
ANY posible way,
without any limitations. The 'map' controls the results.
As the modification is 'map' controlled, their is usally very little
calculation needed to be performed by ImageMagick, as such 'image mapping' is
in general
very fast.
It is also
repeatable. You can apply a very complex map you created
for one image, to a large number of images, to get the exact same
modification. That is to say you can apply it to a whole directory of images
very quickly.
Image Maps
Mapping images are basically "Look Up Tables" or LUTs that define how
a particular effect sould be applied to an image on an individual pixel by
pixel bases. That is wether an effect is applied and to what degree is
completely controled by a pre-prepared 'map'.
It provides you with a way to have a very fine control, in a DIY way. You can
even generate or modify the controling mapping image in a image editor to fine
tune or change the application of the mapped effect. You are not just simply
confined to mathematical modifications of images, or modification to a 'masked
area'.
Everything from generating ripples in a pond, to blurring specific areas of an
image. or generating unusual lens effects, or the effect of looking through
distorted glass, or mirrors.
The hardest part is of course to generate a particular 'map' for a particular
effect. And this is where a lot of the work, effort and techniques that are
present on this page is involved with.
Typically this is done by using the various pre-existing operators and
applying them to a
Gradient Image of some
kind. Once you have a map however you can use it many times.
In essence what
Image Mapping does is move the slow, and complex
mathematics of a particular effect from a specific image, to a more general
'map' image. once that 'map' is generated it can then be applied to a lot of
actual images very quickly.
Color Lookup Tables
Color Lookup Tables (CLUTs) are the simplist form of '
Image Mapping' to
understand.
Basically a 'gray-scale' color value, of the source image, is used as a
'position' to lookup a replacement color value in a 'CLUT' whcih is simply
just a linear gradient of colors.
The result in the simplest case (See the
CLUT
Operator) a direct grey-scale to color replacement mapping.
However it can do much more than this, and it is recommend you understand this
technique before you proceed to much more complex techniques below.
See
Recoloring Images with Lookup Tables.
Dithering Patterns and Threshold Maps
In may ways 'dither mapping' is very similar to
Color Lookup
Tables. However instead of mapping color values to a gradient, the values
are used either to select a 'dither pattern' from an array of images, or
to threshold a 'threshold pattern' to generate a bitmap pattern to represent
the original value.
The primary purpose of this is typically to reduce the number of colors within
an image. Especially so you can then save the image to a limited-color image
file format such as
GIF.
For details see..
DIY Dither Patterns and
Threshold Maps.
Variable Blur Mapping
Added to ImageMagick version 6.5.4-0, the "
-compose" method
'
Blur' provides you with a method of replacing each individual
pixel by a Elliptical Gaussian Average (a blur) of the neighbouring pixels,
according to a mapping image.
composite -blur {Xscale}[x{Yscale}[+{angle}]] blur_map image result
convert image blur_map -compose blur \
-set "option:compose:args" {Xscale}[x{Yscale}[+{angle}]] \
-composite result
This means you can blur one part of an image, while leaving another part
completely normal, or produce effects such as Tilt-Shift, where a real world
image is made to appear more like a small artifical model.
For example, here I blur one half of a image of a koala while leaving the
other half completely un-blurred...
convert -size 37x75 xc:black -size 38x75 xc:white +append blur_map_bool.gif
convert koala.gif blur_map_bool.gif \
-compose blur -set option:compose:args 5 -composite \
blur_koala_bool.gif
|
As you can see any pixel that was 'white' on the 'blur_map' image was blurred
by the amount given, while anything that was 'black' was not blurred. In
other words a masked blur. Which could of course be done many ways.
The tricky part of the above is that only the areas blurred will take extra
time needed. Pixels which are not blurred, do not need this extra processing.
This makes the above much faster than either using a
Masked
Composite which is the same as bluring the whole image and merging the
results. This time saving can be even more important when dealing with for
large blurs of very areas of an image.
What makes this blur mapping more versitile is that it is varable. That is if
the blur mapping for a pixel is a gray color, than you will get
a corresponding smaller blurred result, using a smaller 'neighbourhood', for
that pixel. Black in the blur mask is not blurred, while white is maximumally
blurred, by the values given.
For example, lets make the koala progressivally more blurry toward his feet...
convert -size 75x75 gradient:black-white blur_map_gradient.gif
convert koala.gif blur_map_gradient.gif \
-compose blur -set option:compose:args 5 -composite \
blur_koala_gradient.gif
|
And here is the same blur again but showing how the blur varies with the
height.
convert blur_map_bool.gif blur_map_gradient.gif \
-compose blur -set option:compose:args 30 -composite \
blur_edge_gradient.gif
|
For a practical example of Variable Mapped Blurs, have a look at
Photo Tilt Shift Effect, and
Distance Blurred Shadow Font.
Note that it is the neighbourhood around each individual pixel that is used to
generate the 'blurred color' for that pixel. That means that even though you
may specify some part of an image to not be blurred, colors from that
unblurred area may be used as part of the blur of surrounding pixels.
That is just because an area is not blurred does not mean that colors from
that area is not used as part of result of other blurred pixels. That is
colors from the unblurred area can 'leak' into the surrounding blurred areas.
Future example, attempt to blur a background, but not the foreground.
Direct use of variable blur for this will fail!
Elliptical Blurring
The '
Blur' compose setting uses a different technique to the
normal
Blur or Gaussian Blur Operators, as it
is implemented by using a Gaussian
Elliptical Area Resampling algorithm that was developed for scaled image
resampling as part of
Generalized Distortion
Operator.
The elliptical area used for the neighbourhood resampling, also makes this
method of blurring more versitile than a normal uniform 'circluar' blur
provided by the operators "
-blur" and "
-gaussian-blur". The ellipse itself is defined by the
'
width', '
height' of the sigma for the blurred area. The ellipse
can also be rotated from a orthogonal alignment by the given '
angle'
(clock-wise).
For example in the following diagram we show how the blurred color of a single
pixel will get its color from a rotated elliptical area, which is twice as
large (its support factor) as the required width and height. The pixels in
this area are then weighted averaged together according to a
Gaussian Weighting Function (using
a elliptical distance formula, to produce the blurred color.
As mentioned, this is exactly the same color lookup method that is used by the
Generalized Distortion Operator to
generate the colors for its distorted images, as it allows for a scaled (and
filtered) merge of an area of the source image into one pixel, especially in
extreme distortions such as exampled in
Viewing Distant Horizons. For more details of this process see
Area Resampling and
Resize Filters.
As an example of the elliptical controls available for variable blur mapping,
lets use a black dot using the same gradient blur map we used before. But this
time we will scale a long thin horizontal ellipse '30x0', rather
than a circle. The 'x0' may seem weird but basically means no vertical
blurring should be seen, just an ellipse of minimal height needed for a good
result.
convert -size 75x75 xc: -draw 'circle 36,36 36,8' black_circle.gif
convert black_circle.gif blur_map_gradient.gif \
-compose blur -set option:compose:args 30x0 -composite \
blur_horizontal.gif
|
As you can see the amount of the blur is still varied with the map image
provided, producing very little blur at the top of the image, and a lot of
blur at the bottom. But also notice that the bottom edge is blurred
horizontally equally in both directions, but not vertically, producing a sharp
cut off in the vertical direction.
By either rotating the long thin ellipse by giving a third
angle
argument, or by directly defining a vertical ellipse, you can blur the image
vertically only...
convert black_circle.gif blur_map_gradient.gif \
-compose blur -set option:compose:args 0x30 -composite \
blur_vertical.gif
| |
|
Note however that the blur was not applied equally! The top half appears less
blurred than the bottom, because that is what the 'mapping image' told it to
do. This in turn distorts the image making it appear a little truncated by
the blurring effect.
Finally lets do this one more time but with a horizontal ellipse rotated by
a fixed 45 degree angle.
convert black_circle.gif blur_map_gradient.gif \
-compose blur -set option:compose:args 30x0+45 -composite \
blur_angle.gif
| |
|
The image may appear very odd, but that is because the variable blur map is
vertical while the blur itself is angled, producing the odd looking effect,
due to the way the ellipse angle and the angle of the blur map do not align.
  |
Note that using long thin ellipses like this is actually a lot faster that
using a single large circle. In fact the "-blur" operator gets its speed by
using two separate horizontal and vertical blurs, where as the "-gaussian" blur operator does
a full 2 dimensional convolution in
a simpler way to the 'Blur' composition method just described.
|
Blur with Variable Aspect Ratio
So far the we have varied the size of the elliptical area used for the blur
using 'blur map'. However the while the size of the ellipse and even its
angle can be rotated, it shape and angle remains fixed.
Now the 'blur map' is an image that is composed of three color channels: red,
green, and blue. As we used a grey-scale image all three color channels had
the same values. However internally the width of the ellipse is scaled by
just the red channel value, while the height is scaled by the green channel
value. Any effect of the blue channel value is typically ignored except in
a special case which we will look at later.
This means the elliptical shape or its 'aspect ratio' can be varied by using
different maps for the individual red and green channels. As with a normal
blur map a zero (or 'black' in just that channel) value will result in minimal
width or height, while a maximum value (or 'white') will result in the blur
amount given.
For example here I can divide the image so that two quarters of the image is
blurred horizontally (red channel is maximal) while making the other areas
blur vertically (green channel is maximal).
For this example I generated with width and height maps separately, before
Combining them into a single and now colorful
'blur map'. In normal practice you can create the map in any way you like, or
even use pre-prepared maps for specific blur effects.
convert -size 2x2 pattern:gray50 -sample 75x75! blur_map_r.gif
convert blur_map_r.gif -negate blur_map_g.gif
convert blur_map_r.gif blur_map_g.gif -background black \
-channel RG -combine blur_map_aspect.gif
convert black_circle.gif blur_map_aspect.gif \
-compose blur -set option:compose:args 20x20 -composite \
blur_aspect.gif
|
You can of course still set a fixed angle to the ellipse.
convert black_circle.gif blur_map_aspect.gif \
-compose blur -set option:compose:args 30x30+45 -composite \
blur_aspect_angle.gif
| |
|
  |
Before IM version 6.5.8-8 a bug was found in the handling of an angled
vertical elliptical blur.
|
Blur with Variable Angle
So far the angle of the ellipse used for bluring the image has been a constant
angle over the whole image. That is the ellipse used for the blur has always
been at the same angle, even though the aspect ratio of the ellipse can be
varied by modifying the red and green channels of the blur map.
As of IM v6.5.8-8 you can provide two angle values in the blur arguments. This
allows you to vary the angle of the ellipse across the image, according to
the values in the blue channel of the 'blur map'. The first angle argument is
used to define the angle for a zero ('0' or 'black') value in the blue
channel, while the second angle given is used to define the maximum
('QuantumRange' or 'white') value of the blue channel.
If only one angle value is given, then that angle is used to set the angles
for both zero and maximum 'blue' channel value which basically means the angle
becomes fixed, regardless of what value is present in the blue channel of the
'blur map' image. This is why in previous examples, the angle has been
constant.
For example, here I use a horizontally blurring ellipse, but then vary the
angle of the ellipse using the blue channel over the angle range from '+0' to
'+360' around the center of the image. The map generation uses a polar
gradient, the details of which can be found in
Distorted Gradients.
Note how when placing that gradient into the blue channel, I use the "
-background" color setting
with the
Combine Operator to ensure both
the red and green channels are set to a maximum ('white') value, so it does
not scale the angled ellipse.
convert -size 100x300 gradient: -rotate 90 \
+distort Polar '36.5,0,.5,.5' +repage -flop gradient_polar.jpg
convert gradient_polar.jpg -background white \
-channel B -combine blur_map_angle.jpg
convert koala.gif blur_map_angle.jpg \
-compose blur -set option:compose:args 10x0+0+360 -composite \
blur_rotated.jpg
|
The result is as you can see a rotationally blurred image.
Compare the result with the blur mapping that was used. At the top of the
image the angle, the gradient was either white or black, which with the
arguments used means the ellipse was angled at either 0 or 360 so the ellipse
remained horizontal. at the bottom the graident was pure gray, so a angle
midway between the rangle given was use, or 180 degrees. This means the
ellipse is again horizontal. But at the sizes the image the gradient was
either 25% or 75% gray. thus the angle was either 90 or 270 degress making the
ellipse rotate vertically. All the other angles follow though causing the
ellipse to rotate smoothly around the image.
However the center of the resulting image was blurred really weirdly! That is
because the ellipse size remained constant and does not get appropriatally
smaller toward the middle of the image.
The solution is to also set the ellipse size using the red and green channels.
For example.
convert -size 106x106 radial-gradient: -negate \
-gravity center -crop 75x75+0+0 +repage gradient_radial.jpg
convert gradient_radial.jpg gradient_radial.jpg gradient_polar.jpg \
-channel RGB -combine blur_map_polar.jpg
convert koala.gif blur_map_polar.jpg \
-compose blur -set option:compose:args 20x0+0+360 -composite \
blur_polar.jpg
|
A much better result.
Note however that while the result looks good, the blurring ellipse is not
properly curved in an arc as it should be for a true rotationally blurred
image. As such the above is only an approximation of a true rotational blur.
But for small blur distances (equating to blur angle) it quite good.
The better way to do rotational blurs is to use a special
Polar-Depolar Distortion Technique,
or the currently miss-named
Radial Blur
Operator.
By changing the angle range used for the ellipse angle (blue channel) you can
easily convert the above into a radial blur that becomes more blurry with
distance from the center.
convert koala.gif blur_map_polar.jpg \
-compose blur -set option:compose:args 10x0+90+450 -composite \
blur_radial.jpg
| |
|
But you can also do much more than these radial/rotational blurs, as you can
rotate and scale the blur anywhere by any amount over the whole image. You
have total control.
For example you can make a very weird mixture of the two by using different
angle range so the angle of the blur ellipse does not match the angle around
the image center.
convert koala.gif blur_map_polar.jpg \
-compose blur -set option:compose:args 15x0+0+180 -composite \
blur_weird.jpg
| |
|
Basically you now have complete control of the how and what parts of the image
will be blurred. And with the use of templates you can create a whole library
of blurring effects.
Distorting Images using Image Mapping
While the various distortion operators described in the previous secions of IM
Exampled (such as
Simple Image Warping and
General Image Distortions), you are restricted to
just the various types distortions that have been programmed into the IM
graphics library, generally using specific mathematical equations and
formulas.
However there are times where you want to design your own distortion in a more
freeform and less mathematical way. For example to generate a more complex
distortion, such a mapping an image into a particular shape, or with a
particular complex lens effect, that is easier to draw than to define
mathematically. Sometimes you just want to be able to repeat your distortion
over a large number of images and avoid having to re-calculate the distortion
over and over.
The solution is to pre-calculate your distortion, and save it as special
look-up table (LUT) in the form of a grayscale image.
That is for each output pixel, we look-up the LUT, then use that value to
look-up the color from the source image. That is three steps are needed.
- Look-up up each destination pixel in the LUT
- Map the LUT value to the source image location (two methods)
- Look-up the color from the source image
This image can be quickly applied repeatedly very quickly over many different
images, without needing to re-calculate it all the time. That is you do all
your calculations into a set of look-up table images, applying all the heavy
calculations, distortions and other image warping effects. Then you can apply
the pre-calculated distortion maps to any number of images, as many times as
you like, quickly and easilly.
As a image is used for the 'lookup table' of the distortion, you can create,
or modify a the distortion map using a image editor, such as
'
Gimp' or '
PhotoShop', giving you the freedom to do
some really fancy and complex distortions.
There are two major ways of defining a distortion LUT.
One method is where the values (or colors) of the LUT represent the pixel
coordinate to look-up from the source image. That set of values represent the
absolute coordinates of the source pixel to place in this position on
the destination image. See next section on
Absolute
Distortion Maps.
The other method seems harder, but is actually more versatile. In this case
the LUT image represent coordinate offset, relative to the current position.
Gray mean no offset; White a maximum positive offset, and Black a maximum
negative offset. That is the values represent a
displacement from from
the current position to the lookup coordinate of the source image to use. See
Displacement Maps (Relative Lookup Maps) below.
Both methods have advantages and disadvantages, as you will see.
Absolute Distortion Lookup Maps
FUTURE:
Start with a red-green 'gradient' that represents the image coodinates, and
distort that. The result is a map of distorted image coordinates.
That is the essence of an absolute distortion map.
Creating an
Absolute Distortion LUT Map is the simpler of the two
methods to both understand, create distortion LUT maps for, and to apply.
However, as you will see, it has a very serious drawbacks making them less
practical than a
Relative Displacement Map.
Before proceeding you need to understand that distort maps, like just about
all the distortion methods we have seen, are applied as a
Reverse Pixel Mapping. That is we don't map a source pixel to a
destination coordinate, but use it to lookup the source image from the
destination image coordinates.
What this means is that for each pixel in the destination image, we look-up the
color of the pixel from the source image, using the distortion method being
applied. In this case the method is to look-up the source coordinate from the
provided Look-up Table Image.
Unfortunately at this time IM can not directly handle such distortion maps.
However we can use the
FX, General DIY
Operator to do do all three steps.
First lets start with some basic definitions, of what the LUT will mean.
Any 'black' pixel in the LUT image (value 0.0) will be thought of as the
left-most pixel or '
0' X coordinate of the source image, while
anything that is 'white' in the LUT (value 1.0), is to be thought of as the
right-most pixel (the width of the source image).
Note that this LUT will only look-up the X or horizontal position of color in
the source image. It will not change the height or Y positions of colors.
So lets try this with a simple plain gray-scale horizontal gradient for the
LUT.
convert koala.gif \( -size 75x75 gradient: -rotate 90 \) \
-fx 'p{v*w,j}' distort_noop.gif
|
Note that this did not make any real changes in mapping the source image to
the destination image. That is because the X coordinate that we looked up from
the distortion map, was the same position we were looking up the color for.
However by simply flipping the gradient over the look-up of the pixels is also
flipped, creating a mirror image. That is white is on the left and 'black' is
on the right, and a horizontal gradient across the image.
convert koala.gif \( -size 75x75 gradient: -rotate -90 \) \
-fx 'p{v*w,j}' distort_mirror_x.gif
|
If we take the original gradient and compress it using a contrast enhancement
operator, we can get a much more useful distortion.
convert -size 75x75 gradient: -rotate 90 \
-sigmoidal-contrast 8,50% map_compress.gif
convert koala.gif map_compress.gif -fx 'p{v*w,j}' distort_compress.gif
|
Notice that the sides of the distortion is stretched while the center is
compressed.
We can expand this to two dimensions by using 2 distortion maps, one to adjust
the X coordinate, the other for the Y coordinate.
convert map_compress.gif -rotate 90 map_compress_y.gif
convert koala.gif map_compress.gif map_compress_y.gif \
-fx 'p{u[1]*w,u[2]*h}' distort_compress_2D.gif
|
As you can see the above recreated a variation of the implosion method, though
only by compressing the images along the X and Y axis (simultaneously), rather
than radially as the
Implode operator does.
The NO-OP Distortion Map revisited
Before we get any further I would like to just step back a moment a have
another look at the the 'noop' example above. This actually will blur the
image slightly, as the formula as I have outlined is actually not exactly
correct.
Basically to do a perfect 'copy' of the original image, you need map a 'white'
(or 1.0) value from the LUT to the exact right-most (or bottom-most) pixel.
This pixel is located at width or height of the source image minus 1.
The following command shows a correct "
-fx" absolute distortion LUT mapping formula, and a simple
method of testing that 'no-op' distortion map for accuracy.
convert -size 75x75 pattern:gray50 \( gradient: -rotate 90 \) gradient: \
-fx 'p{u[1]*(w-1),u[2]*(h-1)}' distort_fx_check.gif
|
|
|
The image used in the test above is a gray scale 'checks' pattern that will
highlight any failure in a no-op distortion, whether that distortion is caused
by the mapping image, or inaccuracies of the LUT mapping formula. Either case
will appear as a blurring of the pixels into a grey color either across the
whole image, or in bands or spots. If the formula above is correct the exact
same original checker board pattern (as shown) should be reproduced.
However it should be pointed out these slight inaccuracies are unimportant
when you are actually applying a map with an actual distortion. As such you
can usually ignore this minor adjustment in the LUT mapping, unless such
inaccuracies can become important, such as if you have undistorted areas
in the LUT map you are applying that you want to preserve as accurately as
possible.
Problems with Distortion Maps
The two maps we generated can, as in the previous example, be thought of as
two separate distortions, one in the X direction and the other in the Y
direction. In fact this is quite a valid thing to do.
However you can also view the two maps as representing a single distortion
of both X and Y absolute coordinates. This is how you must picture a
distortion involving rotation. For example...
convert -size 75x75 gradient: -background black -rotate 45 \
-gravity center -crop 75x75+0+0 +repage map_rot45_x.png
convert map_rot45_x.png -rotate 90 map_rot45_y.png
convert koala.gif map_rot45_x.png map_rot45_y.png \
-fx 'p{u[1]*w,u[2]*h}' distort_rot45.gif
|
And we now have another way of rotating any image.
The biggest problem with this technique is that by creating our distortion
maps using rotates, we introduced some oddly colored pixels along the sides of
the diagonal edges. In the last example this caused some random pixels to be
added in a line along the bottom right corner of the image.
These 'random' colors are anti-aliasing values that the rotate introduced to
produce a 'better' image. However for distortion maps, anti-aliased edge
pixels can cause a real problem.
Now we can try to better define the colors at the edges of the rotated LUT
images. In this case we can generate a larger gradient image, then cropping
the rotation down to the correct size.
convert -size 100x20 xc:white xc:black -size 115x75 gradient: \
+swap -append -rotate 45 \
-gravity center -crop 75x75+0+0 +repage map_rot45b_x.png
convert map_rot45b_x.png -rotate 90 map_rot45b_y.png
convert koala.gif map_rot45b_x.png map_rot45b_y.png \
-fx 'p{u[1]*w,u[2]*h}' distort_rot45_better.png
|
In this way all the pixels in the LUT are defined properly, including all the
edges of the rotated LUT gradients.
However that is not the only problem. We have in the process fully defined
all pixels, including the pixels in the corners of the rotated image.
These pixels are technically undefined. They have no meaning to the
final distorted image.
At this represents the biggest problem with using an LUT to specify the
absolute coordinates to get from the source image. You have no way of
specifying what IM should do in these undefined areas. You can't just specify
an extra color as these are just a simple gray-scale image that uses the full
range of values for the LUT.
Set a Undefined Pixel Border
There are a number of ways to solve this 'undefined' pixel problem.
The first and probably the simplest is to add an extra pixel or border
around the edge of the input image. Thus any Lookup that is clipped to the
border of the image will recieved this 'undefined' color.
That is we enlarging the original image (and thus the Distortion LUT image as
well) by adding a border of a suitable 'undefined' color.
convert -size 100x20 xc:white xc:black -size 117x77 gradient: \
+swap -append -rotate 45 \
-gravity center -crop 77x77+0+0 +repage map_rot45f_x.png
convert map_rot45f_x.png -rotate 90 map_rot45f_y.png
convert koala.gif -bordercolor white -border 1x1 \
map_rot45f_x.png map_rot45f_y.png -fx 'p{u[1]*w,u[2]*h}' \
distort_rot45_final.gif
|
This is similar to the use of
Virtual
Pixels or assigning a background color for a simple
Image Rotation distortion.
So lets have a look at a particular example using this technique.
Hourglass Distortion Map
Now I wanted a one dimensional distortion map, that scaled each row of the
image differently based on that rows height. Sort of producing a real
carnival fun house mirror distortion that makes fat people look very thin.
In other words a sort of hourglass distortion.
This is quite a complex LUT image, and after a lot of fiddling I came up with
the following expression to generate the height variable, but horizontally
linear gradient map.
convert -size 100x100 xc: -channel G \
-fx 'sc=.15; (i/w-.5)/(1+sc*cos(j*pi*2/h)-sc)+.5' \
-separate map_hourglass.png
| |
|
  |
When generating gray-scale gradients, you can make the -fx operator 3 times
faster, simply by asking it to only generate one color channel only, such as
the 'G' or green channel in the above example. This channel
can then be Separated to form the
required gray-scale image. This can represent a very large speed boost,
especially when using a very complex "-fx" formula.
|
The '
sc' is the scaling factor for the hourglass (value ranges
from 0 to 0.5), and allows you to adjust the magnitude of the distortion.
Now lets apply this map to the built-in "
rose:" image.
Note that the 100x100 pixel map does not match the 70x46 pixel image. This
complicates things, as we will need to scale the current pixel in the source
image by the appropriate amount to match the distortion map we give, to look-up
the location of that pixels color.
convert rose: map_hourglass.png \
-fx 'p{ v.p{i*v.w/w,j*v.h/h}*w, j}' distort_hourglass.png
| |
|
If you look at this carefully the pixels X coordinate '
i' is
multiplied by the width of the distortion map image '
v.w', and
divided by the original images width '
w', to produce
'
i*v.w/w. The same thing happens for the pixels Y coordinate,
'
j*v.h/h'. This re-scales the pixel coordinate in the
destination image to match the distortion LUT image. The looked up coordinate
is then scaled by multiplying the LUT value with the source images width, to
become the X coordinate for the color look-up.
If you have both an X and a Y distortion map, then you will have to repeat the
scaled look-up for the Y map.
Of course we have the same 'edge' distortions we saw previously, so lets add a
some transparent pixels to the edge of the source image.
convert rose: -matte -bordercolor none -border 1x0 map_hourglass.png \
-channel RGBA -fx 'p{ v.p{i*v.w/w,j*v.h/h}.g*w, j}' \
-shave 1x0 distort_hourglass2.png
| |
|
  |
Note the use of the "-channel" setting, as well as restricting the look-up to
just the green ('v.p{}.g') channel of the distortion map.
This is required when transforming images with transparency.
|
This distortion map could be made even better by using a non-linear gradient
so the image remains rectangular, with just the middle parts pushed inward.
Anyone like to give this a go? Mail Me
Set Undefined Pixels using a Mask
A more general way of solving the 'undefined pixel' problem is to define a map
of what pixels are actually a valid defined result in the distortion. In other
words a masking image.
For example...
convert -size 75x75 xc:white -background black -rotate 45 \
-gravity center -crop 75x75+0+0 +repage map_rot45b_m.png
convert distort_rot45_better.png map_rot45b_m.png \
+matte -compose CopyOpacity -composite distort_rot45_masked.png
|
Now we have three images involved with the distortion map, and the results is
getting complex indeed. Of course in a typical situation you probably will
not need to go that far, but for the general case you do.
Unified Distortion Image
You may however have noticed that all three maps are all grey scale images.
This means it is quite reasonable to merge all the maps into a single
distortion map image.
For example, lets map the 'X distortion map' to the '
red'
channel, the 'Y map' to the '
green', and the mask to the
'
alpha' or transparency channel, which makes it easier to handle.
convert map_rot45b_x.png map_rot45b_y.png \( map_rot45b_m.png -negate \) \
+matte -channel RGA -background black -combine map_rot45u.png
|
  |
The 'blue' channel in the Combined Channel Image is not defined, so takes its value from the
current "-background" color, which I preset to 'black'
or a value of zero in the above.
|
Now lets apply this unified distortion map to our koala image. This
unfortunately requires two image processing steps, one to distort the image,
and one to mask the result.
convert koala.gif -matte map_rot45u.png \
\( -clone 0,1 -fx 'p{v.r*w,v.g*h}' \
+clone -compose Dst_In -composite \) -delete 0,1 \
distort_rot45_unified.png
|
There is still an unused channel (
blue) in the 'unified
distortion map' image. One logical use for it is as a means to add highlights
and shadows to the distorted image. (See
Overlay highlights).
So let use You can see this tecnique applied in the
Spherical Distortion Map example below.
Spherical Distortion Map
In the previous
Hourglass Distortion Map example,
I generated a gradient which was horizontally scaled by a cosine curve.
With a little more work you can generate a spherical shape instead...
convert -size 100x100 xc: -channel R \
-fx 'yy=(j+.5)/h-.5; (i/w-.5)/(sqrt(1-4*yy^2))+.5' \
-separate +channel sphere_lut.png
| |
|
Note however that the above is not strictly accurate. The compressed gradient
remains a liner gradient, just compressed to fit within a circle. A more
accurate representation would probably require the creation of a non-linear
gradient. Which would in absolute position terms be a 'arccos()' function.
Now this mapping also has some large areas which would be classed as invalid,
so will need some type of masking to define what pixels will be valid and
invalid in the final image. A simple circle will do in this case.
convert -size 100x100 xc:black -fill white \
-draw 'circle 49.5,49.5 49.5,0' sphere_mask.png
| |
|
And to complete we also need a shading highlight, such as developed in
Overlay Highlights, for use by
Overlay or
Hardlight composition...
convert sphere_mask.png \
\( +clone -blur 0x20 -shade 110x21.7 -contrast-stretch 0% \
+sigmoidal-contrast 6x50% -fill grey50 -colorize 10% \) \
-composite sphere_overlay.png
| |
|
Remember the above shading will only matter within the bounds of the sphere
object, so the fact that the shade overflows those bounds is not important.
Actually if you like to try and come up with a better spherical shading, that
produces a even better ball like image, would love to see it.
So lets apply all three images: X coordinate LUT, Overlay Shading, and
Transparency Mask; to an actual image of the right size (for simplicity).
convert lena_orig.png -resize 100x100 sphere_lut.png -fx 'p{ v*w, j }' \
sphere_overlay.png -compose HardLight -composite \
sphere_mask.png -alpha off -compose CopyOpacity -composite \
sphere_lena.png
|
This particular example shows the most powerful aspect of a
Absolute Distortion Map. You can define a
gradient on any freeform object (not nessarially mathematically), so that any
image can be mapped onto that object, whether it be curves, wrinkles, folds,
etc. Simply put, once you have the object mapping worked out you can map any
image onto its surface.
Then to make it more realistic looking, you can overlay a second mapping, to
add highlights, shadows, edges, and other features.
Of course as all three images are grayscale, you can combine them into a
single
Unified Distortion Map image, for
easy storage. In this case I'll make it a more spherical distortion
by re-using the X coordinate distortion LUT for the Y coodinate as well.
convert sphere_lut.png \( +clone -transpose \) \
sphere_overlay.png \( sphere_mask.png -negate \) \
-channel RGBA -combine spherical_unified.png
| |
|
It is a rather pretty map. But if trying to interprete it, remember that: the
'red' and 'green' channels are the X and Y coodinate LUT, 'blue' is the high
light and shadow effects overlay, and the transparency channel holds the
invalid pixel mask for the final image.
And here I apply the complete 2-dimensional unified LUT to another image...
convert mandrill_grid_sm.jpg spherical_unified.png \
\( +clone -channel B -separate +channel \) \
\( -clone 0,1 -fx 'p{ v.r*w, v.g*h }' \
-clone 2 -compose HardLight -composite \
-clone 1 -alpha on -compose DstIn -composite \
\) -delete 0--2 spherical_mandrill.png
|
So now you can easily apply this distortion to lots of images. without needing
all the original calculations and image processing used to generate the map.
Circular Arc Distortion Map
Just to show just what is really possible by using positional distortion maps,
here is an absolute distortion LUT, similar to what is provided by the
'Arc' Distortion Method above.
Basically instead of calculating the coordinate mappings for each and every
pixel in each and every image being distorted, we save those calculated
coordinates into the two X and Y coordinate gray-scale LUT maps.
That is we pre-calculate the whole distortion into a simpler look-up table
image, allowing it to be applied over, and over, and over, without needing
further square roots or trigonometric functions.
convert -pointsize 30 -font Candice label:Anthony -trim +repage \
-gravity center -resize 95x95 -crop 100x100+0+0\! \
-flatten text_image.jpg
convert -size 100x100 xc: -channel G -fx 'atan(j/(i+.5))*2/pi' \
-separate -flip -flop map_p_angle.png
convert -size 100x100 xc: -channel G -fx '1-hypot(i,j)/(w*1.6)' \
-separate -transverse map_p_radius.png
convert text_image.jpg map_p_angle.png map_p_radius.png \
-fx 'p{u[1]*w,u[2]*h}' distort_p_curved.jpg
|
Color Source
|
|
Angle - X Map
|
|
Radius - Y Map
|
|
Curved Text
|
Of course generating that distortion map was difficult, but once it has been
done once, using any means you like (even artistically using a image editor
like "
Gimp" ), you can then reuse it on a huge number of images.
Polar Distortion Map
Sometimes you may need the destination image to be defined by the distortion
map, rather than the source image, just to make things work correctly.
For example, if we want to map some text into a circle (also known as a polar
transform), you really need to be able to use an image that is about 3 to 4
times long than it is high (high aspect ratio) or the result will not be very
readable.
To do that we place the distortion map images before the color source image,
so that the first (X map) image will be used to set the size of the final
result, rather than the input source image.
convert -size 100x100 xc: -channel G \
-fx 'atan2(i-w/2,h/2-j)/pi/2 + .5' \
-separate map_p_angular.png
convert -size 100x100 xc: -channel G \
-fx 'rr=hypot(i-w/2,j-h/2); (.5-rr/70)*1.2+.5' \
-separate map_p_radial.png
convert -font Candice -gravity center -size 200x50 \
label:'Around the World' text.jpg
convert map_p_angular.png map_p_radial.png text.jpg \
-fx 'u[2].p{ u*u[2].w, v*u[2].h }' distort_p_circle.jpg
|
Angular - X Map
|
|
Radial - Y Map
|
|
Color Source
|
|
Circled Text
|
Essentially the color source image can now be any size or aspect ratio, and
things will be handled correctly, however you may need to adjust the
generation of the distortion map to handle the source images aspect ratio
correctly.
In generating the above maps, the value '
70' controls the final
size of the circle, along which the mid-line is placed. The
'
1.2' value on the other hand controls the vertical scaling of
the image into the circle, allowing you to adjust the height of the distorted
text.
  |
Remember this "-fx"
expression requires the distortion maps to be given first, and the color
source to be given as the third (index 2) image. However this will also
mean that any meta-data that is stored in the source image will also be
lost.
|
  |
The problem with this distortion map is that there is a very sharp
disjunction of colors in the 'X map' (caused by a asymptote in the
mathematics). This line must remain sharp when you do any color look-up,
or resizing of the map, to produce a larger image. That is you will need
to ensure that any resize or interpolated look-up of this map does not
produce a grey look-up color along that asymptotic line.
If you do generate grey look-ups along this line, you will get a line of
colored pixels (looked up from the middle of the image) in your final
result.
Because of this it is recommend you always generate this distortion map
at the size you need for the final image, and never use any scaling
technique previously shown.
|
You can use this for other effects too, like a circular checkerboard...
convert map_p_angular.png map_p_radial.png \
-size 150x90 pattern:checkerboard \
-fx 'u[2].p{ u*u[2].w, v*u[2].h }' distort_check_circle.gif
|
|
|
Try some of the other
Built-In
Patterns that IM provides with the above for other interesting effects.
  |
The above clearly shows the limits of image distortions using "-fx". Near center of the
image, the radial lines are becoming aliased as pixel merging of a large
areas into a single pixel does not take place. On the other hand the edges
of the image, particularly the corners, show an appropriate blurring the
radial lines.
The cause is that "-fx"
(and most older distortion methods) only so simple unscaled interpolated
look-ups of the colors in the source image. This means that as the image
scales smaller the source image pixels are not merged together to produce
the correct color for the destination pixel.
This is not a problem for areas of enlargement (as in the corners) only of
extreme compression (center). As such one solution is the use of Super Sampling, but all this does is postpone the
problems to a higher level of compression.
|
  |
The same asymptotic line in the distortion map also produces a sharp color
change along that line in the above example. Compare that line with the
other radial lines which get very fuzzy around the edge of the image due
to interpolated look-up.
This may be a problem when generating a circular pattern with a tileable
image (such as the above), and may require some special handling to avoid
visible differences in that part of the image.
To avoid this it may be better to distort the top half of the image
separately to the bottom half so as to avoid the asymptotic region.
|
Relative Lookup Displacement Maps
As you can see creating a
Absolute Distortion
Map is reasonably easy to create and use. However it has a serious
problem when a distortion has 'undefined' regions, or areas where the
distortion goes 'outside' the normal bounds of the source image.
A more serious problem is that you are always dealing with gradents, which
define the absolute coordinates for the color lookup. No part of the mapping
image is simple, or clean, or easy to modify or edit by hand. Special
techniques and mathematics is needed in there creation and use. That that
generally means there is very little in the way of 'artistic' development
involved.
There is however another method of using a Lookup Table, to specify the
coordinates in which to get the final color. By using a
Relative
Displacement Map.
Instead of the 'map' defining the exact coordinate in which to lookup each
pixels color from the source image, it instead defines a offset or
displacement relative to the current position.
Now an offset could be a positive or negative value, and a negative value
requires a little trickiness to encode into a color value. So what they do is
define 'pure gray' and being a 0 displacement of the coordinate (no change).
They then make 'black' mean a maximum negative displacement, and 'white' to
mean a maximum positive displacement.
This can be hard to describe so lets look at an example.
First we create a test image to 'displace'.
convert -font Candice -gravity center -size 150x50 \
label:'Anthony' label.jpg
| |
|
Now I'll use some 'magick' to create a 'pure gray' image which includes a
some 'pure white' area and a 'pure black' area.
echo "P2 5 1 255\n 127 0 127 255 127" |\
convert - -scale 150x50\! +matte displace_map.jpg
| |
|
Now to use this image as a 'displacement map' we get the 'gray value' from the
displacement map, and add it either (or both) the X and Y coordinate. That is
we 'displace the lookup by a relative amount from the current position.
The 'value' is handled in a special way, so that a 'pure-gray' will mean
a zero displacement of the lookup point (just the Y coodinate in this case)
but a 'maximum displacement' is used for a 'white' (positive) or 'black'
(negative) value.
For example, lets apply the displacement map to our "label" image.
convert label.jpg displace_map.jpg -virtual-pixel Gray \
-fx 'dy=10*(2v-1); p{i,j+dy}' displaced.jpg
| |
|
As you can see the various sections of the image looked like they 'moved'
according to the color of the displacement map. A 'white' region will
add the given 'displacement value' to the lookup point, so in that area
each pixel looks up the source image '10' pixels 'south-ward' (positive Y
direction). As a result it looks as if the source image moved upward.
Remember it is the lookup that is displaced, NOT the actual image itself,
which is why it appeared to move upward or a negative direction for white.
A similar effect was also seen for the areas with a 'black' displacement.
The source image appeared to move downward, because the lookup displacement
was done in a negative direction. Think about it carefully.
You will also notice that the 'displaced lookup' can actually look beyond
the normal image bounds allowing you to use a
Virtual Pixel setting to control these out of bounds pixels. In the
above I just requested that a gray pixel be returned.
The 'maximum displacement' value '
10' in the above example is
very important, and is the maximum relative distance any part of the source
image appears to move, for a 'pure white' or 'pure black' displacement value
in the mapping image. You can not displace the lookup, and thus the input
image anything further than this value.
Other shades of gray between the maximum white or black values and the central
no-displacement 50% gray value, will displace the lookup by an appropriate
amount. As such a 25% gray value will displace the lookup by 1/2 the
displacement value in the negative direction, while a 75% gray will displace
by 1/2 that value in the positive direction.
This value is a key difference between a
Absolute
Distortion Map and
Relative Displacement
Map. You can increase or decrease the relative displacements, making the
image more or less distorted, simply by changing the displacement value,
without needing to change the displacement map at all.
Also as a 'zero-displacement' map is just a solid 50% or pure gray, and not a
complex gradient, you can start with a simple gray image, and artistically
lighten or darken areas to generate the desired displacements. You can do
this simply by drawing shapes or areas, rather then needing a complex
and exact mathematical formula.
And finally as all the displacements are relative, wild values such as
produced by edge-effects does not produce wild or random pixel colors. In
fact as you will see smoothing or blurring
Displacement Maps is actually a good thing as it removes the disjoint or
discontinuous 'cutting' effect you can see in the above example.
In summary A displacement map are much more controllable, and artistic,
, providing localized displacements without the need for complex and exacting
mathematics, and are very forgiving with regard to mistakes, edge effects, or
even displacement map blurring.
It is ideal for simple 'displacement' type distortions such as when generating
effects such as water, waves, distorting mirrors, the bending of light,
lens-like effects, or frosted or bubbles glass effects.
On the other hand highly mathematical distortions such as 'polar', rotational,
and 'perspective' distortions, or other real-world 3-d type mappings, are not
easily achieved. That is not to say it is impossible, as later we will show
that you can in fact convert between the two styles of maps, just more
difficult.
Composite Displacement Operator
In the last example the
DIY FX Operator was
used to do the displacement mapping. Naturally this is a very slow operator.
However the IM "
composite" command does allowed the use of a
special type of image composition method "
-displace" to do
Displacement Mapping in a faster way.
Here is how you you use it...
composite {displacement_map} {input_image} \
-displace {X}x{Y} {result}
|
Note the order. The '
displacement_map' is given before the destination
image being modified.
The '
X' and '
Y' values define the direction and 'maximum
displacement' that will be used for 'white' and 'black' colors in the given
displacement map. You can define either one, or both values, so as to allow
you to displace in any particular direction.
For example here is the same Y displacement example we has above, but this
time displaced using the "
composite" command.
composite displace_map.jpg label.jpg -virtual-pixel Gray \
-displace 0x10 displaced_y.jpg
| |
|
  |
If your image does not produce the same result, probably in this case no
change from the input image, then you have older IM that dates from before
version 6.2.8. Upgrade as soon as possible. See Displacement Map Bugs for details.
If you must use a IM version before this, always use the two map image
form of the operator, supplying both a overlay (X displacement) image and
a mask (Y displacement) image. See below for a 2 displacement command
synopsis.
|
The "
composite" "
-displace" operator also allows you to position the overlay/mask
displacement map images over a larger input or background image, according to
"
-geometry" "
-gravity" settings, the areas
not overlaid by the displacement map will be left, as is. However the
displacement maps offsets can still reference areas outside the overlaid part
of the image, duplicating them.
However due o a quirk in the way in which the "
composite"
"
-tile" setting works, you
may not get quite the results you expect. It is thus not a recommend setting
to use with
Displacement Mapping.
Simple Displacement Examples
A displacement map of raw areas of colors, without any smooth transition will
generaly produce disjoint (discontinuous) displacements between the different
areas in the resulting image, just as you saw above.
You can in fact produce a displacement map that 'fractures' as if you were
looking into a cracked mirror, using this technique. For example see the
Fractured Mirror below.
You can produce nicer and smoother results if the colors smoothly flowed from
one area to another. For example, by bluring the displacement map you
generate a wave like transtion between the displaced areas...
convert displace_map.jpg -blur 0x10 dismap_wave.jpg
composite dismap_wave.jpg label.jpg -displace 0x10 displaced_wave_y.jpg
| |
|
Rather than displace the image in a Y direction you can also use a map to
displace the image in a X direction resulting in a sort of compression wave.
composite dismap_wave.jpg label.jpg -displace 10x0 displaced_wave_x.jpg
| |
|
By using the same displacement map for both X and Y directions, we can
add both a compression wave as well as an amplitude wave.
composite dismap_wave.jpg label.jpg -displace 10x10 displaced_wave_xy.jpg
| |
|
Note that the image is still displaced in a single direction, resulting in the
above image being stretched on the downward slope, and squeezed together on
the upward slope. That is the distortion is at a angle or 'vector' with some
horizontal as well as vertical components.
You can see that this effect is remarkably like it is underwater, with the
image being distorted by gental ripples on the water's surface.
A distortion map however can contain multiple copies of the original image,
just as you can in a reflected or refracted images...
echo "P2 3 1 255\n 255 127 0 " | convert - -scale 150x50\! dismap_copy.jpg
composite dismap_copy.jpg label.jpg -displace 66x0 displaced_copy.jpg
| |
|
You can also create mirrored flips or flops of parts of the image, by using
gradients. For example here you can use a linear displacement map, to copy
pixels from one side of the image to the other.
convert -size 50x150 gradient: -rotate -90 +matte dismap_mirror.png
composite dismap_mirror.png label.jpg -displace 150x0 displaced_mirror.jpg
| |
|
Can figure out how this displacement map works? As a hint figure out
what displacement the left most and the right most edges have, then see how
the rest of the image fits into that.
However as you are again using a gradient image you loose the simplicity of
displacement maps. As such mirrors are either better done using a direct
Flip Operation on the image, or by using a
Absolute Distortion Map instead.
Note that by flipping the gradient over, you shrink the image.
convert -size 50x150 gradient: -rotate 90 +matte dismap_shrink.png
composite dismap_shrink.png label.jpg -displace 150x0 displaced_shrink.jpg
| |
|
The above also demonstrates a particular problem that displacement maps have.
When an area (or all) of an image gets compressed by more than 50%, you will
start to generate
Aliasing Effects. this
is particularly noticable in the staircased 'aliased' edges that is clearly
visible.
As previously discussed, one solution to this is to
Super Sample the number of pixels being
used to generate each output pixel. To do that we enlarge both the image and
displacement map, then resize the resulting image back to its more normal
size. This will allow more pixels to take part in the setting of a specific
pixel in the result, and thus produce better image.
For example...
convert dismap_shrink.png label.jpg -resize 200% miff:- |\
composite - -displace 400x0 miff:- |\
convert - -resize 50% displaced_resized.jpg
| |
|
A much better and smoother result.
Graphing a gradient
Directly resulting from the above examples was an idea that by using Y
displacements of a simple line, you can generate a graph of the colors of a
displacement map.
For example here I generate a mathematical
sinc() function (
which is defined as '
sin(x)/x'), and graph that gradient by
using it as a displacement map...
convert -size 121x100 xc: -fx 'sin(i*24/w-12)/(i*24/w-12)/1.3+.2' \
gradient_sinc.gif
convert -size 121x100 xc: -draw 'line 0,50 120,50' graph_source.gif
composite gradient_sinc.gif graph_source.gif \
-displace 0x49 displace_graph.gif
|
As you can see it works, though I wouldn't like to use it for mathematical
plots. Better to use a proper graphing package. This technique however is
useful as a dirty method of plotting the intensities of a row or column of
pixels in an image.
What it does do is show how large differences in displacements can easily
produce a discontinuity or non-smooth result. Basically as only each
individual pixel in the "graph source" is only looked at one at a time,
without averaging, a large difference in the displaced lookup from one pixel
to the next, can produce a large color change in the result.
The moral is that displacement work best not only with smooth displacement
maps, but also with displacing images that contains large areas or shades of
color. It does not work so well for sharp thin lines.
Of course you can improve things by again
Super Sampling the distort map...
convert gradient_sinc.gif graph_source.gif -resize 400% miff:- |\
composite - -displace 0x196 miff:- |\
convert - -resize 25% displace_graph_2.gif
| |
|
The result is a lot better, though not as good as what can be achieved using a
graphing package. Still only ImageMagick was used in its creation.
Here is another version of the same graph, but this time using a solid color,
which works a lot better than displacing a thin line.
convert -size 121x50 xc:white xc:black -append \
gradient_sinc.gif +swap -resize 400% miff:- |\
composite - -displace 0x196 miff:- |\
convert - -resize 25% displace_graph_3.gif
| |
|
Area Displacement (Linear)
Lets try a more logical displacement problem. Moving a area of an image in a
straight line from one location to another.
As we have seen a 'pure gray' image will not cause any displacement, while a
'white' color will cause a positive lookup displacement from the source image.
For example lets create such an image....
convert -size 75x75 xc:gray50 -fill white \
-draw 'circle 37,37 37,20' dismap_spot.jpg
| |
|
Now when we apply this image the contents of the area marked should have
a copy of whatever appears in the direction of the given displacement value.
So lets try a displacement value of
X+10 and
Y+10 or
'
10x10'...
composite dismap_spot.jpg koala.gif -displace 10x10 displace_spot.png
|
As you can see the contents of the marked area now contains a copy of the
image that was
+10,+10 pixels to the South-East. Basically a
image of the koala's 'tail'. In other words, within the circle the image was
displaced North-East, or
-10,-10 pixels.
Remember the displacement is of the lookup, so the source image is shifted by
a negative amount due to the
Reversed Pixel
Mapping. The image displaces in the reverse direction!
Note also that it is the image within the area marked that is moved. You are
not displacing the image marked, but displacing the image
INTO the area
marked.
And finally note the sharp discontinuity at the edges of the circle. Areas
inside the marked area are moved, while the areas outside remain exactly as
they were.
These are the facts, so it is worth repeating.
Displacement is in the reverse to the displacement value.
Only the areas marked not gray will be displaced.
Sharp color changes produce sharp image discontinuities.
So lets try something more practical. Lets move the center between
the nose and eyes of the koala, located at '
32,22', to the center
of our white (full positive displacement) circle at '
37,37'.
That needs a displacement value of '
-5,-15' (remeber it is
a reversed direction)...
composite dismap_spot.jpg koala.gif -displace -5x-15 displace_head.png
| |
|
And there we have a nicely centered copy of the central part of the koalas
head.
But the image is still 'disjoint', and using a negative value is not very
nice. The solution is to use a black spot instead, but also to blur the edges
of that spot. Also lets make it larger to encompass more of the koala's head.
So here is out 'positive movement spot' image...
convert -size 75x75 xc:gray50 -fill black \
-draw 'circle 37,37 37,17' -blur 0x5 dismap_area.jpg
| |
|
You do not want to blur the image too much or the center of the spot will
no longer be a flat black color. Alternatively you could just
Normalize,
Reverse Level Adjust the image to ensure that the drawn area is black,
and surrounding parts are perfect grays. You will see this done a lot in
later examples.
Now lets repeat that last 'head' displacing using our black 'fuzzy spot'
displacement map.
composite dismap_area.jpg koala.gif -displace 5x15 displace_area.png
| |
|
As you can see we move move the image
+5,+15 into the 'fuzzy'
area, but this time the border of the area is smoother and connected to the
rest of the image.
Of course the ears on the edge of the circle was distorted by the fuzzy edge,
and the body of the koala compressed as well, but it is still a lot better
than what we had before.
To prevent the 'tearing' of the image you see on the trailing side, or leaving
copies of the displaced part, you want to expand that spot, or make a more
complex gradient type of displacement image.
For example suppose you want to move the koalas head from its starting position
at '
32,22', to the center of the image at '
37,37',
or a movement of
+5,+15 pixels, but you want to adjust the whole
image to this change, to give a much smoother effect.
To do this you will want the maximum displacement of black (a positive image
displacement) at '
37,37' and displacing by a value of
+5,+15. but you also want the make sure the rest of the image
remains intact by 'pinning' the corners at 50% gray. That is perfect for a
Shepard's Interpolated Sparse Gradient.
convert -size 75x75 xc: -sparse-color Shepards \
'37,37 black 0,0 gray50 74,74 gray50 0,74 gray50 74,0 gray50' \
dismap_move.jpg
composite dismap_move.jpg koala.gif -displace 5x15 displace_move.png
|
As you can see you get a larger area of displacement spread is spread over the
whole image. The result is a much more smoothly changing image than the
tighter 'spot' method used before.
This is actually exactly equivalent to the
Shepard's Distortion but only for a one moving control point. It is also
the exact same method used in Fred Weinhaus script '
shapemorph', but with some animation.
In summary: For small localized displacements a 'blurred spot' displacements
can be used. But for larger displacements over a longer distance, a larger
smooth gradient displacement map should be used to prevent tearing or
duplicating the source image.
Under Construction
Simple Displacement Morphing
Modifying the Size of Displacement Vectors
Two Image Morphing
Random 1D Displacements
Rippled Water Reflections
As mentioned before displacement maps are especially useful for generating
water and glass like distortions.
![[IM Output]](flower.jpg)
For this example I generated a small image by
Cropping a flower image. Now I want to make it look like it is
sitting on some rippled water.
To generate ripples, I need a sine wave gradient of the same size which I can
generate using the
Evaluate Sin
Function. The number '
8' represents the number of 'waves'
that will be added to the gradient.
convert -size 150x80 gradient: -evaluate sin 8 wave_gradient.png
| |
|
Now lets distort that image using the an angled displacement vector, not just
a simple vertical or horizontal distortion, so as to give it more emphasis.
composite wave_gradient.png flower.jpg -displace 5x5 flower_waves.png
| |
|
Now that does not seem very interesting, but what if you flip that image
compress it vertically and append it to the original...
convert flower_waves.png -flip \
flower.jpg +swap -append flower_waves_2.png
| |
|
Unfortunately it still looks rather artificial. The reason is that the
reflection looks the same at both the top and bottom of the image. It has no
sense of 'depth' to it. The reflection is also the same brightness as the
original image which is rarely the case.
To make it more realistic you need to use ripple pattern that varies in
intensity.
The following uses some fancy
Gradient
Mathematics to 'attenuate' the wave gradient we were using above.
That is we made the wave pattern linearly smaller as it goes from top to the
bottom. This trickiness ensures that the waves finishes at the pure-gray or
'no displacement' color at the bottom of the image (which is later flipped).
convert -size 150x80 gradient: \
\( wave_gradient.png \
+clone -compose multiply -composite \) \
\( -clone 0 -negate -evaluate divide 2 \
-clone 1 -compose plus -composite \) \
-delete 0-1 waves_decreasing.png
| |
|
So lets apply this gradient, to form a new reflection of the flower. I also
darkened the reflected image slightly to represent some light being lost into
the water itself, making it seem more like a water reflection.
composite waves_decreasing.png flower.jpg \
-displace 8x8 miff:- |\
convert miff:- -flip +level 0,80% \
flower.jpg +swap -append flower_in_water.png
| |
|
Note that as the distorted image is
Flipped to
form a reflection, the image will have less 'ripples' at the top of the
'water' closest to where it joins the original image, than at the bottom.
This gives the distortion a sense of distance from the viewer.
You can make it even more realistic by distorting the wave displacement maps
with a slight rotation, arc, or just with 'random' displacements. This will
give the waves a more natural look. Though it is better to do it before it is
'attenuated' so that the 'depth' is added afterward.
Try it, experiment, and let me know what you come up with.
Animated Ripples -
Using -function Sinusoid with phase changing
End with a polar-depolar version
2-Dimensional Displacement Mapping
So far all the displacement maps have only displace the image in one direction
only. Though that direction can be at any angle by setting the appropriate
'
XxY' displacement value or 'vector'.
However you can produce a much more complex displacement by using two separate
displacement for both the X and Y directions separately. By doing this each
part of the image can move in completely different directions to each other.
However the "
composite" command to this is is a little more
complex.
composite {X displacement} {input image} {Y displacement}+ \
-displace {X}x{Y} {result+}
|
Note the input image order. It is caused by the need to abuse the
"
composite" option handling, and historical reasons. It is vital
you get this correct.
I am hoping to implement a nicer "
convert" method for
displacement mapping soon
  |
Before IM v6.4.4 using 2 separate displacement maps for separate X and Y
displacements was a hit or miss affair. It sometimes worked, and sometimes
did not. It is not recommended to attempt to even try to use it on IM's
older than this version.
|
Cylindrical Displacement
Under Construction
convert rose: -background black -gravity south -splice 0x8 \
\( +clone -sparse-color barycentric '0,0 black 69,0 white' \) \
\( +clone -function arcsin 0.5 \) \
\( -clone 1 -level 25%,75% \
-function polynomial -4,4,0 -gamma 2 \
+level 50%,0 \) \
-delete 1 -swap 0,1 miff:- |\
composite - -virtual-pixel black -displace 17x7 rose_cylinder.png
| |
|
The above is very complex but in essence two separate displacements
are being performed simultaniously. A X compression and a Y Displacement
The key to remember is that the two map displacement the lookups of both
X and Y values.
I will annotate...
rose:
the image to wrap around the front half of a cylinder
-background black -gravity south -splice 0x8 \
add extra space at the bottom for vertical displacement
\( +clone -sparse-color barycentric '0,0 black 69,0 white' \) \
generate a gradient across the actual image part only
which is 70 pixels wide
\( +clone -function arcsin 0.5 \) \
generate the horizontal distortion needed - dead simple
\( -clone 1 -level 25%,75% -function polynomial -4,4,0 \
-gamma 2 +level 50%,0 \) \
generate a vertial displacement circular arc
This is also very complex, so...
-level 25%,75%
compress gradient into 50% width
that will be produced by the arcsin
-function polynomial -4,4,0 -gamma 2
the gamma 2 is equivelent to sqrt
making the above sqrt( -4u^2 + 4u )
+level 50%,0
set the displacement gradient so edges are not
displaced, and center has black (move image down) displace
-delete 1 -swap 0,1 miff:- |\
remove working gradient image, and get order correct for composite
EG: composite X-map image Y-map -displace XxY result
composite - -displace 17x7 show:
Read in the three images, and displace the image.
the value 17 = 1/4 width for horizontal arcsin displacement
and a vertical circular arc of 7 pixels (about 1/8 the original width)
giving a rough 30 degree isometric view of cylinder
Result... a rose wrapped correctly into a cylindar
This displacement distortion method has been built into the "
cylinderize" script by Fred Wienhaus.
Fractured Mirror
You can create a 'fractured mirror' look to an image by generating random
areas of X and Y displacements.
convert dragon_sm.gif -sparse-color voronoi ' \
%[fx:rand()*w],%[fx:rand()*h] red
%[fx:rand()*w],%[fx:rand()*h] lime
%[fx:rand()*w],%[fx:rand()*h] black
%[fx:rand()*w],%[fx:rand()*h] yellow
' -interpolate integer -implode 1 mirror_areas.gif
convert mirror_areas.gif -channel R -separate mirror_dismap_x.gif
convert mirror_areas.gif -channel G -separate mirror_dismap_y.gif
composite mirror_dismap_x.gif dragon_sm.gif mirror_dismap_y.gif +matte \
-background white -virtual-pixel background -displace 7 \
mirror_displaced.gif
convert mirror_areas.gif -edge 1 -threshold 20% \
-evaluate multiply .7 -negate mirror_cracks.gif
composite mirror_displaced.gif mirror_cracks.gif -compose multiply \
mirror_cracked.gif
|
Four randomly displaced areas are generated using a randomized
Voronoi Sparse Color image. This is then given
an
Implosion Distortion to warp those areas
into the center of the image. As each of the four colored areas remain solid
colors, each area will contain a undistorted, but displaced copy of the
original image. However each area will have displaced the image in a different
way, just as each shard of a fractured mirror would.
To finish off the mirror,
Edge Detection is
used to outline the edges of the regions and thus the fractured nature of the
resulting image. That is the cracks are also made visible.
Of course if I can find a better method of generating a more 'broken like'
displacement map, then the results of the above will also be a lot better.
Ideas anyone? Mail me.
Shepards Displacement
Random Displacements
Lensing Effects
Frosted Glass Effects
Dispersion Effects (rotated displacements)
Dispersion Effects with Randomized Displacement
FUTURE: Other possible distort / displace mapping examples
- Raytrace a gradient onto 3D objects so that later ANY image can be
be mapped onto those objects.
- X and Y gradient mapped images
- Pure Gray Image for color, highlights and shading
Created: 14 January 2009 (distorts sub-division)
Updated: 25 December 2009
Author: Anthony Thyssen,
<A.Thyssen@griffith.edu.au>
Examples Generated with:
![[version image]](version.gif)
URL: http://www.imagemagick.org/Usage/mapping/