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

Animated gif displays frames incorrectly #1525

Open
JuanPotato opened this Issue Nov 5, 2015 · 16 comments

Comments

Projects
None yet
7 participants
@JuanPotato
Copy link

JuanPotato commented Nov 5, 2015

I'm having the same exact problem as #634 but that issue was "fixed".
image

When I seperate the frames using convert -coalesce animation.gif target.png in ImageMagick, the frames come out looking perfect, but with pillow...

import PIL
import PIL.Image
i = PIL.Image.open('test.gif')
i.show() # correct image displays on left
i.seek(i.tell() + 1)
i.show() # image is incorrect on right

The gif in question

@radarhere

This comment has been minimized.

Copy link
Member

radarhere commented Nov 5, 2015

Just to check before going any further - you've mixed up left and right in your comments, yes?

@JuanPotato

This comment has been minimized.

Copy link
Author

JuanPotato commented Nov 5, 2015

Oh, yeah @radarhere. Note to self: do not open issues while sleepy.

@radarhere

This comment has been minimized.

Copy link
Member

radarhere commented Nov 6, 2015

Thanks for clarifying, and thanks for the link to the previous issue, very helpful.

So, GIFs can have a 'disposal method', and one setting of this that each subsequent frame is to be pasted on top of a previous one, making the raw latter frame simply the difference of the image. Sounds like a good compression technique. So the second frame is an RGBA to be pasted onto the first one.

The good news is that the GifImagePlugin is correctly working all of this out and performing those operations. The bad news is that it's the paste operation that is exhibiting the bug. If I use PIL to save the raw first frame and second frame images and overlay them using an external program, it works fine. If I get PIL to paste them together, then the problematic image is generated.

That's all I have for the moment, but it's a start.

@radarhere

This comment has been minimized.

Copy link
Member

radarhere commented Nov 6, 2015

Okay! It's because the base image is P mode, whereas the mask is RGBA. Convert the base image to RGB, and it works fine.

So that's how to get it working. I'm not quite sure what the best solution is. If the aim was to fix this from GifImagePlugin, then it would be simpler. However, I presume it is better to solve this for the paste method.

The convert method returns a copy. I'm not sure if there is a way for an image to easily convert itself.

@JuanPotato

This comment has been minimized.

Copy link
Author

JuanPotato commented Nov 17, 2015

Convert the base image to RGB, and it works fine.

I'm sorry, I don't understand what you mean. Convert the charmander.gif into RGB?

If I use PIL to save the raw first frame and second frame images and overlay them using an external program

also, how do you save the second frame? I'm really sorry I kinda abandoned this, I looked at it and forgot. Then once I got to my problem again I remembered.

@radarhere

This comment has been minimized.

Copy link
Member

radarhere commented Nov 18, 2015

--- a/PIL/GifImagePlugin.py
+++ b/PIL/GifImagePlugin.py
@@ -281,6 +281,9 @@ class GifImageFile(ImageFile.ImageFile):
         # we do this by pasting the updated area onto the previous
         # frame which we then use as the current image content
         updated = self.im.crop(self.dispose_extent)
+       if self._prev_im.mode == "P":
+            self._prev_im = self._prev_im.convert("RGBA")
+            updated = updated.convert("RGBA")
         self._prev_im.paste(updated, self.dispose_extent,
                             updated.convert('RGBA'))
         self.im = self._prev_im

If you adjust your Pillow installation according to the above diff, then your program should run correctly. That will solve your immediate problem.

@JuanPotato

This comment has been minimized.

Copy link
Author

JuanPotato commented Nov 19, 2015

Thank you, but is there anyway to do it within the code so that people can use it without the fix.

@radarhere

This comment has been minimized.

Copy link
Member

radarhere commented Nov 19, 2015

I don't know how to resolve the bug within the Pillow code. Hopefully this problem will be resolved by someone, and contributions are welcome if you have suggestions.

I thought that you were seeking an immediate solution to your problem, and that was my attempt at one.

@JuanPotato

This comment has been minimized.

Copy link
Author

JuanPotato commented Nov 19, 2015

I was, but my problem exists for my program that a lot of people use and I want it to be fixed for everyone without people having to apply a fix, I will continue to see if this can be fixed within pillow, maybe making an external paste method. Thank you so much for your help. I really appreciate it

@stonebig

This comment has been minimized.

Copy link

stonebig commented Dec 26, 2015

hi @radarhere

You patch works, why didn't you merge it ?

@radarhere

This comment has been minimized.

Copy link
Member

radarhere commented Dec 27, 2015

Hi. My patch works, but with two problems -

  • I suspect that there would be problems for conversion between other modes as well, meaning that it's a specific fix, rather than a general fix.
  • It's a workaround. The real problem does not exist within GifImagePlugin, but is a bug when pasting a transparent image.

If you want to create a PR with the patch, to see if others agree with you that it should be merged, you're welcome to.

@JuanPotato

This comment has been minimized.

Copy link
Author

JuanPotato commented Apr 8, 2018

This is still an issue

@topkecleon

This comment has been minimized.

Copy link

topkecleon commented Apr 8, 2018

This is still an issue

image

@addisonElliott

This comment has been minimized.

Copy link

addisonElliott commented Sep 28, 2018

👍 Having this issue as well but in a different way.

I have a GIF with disposal method set to 1 as well. But the previous frame uses the global color table and the new frame uses a local color table. When pasting the new image on the previous image, the color table for the local color table is not preserved.

@radarhere

This comment has been minimized.

Copy link
Member

radarhere commented Sep 28, 2018

@addisonElliott to keep tracking simple, I'd ask that you create a new issue for your problem, since it is not exactly the same as this one.

@monkey-sheng

This comment has been minimized.

Copy link

monkey-sheng commented Dec 20, 2018

Hi, @radarhere . I stumbled across the same issue and tried your workaround code. I am using PIL 5.2.0 in the simplest loop to show every frame.

But it throws this error:

Traceback (most recent call last):
......
File "C:\Users\hujas\AppData\Local\Programs\Python\Python36\lib\site-packages\PIL\ImageFile.py", line 138, in load
    pixel = Image.Image.load(self)
  File "C:\Users\hujas\AppData\Local\Programs\Python\Python36\lib\site-packages\PIL\Image.py", line 822, in load
    self.im.putpalette(*self.palette.getdata())
ValueError: unrecognized image mode

Also, only the first two frames can be shown correctly, so this leads me to think that PIL is now getting the palette from the previous frame and feeding it to the next frame when load() is called on the next frame.
Apparently self.palette.getdata() doesn't support RGBA mode? So I modified your code like this:

        self._prev_im.paste(updated, self.dispose_extent, updated.convert('RGBA'))
        # I added this following line
        self._prev_im=self._prev_im.convert('P')
        self.im = self._prev_im
    self._prev_im = self.im.copy()

And then every frame comes out as expected.

The real problem does not exist within GifImagePlugin, but is a bug when pasting a transparent image.

I don't think there's a bug with pasting a transparent image, since it's using the paste() method of Image class. I skimmed through the paste() method and saw this:

if self.mode != im.mode:      
    if self.mode != "RGB" or im.mode not in ("RGBA", "RGBa"):
        im = im.convert(self.mode)

So the updated area will be converted to the same mode as the frame(in this case, 'P'). This is probably the reason why transparency went missing. It's more like an incompatibility rather than a bug that can be fixed in the paste() method. A very strong fix would be writing a new paste() and override for GIF images, but I think your workaround is perfectly fine as a general fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.