图片压缩后发灰、透明底变黑、竖图躺平:我跑完 4872 张图的排查笔记

关键词:图片压缩,WebP转换,批量图片处理,ICC色彩配置,EXIF方向

摘要:一次 4872 张电商图的批量处理实录。聊透压缩后颜色发灰、PNG 透明底变黑、手机竖图方向错乱这几个实际会撞上的问题,附具体命令、参数和体积实测数据。

先说背景。上个月帮一个做家居的朋友收拾店铺图,4872 张,来源乱得离谱:一半是 iPhone 15 Pro 拍的 HEIC,一部分是美工早年间用 5D3 拍完从 Lightroom 导的 TIFF,还有几百张是从别人家详情页扒下来的 JPG(这事我劝过,没劝住)。需求听起来很简单——全部转成宽度 1600 的 WebP,塞进新站商品页。我一开始想,这不就一行命令的事。结果第一版跑完打开一看,橙色沙发变成了灰扑扑的土黄。那天晚上我把脚本改了七遍。

图片

颜色发灰基本只有一个原因:色彩配置文件在中间某个环节被丢了,或者压根没做转换就丢了。iPhone 默认拍 Display P3,Adobe 全家桶默认 ProPhoto 或 Adobe RGB,而浏览器、大部分安卓屏、Windows 图片查看器默认都按 sRGB 解释像素值。拿 P3 的数值当 sRGB 显示,红橙会明显掉饱和度,绿色反而更扎眼,整体感觉就是「脏了一层」。我最早那条命令里带了 -strip,它把 EXIF 和 ICC 一起剃干净,像素值原封不动,于是颜色直接跑偏。正确做法是先把色彩空间真的转过去,再决定要不要 strip:magick in.heic -colorspace sRGB -profile /usr/share/color/icc/sRGB.icc -strip -resize 1600x -quality 82 out.webp。这里 -colorspace sRGB 和 -profile 是两件不同的事,前者改数值,后者写入描述,只做后者不做前者等于贴了张假标签。

图片

PNG 转 JPEG 背景变黑,这坑我见过太多次,包括我自己刚学的时候也踩过。根因是 JPEG 根本没有 alpha 通道,解码器碰上透明像素,有的填黑(0,0,0),有的填白,全看实现。ImageMagick 默认就是黑。解决办法是在换格式之前把透明「压平」到底色上:-background white -alpha remove -alpha off,三个参数缺一不可;-flatten 也行,但遇到多帧 GIF 会合并图层,我一般不用。顺带提一句,如果你的图要放在深色背景的页面上,别老实压白底,直接压成页面那个色值,比如 #1a1a1a,不然图周围会留一圈很显眼的白边,看着像抠图没抠干净。

图片

竖图躺平的问题出在 EXIF 的 Orientation 标签上。手机拍照时 CMOS 是横着的,机器懒得物理旋转,只在文件里写一个 1 到 8 的数字,告诉看图软件「你帮我转 90 度再显示」。麻烦在于有大量程序压根不读这个标签——浏览器在 <img> 里读,但在 CSS background-image 里很多版本不读;canvas 读不读取决于你有没有手动处理;而 ImageMagick 的 -resize 也不读。于是你会看到一种诡异现象:本地缩略图里图片是正的,一上网页就横过来。命令里加个 -auto-orient 就能解决,但它必须放在所有几何操作之前,写在 -resize 后面就晚了。用 sharp 的话是 .rotate() 不带参数,它会读 EXIF 自动转,之后再 .withMetadata() 决定要不要保留其余信息。

压缩完「看着糊」,很多时候不是质量的锅,是色度抽样。JPEG 默认用 4:2:0,亮度保留全分辨率、色度只存四分之一。1600px 的图放大看,红边、细字、logo 线条会有明显锯齿和色斑。要修就加 -sampling-factor 4:4:4,代价是体积涨三到四成。我实测一张 1600×1067 的沙发图,q82 下 4:2:0 是 148KB,4:4:4 是 214KB。另一个误区是锐化加太早,先锐化再缩小,等于把噪点一起缩进去,出来一层沙。正确顺序永远是先缩再锐,USM 用半径 0.8、量 60%、阈值 0,这三个数我用了三四年没换过。

图片

格式这块,同一张 1600px 的图我拿三种编码各跑了一遍:JPG q82 = 148KB,WebP(cwebp -q 80 -m 6 -mt)= 94KB,AVIF q63 = 71KB。看起来 AVIF 完胜对吧?但它编码一张要 2.7 秒,WebP 是 0.4 秒,4872 张全量跑 AVIF 得四个多小时,WebP 半小时出头。而且 Safari 16 之前根本不认 AVIF。我最后的选择是 WebP 打底,再用 <picture> 给支持的浏览器挂 AVIF,没做全量转换。真要跑批,别用 ImageMagick 转 AVIF,它底层调的 libheif 版本一乱就报错,用 avifenc 单独跑,--speed 6 --cq-level 26,慢但稳。

图片

最后说个没人提醒过我的坑:ImageMagick 默认内存上限 256MiB,超过就往 /tmp 写磁盘缓存。我有一批 8000×6000 的 TIFF,跑到第 300 张左右速度突然掉到一张 40 秒,top 一看全在等 IO。改 /etc/ImageMagick-6/policy.xml 里的 memory 和 disk 值,或者干脆分成 200 张一批用 xargs -P 4 并发跑。并发数别超过 CPU 物理核数,超了反而更慢,我 8 核的机器跑 -P 4 比 -P 8 快将近 20%。Node 那边的 sharp 是 libvips 的封装,内存表现好太多,同样的活儿我后来迁到 sharp,峰值内存从 3.2G 降到 700M,就是文档写得比较劝退。

图片

所以你要问图片处理有没有银弹,我的答案是:没有,而且大部分时候你并不需要最新最强的格式。先搞清楚你的图从哪来、色彩空间是什么、最终在哪显示,这比研究 AVIF 的 cq-level 怎么调有用得多。我那 4872 张最后跑了两版,第一版全废,第二版才敢上线。美工到现在还以为我那天是在摸鱼。

标签: