Understanding Perspective Projection Distortion ImageMagick - imagemagick

For a project I am trying to create a perspective distortion of an image to match a DVD case front template. So I want to automate this using ImageMagick (CLI) but I have a hard time understanding the mathematical aspects of this transformation.
convert \
-verbose mw2.png \
-alpha set \
-virtual-pixel transparent \
-distort Perspective-Projection '0,0 0,0 0,0 0,0' \
box.png
This code is en empty set of coordinates, I have read the documentation thoroughly but I can't seem to understand what parameter represents what point. The documentation gives me variables and names where I have no clue what they actually mean (more useful for a mathematical mastermind maybe). So if someone could explain me (visually prefered, or give me a link to useful information) on this subject because I have no clue on what I am doing. Just playing around with the parameters just wont do for this job and I need to calculate these points.
Here you will find an easy image of what I am trying to achieve (with CLI tools):
Update:
convert \
-virtual-pixel transparent \
-size 159x92 \
-verbose \
cd_empty.png \
\(mw2.png -distort Perspective '7,40 4,30 4,124 4,123 85,122 100,123 85,2 100,30'\) \
-geometry +3+20 \
-composite cover-after.png
Gives me as output:
cd_empty.png PNG 92x159 92x159+0+0 8-bit sRGB 16.1KB 0.000u 0:00.000
convert: unable to open image `(mw2.png': No such file or directory # error/blob.c/OpenBlob/2641.
convert: unable to open file `(mw2.png' # error/png.c/ReadPNGImage/3741.
convert: invalid argument for option Perspective : 'require at least 4 CPs' # error/distort.c/GenerateCoefficients/807.
convert: no images defined `cover-after.png' # error/convert.c/ConvertImageCommand/3044.
Correction by Kurt Pfeifle:
The command has a syntax error, because it does not surround the \( and \) delimiters by (at least one) blank on each side as required by ImageMagick!
Since there are no links to the source images provided, I cannot test the outcome of this corrected command:
convert \
-virtual-pixel transparent \
-size 159x92 \
-verbose \
cd_empty.png \
\( \
mw2.png -distort Perspective '7,40 4,30 4,124 4,123 85,122 100,123 85,2 100,30' \
\) \
-geometry +3+20 \
-composite \
cover-after.png

Did you see this very detailed explanation of ImageMagick's distortion algorithms? It comes with quite a few illustrations as well.
From looking at your example image, my guess is that you'll get there using a Four Point Distortion Method.
Of course, the example you gave with the 0,0 0,0 0,0 0,0 parameter does not do what you want.
Many of the distortion methods available in ImageMagick work like this:
The method uses a set of pairs of control points.
The values are numbers (may be floating point, not only integer).
Each pair of control points represents a pixel coordinate.
Each set of four values represent a source image coordinate, followed immediately by the destination image coordinate.
Transfer the coordinates for each source image control point into the respective destination image control point exactly as given by the respective parameters.
Transfer all the other pixel's coordinates according to the distortion method given.
Example:
Sx1,Sy1 Dx1,Dy1
Sx2,Sy2 Dx2,Dy2
Sx3,Sy3 Dx3,Dy3
...
Sxn,Syn Dxn,Dyn
x is used to represent an X coordinate.
y is used to represent an Y coordinate.
1, 2, 3, ... n is used to represent the 1st, 2nd, 3rd, ... nth pixel.
S is used here for the source pixel.
D is used here for the destination pixel.
First: method -distort perspective
The distortion method perspective will make sure that straight lines in the source image will remain straight lines in the destination image. Other methods, like barrel or bilinearforward do not: they will distort straight lines into curves.
The -distort perspective requires a set of at least 4 pre-calculated pairs of pixel coordinates (where the last one may be zero). More than 4 pairs of pixel coordinates provide for more accurate distortions. So if you used for example:
-distort perspective '1,2 3,4 5,6 7,8 9,10 11,12 13,14 15,16'
(for readability reasons using more {optional} blanks between the mapping pairs than required) would mean:
From the source image take pixel at coordinate (1,2) and paint it at coordinate (3,4) in the destination image.
From the source image take pixel at coordinate (5,6) and paint it at coordinate (7,8) in the destination image.
From the source image take pixel at coordinate (9,10) and paint it at coordinate (11,12) in the destination image.
From the source image take pixel at coordinate (13,14) and paint it at coordinate (15,16) in the destination image.
You may have seen photo images where the vertical lines (like the corners of building walls) do not look vertical at all (due to some tilting of the camera when taking the snap). The method -distort perspective can rectify this.
It can even achieve things like this, 'straightening' or 'rectifying' one face of a building that appears in the 'correct' perspective of the original photo:
==>
The control points used for this distortion are indicated by the corners of the red (source controls) and blue rectangles (destination controls) drawn over the original image:
==>
This particular distortion used
-distort perspective '7,40 4,30 4,124 4,123 85,122 100,123 85,2 100,30'
Complete command for your copy'n'paste pleasure:
convert \
-verbose \
http://i.stack.imgur.com/SN7sm.jpg \
-matte \
-virtual-pixel transparent \
-distort perspective '7,40 4,30 4,124 4,123 85,122 100,123 85,2 100,30' \
output.png
Second: method -distort perspective-projection
The method -distort perspective-projection is derived from the easier understandable perspective method. It achieves the exactly same distortion result as -distort perspective does, but doesn't use (at least) 4 pairs of coordinate values (at least 16 integers) as parameter, but 8 floating point coefficients.
It uses...
A set of exactly 8 pre-calculated coefficients;
Each of these coefficients is a floating point value (unlike with -distort perspective, where for values only integers are allowed);
These 8 values represent a matrix of the form
sx ry tx
rx sy ty
px py
which is used to calculate the destination pixels from the source pixels according to this formula:
X-of-destination = (sx*xs + ry+ys +tx) / (px*xs + py*ys +1)
Y-of-destination = (rx*xs + sy+ys +ty) / (px*xs + py*ys +1)
(TO BE DONE --
I've no time right now to find out how to
properly format + put formulas into the SO editor)
To avoid (the more difficult) calculating of the 8 required cooefficients for a re-usable -distort perspective-projection method, you can...
FIRST, (more easily) calculate the coordinates for a -distort perspective ,
SECOND, run this -distort perspective with a -verbose parameter added,
LAST, read the 8 coefficients from the output printed to stderr .
The (above quoted) complete command example would spit out this info:
Perspective Projection:
-distort PerspectiveProjection \
'1.945622, 0.071451, -12.187838, 0.799032,
1.276214, -24.470275, 0.006258, 0.000715'

Thanks to ImageMagick Distorting Images Documentation, I ended up with this clean-understandable code:
$points = array(
0,0, # Source Top Left
0,0, # Destination Top Left
0,490, # Source Bottom Left
2.2,512, # Destination Bottom Left
490,838, # Source Bottom Right
490,768, # Destination Bottom Right
838,0, # Source Top Right
838,50 # Destination Top Right
);
$imagick->distortImage(Imagick::DISTORTION_PERSPECTIVE, $points, false);
Please keep in mind that each set of coordinates are separated into two
parts. The first is the X axis and the second is the Y axis .. so when we say 838,0
at Destination Right Top, we mean the X axis of Destination Right Top
is 838 and the Y axis of it is zero (0).

Related

Is it possible to rotate a 360 deg image mapped onto a sphere around an axis?

There's an example in imagemagick tutorial, which demonstrates how to transform equirectangular 360 degree panorama into a polar view by using convert tool.
The resulting image is a somewhat distorted view of a sphere, as viewed from either of its poles.
What I am be interested with would be if it is possible to apply additional transformation which would result in having a polar view image, but as if the 'sphere' was rotated around an axis which goes through the equator. Center of the image would then not show artctic nor Antarctic region, but for instance Berlin or California.
Is this possible with imagemagick, scikit-image, or other open source tool?
EDIT
Example:
Initial picture:
Transformed picture, focused on the polar region:
Desired transformed picture, focused on Europe:
Here is the best that I can do with 2D image processing with Imagemagick rather than a proper 3D projection.
To move the viewpoint, roll the image in x and shift it in y. Then use my spherize script at http://www.fmwconcepts.com/imagemagick
Input:
Spherize of input:
spherize -s worldmap_sm.jpg worldmap_sm_sphere.jpg
Process for other viewpoint:
convert worldmap_sm.jpg -roll +64+0 -define
distort:viewport=256x160+0-32 -virtual-pixel background -background
red -filter point -distort SRT 0 +repage worldmap_sm_usa.png
spherize -s worldmap_sm_usa.png worldmap_sm_usa_sphere.jpg
With my spherize script and the roll and shift, there are areas that will be missing, which I coded in red. This will only occur due to the vertical shift and not with the horizontal roll. (Rolling vertically would have wrapped the South Pole next to the North Pole)

logarithmic image resizing using Imagemagick?

I'd like to rescale an image in one dimension, using a logarithmic scale. I've been searching for some idea on how to do this. Some options:
-evaluate seems to take Pow and other functions
-fx seems to be an option, and/but I'm still working to understand it
Both of the above might not be able to be passed to the -size or resizing operations.
I am not sure what you are asking. But here is how to apply a logarithmic transform to an image in ImageMagick. Since geometric transformations are inverse transformations (specify an output pixel and find where it comes from in the input image), we must use the inverse of the logarithm, which is the exponential. For example for a horizontal transformation:
Input:
convert lena.png -virtual-pixel black -fx "u.p{exp(6*i/(w-1)),j}" lena_ln.png
Now, if you want it to fit exactly into 256 pixels horizontally without the the black region on the right, we need to evaluate a logarithm:
ln(x+1) for x=0 to 255
ln(0+1) = ln(1) = 0
ln(255+1) = ln(256) = 5.545
Now we need the inverse operation for -fx, which is an exponential,
ln(x+1) = 5.545
x+1 = exp(5.545)
x = exp(5.545) - 1
convert lena.png -virtual-pixel black -fx "u.p{exp(5.545*i/(w-1))-1,j}" lena_ln2.png

ImageMagick: How to extract tiles in a grid from a single PNG?

I know questions like this get asked a lot, but I haven't been able to find an answer to my specific use-case.
I frequently have to extract rectangular tile images that are laid out in a grid in a single PNG. The image contains a grid of M x N tiles. The upper-left corner of the upper-left tile in the grid is at offset (X, Y) in pixels. Each tile is WxH pixels in size. In addition, each tile in a row is DX pixels from upper-left corner to upper-left corner (so that DX >= W), and each column is DY pixels below the one above it (DY >= H).
In this picture, M=3 and N=2
Give all these variables, could someone please tell me what command to use to extract the six tiles into their own PNG files? I'm assuming it's using the 'convert' command.
Thanks.
There is no simple crop command in Imagemagick that will do the offset tile cropping. But you can do that with a combination of 3 crop commands.
Crop the larger area that you want from the image
Tile crop with no skip
Crop each tile to remove the skip area
Input:
Here I make an animation simply for demonstration. Use PNG output or JPG output for separate tiles.
convert -delay 50 lena.png \
-crop 200x200+20+20 +repage \
-crop 100x100 +repage \
-crop 90x90+0+0 +repage \
-loop 0 lena_crop.gif
I was able to get this working using the following script:
#!/bin/bash
COLS=8
ROWS=4
WIDTH=263
HEIGHT=500
XOFF=154
YOFF=176
DX=267
DY=523
IMAGE=image
mkdir -p ${IMAGE}
for M in `seq 0 $(($COLS - 1))`
do
for N in `seq 0 $(($ROWS - 1))`
do
X=$(($XOFF + $M * $DX))
Y=$(($YOFF + $N * $DY))
convert ${IMAGE}.png[${WIDTH}x${HEIGHT}+${X}+${Y}] ${IMAGE}/${IMAGE}-${M}-${N}.png
done
done
I'm not sure how elegant this is.

Imagemagick skew image with 4 (x,y) coordinates

I have 4 (x,y) coordinates between which I want place image as example given below.
The whole image must be placed within this area without cropping.
Using this 800x600 balloon:
You can use a "Perspective Distort" like this:
convert balloon.jpg -matte -virtual-pixel transparent \
-distort Perspective '0,0,50,0 0,599,100,599 800,0,750,100 800,600,500,500' result.png
There are basically 4 pairs of points in the parameters,i.e.
Pt1X,Pt1Y,Pt1NewX,Pt1NewY Pt2X,Pt2Y,Pt2NewX,Pt2NewY Pt3X,Pt3Y,Pt3NewX,Pt3NewY Pt4X,Pt4Y,Pt4NewX,Pt4NewY
So the command above moves point 0,0 to 50,0 and moves point 0,599 to 100,599 and so on.
I have labelled each of the points in red and drawn the path along which each one has moved in green.

In ImageMagick, is it possible to -auto-gamma based on a region?

I'm using ImageMagick to prepare a set of ~20,000 photos for a timelapse video. A common problem in timelapse videos is flickering, due to changing lighting conditions, passing clouds, hue changes, etc.
I've used IM to convert each image to greyscale and -auto-gamma, which is a drastic improvement in lighting "stability". Very good, but not yet perfect.
I would now like to do the following, but can't figure out how.
1. determine ideal auto gamma based only on the lower 30% of the image
2. apply that ideal gamma to the entire image
Each of my images has sky above and buildings below. The sky changes dramatically as clouds pass by, but the buildings' lighting is fairly stable.
I tried -region, but as expected, it only applies the gamma to the region specified. Is it possible to do what I'm hoping for? Thanks for any advice!
Yes, I think so.
You can crop the bottom 30% of the image like this:
convert image.jpg -gravity south -crop x30%+0+0 bottom.jpg
so that means you can get the mean of the bottom 30% of the image like this:
convert image.jpg -gravity south -crop x30%+0+0 -format "%[mean]" info:
and you can also get the quantum range as well all in one go if you add that in:
convert image.jpg -gravity south -crop x30%+0+0 -format "%[mean] %[fx:quantumrange]" info:
Now, the gamma is defined as the logarithm of the mean divided by the logarithm of the midpoint of the dynamic range, but we can normalize both these numbers to the range [0-1] as follows:
log(mean/quantumrange) / log(0.5)
so we'll let bc work that out for us like this:
echo "scale=4; l($mean30/$qr)/l(0.5)" | bc -l
and we can use the result of that to apply a gamma correction to the entire image. So, I have put all that together in a single script, which I call b30gamma. You save it under that name and then type:
chmod +x b30gamma
to make it executable. Then you can run it on an image like this and the result will be saved as out.jpg so as not to destroy the input image:
./b30gamma input.jpg
Here is the script:
#!/bin/bash
# Pick up image name as parameter
image=$1
# Get mean of bottom 30% of image, and quantum range (65535 for Q16, or 255 for Q8)
read mean30 qr < <(convert "$image" -gravity south -crop x30%+0+0 -format "%[mean] %[fx:quantumrange]" info:)
# Gamma = log(mean)/log(dynamic range centre point)
gcorr=$(echo "scale=4;l($mean30/$qr)/l(0.5)" | bc -l)
# Debug
echo Image: $image, Mean: $mean30, quantum range: $qr, Gamma: $gcorr
# Now apply this gamma to entire image
convert "$image" -gamma $gcorr out.jpg

Resources