跳到正文

技术资料里的图片怎么压缩:体积、文字和动画的取舍

整理一批离线技术资料时,正文已经是 Markdown,图片也下载到了本地。接着盘点体积,38,461 个独立图片文件一共占了 11.82 GB。

这些资料要跨设备保存,图片就成了比正文更重的负担。最省事的设想,是把它们统一转成一种格式,再降画质、缩尺寸。如果能压到原来的 1/20,就只剩约 591 MB。

我需要保住的是图里的技术信息。照片少一点纹理,通常还能看;代码截图少一个小数点、公式下标糊成一团,资料的意思就可能变了。压缩率再好,也补不回读者无法辨认的内容。

我先做了一轮抽样比较。结果比预想的更分散:有些图无损编码就能缩小一半,有些原本已经压得很好,换格式反而更大;最激进的方案确实能把长截图压到十几 KB,但中文字也已经读不清了。

先看图片里保存了什么

按实际文件格式统计,这批资料主要由 JPEG 和 PNG 构成:

格式 文件数 体积
JPEG 20,968 7.116 GB
PNG 16,411 3.291 GB
GIF 499 1.324 GB
WebP 582 0.088 GB
SVG 1 660 字节

这里的格式只是编码方式,不能直接当成内容分类。JPEG 里也有代码截图,PNG 里也有照片。真正影响选择的,是图里哪些细节必须留下。

公式、命令、表格和流程图经常依赖细笔画、上下标、小数点、箭头与颜色。照片和绘画更多依赖纹理、渐变和主体轮廓。动图还多了一条时间轴,不能只看某一帧清不清楚。

487 个文件是动图,共 1.323 GB。宽度超过 2400 的图有 6,229 张,共 4.372 GB。这两部分都值得检查,但不能由此推导出「动图抽几帧」「宽图统一缩小」的处理规则。

用样本比较,再估算整库

这轮测试先按实际格式、文件大小和宽高比分层,固定种子抽取 127 张静态图,再加入 6 张大文件做压力测试,另按大小分层抽取 12 个动图,总共 145 个样本。

大文件压力样本用于暴露问题,不参与整库容量外推。否则,专挑几张特别能压的大图,很容易得到过于乐观的数字。

编码使用 Sharp 0.35.5,本机底层是 libvips 8.18.7、libwebp 1.6.0 和 AOM 3.15.1。静态样本比较无损、近无损、不同画质的 WebP 和 AVIF,也比较限制宽度与限制最长边两种缩放方式。编码和检查都在本地完成,没有把资料上传给在线压缩服务。

「近无损」允许在编码前调整部分像素来改善压缩,因此不保证逐像素相同。它与后面提到的无损编码计算参数是不同的机制,见 libwebp 的参数说明。

整库估算按每层的样本输出/输入字节比,乘以这一层的实际总字节。变大、校验失败的候选按原图大小计算;未测试的大图、超大动图和特殊色彩文件也保留原大小,约有 1.42 GB 没有计算压缩收益。

方案 整库估计体积 预计节省
原尺寸像素无损 9.56 GB 19.1%
原尺寸 WebP 90 为主 5.48 GB 53.7%
原尺寸 WebP 82 为主 4.85 GB 59.0%
原尺寸 AVIF 75 为主 4.83 GB 59.2%
AVIF 40,并限制最长边为 1600 2.81 GB 76.2%

当前体积与不同压缩策略的样本估算对比

表里的数字是样本外推,不是全库转换后的实测体积。各方案都允许回退原图;AVIF 方案还允许回退 WebP。有损方案的动图使用相同的 WebP 85/无损候选,无损方案只使用无损候选,不能理解为把整库强制转成表头中的一种编码。

对 WebP 90 方案做 500 次层内重采样,结果范围约为 4.58–6.68 GB。每层样本不多,这个范围只提醒我们估算有波动,不构成容量上限,更不能证明所有图片都可读。详细参数和汇总数据见测试口径弹窗。

保持尺寸,已经能省不少空间

比较中,一个很实用的发现是:不必先缩图,原尺寸编码就有明显收益。

一张 2552×870 的彩色日志截图,原文件 809.5 KiB,原尺寸 WebP 90 为 331.1 KiB。命令、颜色和标点仍需要放大对照,但至少没有先丢掉分辨率。

