手机照片上传网站被压缩变糊怎么办?我拿12张4032×3024原图对比了Squoosh、TinyPNG、ImageMagick、FFmpeg和Sharp
先交代一下我的测试环境,不然后面参数没意义。机器是 2020 款 M1 MacBook Air,8GB 内存,macOS 14.5。素材是 12 张 iPhone 13 拍的 JPEG,原始尺寸 4032×3024,单张 3.1MB 到 4.6MB,总计 46.8MB。里面有 5 张室内暖光、3 张逆光窗边、2 张菜单文字、2 张夜景。我的目标不是“压到最小”,而是上传到自建 WordPress 后,在 1440px 宽的笔记本和 390px 宽的手机上看,不糊、不脏、不出现文字边缘彩边。这个目标听起来普通,但我试完发现,很多人把 quality 从 90 调到 80,根本解决不了发糊。
先给一个反常识结论:图片变糊,八成不是 quality 太低,而是“尺寸没先缩”和“色度子采样”在搞事。比如原图 4032×3024,网页里最大显示宽度只有 800px,但你直接上传原图,WordPress 或 CDN 又会生成 768、1024、1536 多个缩略图,浏览器再挑一个 1024 的显示。中间只要有一次用双线性缩放,再叠加 JPEG 的 4:2:0,红色按钮、衣服纹理、菜单小字就会先烂。我的做法是:先按 2 倍显示宽度定长边。博客正文图长边 1600px,卡片图长边 800px,头像 400px。超过这个尺寸的,先在本地重采样,别把 4032px 原图丢给 TinyPNG 或服务器。
具体工具对比,我先说 Squoosh。它跑在浏览器本地,拖进去 12 张,选 MozJPEG,Quality 78,Chroma subsampling 选 4:2:0,Resize 勾 1600,Method Lanczos3。12 张总输出 4.1MB,平均 342KB,肉眼看室内和逆光都行,但菜单文字那张在 100% 下有轻微彩边。优点是隐私好,不传服务器;缺点是一次拖太多会卡,我 8GB 内存的 Air 到第 9 张时风扇起来了。Squoosh 的 WebP 我也试了,Quality 78、Method 6、Sharp YUV 打开,平均 268KB,比 JPEG 小约 21%,但老 Safari 或部分微信内置浏览器兼容性还是得留 JPEG 兜底。
TinyPNG 是很多人搜“图片压缩”第一个碰到的。它免费版单张上限 5MB,一次最多 20 张,12 张上传加下载大概 38 秒,总输出 4.7MB,平均 392KB。它的强项是 PNG 和透明图,但 JPEG 压完有点“平”,菜单文字那张最明显,边缘像被抹了一下。而且它是上传到云端处理,公司内部素材、客户未公开产品图,我一般不用。这里插一句:TinyPNG 不是让你选 quality 的,你没法控制 4:4:4 还是 4:2:0,所以做教程可以,做可复现的批量流程不行。
命令行里,ImageMagick 和 FFmpeg 是我最常用的。ImageMagick 7.1.1-29 批量命令我写成:magick input.jpg -auto-orient -resize '1600x1600>' -strip -interlace Plane -sampling-factor 4:2:0 -quality 82 output.jpg。注意 -auto-orient 要在 -strip 前面,不然 iPhone 的 Orientation 元数据一删,竖图可能变横图。12 张总耗时 6.8 秒,平均 365KB。带文字的截图我会把 -sampling-factor 4:2:0 改成 4:4:4,体积涨到约 510KB,但文字边缘干净很多。FFmpeg 6.1.1 的命令是:ffmpeg -i input.jpg -vf "scale='min(1600,iw)':-2:flags=lanczos" -q:v 4 -map_metadata -1 output.jpg。这里 -q:v 4 大概相当于 JPEG quality 80 上下,不是 4% 质量,别被数字吓到。12 张耗时 4.2 秒,输出平均 350KB,速度比 ImageMagick 快一点,但精细控制不如 IM。
如果你写 Node 服务,Sharp 0.33.2 是我现在做批量上传的首选。代码大概这样:sharp(input).rotate().resize({ width: 1600, withoutEnlargement: true }).jpeg({ quality: 82, mozjpeg: true, chromaSubsampling: '4:2:0' }).toFile(output)。rotate() 不填角度会自动按 EXIF 方向转正,withoutEnlargement 防止小图被拉大。12 张耗时 2.9 秒,平均 358KB。它的坑是默认可能保留 ICC,如果原图是 Display P3,转成 sRGB 不处理,某些安卓浏览器会偏色。我的处理是加 .withMetadata({ icc: 'srgb' }) 或先统一转 sRGB。顺带说 WebP:cwebp -q 78 -m 6 -sharp_yuv -metadata none -resize 1600 0 input.jpg -o output.webp,平均 268KB;AVIF 用 avifenc --min 0 --max 63 -a end-usage=q -a cq-level=30 -a color=420 -a sharpyuv=1 -s 6 input.jpg output.avif,平均 210KB,但编码慢,12 张花了 41 秒,兼容性还要准备 fallback。
最后说一个我踩过的坑:不要先压缩再锐化,不要用“USM 锐化 200%”去救已经糊掉的图。正确顺序是:拿到原图,先 auto-orient,再缩到目标长边,然后做很轻的锐化,最后编码。我在 ImageMagick 里用 -unsharp 0x0.75+0.75+0.02,或者 Sharp 里 .sharpen({ sigma: 0.8 })。如果是截图文字,锐化可以到 sigma 1.0;如果是人像,别超过 0.7,不然毛孔和噪点全出来。还有 EXIF:删除 GPS、相机型号没问题,但版权和 ICC 要看你场景。自建站为了 GDPR,我会 -strip;商业图库反而要保留版权元数据。批量上传前,我固定做三件事:看长边、看是否带文字、看色彩配置文件。这三个判断比背 quality 85 还是 82 有用得多。