How to keep "distort" aligned at a specific coordinate?

Questions and postings pertaining to the usage of ImageMagick regardless of the interface. This includes the command-line utilities, as well as the C and C++ APIs. Usage questions are like "How do I use ImageMagick to create drop shadows?".
Post Reply
Balduin
Posts: 3
Joined: 2012-01-01T09:35:50-07:00
Authentication code: 8675308

How to keep "distort" aligned at a specific coordinate?

Post by Balduin »

Hello,
I want to do the following in a way that works with any sort of distortion:
Image ImageImage
(in this example: red dot at 59,26 and blue target dot at 52,30)
I found how to do that with a ScaleRotateTranslate distortion:

Code: Select all

convert 1.png \( 2.png -distort SRT '59,26 1 45 52,30' \) -compose Multiply -composite 3.png
Are there any ideas how I could do this with a Perspective distortion?
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: How to keep "distort" aligned at a specific coordinate?

Post by fmw42 »

Have tried adding that point to the 4 corner points in -distort perspective and perhaps using +distort and -layers merge http://www.imagemagick.org/Usage/distorts/#box3d? \

If that does not work, then you may need to try one of the following
1) compute your own coefficients for -distort perspectiveprojection http://www.imagemagick.org/Usage/distor ... projection
2) chain the -distort perspective with a -distort SRT to shift the result (probably need to use +distort)
3) try my script 3Drotate at the link below using the input and output pixel displacement arguments.

Anthony may have better suggestions for you.
User avatar
anthony
Posts: 8883
Joined: 2004-05-31T19:27:03-07:00
Authentication code: 8675308
Location: Brisbane, Australia

Re: How to keep "distort" aligned at a specific coordinate?

Post by anthony »

With perspective transformation you need to specify exactly 4 control points for an exact transformation.
Make one of those points the point you want to match up and you will be guarantee that it will be in the right position.

The other three can be modified as you like but should remain roughly in the the same position order (rotated or otherwise) or you may end up twisting the surface or viewing from the back which does not work quite as you would expect.

WARNING: Pixels have area, they are not strictly 'points'

For Distort, integers represent the edges of pixels, to align the pixel center you will need to add 0.5 to the coordinate location. that is 0,0 is the top-left corner of the image, but 0.5,0.5 is the top-level pixel location.

The reason is that Distort uses "Image Coordinates" (which is what the mathematics uses) while other things like -draw and and image composition uses "Pixel Coordinates". The difference between the two is subtle and only a offset of 0.5 but can be important for image alignment, especially as Distort can align images to sub-pixel coordinates.

See Image Coordinates vs Pixel Coordinates
http://www.imagemagick.org/Usage/distor ... oordinates

As such your previous example should be...

Code: Select all

convert 1.png \( 2.png -distort SRT '59.5,26.5  1  45  52.5,30.5' \) -compose Multiply -composite 3.png
so that the pixel centers are aligned rather than the top-level edge of the pixel.
Anthony Thyssen -- Webmaster for ImageMagick Example Pages
https://imagemagick.org/Usage/
User avatar
anthony
Posts: 8883
Joined: 2004-05-31T19:27:03-07:00
Authentication code: 8675308
Location: Brisbane, Australia

Re: How to keep "distort" aligned at a specific coordinate?

Post by anthony »

Also when distorting image you need to define what the image looks like outside its bounds. Best way to do that is using -virtual-pixel setting.

Code: Select all

 ... align_pixel_2.png -alpha set -virtual-pixel Transparent ...
this gives the image a transparency channel and defined the areas beyond the image bounds as being transparent!

The next set of problems is using -distort. This fits the distorted image into the same bounds as the input image! As such some corners will become 'clipped'.

The area distort fits into is called the 'viewport' into the distorts space, and IM distort provides 3 solutions -distort uses the same bounds as the input image. +distort does its best to calculate bounds so as not to lose any information, or you can override both using -define distort:viewport=
See http://www.imagemagick.org/Usage/distor ... t_viewport

NOTE the viewport may have 'layer' offsets arttached to the image. Composite does not used these offsets! the Image layering operators do. see Layering Images
http://www.imagemagick.org/Usage/layers/layers/#flatten

As such for your perspective image the result is...

Code: Select all

   convert align_pixel_2.png -alpha set -virtual-pixel Transparent \
               +distort Perspective '  59.5,26.5  52.5,30.5  
                                                 0,0          3,6
                                                  0,%h 3,66 %w,%h 97,86  ' \
               align_pixel_1.png +swap \
               -compose Multiply -flatten align_pixel_3.png
Image Image Image

