当我们需要上传证件照或其他图片文件的时候,有些平台可能会限制上传图片文件的体积(如2M以内),这时我们可以用一个简易的Python脚本将图像尽量无损压缩至指定大小。下面将会给出两个脚本,若读者只是当作工具使用,任选一个即可。
首先需要安装Pillow库:
pip install Pillow
使用Pillow库(Python Imaging Library的一个分支)来处理图像,再结合二分查找方法来逼近目标文件大小。但要注意,对于JPEG这类有损格式,"无损"是一个相对的概念,因为每次保存时即便是以最高质量,也可能会有微小的质量损失。
以JPEG格式的图片文件为例,下面的脚本代码会尝试通过调整JPEG的保存质量来接近指定的文件大小:
from PIL import Image
import io
import os
def compress_image_to_size(input_path, output_path, target_size_kb, quality_step=5):
"""
尝试将图片压缩至指定的文件大小(KB)
:param input_path: 输入图片的路径
:param output_path: 输出图片的路径
:param target_size_kb: 目标文件大小(KB)
:param quality_step: 每次调整质量的步长
:return: None
"""
# 打开原始图片
img = Image.open(input_path)
# 初始化质量参数
low = 1
high = 100
quality = high
while low <= high:
# 尝试以当前质量参数保存图片到内存中
temp_img = io.BytesIO()
img.save(temp_img, format='JPEG', quality=quality)
# 计算当前图片的大小
current_size_kb = temp_img.tell() / 1024
if current_size_kb <= target_size_kb + 1: # +1 为了给大小留一点浮动空间
# 如果当前大小接近目标大小,则保存图片并退出循环
with open(output_path, 'wb') as f:
f.write(temp_img.getvalue())
print(f"图片已压缩至接近目标大小:{current_size_kb:.2f} KB")
break
elif current_size_kb > target_size_kb:
# 图片过大,降低质量
high = quality - quality_step
else:
# 图片过小,提高质量
low = quality + quality_step
# 更新质量参数
quality = (low + high) // 2
# 如果质量调整范围过小则退出
if quality_step == 1 and high <= low:
print("已尽可能接近目标大小,但可能未完全达到要求。")
break
if __name__ == "__main__":
input_image_path = "path/to/your/input/image.jpg"
output_image_path = "path/to/your/output/image.jpg"
target_size = 500 # 目标大小500KB
compress_image_to_size(input_image_path, output_image_path, target_size)
此方法主要针对JPEG格式的图片,对于PNG等其他格式,可能需要采用不同的策略。同时,由于JPEG是有损压缩,"无损"压缩到特定大小的目标可能难以完全实现,尤其是当目标大小远低于原始图片大小时。
除上方脚本外,若感兴趣还可以进一步了解对于其他格式图片的简易压缩方案。如:PNG是无损压缩,BMP通常是未压缩的。由于技术限制,尤其是JPEG格式,完全无损压缩到特定大小是有挑战性的(当然也可以通过去除人类视觉可感知范围外的信息实现视觉感知上的“无损压缩”)。不过,我们可以尽量通过调整压缩率和图片尺寸来接近目标大小。
以下是一个较为通用的压缩脚本,我们可以通过改变图片尺寸和调整JPEG的保存质量来接近指定的文件大小。对于PNG和BMP格式,它首先将图片转换为JPEG格式,因为这些格式通常不适合大幅度压缩到小文件大小。这个脚本提供了一个基础的起点,但请注意这可能不会完全达到特定的大小要求,尤其是在保持质量的前提下。
from PIL import Image
import os
import io
def compress_image(input_path, output_path, target_size_kb, format="JPEG"):
"""
尝试将图片文件压缩至接近指定的大小(KB)。
:param input_path: 输入图片文件的路径。
:param output_path: 输出图片文件的路径。
:param target_size_kb: 目标文件大小(KB)。
:param format: 输出格式(默认为JPEG)。
"""
img = Image.open(input_path)
img_format = format if format else img.format
# 尝试通过减少图片尺寸和调整质量来压缩图片
step_quality = 10
quality = 90
while quality >= 10:
# 使用内存文件模拟保存文件
with io.BytesIO() as temp_buffer:
img.save(temp_buffer, format=img_format, quality=quality)
size_kb = temp_buffer.tell() / 1024
if size_kb <= target_size_kb + 1: # +1 为了给大小留一点浮动空间
# 满足大小要求,将内存中的图片保存到磁盘
with open(output_path, "wb") as f_out:
f_out.write(temp_buffer.getvalue())
print(f"已将图片压缩至 {size_kb:.2f} KB, 质量设置为 {quality}")
break
# 如果文件仍然太大,降低图片质量
quality -= step_quality
if quality < 10 and size_kb > target_size_kb:
print("警告:无法在保持一定质量的情况下达到目标大小。")
break
if __name__ == "__main__":
input_image_path = "path/to/your/input/image.jpg"
output_image_path = "path/to/your/output/image.jpg"
target_size = 500 # 目标大小(KB)
compress_image(input_image_path, output_image_path, target_size)