http://www.360doc.com/content/16/0517/16/496343_559909505.shtml
http://blog.csdn.net/luotuo44/article/details/26402273
http://ticktick.blog.51cto.com/823160/555791
做视频采集与处理,自然少不了要学会分析YUV数据。因为从采集的角度来说,一般的视频采集芯片输出的码流一般都是YUV数据流的形式,而从视频处理(例如H.264、MPEG视频编解码)的角度来说,也是在原始YUV码流进行编码和解析.
如果只有Y信号分量而没有U、V分量,那么这样表示的图像就是黑白灰度图像。因此用YUV格式由彩色转黑白信号相当简单.
用三个图来直观地表示采集的方式吧,以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量。

先记住下面这段话,以后提取每个像素的YUV分量会用到。
- YUV 4:4:4采样,每一个Y对应一组UV分量。
- YUV 4:2:2采样,每两个Y共用一组UV分量。
- YUV 4:2:0采样,每四个Y共用一组UV分量。
2. 存储方式
下面我用图的形式给出常见的YUV码流的存储方式,并在存储方式后面附有取样每个像素点的YUV数据的方法,其中,Cb、Cr的含义等同于U、V。
(1) YUVY 格式 (属于YUV422)

(2) UYVY 格式 (属于YUV422)
YU12和YV12属于YUV420格式,也是一种Plane模式,将Y、U、V分量分别打包,依次存储。其每一个像素点的YUV数据提取遵循YUV420格式的提取方式,即4个Y分量共用一组UV。注意,上图中,Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00,其他依次类推。
(5)NV12、NV21(属于YUV420)

