为什么大多数压缩工具压不到 50KB
报名表写着”照片不超过 50KB”,你拖进去一张,工具压完 178KB。于是你把画质调低再压一次——这回 31KB,画质糊得连自己都不认识。来回四五次,最后交上去一张既不确定合格、也不确定能看清的东西。
这不是你的问题,是大多数压缩器的做法问题。
常见做法:猜一次,不中就重来
主流在线压缩工具的逻辑是”一次编码”:按某个预设规则缩放一次、用某个固定质量参数压缩一遍,然后把结果丢给你。它们不会去逼近你给的目标体积——因为它不知道该往哪个方向调整。
根本矛盾在于,最终体积同时被两个变量控制:
- 分辨率:像素越多,体积越大
- 画质参数:质量越高,体积越大
这两个变量可以互换——同样的 50KB,你可以给”大分辨率 + 低画质”,也可以给”小分辨率 + 高画质”。一次编码只取其中一个点,当然猜不中。
实测:一次压缩 vs 二段搜索
我造了一张 2400×1600 的合成照片(渐变叠加噪点,让它有接近真实照片的压缩难度),在本地跑了组对照:
| 目标 | 一次压缩(常见做法:固定 1920 宽 + 质量 80) | 二段搜索 |
|---|---|---|
| 不限 | 255.3KB | — |
| 20KB | 达不到 | 1560px 宽 / 质量 43 / 18.0KB |
| 50KB | 达不到 | 2040px 宽 / 质量 40 / 47.7KB |
| 100KB | 达不到 | 2400px 宽(原始分辨率)/ 质量 41 / 99.4KB |
左边那一列最能说明问题:按通用设置压出来的结果是 255KB,对 20/50/100KB 这三个目标一个都不合格,而且它不会告诉你还差多远。
右边就是 Pic2KB 走的路子:
- 先找能装进目标的最大分辨率——从上往下探,找到那个”再高一点就超了”的临界点
- 再在这个分辨率里找最高可用画质——用二分搜索逼近边界,不浪费任何一个字节
顺便说一句方法:以上数据在本机用 libvips 编码器跑出,Pic2KB 在浏览器里用 Canvas 走的是同一套搜索思路,具体数值会随图片内容和编码器实现浮动。真实照片的建议是:拿自己的图试,别信任何”万能参数”。
为什么优先保分辨率,而不是优先保画质
看到上面 “质量 40” 这个数字你可能会犹豫:画质这么低能看吗?
这里有个容易被忽略的现实——大多数要求限制体积的场景,同时也秘密限制着下限。签证照要求最低像素尺寸,考试报名系统会在后端做分辨率校验,太小直接拒收。这种情况下,“像素不够但每个像素很干净”是致命的,反过来才有回旋余地。
所以默认策略是:尽可能多地保留像素,然后把剩下的预算全给画质。这也是为什么在 100KB 目标下,它能原样保留 2400px 的原始分辨率。
(当然,如果你就是要”小图 + 高画质”,那算是另一个目标函数,属于可以调的东西。)
这套事为什么能在浏览器里跑完
体积没有捷径,二段搜索要反复编码好几次。如果在服务器上做,你得上传原图等着;在浏览器里做,省掉的是整个来回。
Pic2KB 的做法是把编解码放进 Web Workers,在里面跑 Canvas 重采样和多轮编码,主线程只负责显示进度,页面不会卡住。副作用是三个:照片不用上传(隐私风险归零)、没有服务器成本(所以能免费做下去)、以及最顽固的限制不见了——没有文件大小上限,没有等待队列。
顺带还有个副产品:图片重新编码后,EXIF 里那些你自己都不知道在里面的东西(拍摄时间、GPS 坐标)会被一并清掉。
装不进去的时候,要说清楚
最后一件我认为最该做、也最容易被省掉的事:
有些目标确实做不到。一张信息量巨大的照片要压到 8KB,在最高压缩下也装不进去。这时候有两种选择,一种是悄悄给你一个仍然超标的图(你去撞那次提交失败),另一种是明确告诉你”这张在最大压缩下也装不进 ××KB”,同时把最接近的结果给你。
我们选后者。工具的价值不在于假装每次都成功,而在于失败的时候让你知道边界在哪。