如何使用Python-Pillow精准调整JPG文件大小?
用Python-Pillow精准控制JPG文件大小的解决方案
问题根源
你当前仅通过缩放尺寸来控制文件大小,但JPG的文件大小由像素总量+压缩质量共同决定。Pillow保存JPG时默认使用75的质量参数,即便你把尺寸调到对应3MB的量级,高压缩率也会让最终文件远小于预期。
解决思路
要精准控制JPG大小,需结合尺寸预调整和压缩质量循环试探:
- 先判断原图尺寸是否过大,若远超目标大小对应的像素量,先按比例缩放缩小基础体积
- 通过二分法循环调整JPG压缩质量参数,每次保存后校验文件大小,直到结果落在目标误差范围内
改进后的代码
import os from PIL import Image from pathlib import Path def get_img_format(suffix): lower_suffix = suffix.lower() return 'jpeg' if lower_suffix in ('.jpg', '.jpeg') else 'png' def adjust_jpg_size(image_path, target_size_mb, error_tolerance=0.05): target_bytes = target_size_mb * 1024 * 1024 # 转换为字节单位 img = Image.open(image_path) img_format = get_img_format(Path(image_path).suffix) # 仅处理JPG格式,跳过其他格式文件 if img_format != 'jpeg': print(f"{image_path} 非JPG格式,跳过处理") return # 第一步:预缩放尺寸(若原图远大于目标大小) current_size = os.path.getsize(image_path) if current_size > target_bytes: # 按面积比例估算缩放系数(像素面积与文件大小正相关) scale_ratio = (target_bytes / current_size) ** 0.5 new_width = int(img.width * scale_ratio) new_height = int(img.height * scale_ratio) # 用LANCZOS插值缩放,保证画质 img = img.resize((new_width, new_height), Image.Resampling.LANCZOS) # 第二步:二分法查找最优压缩质量 min_quality = 10 max_quality = 95 best_quality = 75 best_size = float('inf') temp_path = Path(image_path).parent / f"temp_{Path(image_path).stem}.jpg" while min_quality <= max_quality: mid_quality = (min_quality + max_quality) // 2 # 保存临时文件,开启optimize优化压缩 img.save(temp_path, 'jpeg', quality=mid_quality, optimize=True) temp_size = os.path.getsize(temp_path) # 检查是否在误差容忍范围内 if abs(temp_size - target_bytes) <= target_bytes * error_tolerance: best_quality = mid_quality best_size = temp_size break # 调整质量搜索范围 if temp_size < target_bytes: # 文件过小,提高质量(降低压缩率) min_quality = mid_quality + 1 else: # 文件过大,降低质量(提高压缩率) max_quality = mid_quality - 1 # 记录最接近目标的参数 if abs(temp_size - target_bytes) < abs(best_size - target_bytes): best_quality = mid_quality best_size = temp_size # 保存最终结果并清理临时文件 final_path = Path(image_path).parent / f"{Path(image_path).stem}_resized.jpg" img.save(final_path, 'jpeg', quality=best_quality, optimize=True) os.remove(temp_path) print(f"处理完成:{final_path},实际大小:{best_size/1024/1024:.2f}MB,使用质量参数:{best_quality}") def batch_process(folder, target_size_mb=3): p = Path(folder) extensions = ["*.jpg", "*.jpeg", "*.JPG", "*.JPEG"] for ext in extensions: for file in p.rglob(ext): adjust_jpg_size(str(file), target_size_mb) if __name__ == '__main__': batch_process(r'D:\code_prj\Med_OCR\data')
关键说明
- 压缩质量参数:Pillow的
quality取值范围为1-95,数值越高画质越好、文件越大;optimize=True会启用压缩优化,让结果更稳定 - 二分法查找:相比线性遍历1-95的质量值,二分法能快速缩小范围,提升处理效率
- 误差容忍:设置
error_tolerance=0.05允许最终大小与目标有±5%的误差,避免因JPG压缩的非线性特性导致无限循环 - 插值缩放:使用
Image.Resampling.LANCZOS插值算法,缩放后的图片画质更清晰
注意事项
- 不同内容的图片(纯色图/复杂风景图)压缩率差异极大,相同质量参数下文件大小可能不同,循环试探是最可靠的控制方式
- 若原图本身尺寸过小,即使将质量调到95也无法达到目标大小,可在代码中添加提示逻辑
- 批量处理时建议先测试单张图片,确认参数效果后再推广到全文件夹
内容的提问来源于stack exchange,提问作者yanbin_c
相关产品推荐
相关产品推荐

