PIL - 将 GIF 帧转换为 JPG

17

我尝试用Python Imaging Library将一个gif转换为单个图像,但结果出现了奇怪的帧。

输入的gif是:

源图片 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

但是它会产生奇怪的输出文件:

示例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

这将导致输出如下:

示例#2 http://longcat.de/gif_example2.jpg

与第一次尝试相比,优点是第一帧看起来很好。但是正如您所见,其余部分已经损坏。

接下来我应该尝试什么?

编辑:

我认为我已经更接近解决方案了:

示例#3 http://longcat.de/gif_example3.png

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

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

我不知道为什么我的透明度是黑色而不是白色,即使我修改调色板(将透明通道改为白色)或使用透明度掩码,背景仍然是黑色。

4个回答

26
首先,JPEG不支持透明度!但这不是唯一的问题...当您移动到下一个GIF帧时,调色板信息会丢失(PIL的问题?),因此PIL无法正确地转换为RGBA框架(因此第一帧还可以,但所有其他帧都有问题)。因此解决方法是为每个帧添加调色板(这就是您在上一个代码示例中所做的,但您的问题是保存为RGB而不是RGBA,因此您没有alpha/透明通道。此外,您还进行了一些不必要的操作...)。无论如何,这里是带有透明度的.png和已更正的代码,希望对您有所帮助 :)

enter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description here

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 使用每帧的差异! - Schinken
1
哦,这样替换调色板会在帧使用不同调色板时出现错误的结果。其中一个例子就是图片http://www.imagemagick.org/Usage/anim_opt/bunny_bgnd.gif(我想是它,不确定是否复制粘贴正确的链接)。我看到的另一个问题可能与PIL版本之间的差异有关,但我的结果与您的不同(除了第一张外,所有帧中都有灰色区域,在那里应该是透明的)。对于像http://www.imagemagick.org/Usage/anim_opt/bunny_bgnd_opttrans.gif这样的gif也是如此。 - mmgp
@mmgp - 嗯,我能看出来使用不同调色板的帧可能会造成问题。我同意你得到不同结果可能是版本问题。不幸的是,我现在没有时间去看这个问题,如果你有解决方法,请编辑我的答案或添加你自己的。干得好。 - fraxel
@fraxel PIL并不是很容易处理。我发现这个答案是因为一个新的问题链接到它,我尝试了https://dev59.com/jG3Xa4cB1Zd3GeqPh7rh#14550885上的一些方法,但对于另一种类型的GIF动画(不是我在早期评论中喜欢的那些)仍然失败。 - mmgp

4

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

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 不支持透明度 :( - fraxel

3

source here

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

1
OP 询问如何将动态(多帧)gif 转换为一系列的 jpeg;但这只会保存第一帧。 - Zae Zoxol

2

这里输入图片描述

这对我来说起作用了。下面的示例演示将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


这太棒了,谢谢你!! - alpastor
很棒的方法! - dolgom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接