Python 图片压缩
Python 图片压缩我做了一个处理Markdown图片的小工具markdown-img,在之前给这个工具添加了主流图床支持后功能基本完备了。昨天写了一篇影评的博客扎导版正义联盟观影吐槽,图片都是从电影中截取的,每张都是1Mb以上,颇占云存储空间,所以决定加上图片压缩功能。bing了一番以后,决定使用Pillow库实现压缩功能,虽然使用 python 压缩 png 图片,高达 80% 压缩率,肉眼
Python 图片压缩
我做了一个处理Markdown图片的小工具markdown-img,在之前给这个工具添加了主流图床支持后功能基本完备了。昨天写了一篇影评的博客扎导版正义联盟观影吐槽,图片都是从电影中截取的,每张都是1Mb以上,颇占云存储空间,所以决定加上图片压缩功能。
bing了一番以后,决定使用Pillow库实现压缩功能,虽然使用 python 压缩 png 图片,高达 80% 压缩率,肉眼无差异(一):为什么不用 pillow库这篇文章说了为什么Pillow库用于压缩的缺陷,但该作者给出的另一个压缩方案是windows软件,只能通过命令行调取,并不能直接通过pip库集成,综合考虑下来还是使用Pillow库,至少作为一个可选项是不错的,后期可以加入其它的压缩服务支持,比如在线无损压缩服务tinypng。
先通过pip安装包:
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade Pillow
因为暂时只有这一个压缩服务,就简单地用单个文件进行组织:
from PIL import Image
from ..config import Config
import os
from ..tools.debug import Debug
class Compress:
def __init__(self, imageFile: str, compressLimit: int = 500) -> None:
"""初始化压缩上下文管理器
imageFile: 用于压缩的原图片路径
compressLimit: 压缩门槛(高于该值的图片才会被压缩,单位kb)
"""
self.__imageFile = imageFile
self.__compressLimit: int = compressLimit
def __enter__(self):
# 对png图片使用quantize压缩
basename = os.path.basename(self.__imageFile)
_, _, ext = basename.rpartition(".")
imageSize = self.__class__.getSize(self.__imageFile)
# 没有达到压缩门槛,不压缩
if imageSize < self.__compressLimit:
return self.__imageFile
Debug.print("开始对{}进行压缩,压缩前大小{}kb".format(
self.__imageFile, int(imageSize)))
if ext == "png":
self.__compressPng(self.__imageFile)
else:
self.__compressImage(self.__imageFile)
outPutFile = self.__getOutPutFile(self.__imageFile)
if os.path.exists(outPutFile):
return outPutFile
else:
# 没有产生压缩图片,返回原图
return self.__imageFile
def __exit__(self, expType, expVal, expTrace):
# 如果存在压缩后的临时文件,删除
outPutFile = self.__getOutPutFile(self.__imageFile)
if os.path.exists(outPutFile):
os.remove(outPutFile)
@classmethod
def getSize(cls, file):
# 获取文件大小:KB
size = os.path.getsize(file)
return size / 1024
def __getOutPutFile(self, infile: str) -> str:
"""获取输出文件路径
infile: 待处理文件路径
return: 输出文件路径
"""
fileName: str = os.path.basename(infile)
sysConfig = Config.getInstance()
return sysConfig.getTmpDir()+sysConfig.getPathSplit()+fileName
def __compressPng(self, infile: str) -> None:
"""压缩png图片到输出目录
infile: 待压缩图片
"""
im: Image.Image = Image.open(infile)
new_im = im.quantize(colors=256)
new_im.save(self.__getOutPutFile(infile))
pressedSize = self.__class__.getSize(self.__getOutPutFile(infile))
Debug.print("{}压缩后的大小{}kb".format(infile, int(pressedSize)))
def __compressImage(self, infile, step=10, quality=80) -> None:
"""对图片进行多轮压缩以达到压缩门槛(仅限JPG图片)
infile: 压缩源文件
step: 每一轮压缩增加的压缩率差值
quality: 起始压缩率
"""
maxSize = self.__compressLimit
o_size = self.__class__.getSize(infile)
if o_size <= maxSize:
return
outfile = self.__getOutPutFile(infile)
im = Image.open(infile)
while o_size > maxSize:
im.save(outfile, quality=quality)
if quality - step < 0:
break
quality -= step
o_size = self.__class__.getSize(outfile)
Debug.print("{}压缩后的大小{}kb".format(infile, int(o_size)))
return
Config
和Debug
等项目中地其它组件在这里只起到配置和调试相关功能,并不影响压缩功能。
这里的主要关键在于Pillow
库压缩图片需要对png
格式和jpg
格式单独对待,处理方式不同,im.save(outfile, quality=quality)
这种方式并不能压缩png
格式的图片。
此外之所以我将这个模块写成上下文管理器,是因为在我的程序中是需要压缩后上传到网络图库,本地不需要压缩后的图片,所以最好自动删除,故此上下文管理器的方式应该是最佳的。
使用:
with Compress(localImg, compressLimit) as compressedImg:
imgService = ImgServiceManager.getImgService()
return imgService.upload(compressedImg)
测试:
开始对7526c60df75a574cd2b42da1239308df.png进行压缩,压缩前大小929kb
开始对c105b51e21824730b09f68bbc8352c63.jpeg进行压缩,压缩前大小2002kb
开始对i6q1uk.png进行压缩,压缩前大小1084kb
c105b51e21824730b09f68bbc8352c63.jpeg压缩后的大小340kb
7526c60df75a574cd2b42da1239308df.png压缩后的大小292kb
参考资料:

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。
更多推荐
所有评论(0)