枕头 - 调整GIF大小 [英] Pillow - Resizing a GIF

查看:140
本文介绍了枕头 - 调整GIF大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 gif ,我想用枕头调整大小,以便缩小尺寸。 gif 的当前大小为2MB。



我正在尝试


  1. 调整大小以使其高度/宽度较小


  2. 降低质量。


使用JPEG,以下代码通常足以使大图像的大小急剧减少。

 来自PIL导入图片

im = Image.open(my_picture.jpg)
im = im.resize((im.size [0] // 2,im.size [1] // 2),Image.ANTIALIAS)#减少图像的宽度和高度
im.save(out。 jpg,optimize = True,quality = 85)#降低其质量

但是,使用GIF,它似乎不起作用。以下代码甚至使 out.gif 大于最初的gif:

  im = Image.open(my_gif.gif)
im.seek(im.tell()+ 1)#loading all frames
im.save(out.gif, save_all = True,optimize = True,quality = 10)#应该降低它的质量

print(os.stat(my_gif.gif)。st_size)#2096558 bytes /大约2MB
print(os.stat(out.gif)。st_size)#7536404 bytes /大约7.5MB

如果我添加以下行,则只保存GIF的第一帧,而不是保存所有帧。

  im = im.resize((im.size [0] // 2,im.size [1] // 2),Image.ANTIALIAS)#应减小其大小

我一直在考虑在 im.seek上调用 resize()( ) im.tell()但这些方法都没有返回Image对象,因此我无法调用 resize( )

c $ c>输出。



你愿意吗?现在我如何使用Pillow来减小我的GIF的大小,同时保留所有的帧?



[edit]部分解决方案:





处理后的Gif( out.gif



调整大小后输出GIF(1.7 MB):





我保存了脚本此处。它使用Pillow的缩略图方法而不是 resize 方法,因为我发现调整大小方法不起作用。



这不是完美的,所以随意分叉并改进它。以下是一些未解决的问题:




  • 虽然GIF在imgur托管时显示正常,但是当我打开它时出现速度问题我的电脑,整个GIF只需1.5秒。

  • 同样,虽然imgur似乎弥补了速度问题,但当我尝试将其上传到<时,GIF无法正确显示code> stack.imgur 。仅显示第一帧(您可以在此处查看)。



完整代码(如果上面的要点被删除):

  def resize_gif(path,save_as = None,resize_to = None):

将GIF调整为给定长度:

Args:
path:GIF文件的路径
save_as(可选):调整大小的gif的路径。如果没有设置,原始的gif将被覆盖。
resize_to(可选):gif的新大小。格式:(int,int)。如果没有设置,原始GIF将调整为
大小的一半。

all_frames = extract_and_resize_frames(path,resize_to)

如果不是save_as:
save_as = path

如果len(all_frames)== 1:
print(警告:只找到1帧)
all_frames [0] .save(save_as,optimize = True)
else:
all_frames [0] .save(save_as,optimize = True,save_all = True,append_images = all_frames [1:],loop = 1000)


def analyseImage(path):

预处理通过图像以确定模式(完整或添加)。
评估单帧时不可靠。在处理所有帧之前需要知道模式


im = Image.open(路径)
results = {
'size':im.size,
'mode':'full',
}
尝试:
而True:
if im.tile:
tile = im.tile [0]
update_region = tile [1]
update_region_dimensions = update_region [2:]
if update_region_dimensions!= im.size:
results ['mode'] ='partial'
break
im.seek(im .tell()+ 1)
除了EOFError:
传递
返回结果


def extract_and_resize_frames(path,resize_to = None):

迭代GIF,提取每一帧并调整大小

返回:
所有帧的数组

mode = analyseImage(path)['mode']

im = Image.open(path)

如果不是resize_to:
resize_to =(im.size [0 ] // 2,im.size [1] // 2)

i = 0
p = im.getpalette()
last_frame = im.convert('RGBA')

all_frames = []

尝试:
而True:
#print(保存%s(%s)帧%d,%s% s%(path,mode,i,im.size,im.tile))

'''
如果GIF使用本地颜色表,则每个帧都有自己的调色板。
如果没有,我们需要将全局调色板应用于新帧。
'''
如果不是im.getpalette():
im.putpalette(p)

new_frame = Image.new('RGBA',im.size )

'''
此文件是部分模式GIF,其中帧更新与整个图像不同大小的区域?
如果是这样,我们需要通过将它粘贴在前面的帧之上来构造新帧。
'''
如果模式=='部分':
new_frame.paste(last_frame)

new_frame.paste(im,(0,0),im .convert('RGBA'))

new_frame.thumbnail(resize_to,Image.ANTIALIAS)
all_frames.append(new_frame)

i + = 1
last_frame = new_frame
im.seek(im.tell()+ 1)
除了EOFError:
传递

返回all_frames


I have a gif that I would like to resize with pillow so that its size decreases. The current size of the gif is 2MB.

I am trying to

  1. resize it so its height / width is smaller

  2. decrease its quality.

With JPEG, the following piece of code is usually enough so that large image drastically decrease in size.

from PIL import Image

im = Image.open("my_picture.jpg")
im = im.resize((im.size[0] // 2, im.size[1] // 2), Image.ANTIALIAS)  # decreases width and height of the image
im.save("out.jpg", optimize=True, quality=85)  # decreases its quality

With a GIF, though, it does not seem to work. The following piece of code even makes the out.gif bigger than the initial gif:

im = Image.open("my_gif.gif")
im.seek(im.tell() + 1)  # loads all frames
im.save("out.gif", save_all=True, optimize=True, quality=10)  # should decrease its quality

print(os.stat("my_gif.gif").st_size)  # 2096558 bytes / roughly 2MB
print(os.stat("out.gif").st_size)  # 7536404 bytes / roughly 7.5MB

If I add the following line, then only the first frame of the GIF is saved, instead of all of its frame.

im = im.resize((im.size[0] // 2, im.size[1] // 2), Image.ANTIALIAS)  # should decrease its size

I've been thinking about calling resize() on im.seek() or im.tell() but neither of these methods return an Image object, and therefore I cannot call resize() on their output.

Would you know how I can use Pillow to decrease the size of my GIF while keeping all of its frames?

[edit] Partial solution:

Following Old Bear's response, I have done the following changes:

  • I am using BigglesZX's script to extract all frames. It is useful to note that this is a Python 2 script, and my project is written in Python 3 (I did mention that detail initially, but it was edited out by the Stack Overflow Community). Running 2to3 -w gifextract.py makes that script compatible with Python 3.

  • I have been resicing each frame individually: frame.resize((frame.size[0] // 2, frame.size[1] // 2), Image.ANTIALIAS)

  • I've been saving all the frames together: img.save("out.gif", save_all=True, optimize=True).

The new gif is now saved and works, but there is 2 main problems :

  • I am not sure that the resize method works, as out.gif is still 7.5MB. The initial gif was 2MB.

  • The gif speed is increased and the gif does not loop. It stops after its first run.

Example:

original gif my_gif.gif:

Gif after processing (out.gif) https://i.imgur.com/zDO4cE4.mp4 (I could not add it to Stack Overflow ). Imgur made it slower (and converted it to mp4). When I open the gif file from my computer, the entire gif lasts about 1.5 seconds.

解决方案

Using BigglesZX's script, I have created a new script which resizes a GIF using Pillow.

Original GIF (2.1 MB):

Output GIF after resizing (1.7 MB):

I have saved the script here. It is using the thumbnail method of Pillow rather than the resize method as I found the resize method did not work.

The is not perfect so feel free to fork and improve it. Here are a few unresolved issues:

  • While the GIF displays just fine when hosted by imgur, there is a speed issue when I open it from my computer where the entire GIF only take 1.5 seconds.
  • Likewise, while imgur seems to make up for the speed problem, the GIF wouldn't display correctly when I tried to upload it to stack.imgur. Only the first frame was displayed (you can see it here).

Full code (should the above gist be deleted):

def resize_gif(path, save_as=None, resize_to=None):
    """
    Resizes the GIF to a given length:

    Args:
        path: the path to the GIF file
        save_as (optional): Path of the resized gif. If not set, the original gif will be overwritten.
        resize_to (optional): new size of the gif. Format: (int, int). If not set, the original GIF will be resized to
                              half of its size.
    """
    all_frames = extract_and_resize_frames(path, resize_to)

    if not save_as:
        save_as = path

    if len(all_frames) == 1:
        print("Warning: only 1 frame found")
        all_frames[0].save(save_as, optimize=True)
    else:
        all_frames[0].save(save_as, optimize=True, save_all=True, append_images=all_frames[1:], loop=1000)


def analyseImage(path):
    """
    Pre-process pass over the image to determine the mode (full or additive).
    Necessary as assessing single frames isn't reliable. Need to know the mode
    before processing all frames.
    """
    im = Image.open(path)
    results = {
        'size': im.size,
        'mode': 'full',
    }
    try:
        while True:
            if im.tile:
                tile = im.tile[0]
                update_region = tile[1]
                update_region_dimensions = update_region[2:]
                if update_region_dimensions != im.size:
                    results['mode'] = 'partial'
                    break
            im.seek(im.tell() + 1)
    except EOFError:
        pass
    return results


def extract_and_resize_frames(path, resize_to=None):
    """
    Iterate the GIF, extracting each frame and resizing them

    Returns:
        An array of all frames
    """
    mode = analyseImage(path)['mode']

    im = Image.open(path)

    if not resize_to:
        resize_to = (im.size[0] // 2, im.size[1] // 2)

    i = 0
    p = im.getpalette()
    last_frame = im.convert('RGBA')

    all_frames = []

    try:
        while True:
            # print("saving %s (%s) frame %d, %s %s" % (path, mode, i, im.size, im.tile))

            '''
            If the GIF uses local colour tables, each frame will have its own palette.
            If not, we need to apply the global palette to the new frame.
            '''
            if not im.getpalette():
                im.putpalette(p)

            new_frame = Image.new('RGBA', im.size)

            '''
            Is this file a "partial"-mode GIF where frames update a region of a different size to the entire image?
            If so, we need to construct the new frame by pasting it on top of the preceding frames.
            '''
            if mode == 'partial':
                new_frame.paste(last_frame)

            new_frame.paste(im, (0, 0), im.convert('RGBA'))

            new_frame.thumbnail(resize_to, Image.ANTIALIAS)
            all_frames.append(new_frame)

            i += 1
            last_frame = new_frame
            im.seek(im.tell() + 1)
    except EOFError:
        pass

    return all_frames

这篇关于枕头 - 调整GIF大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