如题,现在要把RGB的格式转成YUV格式。
1. 数据的排列方式
先来看看两种数据在内存里是按什么方式排列的
1.1 RGB的排列
如图所示,1组RGB表示1个像素的颜色,每个像素依次排列。图中有8组RGB数据,即8个像素点。
1.2 YUV的排列
如图所示,这里是 YUV420SP(NV12) 的排列方式,YUV的比例为Y:U:V=4:1:1
。Y代表像素点的明亮度(灰阶),图中有48个Y,即48个像素点。UV代表像素点的色度,1个U和1个V组合到一起能表示一种颜色,在YUV420格式里,1组UV决定了4个像素点的颜色,其对应关系如图所示。U和V加起来的数据量是Y的一半。
YUV的图片还有许多种格式,请参考其它资料。
实际上它在内存里面是按照 Y 1 Y 2 Y 3 Y 4 Y 5 Y 6 Y 7 Y 8 U 1 V 1 U 2 V 2 Y_1Y_2Y_3Y_4Y_5Y_6Y_7Y_8U_1V_1U_2V_2 Y1Y2Y3Y4Y5Y6Y7Y8U1V1U2V2的顺序排列的,如下图所示。存储的时候数据是连续排列的,解析的时候加上宽和高的信息才成为了一张矩形图片。写代码计算地址偏移的时候要注意行和列的关系。
2. RGB转YUV
2.1 RGB转YUV的公式
不知道哪里来的公式,但据说是总所周知的。
Y = (0.257*R) + (0.504*G) + (0.098*B) + 16
Cb = U = -(0.148*R) - (0.291*G) + (0.439*B) + 128
Cr = V = (0.439*R) - (0.368*G) - (0.071*B) + 128
这个公式里面有很多小数,运算起来效率不高,整数的运算会快一点。于是就有了下面的代码:
Y = ( ( 66*R + 129*G + 25*B) >> 8) + 16;
U = ( (-38*R - 74*G + 112*B) >> 8) + 128;
V = ( (112*R - 94*G - 18*B) >> 8) + 128;
什么原理呢?以Y
为例,本来Y
是:
Y = (0.257*R) + (0.504*G) + (0.098*B) + 16
乘以256,再除以256:
Y = ( 256*0.257*R + 256*0.504*G + 256*0.098*B )/256 + 16
Y = ( 65.792*R + 129.024*G + 25.088*B )/256 + 16
舍掉小数部分(因为已经放大256倍了,再丢掉一点小数影响不大,速度要紧)。于是:
Y = ( 65*R + 129*G + 25*B )/256 + 16
众所周知,除法可以用位移来计算,而且速度更快。这里 256 = 2 8 256=2^8 256=28 ,数据在计算机中以二进制的形式存储,因此除以256即向右移8位。于是:
Y = ( ( 65*R + 129*G + 25*B ) >> 8 ) + 16
U和V同理。
2.2 代码
这里参考https://www.jianshu.com/p/e498326a55b1?utm_campaign的代码:
2.2.1 常规版本:
typedef unsigned char byte;
void RGBtoNV12(byte* pNV12, byte* pRGB, int width, int height)
{
int frameSize = width * height;
int yIndex = 0;
int uvIndex = frameSize;
int R, G, B, Y, U, V;
int index = 0;
for (int j = 0; j < height; j++)
{
for (int i