先给结论:我现在所有配图固定 1200px 宽 + q82 + method=6,单图红线 80KB。
这三个数不是抄来的。9 月 28 号晚上张工连着问了两遍「这图到底有没有被压缩」,我嘴上保证没用,得拿数字。拿一张刚生成的 1536px 图,先缩到 1200px,再从 q95 一路压到 q60,每档记体积和 PSNR:
| quality | 1200px 体积 | PSNR |
|---|---|---|
| q95 | 65.1 KB | 36.9 dB |
| q90 | 51.3 KB | 36.7 |
| q85 | 43.9 KB | 36.3 |
| q82 | 40.9 KB | — |
| q80 | 38.4 KB | 36.1 |
| q75 | 34.3 KB | 35.3 |
| q60 | 30.5 KB | 34.9 |
测的是一张中英混排的信息图,不是照片。换一类图,这张表就得重测。
q95 到 q85 是白赚的
65.1KB 掉到 43.9KB,省了三分之一,PSNR 只掉 0.6dB。
这段没有任何代价。还在用 q95 存网页配图的,等于白扔三分之一的流量。q90 那行更没理由选 —— 51.3KB / 36.7dB,两边都不占优。
单看这张表会选 q75,那是错的
q85 往下再压到 q75,还能省 22%。PSNR 35.3dB。
35dB 放在任何一份「PSNR 多少算可接受」的资料里都算过关,通常说法是 30dB 以上人眼难辨。数字不难看,体积又小了近 10KB。我一开始就是顺着数字往下看的。
把这些图按正文实际宽度并排摆开,q75 的文字边缘开始毛了 —— 笔画边上浮出一层浅色糊边,字号越小越明显。图上的字糊了,这张图就没有存在价值了。
PSNR 为什么没报警?我的理解是它算全图逐像素误差的平均,而我这些图大片是纯色背景(背景色是 RGB(22,27,34),一张图里能占六七成),边缘那几千个像素的劣化被分母摊平了。这块我没深究,只是能对上现象。
能用的一句话结论是:对文字密集的图,PSNR 只能看趋势,不能当判据。 真正的判据就一条 —— 按显示宽度并排看字。
再往下 q60 是 30.5KB,比 q75 只小 3.8KB,画质继续掉,不值。
最后停在 82 也没什么深奥理由。q85 和 q82 差 3KB,画质区别我看不出来,就往低取了一格。表上 q82 那行 PSNR 是空的,因为它是最后定的档位,定完直接用上了,没回头补测。
宽度比质量更能决定体积
同一张图固定 q85,只改宽度:
1536px = 58.4KB 1200px = 43.9KB 1000px = 34.3KB 900px = 29.7KB 800px = 25.3KB
站点正文实宽约 800px。按「够用就行」,800px 就该是答案,比 1200 省 42%。
我没选它。这些图是文字密集的信息图,不是照片。压到 1x 在高分屏上一拉伸就糊,糊了字就白画了。1200 是 800 的 1.5x,是个折中:高分屏下不至于明显发虚,体积还能压在 45KB 上下。
照片往 800-1000 压没事,文字图不行。这是两类图的分界线。

定完就写进脚本,别每次手试
固化成一个 to_web():PNG 出来之后自动缩到 1200、转 WebP、量体积,超 80KB 直接标红。
WEB_WIDTH = 1200
WEB_Q = 82
WEB_MAX_KB = 80 # 单图红线,超了就报警
im.save(out, "WEBP", quality=WEB_Q, method=6)
kb = os.path.getsize(out) / 1024
flag = "" if kb <= WEB_MAX_KB else " ← 超红线!"
python gen_images.py # 生成 PNG 原图(1536px)
python gen_images.py to_web # 缩到 1200px、转 WebP q82、报体积
python gen_images.py --no-web # 只要 PNG,不压缩
method=6 是把压缩强度拉满,出图慢一些、体积再小一些。反正是本地跑一次,慢得起。
红线不是为了省流量,是兜住我自己的偷懒。图越画越复杂、元素越堆越多,体积会悄悄涨上去,涨到 80KB 就被拦一下。

那批图最后 3 张合计 116.2KB(40.9 / 38.0 / 37.2),线上 Content-Type 是 image/webp。对比最早那版生图的 3809KB,省了 97%。115-134KB 的 PNG 原图只留在本机,压过的那版才上传。
这些图都是 Pillow 画的,画的时候撞过三个字体坑,那篇里记了。上线清单那篇里我把「图片转 WebP」随手写成 ≤150KB,那个数是拍脑袋的,回看那篇才发现该改成 80KB。
PSNR 摊薄那部分我到现在也只是猜测。真要较真得看量化表怎么作用在边缘块上,我没往下挖。