Python 枕头-调整GIF的大小
我有一个Python 枕头-调整GIF的大小,python,image-processing,gif,pillow,Python,Image Processing,Gif,Pillow,我有一个gif,我想用枕头调整它的大小,这样它的大小就会减小。gif的当前大小为2MB 我正在努力 调整其大小,使其高度/宽度更小 降低其质量 对于JPEG,下面的代码通常足以使大图像的大小急剧减小 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 he
gif
,我想用枕头调整它的大小,这样它的大小就会减小。gif
的当前大小为2MB
我正在努力
调整其大小,使其高度/宽度更小
降低其质量
对于JPEG,下面的代码通常足以使大图像的大小急剧减小
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
不过,对于GIF,它似乎不起作用。下面的代码甚至使out.gif
比初始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
如果添加以下行,则仅保存GIF的第一帧,而不是其所有帧
im = im.resize((im.size[0] // 2, im.size[1] // 2), Image.ANTIALIAS) # should decrease its size
我一直在考虑对im.seek()
或im.tell()
调用resize()
,但这两种方法都不返回图像对象,因此我无法对其输出调用resize()
你知道我如何使用枕头来减少GIF的大小,同时保留所有的框架吗
[编辑]部分解决方案:
接下来,我做了以下更改:
- 我正在使用提取所有帧。值得注意的是,这是一个Python2脚本,我的项目是用Python3编写的(我最初确实提到了这个细节,但它被Stack Overflow社区编辑掉了)。运行
2to3-wgifextract.py使该脚本与Python 3兼容
- 我已经分别调整了每个帧的大小:
frame.resize((frame.size[0]//2,frame.size[1]//2),Image.antialas)
- 我一直在一起保存所有帧:
img.save(“out.gif”,save\u all=True,optimize=True)
新的gif现在已保存并工作,但存在两个主要问题:
- 我不确定resize方法是否有效,因为
out.gif
仍然是7.5MB。最初的gif是2MB
- gif速度增加,gif不会循环。它在第一次运行后停止
例如:
原始gifmy_gif.gif
:
处理后的Gif(out.Gif
)(我无法将其添加到堆栈溢出)。Imgur使其速度变慢(并将其转换为mp4)。当我从电脑打开gif文件时,整个gif大约持续1.5秒。根据Pillow 4.0x,Image.resize功能仅在单个图像/帧上工作
为了达到您想要的效果,我相信您必须首先从.gif文件中提取每一帧,一次调整每一帧的大小,然后重新组装它们
要完成第一步,似乎需要注意一些细节。例如,每个gif帧是否使用本地调色板或全局调色板应用于所有帧,以及gif是否使用完整或部分帧替换每个图像。已经开发了一个脚本来解决这些问题,同时从gif文件中提取每一帧,所以要充分利用这一点
接下来,您必须编写脚本来调整每个提取帧的大小,并使用PIL.Image.resize()和PIL.Image.save()将它们全部组装为一个新的.gif文件
我注意到你写了“im.seek(im.tell()+1)#加载所有帧
”。我认为这是不正确的。相反,它用于在.gif文件的帧之间递增。我注意到您在.gif文件的保存函数中使用了quality=10。我没有发现该文件中提供的内容。您可以通过阅读此了解BiggleZX脚本中提到的tile属性的更多信息。使用,我创建了一个新脚本,它使用枕头调整GIF的大小
原始GIF(2.1MB):
调整大小后输出GIF(1.7 MB):
我已经保存了脚本。它使用枕头的缩略图
方法,而不是调整大小
方法,因为我发现调整大小
方法不起作用
这是不完美的,所以请随意叉和改善它。以下是一些尚未解决的问题:
- 当由imgur托管时,GIF显示正常,但当我从计算机上打开它时,整个GIF只需1.5秒,因此存在速度问题
- 同样,虽然imgur似乎可以弥补速度问题,但当我尝试将GIF上传到
stack.imgur
时,它无法正确显示。仅显示了第一帧(您可以看到它)
完整代码(如果删除上述要点):
我正在使用下面的函数来调整和裁剪图像,包括动画图像(GIF、WEBP)。简单地说,我们需要在GIF或WEBP中迭代每个帧
from math import floor, fabs
from PIL import Image, ImageSequence
def transform_image(original_img, crop_w, crop_h):
"""
Resizes and crops the image to the specified crop_w and crop_h if necessary.
Works with multi frame gif and webp images also.
args:
original_img is the image instance created by pillow ( Image.open(filepath) )
crop_w is the width in pixels for the image that will be resized and cropped
crop_h is the height in pixels for the image that will be resized and cropped
returns:
Instance of an Image or list of frames which they are instances of an Image individually
"""
img_w, img_h = (original_img.size[0], original_img.size[1])
n_frames = getattr(original_img, 'n_frames', 1)
def transform_frame(frame):
"""
Resizes and crops the individual frame in the image.
"""
# resize the image to the specified height if crop_w is null in the recipe
if crop_w is None:
if crop_h == img_h:
return frame
new_w = floor(img_w * crop_h / img_h)
new_h = crop_h
return frame.resize((new_w, new_h))
# return the original image if crop size is equal to img size
if crop_w == img_w and crop_h == img_h:
return frame
# first resize to get most visible area of the image and then crop
w_diff = fabs(crop_w - img_w)
h_diff = fabs(crop_h - img_h)
enlarge_image = True if crop_w > img_w or crop_h > img_h else False
shrink_image = True if crop_w < img_w or crop_h < img_h else False
if enlarge_image is True:
new_w = floor(crop_h * img_w / img_h) if h_diff > w_diff else crop_w
new_h = floor(crop_w * img_h / img_w) if h_diff < w_diff else crop_h
if shrink_image is True:
new_w = crop_w if h_diff > w_diff else floor(crop_h * img_w / img_h)
new_h = crop_h if h_diff < w_diff else floor(crop_w * img_h / img_w)
left = (new_w - crop_w) // 2
right = left + crop_w
top = (new_h - crop_h) // 2
bottom = top + crop_h
return frame.resize((new_w, new_h)).crop((left, top, right, bottom))
# single frame image
if n_frames == 1:
return transform_frame(original_img)
# in the case of a multiframe image
else:
frames = []
for frame in ImageSequence.Iterator(original_img):
frames.append( transform_frame(frame) )
return frames
来自math import floor,fabs
从PIL导入图像,图像序列
def变换图像(原始图像、裁剪w、裁剪h):
"""
如有必要,将图像调整大小并裁剪为指定的裁剪宽度和裁剪高度。
也适用于多帧gif和webp图像。
args:
original_img是由枕头创建的图像实例(image.open(filepath))
crop_w是将被调整大小和裁剪的图像的宽度(以像素为单位)
crop_h是要调整大小和裁剪的图像的高度(以像素为单位)
返回:
图像的实例或帧列表,它们分别是图像的实例
"""
img\u w,img\u h=(原始img.size[0],原始img.size[1])
n_frames=getattr(原始图像,'n_frames',1)
def变换_帧(帧):
"""
调整图像中单个帧的大小并进行裁剪。
"""
#如果配方中的裁剪为空,则将图像调整为指定高度
如果作物w为无:
如果作物高度==img高度:
回程架
新高度=地板(高度/高度)
新高度=作物高度
返回帧。调整大小((新的w,新的h))
#如果裁剪大小等于img大小,则返回原始图像
如果作物w==img\u w和作物h==img\u h:
回程架
#首先调整大小以获得图像的最可见区域,然后进行裁剪
w_diff=晶圆厂(作物w-img_w)
h_diff=晶圆厂(作物h-img_h)
放大图像=如果裁剪>图像,则为真;如果裁剪>图像,则为假
收缩图像=如果剪切图像,则为真
from math import floor, fabs
from PIL import Image, ImageSequence
def transform_image(original_img, crop_w, crop_h):
"""
Resizes and crops the image to the specified crop_w and crop_h if necessary.
Works with multi frame gif and webp images also.
args:
original_img is the image instance created by pillow ( Image.open(filepath) )
crop_w is the width in pixels for the image that will be resized and cropped
crop_h is the height in pixels for the image that will be resized and cropped
returns:
Instance of an Image or list of frames which they are instances of an Image individually
"""
img_w, img_h = (original_img.size[0], original_img.size[1])
n_frames = getattr(original_img, 'n_frames', 1)
def transform_frame(frame):
"""
Resizes and crops the individual frame in the image.
"""
# resize the image to the specified height if crop_w is null in the recipe
if crop_w is None:
if crop_h == img_h:
return frame
new_w = floor(img_w * crop_h / img_h)
new_h = crop_h
return frame.resize((new_w, new_h))
# return the original image if crop size is equal to img size
if crop_w == img_w and crop_h == img_h:
return frame
# first resize to get most visible area of the image and then crop
w_diff = fabs(crop_w - img_w)
h_diff = fabs(crop_h - img_h)
enlarge_image = True if crop_w > img_w or crop_h > img_h else False
shrink_image = True if crop_w < img_w or crop_h < img_h else False
if enlarge_image is True:
new_w = floor(crop_h * img_w / img_h) if h_diff > w_diff else crop_w
new_h = floor(crop_w * img_h / img_w) if h_diff < w_diff else crop_h
if shrink_image is True:
new_w = crop_w if h_diff > w_diff else floor(crop_h * img_w / img_h)
new_h = crop_h if h_diff < w_diff else floor(crop_w * img_h / img_w)
left = (new_w - crop_w) // 2
right = left + crop_w
top = (new_h - crop_h) // 2
bottom = top + crop_h
return frame.resize((new_w, new_h)).crop((left, top, right, bottom))
# single frame image
if n_frames == 1:
return transform_frame(original_img)
# in the case of a multiframe image
else:
frames = []
for frame in ImageSequence.Iterator(original_img):
frames.append( transform_frame(frame) )
return frames