NV12和NV21属于YUV420格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)为交错存储,而不是分为三个plane。其提取方式与上一种类似,即Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00
YUV420 planar数据, 以720×488大小图象YUV420 planar为例,
其存储格式是: 共大小为(720×480×3>>1)字节,
分为三个部分:Y,U和V
Y分量: (720×480)个字节
U(Cb)分量:(720×480>>2)个字节
V(Cr)分量:(720×480>>2)个字节
三个部分内部均是行优先存储,三个部分之间是Y,U,V 顺序存储。
即YUV数据的0--720×480字节是Y分量值,
720×480--720×480×5/4字节是U分量
720×480×5/4 --720×480×3/2字节是V分量。
4 :2: 2 和4:2:0 转换:
最简单的方式:
YUV4:2:2 ---> YUV4:2:0 Y不变,将U和V信号值在行(垂直方向)在进行一次隔行抽样。 YUV4:2:0 ---> YUV4:2:2 Y不变,将U和V信号值的每一行分别拷贝一份形成连续两行数据。
YV12和I420的区别
一般来说,直接采集到的视频数据是RGB24的格式,RGB24一帧的大小size=width×heigth×3 Bit,RGB32的size=width×heigth×4,如果是I420(即YUV标准格式4:2:0)的数据量是 size=width×heigth×1.5 Bit。
在采集到RGB24数据后,需要对这个格式的数据进行第一次压缩。即将图像的颜色空间由RGB2YUV。因为,X264在进行编码的时候需要标准的YUV(4:2:0)。
但是这里需要注意的是,虽然YV12也是(4:2:0),但是YV12和I420的却是不同的,在存储空间上面有些区别。如下:
YV12 : 亮度(行×列) + U(行×列/4) + V(行×列/4)
I420 : 亮度(行×列) + V(行×列/4) + U(行×列/4)
可以看出,YV12和I420基本上是一样的,就是UV的顺序不同。
继续我们的话题,经过第一次数据压缩后RGB24->YUV(I420)。这样,数据量将减少一半,为什么呢?呵呵,这个就太基础了,我就不多写了。同样,如果是RGB24->YUV(YV12),也是减少一半。
但是,虽然都是一半,如果是YV12的话效果就有很大损失。然后,经过X264编码后,数据量将大大减少。将编码后的数据打包,通过RTP实时传送。
到达目的地后,将数据取出,进行解码。完成解码后,数据仍然是YUV格式的,所以,还需要一次转换,这样windows的驱动才可以处理,就是YUV2RGB24。
YUY2 是 4:2:2 [Y0 U0 Y1 V0]
YUV420P,Y,U,V三个分量都是平面格式,分为I420和YV12。I420格式和YV12格式的不同处在U平面和V平面的位置不同。在I420格式中,U平面紧跟在Y平面之后,然后才是V平面(即:YUV);但YV12则是相反(即:YVU)。
YUV420SP, Y分量平面格式,UV打包格式, 即NV12。 NV12与NV21类似,U 和 V 交错排列,不同在于UV顺序。
I420: YYYYYYYY UU VV =>YUV420P
YV12: YYYYYYYY VV UU =>YUV420P
NV12: YYYYYYYY UVUV =>YUV420SP
NV21: YYYYYYYY VUVU =>YUV420SP
============
http://blog.csdn.net/panda1234lee/article/details/48782577
2.视频YUV格式简介
YUV格式,一般用Y,U,V三者的比率来表示不同格式,比如YUV444 表示三者是比值此是 4:4:4,即Y,U,V的空间都是一样大小。目前主要有如下比例,注意所有格式中Y比值都是4,占一个字节,表示没有减少采样。不同格式中,减小只是UV的采样值.
4:4:4:表示色度值(UV)没有减少采样。即Y,U,V各占一个字节,总共占3字节。
4:2:2:表示UV分量采样减半,比如第一个像素采样Y,U,第二个像素采样Y,V,依次类推,这样每个点占用2个字节。二个像素组成一个宏像素.
4:2:0:这种采样并不意味着只有Y、Cb,而没有Cr分量,这里的0说的U,V分量隔行才采样一次。比如第一行采样 4:2:0,第二行采样 4:0:2 ,依次类推……在这种采样方式下,平均每一个像素占用1.5字节.
4:2:2示例
如果原始数据三个像素是 [Y0 U0 V0] ,[Y1 U1 V1],[Y2 U2 V2],[Y3 U3 V3]
经过4:2:2采样后,数据变成了 Y0 U0 ,Y1 V1 ,Y2 U2,Y3 V3
如果还原后,因为某一些数据丢失就补成 [Y0 U0 V1],[Y1 U0 V1],[Y2 U2 V3] ,[Y3 U3 V2]
4:2:0示例
下面八个像素为:[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3] [Y5 U5 V5] [Y6 U6 V6] [Y7U7 V7] [Y8 U8 V8]
存放的码流为: Y0 U0, Y1, Y2 U2, Y3,Y5 V5, Y6,Y7 V7, Y8
映射出的像素点为:[Y0 U0 V5] [Y1 U0 V5] [Y2 U2 V7] [Y3 U2 V7] [Y5 U0 V5] [Y6 U0 V5] [Y7 U2 V7] [Y8 U2 V7]
除了4:4:4采样,其余采样后信号重新还原显示后,会丢失部分UV数据,只能用相临的数据补齐,但人眼对UV不敏感,因此总体感觉损失不大。
图1 YUV420sp存储格式
图2 YUV420p存储格式
ps: ffmpeg中,图像原始数据包括两种:planar和packed。planar就是将几个分量分开存,比如YUV420中,data[0]专门存Y,data[1]专门存U,data[2]专门存V。而packed则是打包存,所有数据都存在data[0]中.
3.YUV格式示例【顺时针旋转90°YUV420图像】:
- public static void rotateYUV420(byte[] src,byte[] des,int width,int height, String type){
- // 原图按行序存储
- int wh = width * height;
- int k = 0;
- //旋转Y分量(按原图的列序存储)
- for(int i=0;i<width;i++) {
- for(int j=0;j<height;j++) {
- des[k] = src[width*j + i];
- k++;
- }
- }
- if(type.equals(“YUV420SP”)){
- // YUV420SP是UV分量交错依次存储的情况
- // 旋转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;
- }
- }
- }
- else if(type.equals(“YUV420P”)){
- // YUV420P是UV分量分开存储的情况
- // 旋转U分量(按原图的列序存储)
- for(int i=0;i<width;i++) {
- for(int j=0;j<height/4;j++) {
- des[k] = src[wh + width*j + i];
- k++;
- }
- }
- // 旋转V分量(按原图的列序存储)
- for(int i=0;i<width;i++) {
- for(int j=0;j<height/4;j++) {
- des[k] = src[(1+1/4.0f)*wh + width*j + i];
- k++;
- }
- }
- }
- }
#ifdef __cplusplus
extern "C" {
#endif
...
#ifdef __cplusplus
}
#endif
=======================
http://blog.csdn.net/luotuo44/article/details/26402273
RGB格式: 用红绿蓝表示各种颜色,并且R、G、B一般用一个字节来存储
YUV中的Y、U、V三个分量分别表示明亮度、色度、浓度,每一个分量也是用一个字节来存放的(只有Y分量就是黑白画面)
http://www.cnblogs.com/my_life/articles/6756167.html
http://ticktick.blog.51cto.com/823160/d-15
YUV和RGB两者相互转换的公式:
从公式中可以看到,两者进行转换的时候,可能会发生溢出。这个在计算的时候需要注意。处理的方法是截断。比如大于255的,就将之设255.小于0的,其值为0.
YUV格式是有很多种的。比较常见的有YUV4:4:4、YUV4:2:0、YUV4:2:2。后两种又可以细分成几种的,比如YUV422有YUYV422、UYVY422、YUV422P等。晕了吧~~
其实只需记得:
- YUV 4:4:4采样,每一个Y对应一组UV分量。
- YUV 4:2:2采样,每两个Y共用一组UV分量。
- YUV 4:2:0采样,每四个Y共用一组UV分量。
维基百科关于他们的布局说明:
- 4:4:4表示完全取样。
- 4:2:2表示2:1的水平取样,垂直完全采样。
- 4:2:0表示2:1的水平取样,2:1垂直采样。
- 4:1:1表示4:1的水平取样,垂直完全采样。
上面的只是逻辑布局,但作为码农更想知道他们的物理布局(存储方式),因为要读取数据。
这要先了解两个概念:planar和packed。
在RGB格式中,假如有3个像素点,那么既可以这样存:RGBRGBRGB,也可以RRRGGGBBB。前面一种称为packed(打包模式),后面一种称为planner(平行模式)。同样对应YUV也是有这两种模式的。
对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。对于packed的YUV格式,每个像素点的Y,U,V是连续交叉存储的。
在FFmpeg中,已经定义了这两种模式。可以查看pixfmt.h文件。每一种图像格式,都会对应的说明是哪种模式的。
(1)YUYV422: packed模式 (422:每两个Y共用一组UV分量; YUYV:按照此顺序进行存储,两个不同的Y公共一组相同的UV, Y00 U00 Y01 V00, Y02 U01 Y03 V01)

(2)UYVY422: packed模式(只是排列方式和前面的不同)
(3)YUV422P: planner模式
(4)YUV420P: planner模式
这里还有一个问题要注意:对于一个width * height的YUV图像,占有多大的字节。这关乎到读取一个YUV文件时,要读多少字节才能把一张图像完全读进内存。对于不同的YUV格式,有不同的大小。
无论RGB还是YUV,都是每个分量占用一个字节(Y分量一个自己,U分量一个字节,V分量一个字节)
//YUV格式中有一个很重要的等量关系,那就是有多少个像素就有多少个y。
对于YUV422来说,是两个y共享一对u和v。所以y0 u y1 v(YUYV422)这4个字节存储的内存代表了两个像素(y0, u, v) 和(y1, u, v)。所以,4比2的关系,所以一个像素会占两个字节,所以对应YUV422来说要width * height * 2个字节。
在FFmpeg中,有一个函数,可以直接算这个大小的,函数的输入参数就是图像的格式、图像的width和height。下一篇博文会说到这个函数。
好了,扯了这么多,要写些代码才能满足码农。
这个例子先在YUV文件中读取一个图像,然后转换成RGB24格式,最后用OpenCV播放图像,可以达到视频的效果。
#include<stdio.h>
#include<highgui.h> //for OpenCV
//转换函数
void YUV2RGB(int y, int u, int v, int* r, int* g, int* b)
{
assert( r != NULL && g != NULL && b != NULL);
*r = y + 1.13983 * (v - 128);
*g = y - 0.39465 * (u - 128) - 0.58060 * (v - 128);
*b = y + 2.03211 * (u - 128);
*r = *r > 255 ? 255 : *r;
*r = *r < 0 ? 0 : *r;
*g = *g > 255 ? 255 : *g;
*g = *g < 0 ? 0 : *g;
*b = *b > 255 ? 255 : *b;
*b = *b < 0 ? 0 : *b;
}
//把RGB数据填充到OpenCV的IplImage结构体成员imageData中
//imageData是一个数组,其用来存放每一个像素点的BGR。
//其排列的形式很简单(BGR)(BGR)(BGR)(BGR)(BGR)
void fillImage(IplImage* pimg, int r, int g, int b)
{
static int h = 0, w = 0;
//这里是BGR的顺序。因为OpenCV存放像素的顺序的BGRBGRBGR
pimg->imageData[h*pimg->widthStep + w++] = b;
pimg->imageData[h*pimg->widthStep + w++] = g;
pimg->imageData[h*pimg->widthStep + w++] = r;
//这部分代码是和OpenCV的一些性质有关,如果看不懂,可以忽略。
//上面的pimg->widthStep也是与OpenCV的性质有关
if( w/3 >= pimg->width )
{
w = 0;
if( h == pimg->height - 1 )
h = 0;
else
++h;
}
}
void convertImage(IplImage* pimg, unsigned char* yuv_buff, int len)
{
int i;
int r, g, b;
int y0, y1, u, v;
for(i = 0; i < len; i += 4)
{
//其排列方式是y0 u y1 v
//直接提取出来y、u、v三个分量,然后使用公式转成RGB即可
//因为两个y共享一对uv。故y0 u y1 v能提取出两组(y, u, v)
y0 = yuv_buff[i + 0];
u = yuv_buff[i + 1];
y1 = yuv_buff[i + 2];
v = yuv_buff[i + 3];
YUV2RGB(y0, u, v, &r, &g, &b);
//将RGB分量填充到OpenCV的IplImage中
fillImage(pimg, r, g, b);
YUV2RGB(y1, u, v, &r, &g, &b);
fillImage(pimg, r, g, b);
}
}
int main(int argc, char** argv)
{
const char* filename = argc > 1 ? argv[1] : "waterfall_yuyv422.yuv";
FILE* fin = fopen(filename, "rb");
if( fin == NULL )
{
printf("can't open the file\n");
return -1;
}
int width = 352;
int height = 288;
//一张完整的图像 对应在 yuv文件中 占据的字节数
//因为是yuyv格式的yuv,所以其排列方式是y0 u y1 v y2 u v y3
//因为是两个y共享一对u和v。所以y0 u y1 v代表两个像素(y0, u, v)
//和(y1, u, v),对应地会有两个RGB像素。
//也就是说y0 u y1 v这4个字节的内容等于2个像素, 2比1的关系。
//所以有width * height个像素,就应该要width * height * 2个字节
//这个关系是yuv422特有的。对于yuv444和yuv420会有不同的比例关系
int frame_size = width * height * 2;
unsigned char* buff = new unsigned char[frame_size];
IplImage* pimg = cvCreateImage(cvSize(width, height),
IPL_DEPTH_8U, 3);
cvNamedWindow("1.jpg");
//这里用图像做成一个视频播放器
while( 1 )
{
int ret = fread(buff, 1, frame_size, fin);
if( ret != frame_size )
{
break;
}
convertImage(pimg, buff, frame_size);
cvShowImage("1.jpg", pimg);
cvWaitKey(33);
}
cvReleaseImage(&pimg);
cvDestroyWindow("1.jpg");
delete [] buff;
return 0;
}
浙公网安备 33010602011771号