今天遇到一个特别奇葩的需求,甲方要求我把图片制作成刚好 200KB,天啦撸。这这这怎么可能,理论上只能压缩到接近于目标大小。但,干了兄弟萌。
压缩图片至低于目标大小,再把差的部分全部填充 “0”。
核心内容是如何补齐,以下提供两种思路:
文件结构
main.py
| - - new
| - - old
| - - | - - test.jpg
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# main.py
from PIL import Image
from io import BytesIO
from os import system
from os import listdir
from os import remove
from os.path import getsize
from os.path import exists
__author__ = 'one-ccs'
"""
功能: 把 ".\old" 目录下的所有图片压缩并填充至 200KB 后存放在 ".\new" 目录下.
"""
settings = {
'loadPath': r'.\old', # 待压缩图片路径
'savePath': r'.\new', # 保存路径
'size': 200,
'quality': 90
}
class ImageFixCompressor():
def __init__(self) -> None:
self.imgBytes = None
self.imgPath = None
self.name = None
self.oldSize = 0
self.newSize = 0
self.saveSize = 0
self.format = 'JPEG'
self.targetSize = 200 # 目标大小 (KB)
self.quality = 90 # 压缩比率 若压缩后图片大于目标大小应减小该值
def split_path(self, path:str='') -> tuple:
"""
提取 path 中的路径与文件名.
"""
if not isinstance(path, str):
raise ValueError(f'参数 "path" 的数据类型应该为 "str", 但是传入了 "{type(path)}" 类型.')
# 判断是否是以 '/' '\' 作为目录分隔符
flag = path[::-1].find('/')
if flag == -1:
flag = path[::-1].find('\\')
if flag == -1:
raise ValueError(f'参数 "path" 的数据类型应该为 "str", 但是传入了 "{type(path)}" 类型.')
name = path[-flag:]
path = path[:-flag]
return (path, name)
def full_path(self, path) -> str:
return fr'{path}\{self.name}'
def open(self, imgPath:str) -> None:
"""
打开图像文件
:参数 imgPath: 图片文件路径.
"""
try:
_, self.name = self.split_path(imgPath)
self.oldSize = getsize(imgPath)
self.image = Image.open(imgPath)
print(f'打开: "{imgPath}" 成功; 大小: {self.oldSize / 1024:.2f} KB.')
except Exception as e:
print(f'错误: 文件 "{imgPath}" 打开失败; 原因: "{e}".')
def show(self) -> None:
self.image.show()
def compress(self, format='JPEG', quality=None) -> None:
if format == 'PNG' or format == 'png':
self.format = 'PNG'
if quality:
self.quality = quality
self.image.tobytes()
self.imageBuffer = BytesIO()
self.image.save(self.imageBuffer, format=self.format, quality=self.quality)
def save(self, savePath:str, cover:bool=False, name=None) -> None:
if cover:
mode = 'wb+'
else:
mode = 'rb+'
try:
self.newSize = self.imageBuffer.tell() / 1024
# ~ 补齐大小
for i in range(0, self.targetSize * 1024 - self.imageBuffer.tell()):
self.imageBuffer.write(b'\x00')
with open(self.full_path(savePath), mode) as fb:
fb.write(self.imageBuffer.getvalue())
self.saveSize = getsize(self.full_path(savePath)) / 1024
print(fr'保存: "{self.full_path(savePath)}" 成功; 压缩大小: {self.newSize:.2f} KB; 保存大小: {self.saveSize:.2f} KB.')
except Exception as e:
print(f'错误: "{self.name}" 保存失败; 原因: "{e}".')
def save2(self, savePath:str, cover:bool=False, name=None) -> None:
if cover:
if exists(self.full_path(savePath)):
remove(self.full_path(savePath))
else:
print(f'异常: 文件 "{savePath}" 已存在, 已放弃保存.')
return
system('@echo off')
system(f'fsutil file createnew {self.full_path(savePath)} {self.targetSize * 1024}')
try:
with open(self.full_path(savePath), 'rb+') as fb:
fb.write(self.imageBuffer.getvalue())
self.newSize = self.imageBuffer.tell() / 1024
self.saveSize = getsize(self.full_path(savePath)) / 1024
print(fr'保存: "{self.full_path(savePath)}" 成功; 压缩大小: {self.newSize:.2f} KB; 保存大小: {self.saveSize:.2f} KB.')
except Exception as e:
print(f'错误: "{self.name}" 保存失败; 原因: "{e}".')
def main(args):
compressor = ImageFixCompressor()
oldImgPaths = listdir(settings['loadPath'])
for oldImgPath in oldImgPaths:
fullPath = f"{settings['loadPath']}\{oldImgPath}"
compressor.open(fullPath)
compressor.compress()
compressor.save(settings['savePath'], cover=True)
# ~ return
return 0
if __name__ == '__main__':
import sys
sys.exit(main(sys.argv))
【如果觉得本文不错 “点赞、收藏、关注”,是对作者最大的鼓励。】