YUV基础知识《转载》

版权声明:GUN For Free https://blog.csdn.net/u010842019/article/details/52086103
                                        <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-e2445db1a8.css">
                    <div class="htmledit_views">

YUV学习文档


     

     


第一幅是Y分量描述黑白图像

第二幅是U(V)分量描述

第三幅是V(U)分量描述

第四幅是YUV三幅合成后得到的正常图像


1.      YUV简介

YUV是一种颜色编码方法,它和我们熟知的RGB红绿蓝颜色体系相对应,它们之间能通过公式相互转换。而YUV区别于RGB的重要一点是采用YUV色彩空间亮度信号Y和色度信号U、V是分离的,这样就使得亮度Y和色差UV三个信号分别进行编码,用同一信道发送出去。

在摄像头之类编程经常是会碰到YUV格式,研究证明,人眼对亮度的敏感超过色度。利用这个原理,可以把色度信息减少一点,人眼也无法查觉这一点。这样操作达到的效果就是对于一张图片使用YUV格式保存将更少的占用存储空间。

 

 

I420: YYYYYYYY UU VV    =>YUV420P
YV12: YYYYYYYY VV UU    =>YUV420P
NV12: YYYYYYYY UVUV     =>YUV420SP
NV21: YYYYYYYY VUVU     =>YUV420SP

 

 

2.      YUV的存储格式

RGB格式中,一个像素点要占用4字节空间32比特位。并且RGB格式每个点的数据是连继保存在一起的。而YUV格式根据采样方式的不同,每个像素点所占用的空间是不同的。YUV格式的每个点不是连续保存的,可以根据UV分量的保存类型可以把YUV存储格式分为:

紧缩格式(packedformats):将Y、U、V值储存成Macro Pixels阵列,和RGB的存放方式类似。

平面格式(planarformats):将Y、U、V的三个分量分别存放在不同的矩阵中。

 

 

——–Planar类型存储格式

 

 

–packed格式其中A代表Alps透明度

 

 

 

 

 

 

 

3.     YUV的采样方式

 YUV采样方式,主要描述像素Y、U、V分量采样比例,即表达每个像素时,Y、U、V分量的数目,通常有三种方式:YUV4:4:4,YUV4:2:2,YUV4:2:0。

YUV4:4:4采样,每一个Y对应一组UV分量。

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

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

用图直观地表示采集的方式,以黑点表示采样该像点的Y分量,以空心圆圈表示采用该像素点的UV分量。

 

 

采样四个像素点下各种YUV采样格式的对比:

 

使用YUV4:4:4采样,一共要进行12次采样分别4个Y,4个U,4个V,对每一个Y,U,V每个需要8个比特位,就需要12*8=96位,平均下每个像素点需要96/4=24位比特位表示。

 

使用YUV4:2:2采样,就需要8*8 =64位,平均每个像素64/4=16位。

 

使用YUV4:2:0采样,就需要6*8 =48位,平均每个像素48/4=12位。

 

 

 

 

 

4.      常见的YUV码流的存储方式

Cb、Cr的含义等同于U、V

YUVY 采用422采样格式,packed存储格式

 

 

 

 

 

 

 

YUVY 采用422采样格式,Planar存储格式

 

 

 

 




YV12,YU12 采用420采样格式,Planar存储格式

YV12,YU12 区别就是信号量V和U哪一个在前。




 

NV12,NV21采用420采样格式,Planar存储格式,但是区别于YV12,YU12,它的U和V分量是相连保存的。

NV12,NV21区别就是信号量V和U哪一个在前。

 


 

YUV420P 和 YUV420SP对比图

 

 

 

 

 

 


5.    YUV数据的转换

程序在运行时,有时候需要根据需求对所处理的YUV数据进行转换。例如程序处理需要YUV420SP数据时,当前数据是YUV420P,那么就需要对数据进行处理。



 

在处理从YUV420P数据格式从YUV420SP数据格式时,对于已经对齐的分辨率如

1280*720,640*480这样的分辨率它的YUV420P数据格式是完全对齐(16位对齐)

而像176*144这样的size它的YUV420P不是16位对齐,需要补空白位,使得176*144这样的size能16位对齐。

 

YUV’420SP数据大小的计算公式是: size = width * heigh * 1.5;

 

YUV420P数据大小的计算公式是:

size = stride * height

stride = ALIGN(width /2, 16) = width +padding

 

         static int ALIGN(int x, int y) {

            return (x + y - 1) & ~(y - 1);  // y must be a power of 2.

}