另一张 1920×1078 的流程图,原文件 104.6 KiB,无损 WebP 为 49.2 KiB,比 WebP 90 的 53.6 KiB 还小。一张终端命令截图也有类似结果,无损候选 23.6 KiB,有损候选反而是 33.9 KiB。

所以我没有把「有损」当成「一定更小」。候选通过验证后,还要和原文件、无损版本比较体积。有些图最合适的选择,就是保持原样。

样本里已有一张 89.1 KiB 的 WebP 表格,重新编码后,无损版本变成 89.2 KiB,90 档增至 153.2 KiB。它不需要再处理一次。文件换了后缀、工具成功退出,都不能说明完成了有效优化。

无损只表示对当前解码像素的保留。已经存在于 JPEG 里的模糊和压缩痕迹,不会因为转成无损 WebP 而恢复。

无损模式下,参数名也容易让人误会。libwebp 的说明区分了 -q 的两种作用:有损模式中影响画质与体积,无损模式中则影响编码速度与压缩程度。无损时把它从 100 调到 75,并不等于让图片只剩「75 分画质」。

我用 8 张图补测,两档都保持完整解码像素一致。75 档的输出总字节增加约 0.4%,本机累计编码耗时从约 14.5 秒降到 7.5 秒。这是小样本的速度取舍,不是全库固定提速保证;对上万张图片,它比不加区分地把所有参数调到最大更实用。

长截图不能照搬照片的缩放参数

这轮最明显的反例,是一张 1080×11520 的长截图。

原文件约 988.1 KiB。把最长边限制为 1600 后,输出变成 150×1600,只有 17.3 KiB。体积看起来非常漂亮,但宽度只剩原来的约七分之一,中文已经无法正常阅读。

问题出在缩放比例。长图的高度先触及上限,宽度也按相同比例缩小:1080 × 1600 ÷ 11520 = 150。

限制最长边与只限制宽度,对长截图产生不同的尺寸结果

如果确实要限制长图尺寸,应该先确定需要保住多宽的文字,再考虑只限制宽度,并禁止放大。Sharp 的 resize 参数可以分别表达这两种意图:

// 限制宽高,按比例缩进 1600×1600;长图的宽度也可能大幅缩小。
image.resize({ width: 1600, height: 1600, fit: 'inside', withoutEnlargement: true });

// 只限制宽度。原图宽 1080,不放大时无需缩放。
image.resize({ width: 1600, withoutEnlargement: true });

这段是缩放选项的对照示例,不是完整批处理程序。对于文字截图,原尺寸仍是更稳妥的起点;只限制宽度,也不能代替阅读检查。

那张长截图原尺寸 WebP 90 还出现了增大,变成 1041.0 KiB。82 档缩到 783.8 KiB,但节省的空间是否值得再次有损编码,需要结合小字判断,不能看到「长图」就自动降低画质。

动图要比较完整时间轴

动图的帧数减少,并不必然意味着画面丢失。

一个终端演示 GIF 有 146 帧、14.6 秒,大小为 2,502,516 字节。无损 WebP 变成 72 帧,大小为 319,316 字节,节省 87.2%。完整时间轴检查显示,减少的是连续重复的源画面,时长、循环设置及对应时刻的画面都保持一致。

用一个 400 毫秒的简化样例就容易理解。前两个 100 毫秒显示相同画面,合成一个持续 200 毫秒的帧,播放内容没有变化:

连续相同画面合并后,帧数减少但每个时间区间的画面与总时长不变

检查时要沿两份动画的帧延迟划分时间区间,再比较对应区间的完整画面。无损候选要求像素一致,还要检查透明度、总时长和循环次数。只核对文件能打开、首帧相同,或者源帧数与输出帧数相等,都不够。

这轮也遇到了改变总时长、合并不同源画面的候选,它们都被保守地排除,保留原动画。不能把某一个 GIF 节省 87% 的结果,推广成所有动图的固定压缩率。

像素检查之外,还要看显示结果

解码得到的像素值相同,也不一定足以说明阅读器中的显示结果相同。色彩配置、透明度及解码和渲染过程,都可能影响最终画面。

