当前位置: 首页 > 科技观察

如何用Python压缩Gif

时间:2023-03-17 10:44:36 科技观察

本文转载自微信公众号「arige」,作者arige。转载本文请联系arige公众号。一、背景前天我在微信公众号上传文章时,文章中有一张图片是gif,上传过程中报错,说图片太大。搜索后发现图片需要小于5m。那么问题就变成了如何将当前gif缩小到5m以内。本着轮子用轮子,没有轮子造轮子的精神,上网搜索。发现一些现象:1、压缩需要会员才能下载;2、如果不是免费的,只能压缩5m以下的文件。考虑到不花钱也能搞定的性质,我觉得还是得自己搞。知识背景:众所周知,gif图片是由几组图片组成的一种文件格式。有一定差异的多张图片以很小的间隔连续播放,欺骗了我们的眼睛和大脑,然后我们就认为是一个完全连续的。其实就是一个类似于快速翻书的操作。2、方案选择方案1因为gif是由很多种图片组成的,那么我们考虑缩小图片。比如原来的100张图片是10m,如果我缩小到10张,那么体积应该缩小到1m是左还是右?当然,为了让用户看起来不那么卡,我就一巴掌把它减到20,也就是只有原来的1/5。解决方案2如果图片缩小太多,gif看起来很卡,可以考虑不减少图片数量,但是可以对图片进行压缩。选项三的最后一个很重要。如果前两个不能满足,那么可以考虑叠加。先减少图片数量,再压缩分割图片。3、项目实现的总体流程如下:if__name__=="__main__":#设置源gif的地址sourceGifPath="/Users/user/test/f79a3e2c2e864863a6b1a66791cb0950_tplv-k3u1fbpfcp-watermark.gif"#将gif分割成多张图片,并保存在本地SplitGif(sourceGifPath)#将指定位置文件下的图片按照文件名索引排序,制作gifCombine2Gif(sourceGifPath[:-4],sourceGifPath[:-4]+"_result.gif")print("==finished==")1.将源gif读入内存2.将gif拆分成png并保存defSplitGif(gifPath):#获取存放png的文件夹地址pngDir=gifPath[:-4]#将要存放的文件夹下清理干净,以免影响当前操作rmPngDir(pngDir)#创建存放文件夹os.mkdir(pngDir)#拆分指定的gif,存放到指定的文件夹中savePngToDir(gifPath,pngDir)2.1、获取要存储的存储2.2.清空并移除存放png的文件夹defrmPngDir(pngDir):ifos.path.exists(pngDir):files=os.listdir(pngDir)#如果不将文件夹中的文件一一移除,无法移除文件夹forfileinfiles:file=pngDir+"/"+fileos.remove(file)os.rmdir(pngDir)2.3、创建文件夹存放png2.4、将gif分割成png,并保存defsavePngToDir(gifPath,pngDir):#Propagate图像对象通过路径image=Image.open(gifPath)try:#循环,通过异常方案退出循环whileTrue:#获取当前索引位置current=image.tell()#创建文件路径pngPath=pngDir+'/'+str(current)+'.png'image.save(pngPath,quality=100)#向后移动索引,越界后得到异常,退出当前循环image.seek(current+1)exceptEOFErrorase:print(e)pass3,按照一定的间隔读取2中的png,并生成gifdefCombine2Gif(folderPath,gifFilePath):GenerateGif(0.1,gifFilePath,getPngArray(folderPath))3.1。getallpngdefgetPngArray(folderPath):files=os.listdir(folderPath)pngFiles=[]#通过设置step,修改文件大小为原来的体积1/stepforiinrange(0,len(files),5):pngFiles。append(folderPath+"/"+('%d.png'%i))returnpngFiles3.2,合并png到gifdefGenerateGif(step,gifPath,filterPngs):images=[]forfilePathinfilterPngs:images.append(imageio.imread(filePath))#generategif,duration为播放两张图片的间隔imageio.mimsave(gifPath,images,duration=step)4.所有代码#!/usr/local/bin/python3#-*-coding:utf-8-*-fromPILimportImageimportosimportimageiodefSplitGif(gifPath):#获取存放png的文件夹地址pngDir=gifPath[:-4]#要存放的文件夹清理干净,以免影响当前操作rmPngDir(pngDir)#创建存放文件夹os.mkdir(pngDir)#拆分指定的gif并存放到指定的文件夹中文件夹are不一一删除,fileinfil文件夹无法删除es:file=pngDir+"/"+fileos.remove(file)os.rmdir(pngDir)defsavePngToDir(gifPath,pngDir):#通过路径传播图片对象image=Image.open(gifPath)try:#循环,通过异常方案退出循环whileTrue:#获取当前索引位置current=image.tell()#创建文件路径pngPath=pngDir+'/'+str(current)+'.png'image.save(pngPath,quality=100)#Index向后移动,越界后出现异常,退出当前循环image.seek(current+1)exceptEOFErrorase:print(e)passdefCombine2Gif(folderPath,gifFilePath):GenerateGif(0.1,gifFilePath,getPngArray(folderPath))#获取文件数组defgetPngArray(folderPath):files=os.listdir(folderPath)pngFiles=[]#通过设置stepforiinrange(0,len(files),5)修改文件大小为原体积的1/step:pngFiles.append(folderPath+"/"+('%d.png'%i))returnpngFilesdefGenerateGif(step,gifPath,filterPngs):images=[]forfilePathinfilterPngs:images.append(imageio.imread(filePath))#生成gif,持续时间是在两个之间播放picturesIntervalbetweenimageio.mimsave(gifPath,images,duration=step)if__name__=="__main__":#设置源gif的地址sourceGifPath="/Users/user/test/f79a3e2c2e864863a6b1a66791cb0950_tplv-k3u1fbpfcp-watermark.gif"#将gif分割成多张图片保存到本地SplitGif(sourceGifPath)#将指定位置文件下的图片按照文件名索引排序,使gifCombine2Gif(sourceGifPath[:-4],sourceGifPath[:-4]+"_result.gif")print("==finished==")5.结束作为一个追求高效率的程序员,我会制定一个能满足我需求的计划,即计划1、计划2和计划3、有兴趣的朋友可以举一反三。