图片压缩到 200KB 还是糊?我把 ImageMagick、mozjpeg、jpegli 的参数挨个试了一遍
上周帮朋友弄一个二手相机店的商品页,两百多张图,要求全部压到 200KB 以内。我一开始随手用 ImageMagick 批量跑了一遍 -quality 80,结果他发来截图:镜头镀膜那圈紫边全糊成一坨,问我是不是把原图压坏了。
没压坏,是参数调错了。但更有意思的是,为了这事我把手上六七个工具全翻出来横向跑了一圈,发现网上流传的那些「quality 80 最划算」「WebP 一定比 JPEG 小」的说法,基本都站不住脚。下面按我自己踩坑的顺序说。
先搞清楚 JPEG 的 quality 到底是个什么数
很多人以为 quality=82 是「保留 82% 的原始质量」,不是。它压根不是百分比,而是把标准 JPEG 量化表整体乘一个缩放系数的档位。JPEG 标准里有张 8×8 的量化矩阵,规定了每个频率分量除以多少,quality 只是给这张表乘上 0.1 到 5 之间的一个系数,系数越大丢得越多。
所以这东西有两个要命的属性。第一,它不线性,quality 从 90 降到 85,和从 85 降到 80,画质损失幅度差得远。第二,它不跨编码器通用。我实测同一张 4000×3000 的街拍,用 ImageMagick 的 -quality 78 出图 412KB,用 mozjpeg 的 cjpeg -quality 78 出来只有 356KB,肉眼几乎看不出区别——因为 mozjpeg 默认开了 trellis quantization 和 progressive 扫描,量化表的实际落点跟 libjpeg 原版就不是一回事。你在 A 工具里调出来的「甜蜜点」,搬到 B 工具就是垃圾参数。
顺便说个坑:ImageMagick 在 quality 到 90 及以上会自动关掉色度子采样,从 4:2:0 切到 4:4:4,文件体积会突然跳一截。你要是写批量脚本按 quality 循环扫,在 89 和 90 之间能看到一个非常明显的台阶,别以为是自己的脚本出 bug 了。
比压缩算法更值钱的,是先把元数据扒掉
我那批图里有一张用 iPhone 拍的,原图 4.1MB。什么都没动,只跑了一遍 -strip,变成 3.2MB——凭空少了 900KB。少的这些是什么?一张 160×120 的 EXIF 内嵌缩略图、GPS 定位、几十条厂商 MakerNotes,还有些人像模式拍的带深度图,那玩意数据量能到 1MB 往上。
清元数据我用 exiftool -all= -overwrite_original *.jpg,一次跑完整个目录,比你在压缩质量上抠半天有用得多。注意 -all= 会把方向标签一起删掉,如果原图是竖拍的,得先做一遍自动旋转再删,否则整批图躺倒。
PNG 那边同理。一张带 ICC 配置文件的图,配置文件本身就可能占几百 KB,ProPhoto RGB 那种带查找表的能到几百 KB 级别,比图片数据还大。用 oxipng -o 4 --strip safe 可以在保留必要色彩信息和透明通道的前提下把冗余块清干净,比 pngquant 那种有损量化更稳。
顺序错了,参数再对也白搭
绝大多数人的处理顺序是:调尺寸 → 锐化 → 压缩。这个顺序有个问题。锐化本质上是往边缘附近人为加高频信号,而 JPEG 恰恰对高频最狠——它会把你不想要的高频噪点和你要的边缘一起吃下去,结果就是边缘出现一圈塑料感的振铃。
我现在的顺序是:统一尺寸 → 极轻降噪 → 压缩 → 存成中间格式再按需锐化。听着麻烦,但用 sharp 写脚本其实就是链式调用的事:
sharp(input)
.resize(1600, null, { withoutEnlargement: true, kernel: 'lanczos3' })
.median(3)
.jpeg({ quality: 82, mozjpeg: true, chromaSubsampling: '4:4:4' })
.toFile(output);
这里 chromaSubsampling: '4:4:4' 是个关键。默认的 4:2:0 把色度信息砍到四分之一分辨率,亮度不动,所以灰扑扑的建筑照看不太出来,但拍红色招牌、彩色衣服、霓虹灯的时候,边缘颜色会渗出去两三个像素。代价是文件大 15% 到 25%,我觉得换边缘干净值。
最近值得看的两个新东西
一个是 jpegli,Google 2024 年放出来的,官方说同等主观质量下比 libjpeg-turbo 小 35%。我在自己这套测试集上跑,比 mozjpeg 还能再小 8% 到 12%,编码速度也不慢,已经有项目把它接进 FFmpeg 了。缺点是它输出就是标准 JPEG,解码端兼容没问题,但很多 CDN 的自动优化链路还没接上,你手动传上去可能被二次处理。
另一个是 AVIF 的 alpha 参数。处理带透明通道的图(商品抠图、UI 资源)时,很多人直接从 PNG 转 AVIF,只设了 --qcolor,忘了 --qalpha。默认 alpha 质量很低,结果主体边缘一圈毛刺,放大特别脏。建议 avifenc --qcolor 65 --qalpha 75 -s 6 input.png out.avif,75 这个 alpha 质量在大部分电商素材上,跟无损的视觉差异已经在噪声水平以下了,而文件能小掉一大半。
最后回到开头那批图:最终方案是 exiftool 先清元数据,sharp 缩到长边 1600、quality 82、4:4:4,两百多张跑完平均 178KB,紫边保住了。朋友后来又发消息说,比我之前给他的那套「quality 80 万能参数」强太多。其实参数没变多少,就是搞清楚每个数字到底在管什么而已。