图片压缩基础:有损压缩 vs 无损压缩,JPEG、PNG、WebP 在嵌入式中的取舍
做嵌入式 GUI 这几年,我踩过最大的坑之一,就是「图片格式选错了」。
你想想看,一块 320×240 的屏幕,如果全用未压缩的 BMP,一张图就能吃掉 225KB 的 Flash。放个启动动画,十几张图下去,2MB 的 Flash 直接见底。更别提解码速度——有些低端 MCU 光读 BMP 就能卡半秒。
所以,压缩不是「要不要」的问题,而是「怎么选」的问题。
这一章,咱们就把 JPEG、PNG、WebP 这三兄弟在嵌入式环境下的底裤扒干净。我会结合自己踩过的坑,告诉你什么时候该用谁。
2.1 有损 vs 无损:本质区别在哪?
先说人话版本:
- 有损压缩——扔掉人眼不太敏感的信息,换体积。你盯着看可能觉得「还行」,但放大看细节已经糊了。
- 无损压缩——像素一个不丢,但压缩率有限。适合文字、图标、UI 控件这些「差一个像素就变味」的场景。
我在一个医疗设备项目里就吃过亏。客户要求屏幕截图必须能看清 6 号字体的小字,我一开始用了 JPEG 压缩,结果字边缘全是振铃效应,根本没法看。后来换成 PNG,问题才解决。
| 特性 | 有损压缩 | 无损压缩 |
|---|---|---|
| 像素还原度 | 不完全还原 | 100% 还原 |
| 压缩率 | 高(通常 5:1 ~ 20:1) | 低(通常 2:1 ~ 5:1) |
| 适用场景 | 照片、渐变背景、自然图像 | 文字、图标、UI 控件、医疗影像 |
| 解码复杂度 | 较高(需要 IDCT、Huffman) | 较低(主要是 LZ77 + 过滤) |
2.2 JPEG:老将,但别乱用
JPEG 在嵌入式里是个「又爱又恨」的存在。
爱的是它压缩率确实高。一张 640×480 的照片,BMP 要 900KB,JPEG 质量 85 压下来可能就 60KB。省了 15 倍的空间。
恨的是它的解码开销。我做过一个 STM32F429 的项目,跑 libjpeg-turbo 解码一张 800×480 的 JPEG,花了 180ms。用户按一下按钮,等 180ms 才看到下一张图——这体验,说实话挺糟糕的。
JPEG 在嵌入式里的典型用法:
- 照片浏览——如果设备有相册功能,JPEG 是唯一选择
- 启动画面——只解码一次,存到显存里,后面不再解码
- 网络传输——从云端拉图,JPEG 体积小,省流量
但千万别用 JPEG 存:
- UI 控件(按钮、滑块、图标)——边缘会糊
- 文字截图——字会「长毛」
- 需要二次编辑的图片——每次重压缩都会损失
2.3 PNG:无损之王,但体积是硬伤
PNG 在嵌入式 GUI 里其实用得比 JPEG 多。为什么?因为大部分嵌入式 UI 都是「扁平化设计」——大色块、少渐变、多文字。这种图 PNG 压缩率反而比 JPEG 高。
举个例子:一张 240×240 的纯蓝色按钮,PNG 压完可能只有 2KB,JPEG 反而要 8KB(因为 JPEG 不适合平坦区域)。
PNG 的压缩原理说白了就是两步:
- 过滤(Filtering)——把像素值转换成差值,让数据更「平」
- Deflate 压缩——用 LZ77 + Huffman 把差值数据压紧
我个人习惯在嵌入式项目里用 PNG 存所有 UI 资源。但有个前提——必须用 索引色模式(PNG-8),而不是真彩色(PNG-24)。
| PNG 子类型 | 色深 | 典型体积 | 适用场景 |
|---|---|---|---|
| PNG-8(索引色) | 8-bit(256 色) | 很小 | 图标、按钮、小控件 |
| PNG-24(真彩色) | 24-bit(1600 万色) | 较大 | 照片级背景(不推荐) |
| PNG-32(带 Alpha) | 32-bit(含透明通道) | 最大 | 需要半透明效果的 UI |
2.4 WebP:新秀,但嵌入式支持参差不齐
WebP 是 Google 搞出来的格式,号称「比 JPEG 小 30%,比 PNG 小 50%」。说实话,在 PC 和移动端它确实香。但在嵌入式里……嗯,情况有点复杂。
WebP 有两种模式:
- 有损 WebP——基于 VP8 帧内编码,压缩率比 JPEG 高
- 无损 WebP——类似 PNG,但用了更先进的预测算法
我在一个基于全志 V3s 的 Linux 嵌入式项目里试过 WebP。解码库用的是 libwebp,解码速度比 libjpeg-turbo 慢大概 20%,但体积确实小了。不过,在裸机或 RTOS 环境下,WebP 的解码器移植起来挺麻烦的——内存占用大,代码量也大。
| 对比项 | JPEG | PNG | WebP |
|---|---|---|---|
| 压缩率(照片) | 高 | 低 | 更高 |
| 压缩率(UI 图) | 低 | 高 | 高 |
| 解码速度 | 快 | 中等 | 中等偏慢 |
| 解码 RAM 需求 | 中等(~50KB) | 低(~20KB) | 高(~100KB+) |
| 库体积 | 小(libjpeg-turbo ~100KB) | 小(libpng ~80KB) | 大(libwebp ~200KB) |
| 嵌入式生态 | 成熟 | 成熟 | 一般 |
2.5 嵌入式场景下的选型决策树
说了这么多,到底怎么选?我画了一张图,把决策逻辑串起来了。
2.6 我的实战建议
做了这么多年嵌入式 GUI,我总结了一套自己的「选型三板斧」:
- UI 资源(图标、按钮、背景)——全部用 PNG-8。颜色数控制在 128 色以内,用 TinyPNG 再压一遍。这是最稳妥的方案。
- 照片类资源——如果 MCU 主频 > 200MHz 且有硬件 JPEG 解码器,用 JPEG。否则,在 PC 端预解码成 RGB565 的 RAW 格式,直接烧进 Flash。
- WebP 暂时别碰——除非你的项目跑 Linux 且有足够 RAM(> 64MB),否则 WebP 带来的体积优势会被解码开销吃掉。