ICC 配置描述像素值与颜色之间的对应关系,PNG 还可能独立携带明暗响应(gamma)、色度坐标等信息。格式转换时需要检查它们怎么处理,而不只是比较 RGB 缓冲。Sharp 的 keepMetadata()有明确的保留范围,但它不承诺原容器字节不变,也不能据此假定所有 PNG 文本信息都会随转换保留。

我对 133 张静态样本追加了 Chrome sRGB 画布检查。132 张无损候选的显示 RGBA 与原图完全相同,另 1 张出现了最大为 2 的通道差异。这点差异很轻微,但既然暂时没有定位到具体的解码或渲染原因,就没有把它归入「显示完全一致」,也没有直接认定是色彩配置丢失。

另一项限制来自阅读工具。当前本机 Chrome 能显示测试中的 AVIF,Codex 图片查看工具却返回不支持格式的错误;同一张图的 WebP 可以直接看。这个结果只说明当前工具链的兼容情况,不代表所有 Agent 或未来版本都不支持 AVIF。

这些资料既供人阅读,也供 Agent 参考。相比再省一点体积,我更在意常用阅读入口能直接打开,因此这次默认采用 WebP,保留 AVIF 的比较数据。

全量应用时,先保住可回退性

样本中的原尺寸 WebP 90 方案,是比较容量潜力的基线。实际批量应用采用更保守的规则:

  • PNG、已有 WebP 和动图先尝试无损,静态无损候选还要通过浏览器显示像素检查。
  • 普通 JPEG 尝试原尺寸 WebP 90;小文件、小尺寸图、较矮的图和宽高比超过 3 的图先尝试无损,避免给公式、命令行和长截图重复有损编码。
  • 候选至少节省 3% 且不少于 4 KiB,才继续考虑替换;尺寸、透明度、ICC 和方向检查不通过时保留原图。
  • 16 位图、超出解码预算的图片、超出 WebP 单边尺寸上限的图片,以及尚未验证的特殊色彩类型保留原件,不通过缩图绕过这些限制。

这是保守的格式与几何代理规则,不是文字识别器。JPEG 仍可能是截图,质量指标也不能保证每一个字符都没有受到影响,所以需要保留原件并抽样复核,不能把一套自动过滤说成逐图阅读验收。

有损候选还计算像素误差,低于质量门槛时不采用。这里使用的峰值信噪比(PSNR)由像素均方误差换算而来,分数较高通常表示平均误差较小,但它不能回答「负号还看不看得见」。对缩放候选来说,如果只与同尺寸参考图比较,高分甚至无法体现原分辨率已经丢失。

批处理因此拆成了两个阶段。先在暂存目录生成候选,记录输入指纹、参数、输出指纹和验证结果;完整一轮处理结束后,才更新正式媒体清单与正文引用。中途中断可以复用已验证的结果,恢复时仍要重新检查输入与候选是否匹配,不能只相信文件存在。

图片转成新的 WebP 后,文件名按新内容指纹生成。来源指纹与派生文件指纹分开保存,不能继续把有损产物记成原始下载文件。正文、可逆改写记录和媒体清单一起进入可恢复写入流程;旧下载日志也要识别已经批准的派生记录,避免续传时把旧路径写回来。

最后保留旧图片的本机备份。内容寻址会让新图片复用已有文件,因此还要检查最终引用:仍被其他条目使用的文件留在资料库,不能在备份旧图时直接移走。备份保留期间,正式资料库的使用体积会下降,但本机磁盘总占用不会按相同比例下降,还要算上原件和暂存文件。这是保留回退能力的真实成本,不应该从压缩报告里省略。

整库处理的实际结果

2026 年 10 月 26 日完成应用后,正式图片从 11.82 GB 降到了 6.95 GB,实际减少约 4.86 GB,41.2%。所有采用的候选都保持原尺寸。

项目 结果
压缩前输入文件 38,461 个
采用更小候选 29,300 个
保持原样 9,161 个
进一步内容去重后的正式文件 38,383 个
正式图片总字节数 6,954,690,959

文件变少来自派生内容去重,不是删除正文里的图片。16,261 个输入采用无损编码,其中 16,011 个静态文件通过完整像素与浏览器显示比较,250 个动图通过完整时间轴检查。另有 13,039 个 JPEG 采用原尺寸 WebP 90,最低 PSNR 为 40.00007,仍在设定门槛之上。

