颜色编码 - YUV


 

YUV

1. 概念

YUV格式,YUV是一种颜色编码方法,主要用于电视相同以及模拟视频领域,它将亮度信息(Y) 与 色彩信息(UV)分离,没有UV信息一样可以显示完整的图像。YUV不像RGB那样要求三个独立的视频信号同时传输,所以用YUV方式传送占用极少的带宽。

YUV分为三个分量,“Y”表示图像的明亮度,也就是灰度值。“UV”表示图像的色彩。

 

2. YUV采样格式

YUV的采样有许多种,常用的有YUV444,YUV422,YUV420等。

请添加图片描述

YUV 4:4:4采样,一个Y使用一组UV分量。

YUV 4:2:2采样,两个Y共用一组UV分量。

YUV 4:2:0采样,四个Y共用一组UV分量。

YUV利用亮度"Y",色彩“UV”代替RGB三原色来压缩图像。

假设YUV的采样率是4:2:2 (YUV422),即每一个像素对于亮度Y的采样频率是1,对于色彩U和V,则每两个像素格取一个U,V。对于单个像素来说,U和V的采样频率为亮度Y的一半,一个像素点占用2字节(16bits),而RGB24三原色一个像素每种颜色各占1字节,所以一个像素需要占用3字节(24bits)。

例YUV与RGB的大小对比:

假如一个4x4图像,如果使用RGB来表示,则占用3 * 8 * 4 * 4 = 384bits。

如果使用YUV来表示,(Y)4 * 4 * 8 +(U)4 * 4 * 8 * 0.5 +(Y)4 * 4 * 8 * 0.5 = 24 + 12 + 12 = 256bits。

 

3. YUV与RGB互相转换

不同的色彩空间的转换公式是不一样的

​ YUV与RGB互转公式有很多,不同的色彩空间的转换公式是不一样的。

YUV有多种表现形式

​ 除了色彩空间,还需要注意YUV的多种表现形式,比如:

YUV:YUV是一种模拟型号,Y ∈ [0,1] U,V ∈ [-0.5, 0.5]

YCbCr:YCbCr是数字信号,它包含两种形式,分别为tv rangefull range,tv range 主要是广播电视采用的标准,full range主要是pc端采用的标准,所以full range有时也叫pc range。

​ tv range的各个分量的范围为 YUV Y∈[16,235] Y∈[16,240] Y ∈ [16,240]

​ full range的各个分量的范围均为:0~255

​ 我们绝大多数接触的格式是YCbCr(tv range),YCbCr420p(tv range)在ffmpeg中描述为YUV420P。

RGB <-> YUV转换

转换公式基于BT601

#1. 小数形式(RGB∈[0,255], UV∈[-128,128])
#YUV -> RGB
R = Y + 1.4075V;  
G = Y - 0.3455U - 0.7169V;  
B = Y + 1.779U;  
#RGB -> YUV 
Y =  0.299R + 0.587G + 0.114B;
U = -0.169R - 0.331G + 0.5B ;
V =  0.5R - 0.419G - 0.081B;

#2. 整数形式(减少计算量)(RGB∈[0,255], UV∈[-128,128])
#YUV -> RGB
R = Y + ((360V)>>8) ; 
G = Y - ((88U + 184V)>>8) ; 
B = Y + 455U>>8 ;
#RGB -> YUV
Y = (77R + 150G + 29B)>>8;
U = ((-44R  - 87G  + 131B)>>8);
V = ((131R - 110G - 21B)>>8);

#3. 量化为tv range公式(RGB∈[0,255], UV∈[16,240],Y∈[16,235])
#YUV -> RGB
R = 1.1644Y + 1.6019V - 223.5521;
G = 1.1644Y - 0.3928U - 0.8163V + 136.1381;  
B = 1.1644Y + 2.0253U - 278.0291;

#RGB -> YUV
Y = 0.2568R + 0.5041G + 0.0979B + 16;    
U = -0.1479R - 0.2896G + 0.4375B + 128;
V = 0.4375R - 0.3666G - 0.0709B + 128;

#4. tv range整数形式(减小计算量)(RGB∈[0,255], UV∈[16,240],Y∈[16,235])
#YUV -> RGB
R = (298Y + 411V - 57344)>>8
G = (298Y - 101U - 211V + 34739)>>8
B = (298Y + 519U- 71117)>>8

#RGB -> YUV 
Y= (66R + 129G + 25B)>>8 + 16 
U= (-38R - 74G + 112B)>>8 +128
V= (112R - 94*G - 18B)>>8 + 128

 

4. YUV存储方式

YUV的存储格式有两大类:packed和planar。

packed的存储格式 :每个像素点的Y,U,V是连续交错存储的。

常见存储格式:

  • YUYV422:Y1 U1 Y2 V1 Y3 U2 Y4 V2 …
    在这里插入图片描述

  • UYVY422:U1 Y1 V1 Y2 U2 Y3 V2 Y4…

请添加图片描述

  • YUV420 Packet: Y1 U1 Y2 Y3 U2 Y4 Y5 V1 Y6 Y7 V2 Y8
    请添加图片描述

