基本概述
网上找了一圈基本上是有两个压缩图片的思路:
剪裁尺寸
简单来说就是图片分辨率低了占用自然就小了,这是很通用的办法
设置quality
对于jpg文件来说,图像信息还有进一步压缩的方法,比如抹去图像的细节等,这都体现在quality上。理论上,可以完全不改变size,专注于减少图像的细节,比如颜色深度,纹理细节等,把图像压缩到一个很极限的尺度。
# encoding=utf-8
import os
from PIL import Image
quality_list = [1, 5, 15, 25, 35, 45, 55, 65, 75, 85, 95]
out_dir = 'test'
if not os.path.exists(out_dir):
os.mkdir(out_dir)
image_data = Image.open('test.jpg')
for q in quality_list:
image_path = os.path.join(out_dir, f'quality_{q}.jpg')
image_data.save(image_path, quality=q)
print(f'quality param {q}, picture size is {os.path.getsize(image_path) // 1024} kb')
输出如下:
quality param 1, picture size is 288 kb
quality param 5, picture size is 348 kb
quality param 15, picture size is 723 kb
quality param 25, picture size is 1129 kb
quality param 35, picture size is 1486 kb
quality param 45, picture size is 1819 kb
quality param 55, picture size is 2164 kb
quality param 65, picture size is 2657 kb
quality param 75, picture size is 3363 kb
quality param 85, picture size is 4591 kb
quality param 95, picture size is 7776 kb
输出的图片quality为55时:
输出的图片quality为1(最低)时:
差别还是挺大的。
其实quality为1的时候图像质量已经奇差了,但是大小仍然有300k,主要是原图像的像素太大,足足有5000*3300,此时resize明显更有性价比。
值得注意的是,
代码实现
自己写的一个类
# encoding=utf-8
import os
from PIL import Image, ImageFile
class ImageCompress:
def __init__(self, input_file_path, output_file_path=None, target_size_kb=100):
"""
:param input_file_path: 输入图片的路径
:param output_file_path: 输出图片的路径,如果不填则直接对源文件进行修改
:param target_size_kb: 目标压缩文件的大小
"""
self.input_file_path = input_file_path
if output_file_path:
self.output_file_path = output_file_path
else:
self.output_file_path = input_file_path
self.target_size_kb = target_size_kb
# ImageFile.LOAD_TRUNCATED_IMAGES = True # 防止图像被截断而报错
o_size = os.path.getsize(input_file_path) // 1024 # 函数返回为字节,除1024转为kb(1kb = 1024 bit)
print(f'picture_before_size:{o_size} target_size:{target_size_kb}')
def get_image_data(self):
"""
原则上是如果目标路径图像不存在,则读取原图像,如果目标路径存在,则对目标路径的图像进行修改
:return: Image产生的图像数据
"""
if not os.path.exists(self.output_file_path):
return Image.open(self.input_file_path)
else:
return Image.open(self.output_file_path)
def drop_size(self, k=0.5, max_pix=1920 * 1080):
"""
:param k: 对尺寸每次的裁切点
:param max_pix: 最高像素数,如果大于这个值就会被重复裁切
:return:
"""
image_data = self.get_image_data()
x, y = image_data.size
while x * y > max_pix:
x *= k
y *= k
image_data = image_data.resize((int(x), int(y))) # 最后一个参数设置可以提高图片转换后的质量
image_data.save(self.output_file_path, quality=95)
def drop_quality(self, q=85):
"""
这个函数就只单纯考虑
:param q: 每次保存的质量,从1(最差)到95(最好),此时为85
:return:
"""
image_data = self.get_image_data()
image_data.save(self.output_file_path, quality=q)
def auto_compress_to_target_size(self):
size = os.path.getsize(self.input_file_path) // 1024 # 函数返回为字节,除1024转为kb(1kb = 1024 bit)
if size <= self.target_size_kb:
print('original picture accord with conditions')
return self.input_file_path
self.drop_size()
size = os.path.getsize(self.output_file_path) // 1024 # 函数返回为字节,除1024转为kb(1kb = 1024 bit)
while size >= self.target_size_kb:
print('execute drop quality')
self.drop_quality()
size = os.path.getsize(self.output_file_path) // 1024
print(f'compress done with target size {size}')
return self.output_file_path
[文章导入自 http://qzq-go.notion.site/bf506f1b80e94b0a9f8cd17b14b4a5ff 访问原文获取高清图片]