欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

用NEON intrinsic实现RGB转YUV420SP(NV12)

最编程 2024-08-15 15:22:43
...

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 Y1​Y2​Y3​Y4​Y5​Y6​Y7​Y8​U1​V1​U2​V2​的顺序排列的,如下图所示。存储的时候数据是连续排列的,解析的时候加上宽和高的信息才成为了一张矩形图片。写代码计算地址偏移的时候要注意行和列的关系。
在这里插入图片描述


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倍了,牺牲一点精度影响不大,后面计算用的无符号8Bit色深,不用太讲究小数点后的位数)。于是:
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 YUV转RGB

也是两个版本:

R = 1.164(Y - 16)                  + 1.596(V - 128)

G = 1.164(Y - 16) - 0.391(U - 128) - 0.813(V - 128) 

B = 1.164(Y - 16) + 2.018(U - 128)
R = Y + ((360 * (V - 128))>>8) ; 
G = Y - (( ( 88 * (U - 128)  + 184 * (V - 128)) )>>8) ; 
B = Y + ((455 * (U - 128))>>8) ;

 

3. 代码

这里参考https://www.jianshu.com/p/e498326a55b1?utm_campaign的代码:


3.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 = 0; i < width; i++)
        {
            R = pRGB[index++];
            G = pRGB[index++];
            B = pRGB[index++];

            Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
            U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
            V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
            
            // NV12  YYYYYYYY UVUV 
            // NV21  YYYYYYYY VUVU
            pNV12[yIndex++] = (byte)((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
            if (j % 2 == 0 && index % 2 == 0)
            {
                pNV12[uvIndex++] = (byte)((U < 0) ? 0 : ((U > 255) ? 255 : U));
                pNV12[