其中stride的含义:

当视频图像存储在内存时,图像的每一行末尾也许【与size是否16位对齐有关】包含一些扩展的内容,这些扩展的内容只影响图像如何存储在内存中,但是不影响图像如何显示出来。

Stride 就是这些扩展内容的名称,Stride 也被称作 Pitch,如果图像的每一行像素末尾拥有扩展内容,Stride 的值一定大于图像的宽度值,就像下图所示:


Pading可能有要看size是否16位对齐,pading也可能不在末尾,视具体size确定。

Padiing = ALIGN(Width/2,16)

Stride = Padiing + Width = ALIGN(Width/2,16)+ Width

Size =Stride * Height

 

 

         static int ALIGN(int x, int y) {

            return (x + y - 1) & ~(y - 1);  // y must be a power of 2.

}

 

 

YUV420P数据格式 如果Pading的计算结果不为Width/2 那么该size不是16位对齐的

根据公式分别代入 1280*720  640*480    176*144

1280*720

Stride = 1280 + Padiing = 1280 + 640 = 1920

Padiing = ALIGN(1280/2,16) = 640

Size =Stride * Height = 1920 * 720 =1382400

 

 

640*480

Stride = 640+ Padiing = 640 + 320 = 960

Padiing = ALIGN(640/2,16) = 320

Size =Stride * Height = 960 * 480 = 460800

 

176*144

Stride = 176+ Padiing = 176+ 96 = 272

Padiing = ALIGN(176/2,16) = 96 【176/2=88 但是计算得到96 该size不是16位对齐】

Size =Stride * Height = 272* 144 = 39168

 

YUV420SP数据格式   size = width* heigh * 1.5;

根据公式分别代入 1280*720  640*480    176*144

 

1280*720

size = width * heigh * 1.5 = 1280*720*1.5 =1382400

【该size是16位对齐YUV420P 和 YUV420SP数据大小一致 都为1382400 】

 

 

640*480

size = width * heigh * 1.5 = 640*480*1.5 =460800

【该size是16位对齐YUV420P 和 YUV420SP数据大小一致 都为460800】

 

176*144

size = width * heigh * 1.5 = 176*144*1.5 = 38016

【该size不是16位对齐YUV420P的数据大小为39168,YUV420SP数据大小为38016】

 

对于16位对齐的size来说,YUV420P 和 YUV420SP的数据大小是一致的,数据码流中不存在为了补齐而填充的空位,对于这样的size来说存在一个统一的函数来进行转换。

Yv12是YUV420P格式

NV21 是YUV420SP格式


          yv12_to_nv21((char*)(node.getImgBuf()->getVirAddr()),

          (char *)(Previewnode.getImgBuf()->getVirAddr()),

          (size_t)(node.getImgBuf()->getBufSize()));

 

         bool yv12_to_nv21(char *pyv12, char *pnv21, size_t size)

{

      size_t wh_size = size * 2 / 3;

// wh_size计算的是Y分量的大小,Y分量占用总空间的二分之三

//4:2:0 中总共6份Y占用4份,UV各占用1份得出

 

     size_t i = 0;

     char *pnv = pnv21 + wh_size; //NV21格式中,V分量的第一个数据的地址

     char *pyv_v = pyv12 + wh_size;//YV12格式中V分量的第一个数据的地址

     char *pyv_u = pyv12 + wh_size*5/4; //YV12格式中U分量的第一个数据的地址

 

      if (!pyv12 || !pnv21)

               return false;

// 从YV12复制Y分量到NV21,总共wh_size个Y分量

      memcpy(pnv21, pyv12, wh_size);

 

//遍历UV分量 UV分量各自的数量是Y分量的四分之一即 wh_size/4

      for (i = 0; i < wh_size/4; i++) {

//每次循环赋值一个V分量后 接着赋值一个U分量由YUV420Sp存储格式决定

               *(pnv++) = *(pyv_v++);

               *(pnv++) = *(pyv_u++);

      }

      if (wh_size/4 == i) //最终的判断语句,//到此数据转换完成。

               return true;

      else

               return false;

}

 

 

对于16位不对齐的size来说,YUV420P 和 YUV420SP的数据大小是不一致的,这就需要根据具体的size的存储方式以及填补的空位来决定转换方法,16位不对齐的size没有统一的一个转换方法,每个16位不对齐的size的转换方法都是不相同的。

对于176*144 这个size

YUV420P的大小为 39168

YUV420Sp的大小为 38016

这就需要写一个专门的函数进行转换