Note I used % escapes to define the 'edges' of the input image, The re-ordering (and +swap) just removed the need for parenthesis. I did not define the top right corner so perspective will be correct!

If you zoom into the 'single pixel' (first set of control point moves) and you will see it properly centered on top of each other!
Anthony Thyssen -- Webmaster for ImageMagick Example Pages
https://imagemagick.org/Usage/
Balduin
Posts: 3
Joined: 2012-01-01T09:35:50-07:00
Authentication code: 8675308

Re: How to keep "distort" aligned at a specific coordinate?

Post by Balduin »

Hello,
many thanks for your detailed replies!!!
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: How to keep "distort" aligned at a specific coordinate?

Post by fmw42 »

I did not define the top right corner so perspective will be correct!
Picking A Nit here...!

Perspective is preserved but not exactly what he specified by using the four corners. Thus it is very close, but to be exactly precise, it would need a two step process, distort perspective, project the input point to this perspective transformation, then translate the result from the computed location to the new location.

(Or Anthony would have to provide offsets within the distort perspective somewhat like specified in the distort SRT, thought not really suggesting this)
User avatar
anthony
Posts: 8883
Joined: 2004-05-31T19:27:03-07:00
Authentication code: 8675308
Location: Brisbane, Australia

Re: How to keep "distort" aligned at a specific coordinate?

Post by anthony »

Here is the final SRT example...

Code: Select all

convert align_pixel_1.png \
           \( align_pixel_2.png -alpha set -virtual-pixel Transparent \
              +distort SRT '59.5,26.5  1  45  52.5,30.5' \
            \) -compose Multiply -flatten align_pixel_srt.png
Image

To remove clipping, caused by 'flatten' limiting its result to the canvas image...

Code: Select all

convert align_pixel_1.png \
           \( align_pixel_2.png -alpha set -virtual-pixel Transparent \
              +distort SRT '59.5,26.5  1  45  52.5,30.5' \
            \) -compose Multiply -layers Merge +repage align_pixel_srt_merge.png
Image

Note the 'offset' of the final image is junked using +repage so that the image will NOT have a negative 'offset' whcih many browsers would have problems with. Remove +repage to preserve this position offset for further processing (such as mutli-image panaorama photo mosaics).

I will be using something like the above as 'distorted layering' examples (if you do not mind).

See Layering Distorted Images (in a few hours)
http://www.imagemagick.org/Usage/layers/#layer_distort
Anthony Thyssen -- Webmaster for ImageMagick Example Pages
https://imagemagick.org/Usage/
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: How to keep "distort" aligned at a specific coordinate?

Post by fmw42 »

For translating the perspective transform, here is the proper way to preserve the transform from the 4 corner control point pairs AND shift (translate) the result.

1) compute and save the perspective projection coefficients by using -verbose with +distort perspective and 4 corner control point pairs
2) project the input dot location to its corresponding output dot location using the projection coefficients (px,py)
3) compute the x and y offsets (delta) for the translation using the desired location (dx,dy) and projected location (px,py) as del=(desired - projected)
4) modify the projection coefficients to take into account the translation

This can be done using:

delx = dx - (sx*Xs + ry*Ys + tx)/(px*Xs + py*Ys + 1.0)
dely = dy - (rx*Xs + sy*Ys + ty)/(px*Xs + py*Ys + 1.0)

where dx,dy are the desired dot locations after translation and the perspective equation which computes px,py is from http://www.imagemagick.org/Usage/distor ... projection We rewrite the equation to solve for dx,dy and then multiply the denominator by delx,dely and combine terms for Xs, Ys and the constant term.

From the above we find that the new coefficients are:

sxn=sx+delx*px
ryn=ry+delx*py
txn=tx+delx
rxn=rx+dely*px
syn=sy+dely*py
tyn=ty+dely

So given the two input images:

Image
Image

And the dot locations of red dot at sx,sy=59,26 and blue target dot at dx,dy=52,30 (along with the four corresponding corner coordinates), then the following script produces the steps above:

Code: Select all

pair1="0,0 3,5"
pair2="100,0 101,0"
pair3="100,75 96,85"
pair4="0,75 3,67"

sx=59
sy=26
dx=52
dy=30

clist=`convert 2e6fxqq.png -verbose -define distort:viewport=1x1+0+0 \
+distort perspective "$pair1 $pair2 $pair3 $pair4" null: 2>&1 \
| sed -n '3,4 p' | tr "'" "\000" | tr "," " "`
cArr=($clist)
c1=${cArr[0]}
c2=${cArr[1]}
c3=${cArr[2]}
c4=${cArr[3]}
c5=${cArr[4]}
c6=${cArr[5]}
c7=${cArr[6]}
c8=${cArr[7]}

