嵌入式开发中的YUV知识点详解
目录
1. 为什么需要YUV?——从RGB说起
2. YUV的灵魂:采样格式 (4:4:4, 4:2:2, 4:2:0)
YUV 4:4:4
YUV 4:2:2
YUV 4:2:0
3. 核心实践:YUV的内存布局
a. Packed (打包格式)
b. Planar (平面格式)
c. Semi-Planar (半平面格式)
4. YUV与RGB的转换
总结:嵌入式开发者应掌握的YUV核心
1. 为什么需要YUV?——从RGB说起
在计算机世界里,我们最熟悉的颜色模型是 RGB(红、绿、蓝)。它通过混合这三种基本色光来表示各种颜色,非常符合我们屏幕显示(如LCD、OLED)的发光原理。每个像素点都由R、G、B三个分量组成,每个分量通常用8位(0-255)表示,因此一个完整的像素需要 8 * 3 = 24
位。
虽然RGB模型直观且适合显示,但在视频传输和存储中却有很大的弊端:
-
数据量大:没有经过压缩的RGB图像数据非常庞大,对带宽和存储空间要求极高。
-
信息冗余:人类的视觉系统对亮度的敏感度远高于对色度的敏感度。在RGB模型中,R、G、B三个分量同等重要,包含了大量的色度信息,而这些信息对于人眼来说很多是“多余”的。
为了解决这些问题,YUV 颜色模型应运而生。它的核心思想是:将亮度和色度分离。
-
Y (Luma):代表亮度分量,也就是图像的灰度信息。如果我们只看Y分量,看到的就是一幅黑白图像。
-
U (Chroma):代表色度分量,描述了颜色与蓝色(B)的差异。
-
V (Chroma):代表色度分量,描述了颜色与红色(R)的差异。
通过将亮度和色度分离,我们可以对色度信息进行“偷懒”处理,即色度二次采样(Chroma Subsampling),从而在人眼几乎无法察觉图像质量损失的情况下,大幅度地减少数据量。这正是YUV在视频领域大行其道的核心原因。
举个实际例子: 你在看电视或在线视频时,视频信号基本都是YUV格式。早期的黑白电视只有一个Y(亮度)通道。后来为了兼容黑白电视,彩色电视标准在原有Y信号的基础上,增加了U和V两个色度信号。黑白电视机接收到信号后,只解析Y信号,就能正常显示黑白画面;而彩色电视机则会结合Y、U、V信号,解码出彩色画面。
2. YUV的灵魂:采样格式 (4:4:4, 4:2:2, 4:2:0)
色度二次采样是理解YUV的关键。它规定了Y、U、V三个分量的采样比例。最常见的格式有以下三种:
YUV 4:4:4
-
含义:完全不进行二次采样。每一个Y分量都对应一组独立的U和V分量。
-
采样示意:对于一个4x2的像素块:
-
YYYY
-
YYYY
-
UUUU
-
UUUU
-
VVVV
-
VVVV
-
-
数据量:每个像素需要
8 (Y) + 8 (U) + 8 (V) = 24
位,与RGB的数据量相同。 -
应用场景:极少用于视频传输和消费级产品。通常只在专业的视频编辑和后期制作中为了保留最完整的颜色信息而使用。在嵌入式领域几乎不会遇到。
YUV 4:2:2
-
含义:水平方向上的色度采样率是亮度的一半,垂直方向上不变。也就是说,水平方向上每两个Y分量共享一组U和V分量。
-
采样示意:对于一个4x2的像素块:
-
YYYY
-
YYYY
-
UU
UU
-
UU
UU
-
VV
VV
-
VV
VV
(这里UU
表示两个像素共享一个U值)
-
-
数据量:每两个像素有
2*Y + 1*U + 1*V
,平均每个像素需要8 (Y) + 4 (U) + 4 (V) = 16
位。相比YUV 4:4:4,数据量减少了1/3。 -
应用场景:在广播电视和专业摄像机中比较常见。一些嵌入式设备中的摄像头芯片(Sensor)可以直接输出这种格式的数据。
YUV 4:2:0
-
含义:水平和垂直方向上的色度采样率都是亮度的一半。也就是说,一个2x2的像素区域(即4个Y分量)共享一组U和V分量。
-
采样示意:对于一个4x2的像素块:
-
YYYY
-
YYYY
-
UU
-
UU
-
VV
-
VV
-
-
数据量:每四个像素有
4*Y + 1*U + 1*V
,平均每个像素需要8 (Y) + 2 (U) + 2 (V) = 12
位。相比YUV 4:4:4,数据量减少了一半。 -
应用场景:这是最最最常见的格式! 你接触到的几乎所有消费级视频,如MP4、RMVB、在线视频流(H.264/H.265),以及绝大多数硬件编解码器,都使用YUV 4:2:0。在嵌入式音视频开发中,你打交道最多的就是它。
举个实际例子: 假设你正在开发一个网络摄像头项目。摄像头传感器(Sensor)捕捉到原始图像后,通过ISP(图像信号处理器)处理,最终可能输出YUV 4:2:2格式的数据。但是,为了通过网络传输,你需要用H.264编码器进行压缩。H.264硬件编码器通常只接受YUV 4:2:0格式的输入。因此,在将数据送入编码器之前,你必须进行一个格式转换:从YUV 4:2:2转换为YUV 4:2:0。这个转换通常由专门的硬件模块(如SOC中的2D图形引擎)或DMA来高效完成。
3. 核心实践:YUV的内存布局
知道了采样率,我们还需要知道这些Y、U、V数据在内存中是如何排列的。这直接关系到你如何读取、处理和显示图像。主要分为三大类:
a. Packed (打包格式)
所有Y、U、V分量交织存储在一个数组(或称为一个平面,Plane)中。
-
YUYV (也叫YUY2):内存顺序是
Y0 U0 Y1 V0 | Y2 U1 Y3 V1 | ...
。这是YUV 4:2:2最常见的打包格式。每4个字节代表2个像素。 -
UYVY:内存顺序是
U0 Y0 V0 Y1 | U1 Y2 V1 Y3 | ...
。与YUYV只是顺序不同。
b. Planar (平面格式)
Y、U、V三个分量分别存储在三个独立的数组(三个平面)中。
-
I420:这是YUV 4:2:0最经典的平面格式。内存布局是:先连续存储所有像素的Y分量,然后连续存储所有U分量,最后连续存储所有V分量。
-
[YYYYYYYY...] [UU...] [VV...]
-
-
YV12:与I420非常相似,只是U和V平面的顺序颠倒了。
-
[YYYYYYYY...] [VV...] [UU...]
-
c. Semi-Planar (半平面格式)
Y分量单独在一个平面,U和V分量交织存储在另一个平面中。这是目前嵌入式和移动端硬件中最主流的格式。
-
NV12:YUV 4:2:0的一种。Y分量在一个平面,U和V交织在第二个平面。
-
[YYYYYYYY...] [UVUVUV...]
-
-
NV21:YUV 4:2:0的一种。与NV12类似,只是U和V的交织顺序相反。
-
[YYYYYYYY...] [VUVUVU...]
-
举个实际例子:一个典型的嵌入式视频处理流程
数据采集:摄像头传感器输出
YUYV (4:2:2 Packed)
格式的数据流。预览显示:你可能需要将此数据直接送到LCD控制器。某些LCD控制器可以直接处理YUYV格式,并在硬件内部将其转换为RGB进行显示,这个过程对CPU是透明的。
视频编码:同时,你需要将视频流编码成H.264发送到网络。你的SOC里的H.264硬编码器要求输入数据格式为
NV12 (4:2:0 Semi-Planar)
。格式转换:此时,你的程序就需要调用驱动或硬件加速接口,将摄像头采集到的
YUYV
数据转换成NV12
。这个过程的伪代码逻辑可能是:* 提取所有Y分量,按顺序放入NV12的Y平面。 * 每隔一个Y提取U和V分量,将它们按UVUV的顺序放入NV12的UV平面。
数据解码:在接收端,H.264解码器解码出的数据帧通常也是
NV12
格式。如果要用软件(例如 FFmpeg)将其渲染到屏幕上,你可能需要先将NV12
转换为RGB
,这个转换过程就需要你精确地知道NV12的内存布局,以便正确地取到每个像素对应的Y、U、V值。
内存大小计算公式 (以width x height的图像为例):
-
YUV 4:2:2 (如YUYV):
width * height * 2
字节 -
YUV 4:2:0 (如I420, NV12):
width * height * 1.5
字节-
Y平面:
width * height
-
U平面:
(width / 2) * (height / 2)
-
V平面:
(width / 2) * (height / 2)
-
总和:
width * height * (1 + 1/4 + 1/4) = width * height * 1.5
-
4. YUV与RGB的转换
虽然在处理和传输中我们用YUV,但最终显示到屏幕上时,还是需要转换回RGB。这个转换通常由一个标准的公式完成。
从 YUV 转换到 RGB:
R = Y + 1.402 * (V - 128)
G = Y - 0.34414 * (U - 128) - 0.71414 * (V - 128)
B = Y + 1.772 * (U - 128)
从 RGB 转换到 YUV:
Y = 0.299 * R + 0.587 * G + 0.114 * B
U = -0.1687 * R - 0.3313 * G + 0.5 * B + 128
V = 0.5 * R - 0.4187 * G - 0.0813 * B + 128
重点:在嵌入式系统中,你几乎永远不要用CPU去做这个转换!
这种涉及大量浮点运算的逐像素计算,如果放在CPU上执行,会消耗大量的计算资源,导致系统卡顿甚至无法正常工作。
正确的做法是:利用硬件。现代的SOC(片上系统)中,显示控制器(Display Controller) 或 GPU 都内置了专门的硬件单元,可以在将图像数据发送到屏幕之前,实时地(on-the-fly) 将YUV数据转换为RGB。你作为开发者,只需要把YUV数据的内存地址和格式信息(如NV12)配置给相应的硬件寄存器即可,整个转换过程无需CPU干预,效率极高。
总结:嵌入式开发者应掌握的YUV核心
-
理解为何使用YUV:为了利用人眼对亮度的敏感度远高于色度的特性,分离亮度和色度,从而可以对色度进行压缩,有效减少数据量。
-
熟悉主流采样格式:牢记 YUV 4:2:0 是视频压缩和硬件编解码的事实标准。了解 YUV 4:2:2 常用于摄像头原始输出。
-
精通内存布局:这是编程实践的根本。必须能够清晰地分辨 Packed (YUYV), Planar (I420), 和 Semi-Planar (NV12, NV21) 的区别,并能根据格式计算出图像数据的大小和各分量的内存地址。特别要重视 NV12 和 NV21,因为它们是当前硬件接口的主流。
-
硬件加速思维:切记颜色空间转换(YUV<->RGB)和图像格式转换(如YUYV->NV12)都是计算密集型任务,应尽可能地交给硬件(GPU, 显示控制器, DMA等)去完成,而不是用CPU去“硬算”。
掌握了以上几点,你在处理嵌入式视频数据时,就会有一个清晰的框架,能够从容地应对各种与视频格式相关的挑战。