PIL - 将 GIF 帧转换为 JPG

Posted

技术标签:

【中文标题】PIL - 将 GIF 帧转换为 JPG【英文标题】:PIL - Convert GIF Frames to JPG 【发布时间】:2012-05-03 09:20:13 【问题描述】:

我尝试使用 Python 图像库将 gif 转换为单个图像, 但它会导致奇怪的帧

输入的 gif 是:

Source Image http://longcat.de/gif_example.gif

在我的第一次尝试中,我尝试将带有 Image.new 的图像转换为 RGB 图像,以 255,255,255 作为白色背景 - 与任何其他图像一样 我在互联网上找到的示例:

def processImage( infile ):

    try:
        im = Image.open( infile )
    except IOError:
        print "Cant load", infile
        sys.exit(1)

    i = 0

    try:
        while 1:

            background = Image.new("RGB", im.size, (255, 255, 255))
            background.paste(im)
            background.save('foo'+str(i)+'.jpg', 'JPEG', quality=80)

            i += 1
            im.seek( im.tell() + 1 )

    except EOFError:
        pass # end of sequence

但它会导致奇怪的输出文件:

Example #1 http://longcat.de/gif_example1.jpg

我的第二次尝试是,先将 gif 转换为 RGBA,然后使用 它的透明蒙版,使透明部分变白:

def processImage( infile ):

    try:
        im = Image.open( infile )
    except IOError:
        print "Cant load", infile
        sys.exit(1)

    i = 0

    try:
        while 1:

            im2 = im.convert('RGBA')
            im2.load()

            background = Image.new("RGB", im2.size, (255, 255, 255))
            background.paste(im2, mask = im2.split()[3] )
            background.save('foo'+str(i)+'.jpg', 'JPEG', quality=80)

            i += 1
            im.seek( im.tell() + 1 )

    except EOFError:
        pass # end of sequence

导致如下输出:

Example #2 http://longcat.de/gif_example2.jpg

与第一次尝试相比的优势在于,第一帧看起来不错 但正如你所看到的,其余的都坏了

接下来我应该尝试什么?

编辑:

我认为我离解决方案更近了

Example #3 http://longcat.de/gif_example3.png

我不得不将第一张图片的调色板用于其他图片, 并将其与前一帧合并(对于使用 差异图像)

def processImage( infile ):

    try:
        im = Image.open( infile )
    except IOError:
        print "Cant load", infile
        sys.exit(1)

    i = 0

    size        = im.size
    lastframe   = im.convert('RGBA')
    mypalette   = im.getpalette()

    try:
        while 1:

            im2 = im.copy()
            im2.putpalette( mypalette )

            background = Image.new("RGB", size, (255,255,255))

            background.paste( lastframe )
            background.paste( im2 )
            background.save('foo'+str(i)+'.png', 'PNG', quality=80)

            lastframe = background

            i += 1
            im.seek( im.tell() + 1 )

    except EOFError:
        pass # end of sequence

但我其实不知道,为什么我的透明度是黑色的,而不是白色的 即使我修改调色板(将透明度通道更改为白色) 或者使用透明蒙版,背景还是黑色的

【问题讨论】:

【参考方案1】:

首先,JPEG 不支持透明度!但这不是唯一的问题。当您移动到GIF 的下一帧时,palette 信息丢失(problem witn PIL?) - 所以PIL 无法正确转换为RGBA 框架(因此第一帧还可以,但其他所有帧都很糟糕)。所以解决方法是为每一帧添加palette,(这是你在上一个代码示例中所做的,但你的问题是你保存为RGB而不是RGBA所以你有没有阿尔法/透明度通道。而且你做了一些不必要的事情..)。无论如何,这里是透明的.png和更正的代码,希望它有一些用处:)

import Image
import sys

