-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Added setting for converting GIF P frames to RGB #6150
Conversation
Why not make |
|
And in the news this week, we say farewell to one of the creators of GIF, one of the people who helped image formats as we know them, and really, much of how people communicate through images on the Internet. |
I like this idea and have three comments/suggestions:
|
|
Depends on whether you view a multi-frame GIF as a video or as a sequence of images. If you see it as a video then yes; returning a P frame seems incorrect because it is unexpected to not get a reconstructed frame. (Which is I guess why the default changed in pillow9.) However, if you see GIF as a sequence of individual images then no; getting a P frame (which is exactly what is stored in the file) seems correct, because it is unexpected that GIF can return RGB/RGBA considering that it is a palette only format (only stores P or L). (Which is I guess the reason why the first frame remains as P by default.) So from my perspective, the flag gives you the option to either
Fair enough, then the name makes perfect sense.
Yep, and that it does achieve. As I said before, it's probably too much of an edge case to be of practical concern; mainly brought it up because I wasn't sure if you are worried about such niche cases or if you would prefer to address it once it actually becomes a problem. |
You know this, but I just want to give some context to anyone reading this in the future.
Ok, so your idea for This isn't what This also isn't what Pillow previously did. It still pasted layers onto each other and used disposal, it just didn't realise that more than a single palette might need to be used to determine colours.
So I really don't think that setting an option called If we were to add this feature, I would want to use a different name. Instead of Just for the record, could you make your case as to why this is a helpful feature? In #5307 (comment), you were interested in this as a debugging tool, essentially.
|
GIF frames do not always contain individual palettes however. If there is only | ||
a global palette, then all of the colors can fit within ``P`` mode. If you would | ||
prefer the frames to be kept as ``P`` in that case, there is also a setting | ||
available:: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't what DIFFERENT_PALETTE_ONLY does. It still combines the frames. [...]
Oh, then I've misunderstood what DIFFERENT_PALETTE_ONLY
does. What do you think of changing the part of the docs that threw me?
GIF frames do not always contain individual palettes however. If there is only | |
a global palette, then all of the colors can fit within ``P`` mode. If you would | |
prefer the frames to be kept as ``P`` in that case, there is also a setting | |
available:: | |
GIF frames do not always contain individual palettes, however. If there is only | |
a global palette, then frames may still use values of previous frames, but all | |
of the colors can fit within ``P`` mode. If you would prefer the frames to be | |
served as ``P`` in that case, there is also a setting available:: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My hope is that a previous part of the documentation already conveys the idea that new frames are layered on top of old modes.
P
mode images are changed toRGB
because each frame of a GIF may introduce up to 256 colors. BecauseP
can only have up to 256 colors, the image is converted to handle all of the colors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the part that you referenced provides the motivation for why the image is converted to RGB
. What isn't clear yet (or at least wasn't clear to me when I read it) is that this behavior also applies if we don't convert to RGB
. Maybe we can rephrase the sentence you referenced to better convey that decoding happens in all cases:
Transparency allows a GIF frame to use more than 256 colors, 256 from a local palette + other colors from previous frames. To handle this correctly, images will - by default - be promoted to
RGB
. While this recombination (decoding) will always happen, you can control how the result will be returned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've pushed a commit. What do you think?
Right. You mentioned this before, but I forgot that pillow is implemented like this; my bad. In that case, it does indeed seem disproportional to add this behavior here since it would essentially be a new feature instead of just "turning off a new feature".
I would still like it as a debugging tool 😅. Another reason is that, yes, a raw frame may easily look terrible, but there are also cases where it contains useful information. For example when creating cartoon/anime animations like this one: https://i.imgur.com/uNdDoPT.gif . In its raw form, the file is fairly well-behaved; at least before any further optimization takes place. It uses disposal to clear each frame and because the background is transparent you can extract the individual steps of the animation [keyframes, but I don't want to alias that word so I will stick to steps]. However, if I now add a complex background things get complicated. Each raw frame still only contains one step of the animation but pillow will only let me access that step overlayed on the complex background. How would I get an animation step back? I could do some complex CV (background removal/extraction), write a small parser myself (it's a fairly simple use-case), or I may have the raw animation steps stored elsewhere. None of these seem particularly ideal. That being said, this is about as niche a use-case as me writing L-mode GIF and since supporting it would mean writing a sophisticated new feature, I agree that it is cleaner not to do it until there is more demand. A third reason - though this is apparently just me and my completionism - is that it seems odd that it is only possible to get the decoded frames, but not the demuxed (raw) frames. We are essentially reading GIF like a video (raw bytes -[decompression]-> demuxed frame -[reconstruct]-> decoded frame). We can view demuxed frames as an intermediate implementation detail, but they are still frames so it seems odd (to me) that there is no easy way to access them. |
src/PIL/GifImagePlugin.py
Outdated
|
||
from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence | ||
from ._binary import i16le as i16 | ||
from ._binary import o8 | ||
from ._binary import o16le as o16 | ||
|
||
|
||
class ModeStrategy(IntEnum): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a versionadded for the docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, that now appears in the docs.
The setting name In the code GifImagePlugin.PALETTE_TO_RGB = GifImagePlugin.ModeStrategy.XXX it isn't clear to my why a |
Fair enough. I've pushed a commit to rename the setting. |
available:: | ||
|
||
from PIL import GifImagePlugin | ||
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be worth noting the name of the default option:
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY | |
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY | |
To restore the default behavior of reading the first frame in ``P`` mode and converting to ``RGB`` for subsequent frames:: | |
from PIL import GifImagePlugin | |
GifImagePlugin.LOADING_STRATEGY = GifImagePlugin.LoadingStrategy.RGB_AFTER_FIRST |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I've pushed a commit with a variation of this.
Thanks all! |
Alternative to #5974
Each frame in a GIF may contain up to 256 colours, and so after the first frame of a GIF, there might be more colours than can be fit into a P mode image. To handle this, #5857 caused subsequent GIF frames to be converted to RGB or RGBA. This resolved a number of open issues, and didn't change the behaviour of Pillow for users who just look at the first frame of a GIF.
Even so, there has been feedback.
To handle both of these cases, this PR suggests adding an enum to control the behaviour.
Also, I wasn't paying much consideration to L mode GIFs in #5857. This has also received feedback, and #6086 tried to address this by upgrading L to LA when transparency was present. Instead, here I'm keeping L mode GIFs as L, always.