先说个让我后背发凉的事
去年11月,我一个开淘宝店的朋友找我救火。她上了一批新品,5100多张图,用某个「在线图片压缩」网站批量处理了一遍,图是变小了,从平均 3.8MB 压到了 190KB 左右,看着挺美。结果平台审核打回来 40 多张,说主图不清晰。我去看她本地文件,原图已经被覆盖了。iPhone 拍的 4032×3024,全没了。
我拿到的是压缩后的残骸。放大到 200% 看,商品边缘有一圈很淡的灰边,文字标签的地方有红蓝色的小噪点。这不是压缩质量的问题,是被重新采样过了,而且是那种偷懒的双线性。我当时就有点上火,不是因为技术,是因为这个网站压根没告诉用户它动了什么。
所以这篇文章我想把一个事说透:压缩这件事,参数只是表象,关键是你得知道软件在背后干了什么。
JPEG 的「质量 80」,在不同软件里不是同一个东西
这个是我最想让人知道的一个点。很多教程说「质量 80 是甜点值」,但它没告诉你,Photoshop 的质量 80、ImageMagick 的质量 80、某个国产小工具的质量 80,出来的文件能差 3 倍。
原因在 libjpeg。这个库里的 quality 参数不是百分比,它是个量化表的缩放系数。具体逻辑大概是:quality 小于 50 的时候,缩放系数 = 5000 / quality;大于等于 50 的时候,系数 = 200 - quality × 2。也就是说,quality 50 的时候系数是 100,用的是 JPEG 标准量化表原样;quality 75 的时候系数是 50,量化表每一项砍一半;到 90 就是 20。
这意味着两件事。第一,从 90 降到 80,实际量化强度是从 20 涨到 40,翻了一倍,体积掉得很猛。第二,不同软件对滑块的映射方式不一样,有的直接把滑块值当 quality 传进去,有的做了自己的曲线。我见过一个工具,滑块拉到「高质量」,它内部实际传的是 62。用户以为自己在用 85。
还有个更少人提的参数:色度抽样,也就是 chroma subsampling。JPEG 存的是 YCbCr,Y 是亮度,CbCr 是色度。人眼对亮度敏感、对色度迟钝,所以可以只保留 1/4 的色度数据,这就是 4:2:0。默认很多软件用的是 4:4:4(不抽样)或者 4:2:2,你手动改成 4:2:0,体积通常能再降 15% 到 25%,肉眼在照片上基本看不出来。但在文字、logo、线稿上,4:2:0 会让黑色文字边缘出现彩色噪点,这个后面细说。
我那次实际跑的命令,以及三个翻车现场
目标:边长 800×800 方图,单张 200KB 以内。工具用的 ImageMagick 7,我这台是 2019 款的 MacBook Pro,i7 八核。命令大概是这样:
magick mogrify -path ./out \
-resize 800x800^ -gravity center -extent 800x800 \
-filter Lanczos -quality 82 -sampling-factor 4:2:0 \
-interlace Plane -strip *.jpg
800x800^ 那个尖角号是填充模式,意思是先把图缩到能盖住 800×800,再 -extent 从中间裁。-filter Lanczos 是重采样算法,降采样的话 Lanczos 比默认的 Mitchell 锐一点,但边缘容易出现轻微的振铃。-interlace Plane 是生成渐进式 JPEG,网页加载时先出模糊轮廓再变清晰,体积会稍微大一点点,大概 1% 到 3%。
翻车一:mogrify 是原地覆盖的。我第一次直接跑在素材目录里,幸好之前备份了。后来乖乖加 -path ./out。这个坑很蠢,但我相信不止我一个人踩。
翻车二:-strip 会删掉 ICC 色彩配置。她那批图里有 300 多张是早期用单反拍的,Adobe RGB 色彩空间。strip 之后浏览器按 sRGB 解释,颜色发闷、发灰,红色特别明显。后来我的做法是转换后再 strip:先 -profile /path/AdobeRGB1998.icc -profile /path/sRGB.icc 把色彩空间转过去,再删。
翻车三:方图裁剪切掉了主体。有几张图商品标签在左下角,居中裁剪刚好切一半。这种只能人工过一遍,或者改成 -resize 800x800 加白底补边(-background white -gravity center -extent 800x800),但补边会有白条,平台主图讲究白底满幅的就不行。
最后结果:5100 张跑完用了 23 分钟,输出平均 118KB,最大的 187KB,没有一个超 200KB。体积比原图降了大概 96.9%。
几个工具的横向对比,我个人的实测口径
| 工具 | 批量能力 | 色度抽样可调 | 保留 ICC | 我测的平均输出 | 备注 |
|---|---|---|---|---|---|
| ImageMagick | 强,命令行 | 是 | 是(需手动) | 118KB | 参数最透明,学习成本高 |
| XnConvert | 强,图形界面 | 是(藏在高级选项) | 是 | 142KB | 免费,界面丑得像 2008 年 |
| Photoshop 存储为 Web | 弱,动作可批 | 是(勾选) | 否(默认丢) | 178KB | 同质量数值下体积偏大 |
| Squoosh 网页版 | 单张为主 | 是 | 否 | 131KB | 实时对比做得好,不适合几千张 |
| Lightroom 导出 | 强 | 否 | 是 | 变化极大 | 那个「限制文件大小」会反复试,偶尔直接掉到质量 40 |
要注意这不是说 Photoshop 不行,它的算法其实很好,只是同一个「质量 80」,PS 出来的文件确实比 ImageMagick 大。我怀疑是它保留了更多的高频信息和色度数据。清晰度上,放大到 300% 去看,PS 那版确实更干净一点点,但代价是体积大 50%。
什么时候你就不该压
含文字的截图和线稿,别用 4:2:0。黑色文字在 YCbCr 里亮度接近 0,色度是中性,被下采样再还原之后,边缘会渗进相邻像素的色度,出现红蓝色描边。你截图存成 JPEG 之后放大看边缘发彩色,就是这个原因。这类图我的做法是:要么 PNG-8,用 pngquant 降到 256 色,一个 1080p 的界面截图能从 1.8MB 降到 180KB 左右,而且文字锐利;要么就用 4:4:4 加质量 90。
证件照也别乱压。身份证照片、签证照通常有明确要求,比如 295×413 像素、300dpi、白底、文件 20KB 到 40KB。20KB 这个数是很紧的,295×413 的 JPEG 压到 20KB 基本得用质量 60 到 70,脸部细节会糊。我一般会告诉人:先确认对方要的是像素尺寸还是厘米尺寸,很多要求冲突的时候,像素优先。
印刷用途的图,压缩这事跟你就没关系,直接 TIFF 或者高质量 JPEG,300dpi 起步。有人拿 72dpi 的图去印名片,出来的字边缘是锯齿的,这个不是压缩的问题,是分辨率不够。
我的一般性判断
如果图是要放到网页或者 App 里,我现在基本直接转 WebP,质量 80,平均比 JPEG 小 25% 到 35%,浏览器支持早就没问题了。但如果是往某些平台后台上传,先小批量试几张,确认它接不接受 WebP,别一口气转完 5000 张发现被转回了 JPEG,那就白折腾还多一层损失。
在线压缩工具我一般不用。三个原因:一是图要上传到别人服务器,商品图、证件照这种我不放心;二是有些会悄悄重采样,就像我朋友遇到的那个;三是很多工具的输出是二次压缩,你输入一张已经压过的图,它再压一遍,伪影叠加,画质掉得比单次压到同等体积更狠。
最后说个心态上的事。很多人压图的时候盯着那个文件体积的数字,觉得越小越好。但真正该盯的是「这张图在最终展示尺寸下看起来怎么样」。一张 800×800 的图在手机上看,200KB 和 400KB 的差别,大部分人分不出来。为了省 100KB 把质量从 82 压到 60,那才是真亏。