保留原图也占了相当一部分结果。5,693 个文件因为收益不足或文件很小而不改;1,176 个没有通过像素或质量检查,另有 1,177 个无损候选在浏览器中出现显示像素差异。其余还涉及解码预算、位深、色彩类型、元数据及动画时长或循环参数等限制。没有完成原因隔离前,我不会把所有显示差异都归因于某一种色彩信息。

这解释了为什么没有达到样本里 5.48 GB 的点估计:实际选择了更保守的对象分类,并对完整输入逐图检查、回退。两者不是相同策略,不能把差额全解释成抽样误差。容量规划需要以最后采用的文件为准。

完成后又对最低分数的 4 张 JPEG 做了左上角与中心的原像素局部对照。能看见有损编码带来的细微色块和边缘变化,所查样本中的表格、数字、文字及箭头仍可辨认。这是对程序检查的补充,不能替代全库人工阅读。

本轮还保留了约 8.52 GB 原件备份和 3.84 GB 候选缓存,尚未清理。6.95 GB 只表示正式图片体积,不能用它描述当前磁盘总占用。压缩前文件与正式文件也已分别重新核对全部指纹,便于后续确认采用了什么、原件在哪里。详细计数与参数见全量压缩结果弹窗。

总结

这批图片最终从 11.82 GB 减到 6.95 GB,所有采用的候选都保持原尺寸,另有 9,161 个文件保留原样。主要收益来自编码选择,不能压或不值得压的图片也构成了最后的容量结果;无损、有损和保留原图都需要有自己的采用条件。

技术图的条件要落到具体信息上:长图保住文字宽度,动图保住完整时间轴,像素比较之外还要检查色彩、透明度和阅读器兼容性。公式、小字和细线是否可辨认,不能只靠体积或质量分数判断。样本帮助选择方向,最终容量仍以完整处理和回退后的文件为准。

长期维护还要求派生图片、正文引用与媒体清单一致,并留下可验证的原件备份。正式图片变小,不代表备份与缓存占用也随之消失。这轮结果尚不足以支持「全库压到原来的 1/20,同时保持技术图可读」这个容量目标。

图片压缩测试口径与参数

打开原文件 benchmark-summary.json

测试采集于 2026 年 10 月 25 日,范围是 38,461 个去重图片文件,总计 11,818,071,086 字节。127 个静态随机样本和 12 个动图样本用于分层估算,6 个大文件压力样本只用于检查风险。

静态样本尝试 9 个候选,动图尝试 3 个,共 1,233 个候选。1,177 个通过基础校验,56 个因透明度、色彩配置、方向或时间轴检查不符而未被采纳。基础校验与阅读验收分开,未覆盖图片按原大小计算。

静态样本限定为 8 位、2400 万像素以内;动图累计解码像素不超过 1 亿。浏览器检查使用隔离的 Chrome 155.0.8059.85,不使用日常登录状态。

无损 WebP 抽样使用 lossless:true, quality:100, effort:6, exact:true;原尺寸有损 WebP 使用 quality:90/82, effort:4, smartSubsample:true, alphaQuality:100, exact:true。AVIF 使用 effort:3,75/60 档为 4:4:4,激进 40 档为 4:2:0。这些 quality 数字是各编码器自己的参数,不能跨格式直接对等,定义见 Sharp 输出参数。全量应用的无损候选使用 effort:4,包含 75 和 100 两档已经验证的结果;它们保留同样的像素,计算投入不同,输出大小也可能不同。

汇总文件仅保留统计、参数和匿名样本数据,不提供第三方课程原图或全文。正文配图为自制统计与机制示意,不是源课程截图。

全量压缩结果与校验统计

打开原文件 application-summary.json

应用于 2026 年 10 月 26 日完成,38,461 个输入文件中采用 29,300 个候选、保留 9,161 个原文件。进一步内容去重后有 38,383 个正式文件,共 6,954,690,959 字节,实际减少 41.15%。

统计包含程序逐图校验的计数,不代表每张图都已人工阅读。保留原因按每文件最先触发的条件计数;原件备份和候选缓存另计,不混入正式图片体积。汇总不包含第三方课程原图、原始 URL 或本机路径。