图像基础与色彩空间:RGB、RGBA、索引色、YUV
做嵌入式GUI,说白了就是跟像素打交道。但像素怎么存、怎么传,这里头的门道可不少。我刚开始做嵌入式图形时,总觉得把RGB数据扔给屏幕就完事了,结果项目一上资源就爆了——Flash不够、RAM不够、刷屏还卡。后来才明白,选对色彩空间,比压缩算法本身还重要。
这一章,咱们就把色彩空间这件事彻底聊透。你想想看,一张图片在内存里到底长什么样?为什么有的格式能压到1/10,有的却越压越大?嗯,答案就在色彩空间里。
1. RGB:最直观,也最占地方
RGB是咱们的老朋友了。红绿蓝三个通道,每个通道8位,组合出一个像素。一个像素就是24位,也就是3个字节。
举个例子,一张320×240的图片:
320 × 240 × 3 = 230,400 字节 ≈ 225 KB
这在PC上不算什么,但在嵌入式设备上——尤其是那些只有几百KB Flash的单片机——这就是灾难。我记得有一次做智能手表项目,光一张开机logo就占了将近200KB,整个Flash才1MB,剩下的空间连字库都放不下。
RGB的优点是直观,缺点是冗余太多。人眼对亮度的敏感度远高于颜色,但RGB三个通道却一视同仁地存了全部信息。说白了,它没有利用人眼的视觉特性。
核心结论:RGB适合作为显示终端的内部格式,但不适合作为存储和传输格式。在嵌入式GUI中,除非你内存管够,否则别直接用RGB存资源。
2. RGBA:多了一个Alpha通道
RGBA就是在RGB的基础上加了一个Alpha通道,用来表示透明度。每个像素变成4个字节:R、G、B、A各8位。
你可能会问:透明度有什么用?我举个实际场景——图标。一个带圆角的按钮图标,如果只用RGB,背景必须是纯色,否则边缘会有锯齿。但用RGBA,背景可以透明,图标就能叠加在任何界面上。
代价也很明显:内存占用又多了33%。320×240的RGBA图片:
320 × 240 × 4 = 307,200 字节 ≈ 300 KB
我曾经在一个项目里犯过傻——把所有图标都存成RGBA,结果资源包比预期大了两倍。后来发现,很多图标根本不需要透明度,用RGB加一个1位掩码就能搞定。嗯,这里要注意:不要为了“万一需要”而滥用Alpha通道。
| 格式 | 每像素位数 | 每像素字节 | 320×240大小 | 适用场景 |
|---|---|---|---|---|
| RGB888 | 24 | 3 | 225 KB | 全彩显示,无透明度需求 |
| RGBA8888 | 32 | 4 | 300 KB | 需要透明度叠加 |
| RGB565 | 16 | 2 | 150 KB | 嵌入式常用,节省一半内存 |
我的建议:在嵌入式GUI中,优先考虑RGB565。人眼对颜色精度的感知有限,16位色在大多数场景下已经足够。我做过对比测试,同一张照片用RGB565和RGB888显示,90%的用户看不出区别。
3. 索引色:用调色板换空间
索引色是嵌入式GUI的“老朋友”了。它的思路很简单:不存每个像素的颜色值,而是存一个颜色编号。
举个例子,一张图只有256种颜色。那我们就建一个调色板,里面存这256种颜色的RGB值。每个像素只需要存一个8位的索引值,指向调色板里的某个颜色。
这样一来,每个像素从3个字节变成了1个字节,压缩比直接到3:1。再加上调色板本身只有256×3=768字节,几乎可以忽略不计。
原始RGB:320 × 240 × 3 = 225 KB
索引色: 320 × 240 × 1 + 768 = 75.75 KB
节省了约 66% 的空间
我在做温控器界面时就用过这个技巧。那个设备的屏幕只有128×64,颜色不超过16种。用索引色后,整个界面的资源从50KB降到了不到10KB。你想想看,对于只有64KB Flash的单片机来说,这省下来的40KB能多放多少功能代码?
但索引色也有局限:颜色数量受限。256色对于照片来说可能不够,但对于图标、按钮、文字界面来说,绰绰有余。
避坑指南:我曾经在一个项目里把一张渐变色背景图转成索引色,结果出现了明显的色带。后来我改用抖动算法(Floyd-Steinberg)来模拟渐变,效果好了很多。记住:索引色适合色块分明的图形,不适合平滑渐变的照片。
4. YUV:把亮度跟颜色分开
YUV跟RGB最大的区别是:它把亮度(Y)和色度(U、V)分开了。人眼对亮度变化很敏感,但对颜色变化没那么敏感。所以我们可以对色度通道进行降采样,而亮度通道保持完整。
最常见的格式是YUV420,它每4个像素共享一组UV值。压缩比是多少?咱们算一下:
Y通道:每个像素8位,共 320×240 = 76,800 字节
U通道:每4个像素共享8位,共 320×240/4 = 19,200 字节
V通道:同上,19,200 字节
总计:76,800 + 19,200 + 19,200 = 115,200 字节 ≈ 112.5 KB
对比RGB的225KB,YUV420直接省了一半。而且视觉质量几乎不受影响——因为人眼根本察觉不到色度信息的丢失。
我记得在做视频播放器项目时,解码器输出的就是YUV420数据。如果转成RGB再显示,不仅多了一步转换,内存还要翻倍。后来我直接在LCD驱动里做了YUV到RGB的硬件转换,刷屏速度提升了将近一倍。
关键点:YUV是视频压缩(H.264、H.265)和图像压缩(JPEG)的基础格式。几乎所有现代压缩算法都在YUV空间里操作,因为分离亮度和色度后,可以针对性地压缩——亮度通道用无损或近无损,色度通道用有损。
5. 色彩空间对压缩策略的影响
聊完了各种色彩空间,咱们得回到一个核心问题:它们对压缩策略到底有什么影响?
我总结了几条经验:
- RGB适合无损压缩:比如PNG、GIF。因为RGB三个通道相关性高,用LZ77这类算法能有效去除冗余。但压缩比有限,一般只能到2:1到3:1。
- YUV适合有损压缩:比如JPEG。因为色度降采样后,数据量直接减半,再加上DCT变换和量化,压缩比可以到10:1甚至20:1。
- 索引色是嵌入式GUI的“杀手锏”:对于图标、按钮、文字界面,索引色配合游程编码(RLE),压缩比可以轻松到5:1以上。而且解码速度快,不需要复杂的数学运算。
- RGBA要慎用:除非你真的需要透明度,否则别用。如果必须用,可以考虑把Alpha通道单独压缩,或者用预乘Alpha技术减少数据量。
下面这张图展示了不同色彩空间在嵌入式GUI中的选择路径:
你看,选对色彩空间,压缩就成功了一半。我个人的习惯是:先分析资源特性,再选色彩空间,最后才考虑压缩算法。顺序不能反,否则事倍功半。
实战建议:在项目初期,先做一个资源清单,把每张图的颜色数、尺寸、透明度需求列出来。然后根据这个清单选择色彩空间。我见过太多团队一上来就用RGB888,结果后期发现内存不够,又回头改格式——那叫一个痛苦。