Python 图片压缩工具
需求背景
图床(如 superbed.cn)限制单张图片不超过 5MB,超过则上传失败。为此需要一个自动压缩脚本,将大图片压缩到限制以下,同时尽量保持清晰度。
实现原理
压缩策略采用先缩小尺寸、再降质量的顺序:
- 缩小尺寸优先 — 大图片(短边 >2000px)先按比例缩小到合理尺寸,这比单纯降质量更有效且保真度更高
- 质量大步降 — JPEG 从
quality=85 开始,若还超标则跳到 70 → 60,不再细粒度一点点降
- 保留 PNG 透明度 — PNG 图片始终保持 PNG 格式,不转为 JPEG(否则丢失透明度)
- 保守下限 —
quality 最低只到 60,scale 最多缩小到 50%,避免图片过度模糊
PNG 图片只支持尺寸缩小来压缩,不支持质量调节。
使用方法
环境准备
1 2
| python -m venv .venv .venv/Scripts/pip install Pillow
|
命令行使用
1
| .venv/Scripts/python tools/compress_image.py <图片路径>
|
以一张 5712×3213 的 PNG 图片(约 14MB)为例:
- 原始大小:13.26 MB
- 压缩后:1.00 MB(减少 92.5%)
- 尺寸:5712×3213 → 3555×2000
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| """图片压缩工具 - 快速将图片压缩到指定大小以下"""
import sys import os from PIL import Image
def get_file_size(path): return os.path.getsize(path) / (1024 * 1024)
def compress_image(input_path, output_path, max_size_mb=5): """压缩策略:先评估,小图只降质量,大图才缩尺寸""" img = Image.open(input_path) width, height = img.size
if img.format == 'PNG': img = img.convert('RGB')
target_short = 2000
if max(width, height) > target_short: ratio = target_short / min(width, height) new_width = int(width * ratio) new_height = int(height * ratio) img = img.resize((new_width, new_height), Image.Resampling.BILINEAR)
img.save(output_path, format='JPEG', quality=85, optimize=True)
if get_file_size(output_path) > max_size_mb: for quality in [70, 60]: img.save(output_path, format='JPEG', quality=quality, optimize=True) if get_file_size(output_path) <= max_size_mb: break
if get_file_size(output_path) > max_size_mb: current_img = Image.open(output_path) w, h = current_img.size scale = 0.9 while get_file_size(output_path) > max_size_mb and scale >= 0.5: new_w, new_h = int(w * scale), int(h * scale) resized = current_img.resize((new_w, new_h), Image.Resampling.BILINEAR) resized.save(output_path, format='JPEG', quality=85, optimize=True) scale -= 0.1
return output_path
if __name__ == '__main__': if len(sys.argv) < 2: print("用法: python compress_image.py <图片路径>") sys.exit(1)
input_path = sys.argv[1] if not os.path.exists(input_path): print(f"文件不存在: {input_path}") sys.exit(1)
original_size = get_file_size(input_path) print(f"原始大小: {original_size:.2f} MB")
if original_size <= 5: print(f"无需压缩") sys.exit(0)
backup_path = input_path + '.bak' os.rename(input_path, backup_path)
try: compress_image(backup_path, input_path, max_size_mb=5) new_size = get_file_size(input_path) ratio = (1 - new_size / original_size) * 100 print(f"压缩后: {new_size:.2f} MB (减少 {ratio:.1f}%)") os.remove(backup_path) print(f"完成: {input_path}") except Exception as e: os.rename(backup_path, input_path) print(f"失败: {e}") sys.exit(1)
|
关键参数
| 参数 |
值 |
说明 |
target_short |
2000 |
缩小目标:短边最多 2000px |
| 质量梯度 |
85→70→60 |
大步降质量,避免反复编码 |
| 尺寸步进 |
90% |
每次缩小到原来的 90% |
| 尺寸下限 |
50% |
最多缩小到原来的一半 |