图片处理踩坑实录:我为什么劝你先别急着上AVIF,先把DPI和EXIF搞明白

关键词:图片处理,批量压缩,WebP与AVIF对比,EXIF方向,DPI陷阱

摘要:从一次被甲方退回的DPI事故讲起,拆解屏幕显示为什么根本不在乎DPI、WebP与AVIF在真实生产环境里的取舍账,以及EXIF Orientation和Display P3这两个最常被忽略的坑。附实测体积/耗时数据与可复用的批处理思路。

一个把我熬到凌晨两点的DPI

图片

2021年我接了个跨境电商详情页的改造,甲方一股脑甩来2400张商品图,第一句话就是“全部转成300dpi”。我当时心想这有什么难的,ImageMagick一行命令的事,顺手还加了 -strip 清理元数据显得专业一点。结果交稿第二天就被打回来:图片插进InDesign之后尺寸全部爆炸,一张4000px的图在版面上占了大半页,设计师一晚上在重新拖位置。

问题出在哪?那张4000px的图本身带的是300dpi标签,物理尺寸约33.3厘米,正好符合版面要求。我把 -strip 一加,XResolution和YResolution这两个字段一起被抹掉了,InDesign找不到分辨率标记,就按默认的72dpi去读,同样4000px一下子变成了141厘米宽,直接溢出画布。糊的、错的不是画质,是被算歪了几倍的物理尺寸。

这件事让我想明白一个很多人反复绕不出来的问题:屏幕显示根本不看DPI。浏览器渲染一张图只认像素宽高,你在Photoshop里把72dpi改成300dpi,文件字节数可能一个都不变,网页上看还是一模一样。DPI真正起作用的地方就两个:一是Word、PDF、InDesign这类版式软件,它会去读EXIF或者JFIF头里的分辨率字段来决定摆放尺寸;二是某些老后台和ERP系统,会拿dpi做二次换算,我上面撞的就是第二种。

图片

所以我的做法是分两条线走:屏幕用图只关心像素尺寸,sharpcwebpPillow 生成的文件开不开DPI字段都不影响显示;要进印刷厂或者Office文档的图,必须显式写 -density 300,而且千万别顺手加-strip。就这一个参数,后来我把它单独拎出来写进了团队的图片处理规范第一节。

格式这件事,别迷信“最新最好”

AVIF这两年确实被吹得很猛,我也在生产环境上了,但有些账得掰开算。我拿一张4000×3000的雪景照做过实测,同一台M1 Mac,命令行逐档跑,这张图细节多、高频纹理密,是压缩算法最啃不动的类型:

图片

格式与参数 文件大小 编码耗时
JPEG (mozjpeg -quality 90 -progressive) 4.1 MB 0.42s
JPEG (mozjpeg -quality 82 -optimize) 2.3 MB 0.38s
WebP (-q 82 -m 6 -sharp_yuv) 1.4 MB 2.7s
AVIF (--min 20 --max 32 --speed 4) 0.9 MB 18.6s

体积上AVIF确实吊打,比JPEG 82小了约60%,比WebP还小近36%。但它编码慢了将近50倍。我当时的场景是每天新增3万张UGC图,全量走AVIF根本扛不住,最后上了个8核worker集群才勉强跟上,每个月多掏的机器钱比省下来的CDN流量费还多。

后来改成混合策略:首屏大图走AVIF,列表缩略图走WebP,用户刚上传的临时图24小时内维持JPEG。逻辑是WebP编码两三秒还能忍,AVIF的二十秒延迟对实时接口是灾难;而且AVIF解码在低端安卓机上很吃CPU,我拿一台2019年的红米测过,解一张1080p的AVIF要340ms,同尺寸WebP只要90ms,滚动列表能明显看出掉帧。

图片

格式没有绝对优劣,只有在你的延迟预算机器预算里扛不扛得住的问题。把参数表背下来没用,你得先知道自己每天要处理多少张、QPS峰值是多少、机器规格是什么。

藏在元数据里的两个坑,比压缩算法更致命

第一个是EXIF Orientation。 手机竖着拍的照片,传感器实际存的是横图,靠EXIF里Orientation=6这个标记告诉阅读器“请旋转90度”。问题在于很多处理库默认不读这个标记。ImageMagick要加 -auto-orient,Sharp要写 .rotate()(不带参数的那种),Pillow更得手动 ImageOps.exif_transpose()。不加的后果就是公众号后台、某些网盘缩略图里,用户的照片全躺倒了。

图片

第二个是色彩空间。 iPhone从2016年的7开始默认拍Display P3,色域比sRGB宽大约25%。如果你的管线不做ICC转换,或者干脆像我那次一样用了 -strip 把ICC Profile一起删了,P3的图会被当成sRGB直接显示,红色和绿色会明显寡淡发灰。用户的投诉语通常是“传上去颜色变淡了”,你去查文件数值一个没变,非常难对线。

正确姿势是保留ICC Profile,或者显式转到sRGB:ImageMagick用 -profile sRGB.icc,Sharp用 .withMetadata() 配合 .toColourspace('srgb')。转换会掉一点色域,但至少所见即所得,不会在两台设备上打架。

我的独立结论:先缩尺寸,再谈压缩

图片

做了这几年图片处理,我最深的体会是——绝大多数人把精力花错了地方

大家热衷于讨论“用哪个软件修图”“哪个算法更先进”,但真正决定一个图片系统好坏的,是“有没有在正确的环节把图缩到正确的尺寸”。一张4000px的图每被解码一次,服务器上平均要吃掉60–120ms的CPU;一张预生成好的800px缩略图,同样一份CPU只要8–12ms,差了一个数量级。100万次图片请求,前者是20小时机时,后者2.5小时。压缩算法那20%的体积优化,在这个差距面前根本不值一提。

所以我的优化顺序是:先把尺寸管理做对(按容器最大宽度预生成多档,比如1920/1280/800/400/200),再把元数据做对(Orientation、ICC),最后才去纠结编解码格式。前三步做完,你的图片系统已经超过90%的同类产品了,剩下的那点体积,慢慢磨来得及。反过来先花两周折腾AVIF参数,结果缩略图还在临时解码4000px原图,那就是典型的用力用错了方向。