一. 说明
在视频解码后,我们通常需要将它们显示出来,也就是Render;这个时候往往后发现有很多种图像格式,而这些格式名称似是而非,很容易被搞混,因此这里对它们进行一些介绍和分类:
名词解释:
平面格式(planar formats):针对Y U V 三种元素,使用三个矩阵来进行存储
打包格式(packed formats): YUV存储在同一个平面,Luma和chroma数据交错排列,类似RGB的排列方式
半平面格式(semi-planar): 半平面格式与平面格式的区别是,luma和chroma使用不同的平面,就是说U V被存储在一个平面矩阵了
灰度空间(luminance): Y
色度空间(chrominance): U(Cb)和V(Cr)
交错排列(interleaved)
- 顺序排列(progressive)
二. 颜色空间:
常用的颜色空间有 RGB YUV HSV
其中YUV颜色模型是来源于RGB的, 而YCbCr模型来源于YUV,它是YUV颜色空间的缩放和偏移应用;
2.1 RGB介绍:
在对RGB进行渲染的时候,经常需要设备的参数有RGB.888和RGB.565两种,它们分别对应RGB24和RGB16
2.1.1. 通过内存位进行分类
- RGB1 每像素用1位表示,需要调色板
- RGB4 每像素用4位表示,需要调色板
RGB8 每像素用8位表示,需要调色板
RGB565 每像素用16位表示
RGB555 每像素用16位表示,空一位
RGB24 每像素用24位表示, 每个RGB分量8位
RGB32 每像素用32位表示 每个RGB分量8位,剩下8位为空
- ARGB32 每像素用32位表示,每个RGB分量用8位表示,剩下8位表示alpha通道
2.1.2 调色板类RGB
上面的RGB1 RGB4 RGB8都是调色板类RGB格式,当表示实际的颜色的时候,它们通常需要配合一个调色板来进行转换;比如在JPEG中,对于这种格式,都有个定义的调色板;
使用这种模型的图像数据,它们的源数据就不是真正的颜色值,而是当前像素颜色值在调色板中的索引;
比如RGB1(黑白位图),在调色板中定义了黑白两种颜色分别为0X000000(黑色)和0XFFFFFF(白色),这样图像源数据中的:0101 对于的像素的实际颜色就是:黑白黑白
2.1.3 RGB565
RGB565使用16位表示一个像素,这16位中的5位用于R,6位用于G,5位用于B。程序中通常使用一个字(WORD,一个字等于两个字节)来操作一个像素。当读出一个像素后,这个字的各个位意义如下:
高<—————-低
RRRRRGGGGGGBBBBB
编程时,可以通过位操作来完成RGB各分量的提取
#define RGB565_MASK_RED 0xF800
#define RGB565_MASK_GREEN 0x07E0
#define RGB565_MASK_BLUE 0x001F
R = (wPixel & RGB565_MASK_RED) >> 11; // 取值范围0-31
G = (wPixel & RGB565_MASK_GREEN) >> 5; // 取值范围0-63
B = wPixel & RGB565_MASK_BLUE; // 取值范围0-31
2.1.4 RGB555
RGB555是另一种16位的RGB格式,RGB分量都用5位表示(剩下的1位不用)。使用一个字读出一个像素后,这个字的各个位意义如下:
(X表示不用,可以忽略)
高<—————-低
XRRRRGGGGGBBBBB
编程时,可以通过位操作来完成RGB各分量的提取
#define RGB555_MASK_RED 0x7C00
#define RGB555_MASK_GREEN 0x03E0
#define RGB555_MASK_BLUE 0x001F
R = (wPixel & RGB555_MASK_RED) >> 10; // 取值范围0-31
G = (wPixel & RGB555_MASK_GREEN) >> 5; // 取值范围0-31
B = wPixel & RGB555_MASK_BLUE; // 取值范围0-31
2.1.5 RGB24
RGB24使用24位来表示一个像素,RGB分量都用8位表示,取值范围为0-255。注意在内存中RGB各分量的排列顺序为:BGR BGR BGR…
编程时,一般名称为RGB.888,通常可以使用RGBTRIPLE数据结构来操作一个像素,定义为
typedef struct tagRGBTRIPLE {
BYTE rgbtBlue; // 蓝色分量
BYTE rgbtGreen; // 绿色分量
BYTE rgbtRed; // 红色分量
} RGBTRIPLE;
2.1.6 RGB32
RGB32使用32位来表示一个像素,RGB分量各用去8位,剩下的8位用作Alpha通道或者不用。(ARGB32就是带Alpha通道的 RGB32。)注意在内存中RGB各分量的排列顺序为:BGRA BGRA BGRA…
编程时,命名为ARGB 定义为:
typedef struct tagRGBQUAD {
BYTE rgbBlue; // 蓝色分量
BYTE rgbGreen; // 绿色分量
BYTE rgbRed; // 红色分量
BYTE rgbReserved; // 保留字节(用作Alpha通道或忽略)
} RGBQUAD;
2.2 YUV介绍:
YUV的种类非常多且繁杂,其分类方式也多种多样,我们将重点介绍YUV420.
2.2.1. 根据排列方式划分:
- 平面格式(planar formats):针对Y U V 三种元素,使用三个矩阵来进行存储;
- 打包格式(packed formats): YUV存储在同一个平面,Luma和chroma数据交错排列,类似RGB的排列方式;
- 半平面格式(semi-planar): 半平面格式与平面格式的区别是,luma和chroma使用不同的平面,就是说U V被存储在一个平面矩阵了;
2.2.2. 根据采样方式划分:
YUV444:每个Y对应一组UV分量;
YUV422:每两个Y对应一组UV分量;
YUV420:每四个Y对应一组UV分量;
2.2.3. 根据 FOURCC 命名来划分:
YUV444
4:4:4 每像素32位
i.FOURCC: AYUV:A为alpha值(透明度参数)
内存布局(从低到高) V0U0Y0A0 V1U1Y1A1 V2U2Y2A2
ii.FOURCC:IYU2: ( YUV444, U-Y-V, progressive, packed)
YUV422
4:2:2 每像素16位
i. FOURCC: UYVY (YUV422, U-Y-V-Y, progressive, packed)
内存布局: U0Y0V0Y1 U1Y2V1Y3 U2Y4V2Y5
ii.FOURCC: YUY2 (same as YUYV)
内存布局: Y0U0Y1V0 Y2U1Y3V1 Y4U2Y5V2
下面的不再介绍内存布局,使用时查找
* UYNV (same as UYVY )
* Y422 (same as UYVY )
* IUYV ( YUV422, U-Y-V-Y, interlaced, packed)
* YUYV (YUV422, Y-U-Y-V, progressive, packed)
* YUNV (same as YUYV)
* V422 (same as YUYV)
* YVYU (YUV422, Y-V-Y-U, progressive, packed)
* YV16 (YUV422, Y-U-V, progressive, planar)
* YV12 (YUV420, Y-V-U, progressive, planar)
* IYUV (YUV420, Y-U-V, progressive, planar)
* I420 (same as IYUV)
YUV420
4:2:0 每像素有不同的位表示:
每像素16位表示
i. FOURCC:IMC1
内存布局:
ii.FOURCC:IMC3
内存布局:
每像素12位表示
i. FOURCC:IMC2
内存布局:
ii.FOURCC:YV12
内存布局:
所有 Y 样例都会作为不带正负号的 char 值组成的数组首先显示在内存中。此数组后面紧接着所有 V (Cr) 样例。V 平面的跨距为 Y 平面跨距的一半,V 平面包含的行为 Y 平面包含行的一半。V 平面后面紧接着所有 U (Cb) 样例,它的跨距和行数与 V 平面相同
iii.FOURCC:NV12
内存布局:
所有 Y 样例都会作为由不带正负号的 char 值组成的数组首先显示在内存中,并且行数为偶数。Y 平面后面紧接着一个由不带正负号的 char 值组成的数组,其中包含了打包的 U (Cb) 和 V (Cr) 样例,如图所示。当组合的 U-V 数组被视为一个由 little-endian WORD 值组成的数组时,LSB 包含 U 值,MSB 包含 V 值。NV12 是用于 DirectX VA 的首选 4:2:0 像素格式。
2.2.4. 编程常用描述:
在代码中,我们经常会看到YUV420P,YUV420SP,YV12,NV21,I420等表示,这里对其进行一些说明:
I420 与 YV12 类似,它们的区别UV的顺序不同;I420按 YYYY U V 排列; YV12按 YYYY V U 排列;
yuv420p 是I420的别名,它们等同,排列图如下
- yuv420sp 是 NV12 的别名, 它们等同,排列图如下
总结
- I420: YYYYYYYY UU VV =>YUV420P
- YV12: YYYYYYYY VV UU =>YUV420P
- NV12: YYYYYYYY UVUV =>YUV420SP
- NV21: YYYYYYYY VUVU =>YUV420SP
旋转
public static void rotateYUV240SP(byte[] src,byte[] des,int width,int height){
int wh = width * height;
//旋转Y
int k = 0;
for(int i=0;i<width;i++) {
for(int j=0;j<height;j++)
{
des[k] = src[width*j + i];
k++;
}
}
// 旋转UV
for(int i=0;i<width;i+=2) {
for(int j=0;j<height/2;j++)
{
des[k] = src[wh+ width*j + i];
des[k+1]=src[wh + width*j + i+1];
k+=2;
}
}
}
三. 测试方法:
如何用VLC播放YUV图像:
eg: demux=rawvideo :rawvid-width=704 :rawvid-height=576 :rawvid-chroma=UYVY :rawvid-fps=5
如何使用ffplayer播放YUV图像:
eg: ffplay -f rawvideo -video_size 1920x1080 a.yuv