首先YUV420P 数据中肯定存在空白数据,其中38016个数据是有用,可知总共有

39168 - 38016=1152个空白的数据,我们的转换的方法就是过滤掉这1152个数据点,得到有用的那38016个数据。

 

由YUV420P的计算公式

176*144

Stride = 176+ Padiing = 176+ 96 = 272

Padiing = ALIGN(176/2,16) = 96 【176/2=88 但是计算得到96 该size不是16位对齐】

Size =Stride * Height = 272* 144 = 39168


通过遍历数据点得到空白数据点的位置在UV分量中

 

 

 

 

bool yv12_to_nv21(char *pyv12, char *pnv21, size_t size){

for(int i =0 ; i<272; i++){

 for(int j=0 ; j<144; j++){

// 打印所有数据点 查看空白点

MY_LOGE(“row = %d  colum =%d pyv12 = %c”, i,j, *( pyv12++));

}

}

}

 

通过查看数据得到

Y分量区域不存在空白数据点

U分量区域空白数据点是在

奇数行 第一行的88 89 90 91 92 93 94 95 位上的数据 总共8位数据

第三行的88 89 90 91 92 93 94 95 位上的数据 总共8位数据

……

偶数行 第二行的40 41 42 43 44 45 46 47 位上的数据

               136 137 138 139 140 141 142 143 位上的数据为空数据

 

第四行的40 41 42 43 44 45 46 47 位上的数据

               136 137 138 139 140 141 142 143 位上的数据为空数据

U分量区域的空白数据位 =  (8+16)* 24 = 576个

 

V分量区域空白数据点排列与U分量区域的空白数据一致

奇数行 第一行的88 89 90 91 92 93 94 95 位上的数据 总共8位数据

第三行的88 89 90 91 92 93 94 95 位上的数据 总共8位数据

……

偶数行 第二行的40 41 42 43 44 45 46 47 位上的数据

               136 137 138 139 140 141 142 143 位上的数据为空数据

 

第四行的40 41 42 43 44 45 46 47 位上的数据

               136 137 138 139 140 141 142 143 位上的数据为空数据

V分量区域的空白数据位 =  (8+16)* 24 = 576个

 

总共的空白数据点位 = V分量区域的空白数据位 + U分量区域的空白数据位

= 576 +576

                   =1152

YUV420p的size = 39168

YUV420sp的size = 38016

39168 – 38016 = 1152 由此可知 空白数据已经完全被找出来,剩下的就是位这样的size写一个专门的函数,进行转换。

 

 


得到的转换函数如下:

bool yv12_to_nv21_mms(char *pyv12, char*pnv21, size_t size)

  {

         size_t i = 0;

         size_t j = 0;

         char *pyv12_mms = pyv12;

         char *pnv21_mms = pnv21;

         char *pyv12_mms_v;

         char *pyv12_mms_u;

 

                  for (i = 0; i < 176; i++){

                          for (j = 0; j <144; j++)

                                 *(pnv21_mms++) = *(pyv12_mms++);

                  }

 

                  pyv12_mms_v = pyv12+25344;

                  pyv12_mms_u =pyv12+6912+25344;

 

                  for (i = 0; i < 48; i++) {

                  if(i%2 == 0){

                        for (j=0; j < 88;j++) {

                        *(pnv21_mms++) =*(pyv12_mms_v++);

                        *(pnv21_mms++) = *(pyv12_mms_u++);

                        }

                        pyv12_mms_v =pyv12_mms_v + 8;

                        pyv12_mms_u =pyv12_mms_u + 8;

 

                        for (j=0; j < 48;j++) {

                        *(pnv21_mms++) =*(pyv12_mms_v++);

                        *(pnv21_mms++) =*(pyv12_mms_u++);

                        }

                    }

                    else {

                        for (j=0; j < 40;j++) {

                        *(pnv21_mms++) = *(pyv12_mms_v++);

                        *(pnv21_mms++) =*(pyv12_mms_u++);

                        }

                        pyv12_mms_v =pyv12_mms_v + 8;

                        pyv12_mms_u =pyv12_mms_u + 8;

 

                        for (j=0; j < 88;j++) {

                        *(pnv21_mms++) =*(pyv12_mms_v++);

                        *(pnv21_mms++) =*(pyv12_mms_u++);

                        }

                        pyv12_mms_v =pyv12_mms_v + 8;

                        pyv12_mms_u = pyv12_mms_u+ 8;

                   }

               }

         return true;

    }

 

剧终!

        </div>
            </div>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值