无损压缩
一个 PNG 95KB 的故事
“光影”上线的第三周,一个设计师用户上传了一套 UI 设计稿——全是 PNG 格式的截图和矢量图。
她很快在群里反馈:“我的图上传后变糊了。”
我一看,果然——她上传的是一张 1920×1080 的 UI 截图,PNG 格式,原始大小 95 KB。我的系统检测到 PNG,走的是有损压缩流程,转成 WebP quality=80 后只有 28 KB。
问题是,UI 截图里有大量文字,文字被有损压缩后边缘发虚,肉眼可见。
95 KB 的 PNG → 28 KB 的有损 WebP,看起来省了很多,但 UI 截图的文字糊了。而如果用无损压缩,95 KB 的 PNG 可以转成 58 KB 的无损 WebP——文字完全清晰,体积还小了 39%。
我意识到:不是所有图片都应该有损压缩。
哪些图片应该无损压缩?
先搞清楚哪些类型的图片需要无损压缩:
必须无损的图片类型:
1. UI 截图 → 文字必须清晰,一个像素都不能差
2. 产品白底图 → 白色背景不允许有色块
3. Logo / 图标 → 硬边缘,有损会产生毛刺
4. 文字截图 → 同 UI 截图
5. 线条图/图表 → 硬边缘 + 少量颜色
可以有损的图片类型:
1. 摄影作品 → 噪点天然掩盖压缩失真
2. 风景照 → 渐变丰富,适合有损
3. 人像照 → 肤色过渡,有损压缩效果好
4. 动物/植物 → 自然纹理,失真不明显关键区别:人眼对”人造内容”(文字、线条、纯色)的失真极度敏感,对”自然内容”(噪点、渐变、纹理)的失真相对迟钝。
PNG 压缩的三个层次
对于需要无损压缩的图片,我研究了 PNG 优化的三个层次:
层次一:标准 PNG 优化(OptiPNG)
PNG 文件内部有很多可以无损去除的冗余数据:不必要的 metadata、非最优的过滤策略、未压缩的文本块等。
结果:
PNG OptiPNG 优化效果(-o5 级别)
图片类型 原始大小 优化后 节省
─────────────────────────────────────────────────
UI 截图 95 KB 72 KB 24.2%
白底产品图 320 KB 245 KB 23.4%
Logo 图标 12 KB 8 KB 33.3%
数据图表 45 KB 32 KB 28.9%
照片(被存为PNG) 2.1 MB 1.8 MB 14.3%OptiPNG 对所有类型都有 14%~33% 的优化。但这还不够——无损压缩的天花板在哪里?
层次二:有损化 PNG(pngquant)
pngquant 做了一件看似矛盾的事:把 PNG 变成有损的,但仍然是 PNG 格式。 它通过减少颜色数量(从 24 位真彩色降到 8 位调色板)来大幅减小文件体积,同时尽量保持视觉效果。
结果:
PNG pngquant 有损化效果(quality 65-80)
图片类型 原始大小 有损化后 节省
──────────────────────────────────────────────────
UI 截图 95 KB 28 KB 70.5% ← 文字开始轻微发虚!
白底产品图 320 KB 68 KB 78.8% ← 背景出现轻微色带
Logo 图标 12 KB 4 KB 66.7% ← 颜色减少,渐变不平滑
数据图表 45 KB 12 KB 73.3% ← 细线条变粗
照片(被存为PNG) 2.1 MB 380 KB 81.9% ← 看起来还行pngquant 的压缩率非常惊人,但对 UI 截图和 Logo 来说,有损化的代价太大了——文字和线条的质量明显下降。
结论:pngquant 适合”照片被存为 PNG”的情况,不适合真正的 UI 截图和 Logo。
层次三:无损 WebP 转换
这是最终的答案:把 PNG 转成无损 WebP。
结果:
PNG → 无损 WebP 转换效果
图片类型 PNG 大小 无损 WebP 节省 像素差异
──────────────────────────────────────────────────────────────
UI 截图 95 KB 52 KB 45.3% 0(完全一致)
白底产品图 320 KB 180 KB 43.8% 0(完全一致)
Logo 图标 12 KB 7 KB 41.7% 0(完全一致)
数据图表 45 KB 24 KB 46.7% 0(完全一致)
照片(被存为PNG) 2.1 MB 1.2 MB 42.9% 0(完全一致)完美!无损 WebP 在不丢失任何像素信息的前提下,平均减少了 44% 的体积。
三种方法对比
PNG 优化方法对比(UI 截图 95KB 为例)
方法 输出大小 节省 像素损失 适用场景
─────────────────────────────────────────────────────────────
OptiPNG (-o5) 72 KB 24% 无 PNG 格式内优化
pngquant 28 KB 70% 有 照片误存为 PNG 时
无损 WebP 52 KB 45% 无 ✅ 最佳方案
有损 WebP (q=80) 28 KB 70% 有 不适合文字/UI决策树:
图片内容是什么?
├── 照片/自然图像 → 有损 WebP(用上一节的智能压缩)
└── UI/文字/Logo/产品图
├── 需要 PNG 格式(兼容性要求)
│ └── OptiPNG 优化
└── 可以用 WebP
└── 无损 WebP ✅ 最佳方案完整的无损压缩实现
线条图 vs 照片:压缩差异的深层原因
我在测试中发现一个很有意思的现象:同样是 1920×1080 分辨率的图片,线条图的压缩率远低于照片。
结果:
线条图 风景照
唯一颜色 1,247 482,351
颜色比率 0.06% 23.3%
相邻像素差异 8.7 28.4
信息熵 3.2 bits 16.8 bits原因:
线条图:少量颜色 + 硬边缘 + 低熵
- 颜色少但位置精确 → 需要精确编码每个像素位置
- 硬边缘 → 相邻像素差异大但规律性强
- 低熵 → 无损压缩效果好(Run-Length + 字典编码效率高)
- 但有损压缩会破坏硬边缘的精确性
风景照:海量颜色 + 柔和过渡 + 高熵
- 颜色丰富且连续 → 相邻像素差异小且平滑
- 柔和过渡 → 频域中高频分量少
- 高熵 → 无损压缩效果差(信息量大,很难找到规律)
- 但有损压缩效果极好(可以丢弃大量高频分量)结论:线条图天生适合无损压缩(信息熵低),照片天生适合有损压缩(高频分量可丢弃)。
前端适配
前端需要知道图片是否需要无损压缩。我在上传时加了一个判断:
后端根据 hint 做最终决定: