ruby-vips
ruby-vips copied to clipboard
Rotate leaves black lines along one or more sides of the image.
Describe the bug Rotating in 90º increments leaves a black line on one or more sides of non-square images
To Reproduce Steps to reproduce the behavior:
Using this image: flip-me-for-real
path = '/path/to/flip-me-for-real.jpg'
img = Vips::Image.new_from_file(path)
img.rotate(90).write_to_file('out90.jpg')
Expected behavior
Expect image to output rotated by 90 degrees without additional image content.
Actual Behavior
actual output out90

Desktop:
- OS: macOS
- Version 12.4
- ruby-vips 2.1.4
- libvips 8.12.2-Tue Jan 25 09:34:32 UTC 2022
** Server **
- heroku-stack 20 (Ubuntu 20.04-based)
- ruby-vips 2.1.4
- libvips 8.12.2-Tue Jan 25 09:34:32 UTC 2022
I reported this to the image_processing gem (https://github.com/janko/image_processing/issues/104) but it looks like this might be happening at a lower level than either of these gems. My doing-anything-with-c skills are very much atrophied or I'd take a swing at reproducing directly against libvips.
I also attempted to apply some of the suggestions in https://github.com/libvips/libvips/issues/1051 but would be completely unsurprised to find out I missed a trick somewhere.
Hi @etherbob,
This is expected behaviour. libvips uses corner convention by default for affine transforms, so a 90 degree rotate will translate the image by 1 pixel.
Your best fix (if you want a no-border 90 degree rotate) is to use the rot90 method. This does a simple pixel swap rotation, so it can only do multiples of 90, but you'll get a borderless result. Something like:
result = image.rot90()
There's also rot180, rot270 and rot. Don't use rot45, that's just for matrix arithmetic.
If you want fractional rotations, then you could use centre convention with eg.:
result = image.rotate(90, idx: 05, idy: 0.5)
Now you'll get symmetrical edges, but the 0.5 pixel displacement will make your image a little fuzzy.
Thank you for the quick response @jcupitt, that did the trick. Do you think it would be possible/reasonable to have ruby-vips switch to the appropriate rot command if the angle is divisible by 90?
I think flipping between rotate methods depending on the argument value would produce some unfortunate transition effects.
I think the best solution is to have two separate methods: one to do an exact rotate in 90 degree steps, and one to do a free rotation (this is what ruby-vips does now).
Perhaps we can do more to improve rotate, I'll make an issue to look at this again for 8.14.