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

Closed
JuanPotato opened this issue Nov 5, 2015 · 17 comments · Fixed by #5857
Closed

Animated gif displays frames incorrectly #1525

JuanPotato opened this issue Nov 5, 2015 · 17 comments · Fixed by #5857
Labels
Bug Any unexpected behavior, until confirmed feature. Conversion GIF Palette

Comments

@JuanPotato
Copy link

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
Copy link
Member

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

@JuanPotato
Copy link
Author

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

@radarhere
Copy link
Member

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
Copy link
Member

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
Copy link
Author

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
Copy link
Member

--- 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
Copy link
Author

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

@radarhere
Copy link
Member

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
Copy link
Author

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
Copy link

hi @radarhere

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

@radarhere
Copy link
Member

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
Copy link
Author

This is still an issue

@topkecleon
Copy link

This is still an issue

image

@addisonElliott
Copy link

👍 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
Copy link
Member

@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
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.

@radarhere
Copy link
Member

I've created PR #5857 to resolve this.
0
1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Any unexpected behavior, until confirmed feature. Conversion GIF Palette
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants