Page 1 of 1

About "-shear" and "-distort affineprojection"

Posted: 2016-12-18T23:44:17-07:00
by GeeMack
IM 7.0.4 — Windows 10 64 — I know the "-shear" operator lets me create a parallelogram with, for example, a 36 degree slant. (I'm using 36 degrees for these tests, but in actual use it could be anything, and I'll plug in a shell variable.) Here's a command to get that result...

Code: Select all

magick logo: -alpha on -resize 50% -background none -shear -36x0 sheartest1.png
This is the output...

Image

The "-shear" operator extends the canvas and fills the added space with the background color, but... Sometimes I'd like to have that background filled according to the virtual-pixel setting instead. So I've been experimenting with "+distort affineprojection" to create a shear, slanted at a specified angle, and getting its background from virtual-pixel.

Working from an example at THIS link, I've tried to get that same 36 degree slant using a command like this...

Code: Select all

magick logo: -alpha on -resize 50% -background none -virtual-pixel none ^
   +distort affineprojection "1,0,%[fx:sin(36*pi/180)],1,0,0" -shave 1x1 sheartest2.png
It creates a parallelogram sure enough, but the angle is not nearly the 36 degrees I would have expected. It's more like 30 degrees. Here's the result...

Image

Maybe I misunderstand what the example page means by using an angle to calculate the amount of shear, but it really seemed like those two above commands should create nearly the same result. Am I missing something obvious here?

Re: About "-shear" and "-distort affineprojection"

Posted: 2016-12-19T00:56:23-07:00
by fmw42
The x-shift is determined by sin(angle)=x-shift/height, assuming the skew keeps the vertical height constant. If not then tan(angle)=x-shift/height (assuming the height is the diagonal edge and remains constant distance. Try the tan rather than the sin. You may also need to compensate if your input image is not square.

See also my script, skew, if on a Unix-type platform.

Re: About "-shear" and "-distort affineprojection"

Posted: 2016-12-19T12:44:10-07:00
by snibgo
ShearImage() in shear.c uses tan(), not sin().

Re: About "-shear" and "-distort affineprojection"

Posted: 2016-12-19T23:40:46-07:00
by anthony
if you are resizing an image as well as shear.. I would seriously consider doing the shear first then then resize.

Note however that shear uses pixel movement to do the 'warping', An Affine distortion uses 'ellipticatal area sampling) whcih will often give better results, and will let you do the resize (scaling) as part of the operation as well. The three point affine distortion (using floating point numbers for the coordinates) makes it relatively easy to set up shears (two points remain fixed, and only one coordinate of the thrird point needs to change). You can even do angle to coordinate calculations within the distort arguments!

Re: About "-shear" and "-distort affineprojection"

Posted: 2016-12-20T12:19:08-07:00
by GeeMack
fmw42 wrote:Try the tan rather than the sin.
Substituting the tangent function with the above command, like this...

Code: Select all

magick logo: -alpha on -resize 50% -background none -virtual-pixel none ^
   +distort affineprojection "1,0,%[fx:tan(36*pi/180)],1,0,0" -shave 1x1 sheartest3.png
... does result in a shear with a proper 36 degree angle (or whatever variable I use for degrees in the formula).

The example command at THIS link makes a shear that looks like about 20 degrees, but it's off by a bit. Substituting another angle, like 45 degrees, clearly shows an incorrect result. That example command will work properly, however, by using the tangent function instead of the sine function.
See also my script, skew, if on a Unix-type platform.
Thanks. I'm usually working on a Windows machine, but I can read and write *nix shell, so I was able to get some useful hints from your script.

Re: About "-shear" and "-distort affineprojection"

Posted: 2016-12-20T12:55:18-07:00
by GeeMack
anthony wrote:The three point affine distortion (using floating point numbers for the coordinates) makes it relatively easy to set up shears (two points remain fixed, and only one coordinate of the thrird point needs to change). You can even do angle to coordinate calculations within the distort arguments!
That's pretty much what I started with. My script uses a variable %ANGLE% to calculate the number of pixels for the shear, then I use a three point "-distort affine ..." to slide the one edge over by that many pixels. I use the known right angle, the known height, and the variable angle inserted into the formula, then figure the length of the leg opposite the variable angle using an FX formula based on this...

Code: Select all

... %[fx:(h/sin((90-%ANGLE%)*pi/180))*sin(%ANGLE%*pi/180)] ...
I was experimenting with "-distort affineprojection" to find a way to reduce the math. I guess I should have taken trigonometry in high school. ;)