def processImage(infile):
    try:
        im = Image.open(infile)
    except IOError:
        print "Cant load", infile
        sys.exit(1)
    i = 0
    mypalette = im.getpalette()

    try:
        while 1:
            im.putpalette(mypalette)
            new_im = Image.new("RGBA", im.size)
            new_im.paste(im)
            new_im.save('foo'+str(i)+'.png')

            i += 1
            im.seek(im.tell() + 1)

    except EOFError:
        pass # end of sequence

processImage('gif_example.gif')

【讨论】:

非常感谢!我在最后一帧使用粘贴,因为有些 gif 对每一帧都使用了差异! 嗯,当框架有不同的调色板时,像这样替换调色板会得到不正确的结果。其中一个例子是图像imagemagick.org/Usage/anim_opt/bunny_bgnd.gif(我想,不确定我是否在复制和粘贴正确的链接)。我看到的另一个问题可能与 PIL 版本之间的差异有关,但我没有得到与你相同的结果(除了第一个,我在所有帧中都得到灰色区域,它应该是透明的)。对于像 imagemagick.org/Usage/anim_opt/bunny_bgnd_opttrans.gif 这样的 gif,也会发生同样的情况。 @mmgp - 嗯,是的,我可以看到具有不同调色板的框架可能会造成问题。我同意你得到不同的结果可能是版本问题。不幸的是,我现在没有时间看这个,如果你有修复,请编辑我的答案,或者添加你自己的。不错。 @fraxel PIL 并不容易。我找到了这个答案,因为一个新的问题与之相关,我在***.com/a/14550885/1832154 尝试了一些东西,但是对于另一种类型的 GIF 动画(不是我在之前评论中喜欢的那些),这仍然失败。【参考方案2】:

在图像查看器上查看图像时,即使透明度设置为零,它也倾向于将图像显示为黑色。确保您的图像真正透明的一种方法是将其合并到另一个上。应该看到“表情符号”,同时不妨碍其他图像。尝试:

background = Image.open('someimage.jpg') #an existing image
foreground = Image.open('foo.jpg') #one of the above images
background.paste(foreground, (0,0), foreground)
background.save('trial.jpg') #the composite image

理论上,如果您在图像查看器中打开“trial.jpg”并保留初始图像的内容,并且在其顶部是 foo 图像,那么您将确定它是否只是图像查看器和您的图片很好……

【讨论】:

.jpeg 不支持透明度 :(【参考方案3】:

source here

Image.open('image.gif').convert('RGB').save('image.jpg')

【讨论】:

OP 询问是否将动画(多帧)gif 转换为一系列 jpeg;这只会保存第一帧【参考方案4】:

这对我有用。下面的例子展示了将 image.gif 转换为 8 个白色背景的 jpg 格式图像。

from PIL import Image
from PIL import GifImagePlugin


def gif2jpg(file_name: str, num_key_frames: int, trans_color: tuple):
    """
    convert gif to `num_key_frames` images with jpg format
    :param file_name: gif file name
    :param num_key_frames: result images number
    :param trans_color: set converted transparent color in jpg image
    :return:
    """
    with Image.open(file_name) as im:
        for i in range(num_key_frames):
            im.seek(im.n_frames // num_key_frames * i)
            image = im.convert("RGBA")
            datas = image.getdata()
            newData = []
            for item in datas:
                if item[3] == 0:  # if transparent
                    newData.append(trans_color)  # set transparent color in jpg
                else:
                    newData.append(tuple(item[:3]))
            image = Image.new("RGB", im.size)
            image.getdata()
            image.putdata(newData)
            image.save('.jpg'.format(i))


gif2jpg("image.gif", 8, (255, 255, 255))  # convert image.gif to 8 jpg images with white background

【讨论】:

以上是关于PIL - 将 GIF 帧转换为 JPG的主要内容,如果未能解决你的问题,请参考以下文章

Julia:将 GIF 帧转换为 PNG

CodeIgniter - 将 GIF 转换为 JPG?可能的?

将每个动画 GIF 帧转换为单独的 BufferedImage

Python:将 GIF 帧转换为 PNG

需要帮助将 JPG 链接转换为 GIF 格式

加载 Jpg/Gif/Bitmap 并转换为 Bitmap