3. 图片压缩基础:有损压缩与无损压缩的区别,常见图片格式(JPEG、PNG、WebP、AVIF)
说到图片压缩,很多同学第一反应就是「压一下,小一点」。但实际项目里,选错格式的代价可不小。我见过有人把截图存成 JPEG,结果文字边缘全是马赛克;也见过有人把所有图片都转成 PNG,一个页面加载了 5MB 的图片资源。
说白了,图片压缩的核心就两个方向:有损和无损。搞懂它们的区别,你才能在不同场景下做出正确的选择。
3.1 有损压缩 vs 无损压缩
先看一张图,帮你快速建立整体认知:
3.2 有损压缩:用「看不见」换「体积小」
有损压缩的原理,说白了就是丢掉人眼不太敏感的信息。比如一张蓝色天空的照片,相邻几百个像素都是差不多的蓝色,没必要每个像素都精确记录。
JPEG 用的就是这种思路。它先把图像从 RGB 转到 YCbCr 色彩空间,然后对亮度(Y)保留高精度,对色度(Cb、Cr)做大幅下采样。人眼对亮度变化敏感,对颜色变化迟钝——这就是 JPEG 能压到很小但看起来还不错的秘密。
核心指标:质量因子(Quality Factor)
大部分图片库用 0-100 的数值控制压缩程度。我个人习惯:
- 85-95:高质量,适合摄影作品、产品图
- 60-75:平衡点,适合大多数 Web 场景
- 30-50:低质量,适合缩略图或预览图
举个例子,一张 1920×1080 的风景照,原始 BMP 大约 6MB。用 JPEG 质量 80 压缩,能压到 200KB 左右,肉眼几乎看不出区别。但如果你把质量调到 20,文件可能只有 50KB,不过天空会出现明显的色块——这就是「压缩伪影」。
⚠ 注意:有损压缩不可逆
我曾经接手过一个项目,设计师把所有源文件都存成了 JPEG。每次修改都要重新导出,反复保存几次后,图片质量肉眼可见地下降。记住:JPEG 每保存一次,质量就损失一次。源文件一定要用无损格式保存。
3.3 无损压缩:像素级「零误差」
无损压缩不会丢掉任何像素数据。它用的是更聪明的编码方式——比如把重复的数据用更短的符号代替。
PNG 就是无损压缩的代表。它用 DEFLATE 算法(和 ZIP 一样)来压缩数据。对于有大面积纯色的图片(比如截图、图标、UI 界面),PNG 的压缩效果非常好。但如果是照片这种色彩丰富、细节复杂的图,PNG 的压缩率就远不如 JPEG 了。
💡 什么时候必须用无损?
- 截图、UI 设计稿、文字图片
- 需要透明背景的图片(PNG-32 支持 Alpha 通道)
- 医学影像、法律文档等不允许任何失真的场景
- 作为中间格式反复编辑的源文件
3.4 常见图片格式对比
下面这张表是我在实际项目中总结的,你可以直接拿来当参考:
| 格式 | 压缩类型 | 透明通道 | 压缩率 | 浏览器支持 | 最佳场景 |
|---|---|---|---|---|---|
| JPEG | 有损 | 不支持 | 高(照片可压 10-20 倍) | 全部 | 照片、复杂渐变图 |
| PNG-8 | 无损 | 支持(1-bit) | 中(256 色) | 全部 | 图标、Logo、小尺寸 UI |
| PNG-24/32 | 无损 | 支持(8-bit Alpha) | 低(照片压缩率差) | 全部 | 截图、需要透明背景的图 |
| WebP | 有损/无损 | 支持 | 比 JPEG 小 25-35% | Chrome/Edge/Firefox | Web 通用替代方案 |
| AVIF | 有损/无损 | 支持 | 比 JPEG 小 50% 左右 | Chrome/Firefox | 下一代 Web 图片格式 |
3.5 WebP 和 AVIF:新一代格式
JPEG 和 PNG 都是上世纪 90 年代的标准了。这些年 Web 图片的需求变了——移动端流量贵、加载速度要求高,老格式越来越力不从心。
WebP 是 Google 在 2010 年推出的。它同时支持有损和无损压缩,还能带透明通道。我做过对比测试:同一张照片,JPEG 质量 80 是 200KB,WebP 有损模式只要 130KB,肉眼几乎看不出区别。无损模式下,PNG 的截图是 150KB,WebP 无损只要 90KB。
AVIF 更新,2019 年才定稿。它基于 AV1 视频编码技术,压缩率比 WebP 还高 30-40%。不过编码速度慢——压缩一张图可能要好几秒,不适合实时场景。
我的建议:
如果你在做新项目,可以这样选:
- 照片类 → 优先 WebP 有损,降级到 JPEG
- 截图/UI → 优先 WebP 无损,降级到 PNG
- 追求极致体积 → 用 AVIF,但要准备 JPEG/PNG 兜底
浏览器不支持 WebP 或 AVIF 的情况越来越少,但还是要做 <picture> 标签的降级处理。
3.6 实战:用 Python 做格式转换
说了这么多理论,来点实际的。下面是我常用的图片压缩脚本,用 Pillow 库实现:
from PIL import Image
import os
def compress_image(input_path, output_path, format='WEBP', quality=80):
"""
图片压缩与格式转换
:param input_path: 输入文件路径
:param output_path: 输出文件路径
:param format: 目标格式(JPEG/PNG/WEBP/AVIF)
:param quality: 质量因子(仅对有损格式有效)
"""
img = Image.open(input_path)
# 如果是 JPEG 且图片有透明通道,先转成 RGB
if format == 'JPEG' and img.mode in ('RGBA', 'P'):
img = img.convert('RGB')
# 保存
img.save(output_path, format=format, quality=quality)
print(f"压缩完成: {input_path} → {output_path}")
# 使用示例
compress_image('photo.png', 'photo.webp', format='WEBP', quality=85)
compress_image('photo.png', 'photo.jpg', format='JPEG', quality=80)
compress_image('screenshot.png', 'screenshot_lossless.webp', format='WEBP', quality=100)
💡 小技巧:批量处理
我在项目中经常要处理几百张图片。用 glob 配合上面的函数,几秒钟就能搞定整个文件夹的格式转换。记得先备份原图——我吃过这个亏,一次误操作把源文件全覆盖了。
3.7 避坑指南
最后分享几个我踩过的坑:
- 不要用 JPEG 保存带文字的截图——文字边缘会出现模糊和色块,阅读体验极差。截图请用 PNG 或 WebP 无损。
- PNG 不是万能的——很多人觉得 PNG 是「无损」就所有图都用它。但一张 4K 照片存成 PNG 可能有 15MB,同样的图 JPEG 只要 300KB。选格式要看场景。
- WebP 的编码质量参数含义不同——WebP 的 quality 是 0-100,但和 JPEG 的 quality 不是线性对应的。我习惯 WebP 用 80-90,效果相当于 JPEG 的 85-95。
- AVIF 编码慢——如果你在做批量处理,AVIF 的编码时间可能是 WebP 的 5-10 倍。线上实时转换不推荐用 AVIF。
嗯,图片压缩这块,说白了就是在体积和质量之间找平衡。没有银弹,每种格式都有自己的脾气。你只要记住:照片用有损,截图用无损,新项目优先考虑 WebP,就基本不会出大问题。