ffmpeg之RGB/YUV格式详解

上一节,我们采集了屏幕图像 并且转换成YUV420P写入了文件中。

既然得到了yuv420p数据,那么接下来的操作就是编码了。


在开始编码之前,我们先发点时间了解下YUV420P这种格式,当然,不了解这个对我们开发录屏软件是没有影响的。

不过既然要做视频开发,不了解YUV420P似乎有点说不过去吧。


那么,下面就开始讲解YUV420P吧。


说到图像大家比较熟悉的估计还是RGB吧,即使不懂RGB的细节,至少也到知道R G B分别代表什么。


计算机彩色显示器显示色彩的原理与彩色电视机一样,都是采用R(Red)、G(Green)、B(Blue)相加混色的原理:通过发射出三种不同强度的电子束,使屏幕内侧覆盖的红、绿、蓝磷光材料发光而产生色彩。这种色彩的表示方法称为RGB色彩空间表示(它也是多媒体计算机技术中用得最多的一种色彩空间表示方法)。


根据三基色原理,任意一种色光F都可以用不同分量的R、G、B三色相加混合而成。

F = r [ R ] + g [ G ] + b [ B ]

  其中,r、g、b分别为三基色参与混合的系数。当三基色分量都为0(最弱)时混合为黑色光;而当三基色分量都为k(最强)时混合为白色光。调整r、g、b三个系数的值,可以混合出介于黑色光和白色光之间的各种各样的色光。


那么YUV又从何而来呢?在现代彩色电视系统中,通常采用三管彩色摄像机或彩色CCD摄像机进行摄像,然后把摄得的彩色图像信号经分色、分别放大校正后得到RGB,再经过矩阵变换电路得到亮度信号Y和两个色差信号R-Y(即U)、B-Y(即V),最后发送端将亮度和色差三个信号分别进行编码,用同一信道发送出去。这种色彩的表示方法就是所谓的YUV色彩空间表示。
  采用YUV色彩空间的重要性是它的亮度信号Y和色度信号U、V是分离的。如果只有Y信号分量而没有U、V分量,那么这样表示的图像就是黑白灰度图像。彩色电视采用YUV空间正是为了用亮度信号Y解决彩色电视机与黑白电视机的兼容问题,使黑白电视机也能接收彩色电视信号。



YUV420介绍:


YUV420格式是指,每个像素都保留一个Y(亮度)分量,而在水平方向上,不是每行都取U和V分量,而是一行只取U分量,则其接着一行就只取V分量,以此重复(即4:2:0, 4:0:2, 4:2:0, 4:0:2 .......),所以420不是指没有V,而是指一行采样只取U,另一行采样只取V。在取U和V时,每两个Y之间取一个U或V。但从4x4矩阵列来看,每4个矩阵点Y区域中,只有一个U和V,所以它们的比值是4:1。所以对于一个像素,RGB需要8 * 3 = 24位,即占3个字节;而YUV420P,8 + 8/4 + 8/4 = 12位,即占2个字节,其中8指Y分量,8/4指U和V分量。


从这里也可以看出,YUV要比RGB节省存储空间。


这里为什么一个分量是8位呢?

不管是R G B还是 Y U V他们每个分量的取值都是0-255,而计算机都是用二进制来存储的。巧了,一个字节(8bit)刚好可以记录0-255的数,所以刚好就是这样存了吧


yuv420又分为yuv420p和yuv420sp.


yuv420sp格式如下:


yuv420P格式如下:


还是图像比较直观,从上面的图片中就能看出来yuv420sp和yuv420p的区别了。

对于所有YUV420图像,它们的Y值排列是完全相同的,因为只有Y的图像就是灰度图像。YUV420sp与YUV420p的数据格式它们的UV排列在原理上是完全不同的。420p它是先把U存放完后,再存放V,也就是说UV它们是连续的。而420sp它是UV、UV这样交替存放的。(记得结合上图查看)


目前我所知道的安卓手机上摄像头采集到的数据就是yuv420sp。

而我们前面保存的是YUV420P格式的数据。

因此我们先抛开yuyv420sp,直接去研究yuv420p。


有了上面的理论,就可以准确的计算出一个YUV420在内存中存放的大小。
Y = width * hight (总和)
U = Y / 4  
V = Y / 4


所以一张YUV图像他的存储空间就是:

 width * height + width * height / 4 + width * height / 4 

化简后就是 width * height *3 /2

看来数学还是有点用的


YUV420P内存中的存储方式:

YUV格式通常有两大类:打包(packed)格式和平面(planar)格式。

前者将YUV分量存放在同一个数组中,通常是几个相邻的像素组成一个宏像素(macro-pixel);

而后者使用三个数组分开存放YUV三个分量


YUV420P(planar格式)在ffmpeg中存储是在struct AVFrame的data[]数组中

data[0]-------Y分量               

data[1]------U分量                          

data[2]-------V分量


现在回过头看一下,我们前面保存yuv420图像的代码:

1
2
3
4
5
6
.....
int  y_size=pCodecCtx->width*pCodecCtx->height;                
fwrite (pFrameYUV->data[0],1,y_size,fp_yuv);     //Y
fwrite (pFrameYUV->data[1],1,y_size/4,fp_yuv);   //U
fwrite (pFrameYUV->data[2],1,y_size/4,fp_yuv);   //V
....

是不是完全搞懂了。

你可能感兴趣的:(ffmpeg)