px=`convert xc: -format "%[fx:($c1*$sx+$c2*$sy+$c3)/($c7*$sx+$c8*$sy+1)]" info:`
py=`convert xc: -format "%[fx:($c4*$sx+$c5*$sy+$c6)/($c7*$sx+$c8*$sy+1)]" info:`

delx=`convert xc: -format "%[fx:$dx-$px]" info:`
dely=`convert xc: -format "%[fx:$dy-$py]" info:`

cn1=`convert xc: -format "%[fx:$c1+$delx*$c7]" info:`
cn2=`convert xc: -format "%[fx:$c2+$delx*$c8]" info:`
cn3=`convert xc: -format "%[fx:$c3+$delx]" info:`
cn4=`convert xc: -format "%[fx:$c4+$dely*$c7]" info:`
cn5=`convert xc: -format "%[fx:$c5+$dely*$c8]" info:`
cn6=`convert xc: -format "%[fx:$c6+$dely]" info:`

convert 1231g8z.png \
\( 2e6fxqq.png -alpha set -virtual-pixel transparent +distort perspectiveprojection "$cn1 $cn2 $cn3 $cn4 $cn5 $cn6 $c7 $c8" \) \
-compose multiply -flatten 1and2_flatten1.png
Image


Now if we want the blue dot to be shifted 10 more pixels in x and y to dx=62 and dy=40, the we use:

Code: Select all

pair1="0,0 3,5"
pair2="100,0 101,0"
pair3="100,75 96,85"
pair4="0,75 3,67"

sx=59
sy=26
dx=62
dy=40

clist=`convert 2e6fxqq.png -verbose -define distort:viewport=1x1+0+0 \
+distort perspective "$pair1 $pair2 $pair3 $pair4" null: 2>&1 \
| sed -n '3,4 p' | tr "'" "\000" | tr "," " "`
cArr=($clist)
c1=${cArr[0]}
c2=${cArr[1]}
c3=${cArr[2]}
c4=${cArr[3]}
c5=${cArr[4]}
c6=${cArr[5]}
c7=${cArr[6]}
c8=${cArr[7]}

px=`convert xc: -format "%[fx:($c1*$sx+$c2*$sy+$c3)/($c7*$sx+$c8*$sy+1)]" info:`
py=`convert xc: -format "%[fx:($c4*$sx+$c5*$sy+$c6)/($c7*$sx+$c8*$sy+1)]" info:`

delx=`convert xc: -format "%[fx:$dx-$px]" info:`
dely=`convert xc: -format "%[fx:$dy-$py]" info:`

cn1=`convert xc: -format "%[fx:$c1+$delx*$c7]" info:`
cn2=`convert xc: -format "%[fx:$c2+$delx*$c8]" info:`
cn3=`convert xc: -format "%[fx:$c3+$delx]" info:`
cn4=`convert xc: -format "%[fx:$c4+$dely*$c7]" info:`
cn5=`convert xc: -format "%[fx:$c5+$dely*$c8]" info:`
cn6=`convert xc: -format "%[fx:$c6+$dely]" info:`

convert 1231g8z.png \
\( 2e6fxqq.png -alpha set -virtual-pixel transparent +distort perspectiveprojection "$cn1 $cn2 $cn3 $cn4 $cn5 $cn6 $c7 $c8" \) \
-compose multiply -flatten 1and2_flatten2.png

Image


Use -layers merge in stead of -flatten if you don't want the projected red quadrilateral to be cropped by the size of the blue background image during the compositing (for example if dx and dy were shifted by 30 either way, positive or negative, rather than by +10). That way the canvas expands to hold it all.as per Anthony's comments above.
User avatar
anthony
Posts: 8883
Joined: 2004-05-31T19:27:03-07:00
Authentication code: 8675308
Location: Brisbane, Australia

Re: How to keep "distort" aligned at a specific coordinate?

Post by anthony »

Note that if you are translating by integer units, you can use -repage '+X+Y!' to translate the distorted images virtual canvas offset by a 'relative' amount, before overlaying them...
See -repage...
http://www.imagemagick.org/Usage/basics/#page
or the option documentation
http://www.imagemagick.org/script/comma ... hp?#repage

Of course if the translation is not an integer amount, then you need to actually translate the image using "distort" so the actual image colors will reflact the 'sub-pixel' translation of the image.

However as you are already dealing with 'control points' why not just change ALL the destination 'control points' by the
amount of X and Y displacement you want (floating point translation)! If all the control point destinations are modified by the same amount, then the result is just a 'normal translation.

No need to then 'extract' or 'adjust' a Perspective Projection!!!