planar的存储格式:先连续存储所有像素点的Y,接着存储所有像素点的U,随后是所有像素点的V。

常见存储格式:

  • YUV420P

请添加图片描述

semi-planar 存储格式:先连续存储所有像素点的Y,接着交错存储像素点U,V,即UVUVUV…。

常见存储格式:

  • YUV420SP

请添加图片描述

注:

其中YUV420PYUV420SP根据U,V的顺序,又可分出2种格式。

YUV420P:

  • U前V后即YUV420P,也叫I420。
  • V前U后,叫YV12(YV表示Y后面跟着V, 12表示12bit)。

YUV420SP:

  • U前V后叫NV12。
  • V前U后叫NV21。

数据排列如下:

I420:YYYYYYYY UU VV

YV12:YYYYYYYY VV UU

NV12:YYYYYYYY UVUV

NV21:YYYYYYYY VUVU

 

NV12 -> RGB 图片转换代码

/*
	width:N  Height:M
	1:   Y-1 ... ... Y-N
	2:   Y-1 ... ... Y-N
	...
	M:   Y-1 ... ... Y-N
	1:   U-1V-1 ... ... U-N/2 V-N/2
	...
	M/2: .....
*/
//NV12 -> RGB
//NV12的UV分量是交叉排列的
//RGB∈[0,255], YUV∈[0,255]
//使用以下转换公式
//R = Y + 1.4075V;  
//G = Y - 0.3455U - 0.7169V;  
//B = Y + 1.779U;  
int yuv2rgb_nv12(unsigned char* pYuvBuf, unsigned char* pRgbBuf, int height, int width)
{
    if(width < 1 || height < 1 || pYuvBuf == NULL || pRgbBuf == NULL)
    {
        return 0;
    }

    const long len = height * width;
    
    // Y与UV数据地址
    unsigned char *yData = pYuvBuf;
    unsigned char *uvData = yData + len;

	// 	R、G、B数据地址
    unsigned char *rData = pRgbBuf;
    unsigned char *gData = rData + len;
    unsigned char *bData = gData + len;

    int R[4], G[4], B[4];
    int Y[4], U, V;
    int y0_Idx, y1_Idx, uIdx, vIdx;

    for (int i = 0; i < height; i=i+2)
    {
        for (int j = 0; j < width; j=j+2)
        {
            y0_Idx = i * width + j;
            y1_Idx = (i + 1) * width + j;

			// Y[0]、Y[1]、Y[2]、Y[3]分别代表 Y00、Y01、Y10、Y11
            Y[0] = yData[y0_Idx];
            Y[1] = yData[y0_Idx + 1];
            Y[2] = yData[y1_Idx];
            Y[3] = yData[y1_Idx + 1];

            uIdx = (i / 2) * width + j;
            vIdx = uIdx + 1;

            U = uvData[uIdx];
            V = uvData[vIdx];

            R[0] = Y[0] + 1.4075 * (V - 128);
            G[0] = Y[0] - 0.3455 * (U - 128) + 0.7169 * (V - 128);
            B[0] = Y[0] + 1.779 * (U - 128);

            R[1] = Y[1] + 1.4075 * (V - 128);
            G[1] = Y[1] - 0.3455 * (U - 128) + 0.7169 * (V - 128);
            B[1] = Y[1] + 1.779 * (U - 128);

            R[2] = Y[2] + 1.4075 * (V - 128);
            G[2] = Y[2] - 0.3455 * (U - 128) + 0.7169 * (V - 128);
            B[2] = Y[2] + 1.779 * (U - 128);

            R[3] = Y[3] + 1.4075 * (V - 128);
            G[3] = Y[3] - 0.3455 * (U - 128) + 0.7169 * (V - 128);
            B[3] = Y[3] + 1.779 * (U - 128);

			// 像素值限定在 0-255
            for (int k = 0; k < 4; ++k)
            {
                if(R[k] >= 0 && R[k] <= 255)
                {
                    R[k] = R[k];
                }
                else
                {
                    R[k] = (R[K] < 0) ? 0 : 255;
                }

                if(G[k] >= 0 && G[k] <= 255)
                {
                    G[k] = G[k];
                }
                else
                {
                    G[k] = (G[K] < 0) ? 0 : 255;
                }

                if(B[k] >= 0 && B[k] <= 255)
                {
                    B[k] = B[k];
                }
                else
                {
                    B[k] = (B[K] < 0) ? 0 : 255;
                }
            }

            *(rData + y0_Idx) = R[0];
            *(gData + y0_Idx) = G[0];
            *(bData + y0_Idx) = B[0];

            *(rData + y0_Idx + 1) = R[1];
            *(gData + y0_Idx + 1) = G[1];
            *(bData + y0_Idx + 1) = B[1];

            *(rData + y1_Idx) = R[2];
            *(gData + y1_Idx) = G[2];
            *(bData + y1_Idx) = B[2];

            *(rData + y1_Idx + 1) = R[3];
            *(gData + y1_Idx + 1) = G[3];
            *(bData + y1_Idx + 1) = B[3];
        }
    }
    return 1;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值