Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to Reverse Pillow Rotate Translation when expand set to True, or not cropping when expand set to False #4556

Closed
OneWorld-github opened this issue Apr 14, 2020 · 2 comments

Comments

@OneWorld-github
Copy link

OneWorld-github commented Apr 14, 2020

Please see investigation here:-

https://stackoverflow.com/questions/61128407/reversing-the-translation-of-the-pillow-image-library-rotation

What did you do?

I rotated two images on a still background, and made them into a still video.

What did you expect to happen?

I was expecting the two images to be rotated, and their centre points to be still in the same location after rotation.

What actually happened?

They did rotate, however, the centre points of the two rotated images also moved !?
This happens when expand is set to True. However, when expand is set to False, it crops the rotated images corners symmetrically, as can be seen in the google drive links shared.

Is there a way I can rotate an image without the centre point moving, and without the corners being cropped off the other corners? Or is there a way to reverse the translation that comes about when expand=True, as a post process translation ?

What are your OS, Python and Pillow versions?

  • OS: Windows 10
  • Python: 3.7
  • PIL: 6.1.0

There is code and video examples in the Stackoverflow link at the top. I am not sure if this is a bug or a question !?

@radarhere
Copy link
Member

Hi. Bear with me for a second while I run through an explanation, and then I'll say something that could be useful.

Allow me to start out with some visual aids.

This is one of our test images.

hopper

Here is what I get if I run im.rotate(45),

hopper_45

Here is what I get if I run im.rotate(45, expand=True),
hopper_45_expand

You are talking about rotating an image, allowing it to expand to stay uncropped, while keeping it centered around the same point. That really doesn't make sense to me within the world of Pillow. In Pillow, each image has their own co-ordinates, with (0, 0) as the upper left corner. To say that you'd like coordinates to be different in any way without changing the size of the image is a contradiction in terms.

We are dealing exclusively with images, and I think the coordinate system you're thinking about really only comes into play when you start talking about moviepy. If you can imagine a rotation operation that doesn't look like my second or third images, please describe it further or craft an image to demonstrate.

Now, for something potentially useful.

from PIL import Image, ImageDraw
im = Image.open("Tests/images/hopper.png")
im_rotated = im.rotate(45, expand=True)
print("The center of im_rotated has been offset by ("+(str(im_rotated.width / 2 - im.width / 2))+", "+(str(im_rotated.height / 2 - im.height / 2))+")")

That short script shows how much the center of the image has moved by. I am imagining that you can use this in your script to move the image to where you would like.

To demonstrate, here is a script that draws a dot in the middle of the original image, and then rotates it, so you can see that the position change between the two dots is the same as the above script outputs.

from PIL import Image, ImageDraw
im = Image.open("/Users/andrewmurray/Dropbox/docs/pillow/Pillow/Tests/images/hopper.png")

d = ImageDraw.Draw(im)
im_center = int(im.width / 2), int(im.height) / 2
radius = 5
d.ellipse((im_center[0] - radius, im_center[1] - radius, im_center[0] + radius, im_center[1] + radius), fill='#f00')

im_rotated = im.rotate(45, expand=True)
im_rotated_center = int(im_rotated.width / 2), int(im_rotated.height) / 2

out = Image.new(im_rotated.mode, im_rotated.size)
out.paste(im)
im_rotated_mask = im.convert("RGBA").rotate(45, expand=True)
out.paste(im_rotated, (0, 0), im_rotated_mask)

d = ImageDraw.Draw(out)
d.ellipse((im_center[0] - radius, im_center[1] - radius, im_center[0] + radius, im_center[1] + radius), fill='#f00')
d.ellipse((im_rotated_center[0] - radius, im_rotated_center[1] - radius, im_rotated_center[0] + radius, im_rotated_center[1] + radius), fill='#f00')

print("The center of im_rotated has been offset by ("+(str(im_rotated.width / 2 - im.width / 2))+", "+(str(im_rotated.height / 2 - im.height / 2))+")")

out.save("dots.png")

dots

@OneWorld-github
Copy link
Author

This is perfect. Thank you so much for explaining this in so much detail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants