-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Find GIF disposal method #8563
Comments
However, be aware that it may be different for different frames. from PIL import Image, ImageSequence
with Image.open("Tests/images/dispose_bgnd.gif") as im:
for frame in ImageSequence.Iterator(im):
print(frame.disposal_method) gives
That's ok though, you can use More importantly though, why are you interested in this? GIFs do not necessarily consist of separate whole images - the second frame might only contain the pixels that are different. To give a usual result, Pillow will combine the GIF frames as it loads them from the file - meaning that the disposal that worked for the raw frames will not have the same effect for you when you resave it with the images you are receiving. If you'd like to get the original, uncombined frames, it is possible, but I expect it to generate more problems for you later. |
Thanks. So what is the correct way to rotate frames in a GIF while preserving all other attributes like duration, loop and disposal? |
If all you are doing is rotating images by 90, 180 or 270 degrees, that is an operation that doesn't change the pixel values. So that should work. The following code should preserve the duration, loop, disposal and transparency (yes, by default, it should stay the same) of each frame. from PIL import Image, ImageSequence, GifImagePlugin
# The following line patches Pillow so that the images aren't loaded loaded as combined RGB images
# The individual images will look broken,
# but if the GIF is saved with the same settings as the original, the combined result will look ok
GifImagePlugin.GifImageFile.load_end = lambda self: None
frames = []
disposal_method = []
with Image.open("input.gif") as im:
for frame in ImageSequence.Iterator(im):
disposal_method.append(frame.disposal_method)
rotated_frame = frame.rotate(90)
rotated_frame.info["transparency"] = frame._frame_transparency
frames.append(rotated_frame)
frames[0].save(
"output.gif",
save_all=True,
append_images=frames[1:],
loop=im.info["loop"]
) However, if you wish to perform any operation that does change pixel values, I'm still not clear on why you feel the need to resave the disposal method. If you think that you're unable to get a visually correct result, sure, let's discuss that, but preserving the exact way that image data was originally saved is not really a goal that image encoders have. |
Thank you for the detailed answer. I'm trying to rotate animated GIF (test1.gif) with arbitrary angle, not just multiples of 90 degrees. from PIL import Image, ImageSequence
frames = []
with Image.open('test1.gif') as im:
for frame in ImageSequence.Iterator(im):
frame = frame.rotate(35)
frames.append(frame)
frames[0].save('test2.gif', save_all=True, append_images=frames[1:], loop=im.info['loop']) |
To rewind a bit, here are what the different disposal values mean, as seen at https://www.w3.org/Graphics/GIF/spec-gif89a.txt 0 - No disposal specified. The decoder is not required to take any action. For your image, the disposal method is mostly 2, so it isn't a great example of what I'm trying to explain. Let's look at the image from #8251 instead. This image uses just the disposal method of 1. The first two raw frames in the image are The second frame is saving space by only containing the pixels that have changed. The first frame is set to 'Do not dispose', so those new pixels will be pasted on top of the old ones, and the image will look good. Now, when Pillow opens the GIF and seeks to the second frame, from PIL import Image
with Image.open("input.gif") as im:
im.seek(1)
im.save("second.png") the average user would not expected to see the mostly black raw image. They would come here and report that something is broken. Instead, we give them the combined image. So when it comes time to resave the image, you are not resaving the first two raw frames, you are resaving There is no need to keep the original disposal. These aren't really the same images that were in the original GIF at all. If there was transparency in each frame of this GIF, I think some disposal settings might even produce incorrect results. Furthermore, you might not know that each frame of a GIF can only introduce 256 new colours to the image. If I opened a GIF, and added 300 new colours to each frame, I couldn't save those exact colours to a GIF. Pillow isn't only here to just open an image and resave it. Users might look at the images in between those two actions. Users might make changes like you are doing, or something more drastic. Disposal methods are a space saving mechanism that involves deliberately adding transparency and cropping to later images, so that when they are layered on top of each other, like layering one sheet on top of another for an overhead projector, the result still looks good. |
Thanks. I now understand the difference between accessing raw frames and getting something useful to work with. Do I also need to preserve each frame transparency like in your code or will it work automatically? Is this necessary? rotated_frame.info["transparency"] = frame._frame_transparency Final code: from PIL import Image, ImageSequence
frames = []
with Image.open('test1.gif') as im:
for frame in ImageSequence.Iterator(im):
rotated_frame = frame.rotate(35)
#rotated_frame.info["transparency"] = frame._frame_transparency
frames.append(rotated_frame)
frames[0].save('test2.gif', save_all=True, append_images=frames[1:], loop=im.info['loop'], disposal=2) |
You shouldn't need to set it, no. |
Thanks. |
I'm trying to modify all frames in an animated GIF and save to a new GIF.
Is there a way to get the original GIF disposal method to preserve it in the new GIF?
The image info dict has the loop value but missing the disposal value.
I'm currently using hard coded value disposal=2:
What are your OS, Python and Pillow versions?
The text was updated successfully, but these errors were encountered: