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

Pillow animated gifs #76

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open

Pillow animated gifs #76

wants to merge 6 commits into from

Conversation

kaedroho
Copy link
Contributor

This PR adds support for animated GIFs in the Pillow backend

@kaedroho kaedroho force-pushed the pillow-animated-gifs branch from e1c648b to 63ee7c0 Compare April 12, 2019 14:09
Copy link

@m1kola m1kola left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good, but I have few questions.

Also would be great to test it on a real project: I don't have a chance to do it.

willow/plugins/pillow.py Outdated Show resolved Hide resolved
willow/plugins/pillow.py Outdated Show resolved Hide resolved
@Image.converter_to(RGBAImageBuffer, cost=200)
def to_buffer_rgba(self):
if self.has_animation():
pass # TODO: Raise warning
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify: are you planning to fix these todos separately or in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, forgot I had these. I'll have another look in this PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Willow is forced to decide which frame to use when saving the image, I think this should raise a warning. I know it's common to just pick the first frame but I think it's better to require them to explicitly state this as they might be calling .save_as_png without considering what to do with animated GIFs.

It also never makes sense to attempt to do face detection on an animated image. So if this happens there must be a missing check and a warning should help surface this issue.

We probably should have an operation available on all image classes to allow selecting individual frames in the image. This way, users can always explicitly choose the first frame if they want to remove animation (and this would have no effect on non-animated images).

Something like:

myimage.frame(0).save_as_png()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choosing a frame sounds like a good idea. Should probably be a separate PR to keep this focussed.

willow/registry.py Show resolved Hide resolved
}

if 'transparency' in image.info:
params['transparency'] = image.info['transparency']
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not find any reference of variability in frames transparency, but if it turns out to be true, this is another place to consider. also why not user has_alpha() as in the image operation?

Copy link
Contributor Author

@kaedroho kaedroho Oct 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only information I could find on this is in the GIF spec

Section 23 says that the fields which define transparency are assigned per graphics block, so it's possible that these settings can change per-frame but even on different areas of a single frame as well.

To check if Pillow supports multiple GIF palettes, I tried opening an image with multiple palettes and saving it again.

Before:
SmallFullColourGIF

After:
pillow-saved

(Image source https://en.wikipedia.org/wiki/GIF#/media/File:SmallFullColourGIF.gif)

I think the answer to that is clear! And looking at the Pillow source code, looks like only one palette gets used in the _save function so it's not possible for it to have different palettes for each block: https://github.com/python-pillow/Pillow/blob/master/src/PIL/GifImagePlugin.py#L507

Thinking about it, this is probably a really hard problem. Whenever we save a GIF image, we can't assume the colour palette of the original image is still valid as resizing would generate many more colour variations than there were in the original image, and it's also possible that the image could've been modified in another way that could add more colours as well.

This would mean that we would have to programmatically re split each frame of the image back into blocks and generate a new colour pallete for each one. It would need to balance between having enough blocks in the image to represent as many colours as possible yet not having too many so the image file isn't too big.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To demonstrate a tool that can actually do this image blocking/pallette generation automatically, I generated a new GIF using the following website: https://ezgif.com/

ezgif com-gif-maker

This clearly has lower quality than the original, but also it's 6x larger! (315KB up from 54KB)

Trying out other formats, this APNG is 79KB and looks much better:

ezgif com-apng-maker

I can't show them here, but a high quality WEBP comes out at 115KB and a 70% quality WEBP (which looks roughly the same as the GIF above) is 40KB

All modern browsers except IE11 supports at least one of these formats (apng, webp). So I think it would be worth adding support for these into Willow.

@danihodovic
Copy link

What's the status of this PR?

@tomdyson
Copy link

@danihodovic it's awaiting review

Copy link
Collaborator

@zerolab zerolab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A great improvement, @kaedroho!

Found two issues with RGBA animated gifs which I documented in separate comments.

Ran a few quick and dirty memory tests:

  • the linked image (5.5MB) takes about 35MB on the image edit page (using the max-800x600 rendition)
  • the same image but in higher resolution (17.2MB) took 79MB

willow/plugins/pillow.py Outdated Show resolved Hide resolved
willow/plugins/pillow.py Outdated Show resolved Hide resolved
@gasman
Copy link
Contributor

gasman commented Oct 2, 2019

Code looks good to me. One thing I'm not clear on, though: given that PillowAnimatedImage can presumably do everything PillowImage can do and more, is there any reason for the "choose the best backend" logic in registry.py to ever prefer PillowImage - and if not, do we really need both classes to exist?

@kaedroho
Copy link
Contributor Author

kaedroho commented Oct 4, 2019

Code looks good to me. One thing I'm not clear on, though: given that PillowAnimatedImage can presumably do everything PillowImage can do and more, is there any reason for the "choose the best backend" logic in registry.py to ever prefer PillowImage - and if not, do we really need both classes to exist?

Good point! I've merged the two classes together now

@kaedroho
Copy link
Contributor Author

kaedroho commented Dec 3, 2019

My plan for this is to implement both animated GIF and animated WEBP support in Willow, then add logic into Wagtail so that it always transcodes animated GIFs into animated WEBP files. This fixes issues with having to save a GIF that contains more than 256 colours (which will be inevitable if we've resized the image). See discussion in #76 (comment)

@coredumperror
Copy link

coredumperror commented Jan 24, 2020

I just tried out this PR on my shop's codebase, because the Wand-based solution causes our servers to die when someone uploads a sufficiently large or complex animated GIF.

Using this code, I was able to get one of the offending GIFs to generate renditions without crashing, but they're... broken.

Here's the original (appologies for the gigantic GIF):
funnel_pluviation

And here's the Image Chooser rendition:
funnel_pluviation max-165x165

Maybe I've done something wrong? Is there anything I can tweak to try to make Pillow generate animated GIFs in a different way, which hopefully won't break them?

@kaedroho
Copy link
Contributor Author

Hey @coredumperror

I'm getting similar results with a different image here

I think this is just a Pillow limitation. It must be difficult saving GIF images like this which have a limit of 256 colours. I think the best solution would be for us to add support for animated webp/apng so images can be saved in those formats instead.

@coredumperror
Copy link

I think the best solution would be for us to add support for animated webp/apng so images can be saved in those formats instead.

Yes, please! Animated GIF is soooo 1990s. Hell, mosts sites that let you share GIFs just convert to mp4 video with no audio track. I wonder if that would be possible?

@kaedroho
Copy link
Contributor Author

Hell, mosts sites that let you share GIFs just convert to mp4 video with no audio track. I wonder if that would be possible?

Hadn't thought of that before! I think we could view MP4 w/o audio as just another animated image format, so could support that if Pillow/wand does (but can always add an ffmpeg plugin if not!)

@gasman
Copy link
Contributor

gasman commented Jan 27, 2020

Relevant blog post: https://paulbakaus.com/2020/01/17/gifs-must-die/
Historically Wagtail has deliberately shied away from dealing with video because doing it well (handling arbitrary format conversions, extracting metadata, generating thumbnails, dealing with large file uploads...) would be as big a job as the content management side... but given that the demand for animated GIFs isn't going away, and the border between animation and video is getting ever more blurred, I think it might be time to rethink that.

@coredumperror
Copy link

I think it might be time to rethink that.

I am exceedingly glad to hear this. :)

@jamesray
Copy link

jamesray commented Jan 26, 2021

@zerolab possibly in relation to issue #80 , is there any traction on this?

@coredumperror
Copy link

We're still very interested in Wagtail properly supporting animated whatevers (GIF, webp, mp4, etc.).

@zerolab
Copy link
Collaborator

zerolab commented Jan 27, 2021

Incidentally @thenewguy makes the case for animated GIFs to be treated as "media" (i.e. no resizing and other Willlow filters) - torchbox/wagtailmedia#127
Might that be the way to go?

@kaedroho
Copy link
Contributor Author

kaedroho commented Jan 27, 2021

I like that idea! In many cases, GIFs would be both smaller and better quality if we don't try to process them. Wagtailmedia may also be able to provide the processing code required to convert them into videos as well.

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

Successfully merging this pull request may close these issues.

8 participants