图片压缩后变模糊、颜色发灰?我批量处理3174张商品图的踩坑记录
先说结论我尽量少说,直接讲事。起因很简单:朋友开淘宝店,手机拍的图直接往上传,详情页在4G下打开要8秒多,他自己看后台数据说跳出率高得吓人。他让我帮忙批量处理一下,我想这不就是个resize吗,结果从晚上9点干到凌晨3点,前后返工三遍。下面是我真正踩到的坑,和最后跑通的参数。
一、先把“加工”拆开,不然你永远在返工
我统计了一下手头的素材:3174张,iPhone 13拍的HEIC,分辨率4032×3024,单张2.8~4.5MB,平均3.6MB。听起来是个体力活,但真正需要“创意加工”的——调色、去背景、换底色的——不到200张。剩下的全是四件枯燥的事:转格式、改尺寸、修色彩、清元数据。
很多人把权重搞反了。我自己实测下来,对最终观感的影响从大到小大概是:分辨率 > 压缩质量 > 锐化 > 调色。你把一张750px宽的图调得再通透,放到1440px的屏幕上照样糊;反过来,尺寸给够、质量给够,哪怕完全不动颜色,看起来也是“清楚”的。
所以第一步不是打开PS,是先问清楚这张图最终出现在哪:淘宝主图800×800还是1200×1200,详情页750px宽,微信朋友圈1080px长边,公众号封面900×383。目标尺寸定错了,后面全是白干。这一步花五分钟,能省你一晚上。
二、颜色发灰:这个坑卡了我两小时
批处理第三遍跑完,我打开输出文件夹,人有点傻——所有图都像蒙了一层灰。红色衣服尤其明显,原图是正的朱红,输出后变成暗红发土,白衬衫变成了米白。
问题不在压缩,在ICC配置文件被剥掉了。iPhone拍的照片默认带Display P3的色彩配置,Pillow保存JPEG的时候如果不显式传icc_profile,这个配置就丢了,浏览器只好按sRGB去解释那堆像素值,颜色自然偏。
# 保色彩:把原图的ICC原样带过去
img.save(out_path, 'JPEG', quality=88, subsampling=0,
icc_profile=img.info.get('icc_profile'))
# 或者干脆统一转成sRGB(多数电商平台要求这个)
from PIL import ImageCms
srgb = ImageCms.createProfile('sRGB')
img = ImageCms.profileToProfile(img, img.info.get('icc_profile'),
srgb, outputMode='RGB')
ImageMagick那边对应的是 -profile /path/sRGB.icc,但注意 -strip 要放在 -profile 前面,顺序反了配置文件会被清掉——这个我真试过,出来的图还是灰的,查了半天才发现是顺序问题。
三、图歪了?EXIF方向和HEIC解码
第二个莫名其妙的bug:一部分竖着拍的照片,处理完全躺下了。原因是有个东西叫EXIF Orientation,手机拍照时传感器方向固定,靠EXIF里的一个标记告诉阅读器“请旋转90度”。你在系统相册里看是正的,但代码里读进来是没转过的原始像素。
from PIL import Image, ImageOps
img = ImageOps.exif_transpose(img) # 必须放在resize之前
放前面和放后面差别很大:放后面等于你按错的方向缩了一遍再转正,长宽比都拧了,出来的图要么被裁要么模糊。
HEIC解码要装 pillow-heif,新版直接 register_heif_opener() 就行,老版本API不一样,网上抄的代码经常对不上,建议看一眼自己装的是哪个版本再复制。
四、先缩后锐,顺序反了就是白干
这是我的核心观点,也是最容易被忽略的一条:锐化永远放在缩放之后。先锐化再缩小,等于把锐化出来的高反差边缘重新采样一遍,好处全被平均掉了,还多出一圈振铃。
缩放的滤波器也有讲究。默认的双线性(BILINEAR)缩图会偏软;LANCZOS更锐,但缩得太狠(超过3倍)时容易在边缘出现一圈白边;BICUBIC相对保险。我的做法是:能整数倍缩就先 img.reduce(2) 走一次,剩下的零头再用LANCZOS收尾。
锐化的参数(8位通道下):radius 0.5~0.8,amount 60%~90%,threshold 2~4。缩得越狠,amount反而要给得小一点,不然噪点会被一起放大。这条我一开始也搞反了,设了150%,结果商品图的布纹全变成了雪花。
五、格式和参数:一张表说清楚
| 参数 | 我的取值 | 说明 / 代价 |
|---|---|---|
| JPEG quality | 88(图片带文字)/ 82(纯色块) | 低于75开始出现块状伪影 |
| 色度抽样 | 4:4:4(subsampling=0) | 比4:2:0大10%~20%,但红字不糊 |
| 目标宽度 | 主图1200px,详情页750px | 别把4032px的成品传上去 |
| WebP quality | 80 | 同等观感下比JPEG小25%~35% |
| PNG有损 | pngquant --quality=70-90 --speed 1 | 能减60%~70%体积 |
| 元数据 | 保留版权 + 必须清掉GPS | 别把家里坐标一起传上去 |
实测数字:原图3.6MB → JPEG quality 88、4:4:4、1200px宽,平均210KB,缩到原来的5.8%。换WebP quality 80,平均138KB,肉眼看不出差别。注意WebP不是哪都能传,有些平台的上传接口还是只吃JPEG,别一股脑全转WebP。
六、工具横评:我最后留下了三个
| 工具 | 批量 | 色彩管理 | 元数据 | 上手成本 | 我会拿它做什么 |
|---|---|---|---|---|---|
| Photoshop 动作/批处理 | 中 | 强 | 强 | 高 | 少量需要精细抠图的 |
| ImageMagick | 强 | 强 | 强 | 中 | 服务器上跑批,配shell |
| Python + Pillow | 强 | 中(要自己写) | 中 | 中 | 逻辑复杂的,比如按文件名分目录 |
| Squoosh(网页) | 弱 | 弱 | 弱 | 极低 | 临时压一两张 |
| sharp(Node) | 极强 | 中 | 中 | 中 | 接在构建流程里自动跑 |
我的组合是:ImageMagick做第一道批处理(转格式、缩放),Python脚本处理那些需要按规则分目录、加白底的,最后人工只挑那200张精修。3174张图,全流程跑完大概12分钟,其中机器占了11分半。
七、最后送你一份可以直接抄的清单
- 先确定目标尺寸和投放到哪个平台,再动手。
- 转格式之前确认ICC要不要带、要不要转sRGB。
exif_transpose放在resize之前。- 缩放 → 再锐化,绝不反过来。
- JPEG带文字用quality 88 + subsampling=0。
- 有透明通道才用PNG,否则一律JPEG或WebP。
- 批量前先拿5张试跑,肉眼对比之后再全量跑。
- 发布前清GPS定位,用
exiftool -gps:all= -overwrite_original。
有个反常识的点最后说一下:别迷信“原图”。你手机里的JPEG本身已经是有损编码过的产物,所谓无损压缩只是“不在这一次再损失”,不代表它干净。真正决定观感的,是你把它放到正确尺寸、正确色彩空间、正确压缩率上——这几件事做对了,图就赢了八成的加工。