WARNING: remember distort control points are in "image coordinates", as such if you want to position the 'center' of the 'pixel' (a pixel is NOT a mathematical point) you need to add 0.5 to convert integer 'pixel coordinate' to a floating point 'image coordinate'. In my example above only the special point used a 0.5 'pixel center' as the other three coordinates were true 'image edges' which are also 'pixel edges'.

This 0.5 conversion typically will only become visible if aligning images very precisely (they images will be 1/2 a pixel out) or performing large scale changes in image size, so that a single pixel become spread over a large number of pixels in the destination, something that is pretty rare.

NOTE while "distort" using 'image coordinates' "draw" uses 'pixel coordinates' even though draw can draw using floating point values. It is typically when aligning "distorts" with image "drawing" that you will see these 1/2 pixel miss-alignments.

Still IM is exact enough for you to be exact too.
Anthony Thyssen -- Webmaster for ImageMagick Example Pages
https://imagemagick.org/Usage/
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: How to keep "distort" aligned at a specific coordinate?

Post by fmw42 »

However as you are already dealing with 'control points' why not just change ALL the destination 'control points' by the
amount of X and Y displacement you want (floating point translation)! If all the control point destinations are modified by the same amount, then the result is just a 'normal translation.
You do not know how much to shift the image, because you don't know the location of the point in the perspective distorted image before you do a perspective distort.
User avatar
anthony
Posts: 8883
Joined: 2004-05-31T19:27:03-07:00
Authentication code: 8675308
Location: Brisbane, Australia

Re: How to keep "distort" aligned at a specific coordinate?

Post by anthony »

fmw42 wrote:
However as you are already dealing with 'control points' why not just change ALL the destination 'control points' by the
amount of X and Y displacement you want (floating point translation)! If all the control point destinations are modified by the same amount, then the result is just a 'normal translation.
You do not know how much to shift the image, because you don't know the location of the point in the perspective distorted image before you do a perspective distort.
Of course you do... Just shift the control points by the same amount!
You can even use %[fx:...] to do the addition.

unsifted points...

Code: Select all

pair1="0,0 3,5"
pair2="100,0 101,0"
pair3="100,75 96,85"
pair4="0,75 3,67"
shifted...

Code: Select all

 dx=-10.3123  dy=8.7452
pair1="0,0 %[fx:3+$dx],%fx:5+$dy]"
pair2="100,0 %fx:101+$dx],%fx:0+$dy]"
pair3="100,75 %fx:96+$dx],%fx:85+$dy]"
pair4="0,75  %fx:3+$dx],%fx:67+$dy]"
And you can just do the distort directly.

Unless you are trying to allign a specific 'perspective shape' not at any specific location, to a fifth 'control point' (specifying its final location)

In which case YES. You need to do a 'test' distort, so you can then use the forward perspective projection coefficients to map the specific point to the destination, and then use that to figure out the translation needed. EG: find the difference between mapped point and desired location.

FYI: the mapping is documented in IM examples on the 'Projection' distortion
http://www.imagemagick.org/Usage/distor ... projection

However directly modify the 'Projection coefficients' is just a lot of WORK as Fred's scripts show! Just add the translation to each of the destination coefficients of the 'perceptive shape', and let IM work out the new coefficients it needs for the distortion! (That is just do it)

Note that their is no need to worry about 'image coordinates' vs 'pixel coordinates' in the fifith point locations as the difference will be the same. However the same can NOT be said for the coordinates taht define the 'shape' of the projection.

I'll leave you to generate a example script.
Anthony Thyssen -- Webmaster for ImageMagick Example Pages
https://imagemagick.org/Usage/
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: How to keep "distort" aligned at a specific coordinate?

Post by fmw42 »

Withdrawn.

I read Anthony's last comment too fast and missed his "Unless" paragraph. My only point was that a forward projection was needed to compute the difference. Then one can do as Anthony suggested to shift the control points or do as I did and modify the projection coefficients. And I agree that Anthony's solution is much simpler. And is easier for Bilinear as well.
Last edited by fmw42 on 2012-01-21T23:29:15-07:00, edited 3 times in total.
User avatar
anthony
Posts: 8883
Joined: 2004-05-31T19:27:03-07:00
Authentication code: 8675308
Location: Brisbane, Australia

Re: How to keep "distort" aligned at a specific coordinate?

Post by anthony »

I'll see about adding this as an example as well.

Done. Though not quite a fancy as Freds Script, it explains the process
http://www.imagemagick.org/Usage/layers/#layer_distort
Anthony Thyssen -- Webmaster for ImageMagick Example Pages
https://imagemagick.org/Usage/
Post Reply