如何使用Python對(duì)Gif進(jìn)行壓縮
本文轉(zhuǎn)載自微信公眾號(hào)「arige」,作者arige。轉(zhuǎn)載本文請(qǐng)聯(lián)系arige公眾號(hào)。
一、背景
前天在給微信公眾號(hào)上傳文章的時(shí)候,文章里面有一個(gè)圖片是gif的,在上傳的過程中報(bào)錯(cuò)了,說是圖片超大了。搜索之后發(fā)現(xiàn)圖片需要小于5m。
那么問題就轉(zhuǎn)化為怎么把當(dāng)前的gif給縮減到5m以內(nèi)本著有輪子用輪子,沒有輪子造輪子的精神,網(wǎng)上搜索一番。
發(fā)現(xiàn)一些現(xiàn)象
1、壓縮要不就是需要會(huì)員才能下載;
2、要不免費(fèi)的只能壓縮5m以下的。
考慮到能動(dòng)手不花錢的本性,我覺得要自己搞一下。
知識(shí)背景:
眾所周知,gif圖就是由若干組圖片組成的一種文件格式,有多張有一定差異的圖片連續(xù)播放,間隔時(shí)間較小,欺騙了我們的眼睛和大腦,然后我們以為是一個(gè)完全連續(xù)的。其實(shí)就是一個(gè)類似快速翻書的操作。
二、方案選型
方案一
因?yàn)間if是有多種圖片做的,那我們就考慮把圖片減少一些,比如說原來是100張是10m,我給縮減到10張,那體積可不就要縮小到1m左右了嗎?當(dāng)然,為了用戶看起來不是那么卡頓,我就拍腦袋給縮減到20張吧,即只有原來的1/5。
方案二
如果縮減的圖片太多導(dǎo)致gif看起來卡頓的話,我們可以考慮不縮減圖片的張數(shù),但是我們可以壓縮圖片。
方案三
最后的都是重要的,如果前面兩個(gè)都無法滿足的話,那就可以考慮把他們進(jìn)行疊加。先減張數(shù),再壓縮拆分的圖片。
三、項(xiàng)目落實(shí)
整體流程如下:
- if __name__ == "__main__":
- # 設(shè)置源gif的地址
- sourceGifPath = "/Users/user/test/f79a3e2c2e864863a6b1a66791cb0950_tplv-k3u1fbpfcp-watermark.gif"
- # 將gif拆分成多個(gè)圖片,并保存在本地
- SplitGif(sourceGifPath)
- # 將指定位置的文件下的圖片按照文件名索引排序,做成gif
- Combine2Gif(sourceGifPath[:-4], sourceGifPath[:-4] + "_result.gif")
- print("== finished ==")
1、將源gif讀入內(nèi)存
2、將gif拆分成png,并保存
- def SplitGif(gifPath):
- # 獲取png存儲(chǔ)的文件夾的地址
- pngDir = gifPath[:-4]
- # 要存儲(chǔ)的文件夾下清理干凈,避免影響當(dāng)前操作
- rmPngDir(pngDir)
- # 創(chuàng)建存儲(chǔ)的文件夾
- os.mkdir(pngDir)
- # 把指定gif拆分后存儲(chǔ)到指定文件夾
- savePngToDir(gifPath, pngDir)
2.1、獲取要存儲(chǔ)的地址
2.2、清空并移除存儲(chǔ)png的文件夾
- def rmPngDir(pngDir):
- if os.path.exists(pngDir):
- files = os.listdir(pngDir)
- # 如果不一個(gè)一個(gè)的移除文件夾下的文件的話,無法移除文件夾
- for file in files:
- file = pngDir + "/" + file
- os.remove(file)
- os.rmdir(pngDir)
2.3、創(chuàng)建存儲(chǔ)png的文件夾
2.4、將gif拆分成png,并保存
- def savePngToDir(gifPath, pngDir):
- # 通過路徑傳教image對(duì)象
- image = Image.open(gifPath)
- try:
- # 循環(huán),通過異常方案退出循環(huán)
- while True:
- # 獲取當(dāng)前的索引的位置
- current = image.tell()
- # 創(chuàng)建文件路徑
- pngPath = pngDir + '/' + str(current) + '.png'
- image.save(pngPath, quality=100)
- # 索引后移,越界后異常,退出當(dāng)前循環(huán)
- image.seek(current + 1)
- except EOFError as e:
- print(e)
- pass
3、按照一定的間隔讀取2中的png,并生成gif
- def Combine2Gif(folderPath, gifFilePath):
- GenerateGif(0.1, gifFilePath, getPngArray(folderPath))
3.1、獲取所有的png
- def getPngArray(folderPath):
- files = os.listdir(folderPath)
- pngFiles = []
- # 通過設(shè)置step,將文件的大小修改為原來的體積的1/step
- for i in range(0, len(files), 5):
- pngFiles.append(folderPath + "/" + ('%d.png' % i))
- return pngFiles
3.2、將png合并成gif
- def GenerateGif(step, gifPath, filterPngs):
- images = []
- for filePath in filterPngs:
- images.append(imageio.imread(filePath))
- # 生成gif,duration 是播放兩個(gè)圖片之間的間隔時(shí)間
- imageio.mimsave(gifPath, images, duration=step)
四、全部的代碼
- #! /usr/local/bin/python3
- # -*- coding: utf-8 -*-
- from PIL import Image
- import os
- import imageio
- def SplitGif(gifPath):
- # 獲取png存儲(chǔ)的文件夾的地址
- pngDir = gifPath[:-4]
- # 要存儲(chǔ)的文件夾下清理干凈,避免影響當(dāng)前操作
- rmPngDir(pngDir)
- # 創(chuàng)建存儲(chǔ)的文件夾
- os.mkdir(pngDir)
- # 把指定gif拆分后存儲(chǔ)到指定文件夾
- savePngToDir(gifPath, pngDir)
- def rmPngDir(pngDir):
- if os.path.exists(pngDir):
- files = os.listdir(pngDir)
- # 如果不一個(gè)一個(gè)的移除文件夾下的文件的話,無法移除文件夾
- for file in files:
- file = pngDir + "/" + file
- os.remove(file)
- os.rmdir(pngDir)
- def savePngToDir(gifPath, pngDir):
- # 通過路徑傳教image對(duì)象
- image = Image.open(gifPath)
- try:
- # 循環(huán),通過異常方案退出循環(huán)
- while True:
- # 獲取當(dāng)前的索引的位置
- current = image.tell()
- # 創(chuàng)建文件路徑
- pngPath = pngDir + '/' + str(current) + '.png'
- image.save(pngPath, quality=100)
- # 索引后移,越界后異常,退出當(dāng)前循環(huán)
- image.seek(current + 1)
- except EOFError as e:
- print(e)
- pass
- def Combine2Gif(folderPath, gifFilePath):
- GenerateGif(0.1, gifFilePath, getPngArray(folderPath))
- # 獲取文件的數(shù)組
- def getPngArray(folderPath):
- files = os.listdir(folderPath)
- pngFiles = []
- # 通過設(shè)置step,將文件的大小修改為原來的體積的1/step
- for i in range(0, len(files), 5):
- pngFiles.append(folderPath + "/" + ('%d.png' % i))
- return pngFiles
- def GenerateGif(step, gifPath, filterPngs):
- images = []
- for filePath in filterPngs:
- images.append(imageio.imread(filePath))
- # 生成gif,duration 是播放兩個(gè)圖片之間的間隔時(shí)間
- imageio.mimsave(gifPath, images, duration=step)
- if __name__ == "__main__":
- # 設(shè)置源gif的地址
- sourceGifPath = "/Users/user/test/f79a3e2c2e864863a6b1a66791cb0950_tplv-k3u1fbpfcp-watermark.gif"
- # 將gif拆分成多個(gè)圖片,并保存在本地
- SplitGif(sourceGifPath)
- # 將指定位置的文件下的圖片按照文件名索引排序,做成gif
- Combine2Gif(sourceGifPath[:-4], sourceGifPath[:-4] + "_result.gif")
- print("== finished ==")
五、結(jié)尾
作為一個(gè)追求高效的程序員,我就做一個(gè)能滿足我需求的方案,即方案一。至于方案二和方案三,有興趣的朋友可以舉一反三。