Re: About "-shear" and "-distort affineprojection"

Posted: 2016-12-20T17:39:28-07:00
by anthony
GeeMack wrote:The example command at THIS link makes a shear that looks like about 20 degrees, but it's off by a bit. Substituting another angle, like 45 degrees, clearly shows an incorrect result. That example command will work properly, however, by using the tangent function instead of the sine function.
I'll update that example to use tan instead!

In terms of maths. the control point method is probably easier to visualise, but both are matrix or control points would probably have the same complexity in calculations.
GeeMack wrote:I use the known right angle, the known height, and the variable angle inserted into the formula, then figure the length of the leg opposite the variable angle using an FX formula based on this...

Code: Select all

... %[fx:(h/sin((90-%ANGLE%)*pi/180))*sin(%ANGLE%*pi/180)] ...
I was experimenting with "-distort affineprojection" to find a way to reduce the math. I guess I should have taken trigonometry in high school. ;)
That formula is a cos()/sin() which is basically the definition of a tangent or tan().

I myself remember this from grade 11 (gosh that was a long time ago) as being opposite over adjacent when looking at a triangle. I also remember that cosine was oppisite, with sine as adjacent, and sin is a 90 degree offset to cosine. -- A little trigonometry knowledge can corrupt a young mind in evil ways!

If you can pick the control points, (which can be floating point) why not just pick coordinates 0,0 1,0 and 0,1 for your starting points. That would remove any need worry about the image height or width, leaving it to the built in "+distort" viewport calculations to figure the best results. The +distort does this by 'forward mapping' the corners of the image as positioned on input virtual canvas to find the viewout on the output virtual canvas.

ASIDE: It is that 'forward mapping' requirement for determining the output viewport which stops +distort determining the best viewport for some of the more complex distortions (like shepards). Not all distortions (which are reverse mapped) have a forward mapping.

Re: About "-shear" and "-distort affineprojection"

Posted: 2016-12-20T18:33:50-07:00
by fmw42
Whether you use the sin or the tan, depends upon how you want to deal with the height of the input for a left/right shear. If you want the output height to be the same as the input height while stretching the sides, then use the tan. If you want the sides to be the same length as the input height letting the output height shrink, then use the sin. This becomes clearer by looking at the shear triangle and the ratio of the sides of the right angle forming the tan or the ratio of the horizontal to the diagonal forming the sin.

Re: About "-shear" and "-distort affineprojection"

Posted: 2016-12-20T18:53:11-07:00
by anthony
There are basically two types of shear. One by by rotating a vector, The other by just pushing the vector to one side.

Shears are normally given to be a simpler 'side-push', So tan would be the correct math. I updated the linked to example to reflect that. I only used sin() in that example because I was thinking in terms of rotation and not thinking the example through by making the shear a side push. Basically tan() was what was needed to set the 'shear' angle correctly using a side push. But shearing an image to say 89 degrees would generate a VERY tall or wide image as tan() approaches infinity.

Rotational shears, basically tries to preserve the side length of the image. A rotation shear would require two numbers in the AffineProjection Matrix to be modified, so as to rotate the vector both in the X and Y, and thus get the angle to match the angle requested. You also need to modify two numbers in a Affine distortion using control points (vectors) for the same reason.