本来不想写YUV的,不过再写编码程序的时候,老是编码不成功,没办法,该还的总是要还的,还是来补补知识吧。
7.1 引入YUV
我们常见的视频格式RGB888,不知道有没有做过显示屏的同学,我们在显示屏显示中,每个像素是使用RGB888或者RGRB565,这里利用了三种基色可以组合成任意一种颜色,这样我们人眼就可以看到任何颜色了。但是RGB这种存储方式会带来一个问题,我们用RGB888来举例来计算视频的大小:
一个像素就用3byte存储,那如果是1280720的(低清通道)图片,存储一张需要:12807203=2.637MB,如果是视频的话(应该都知道视频其实就是好多张图片连接起来的,这样就可以看到是视频了),如果是一个90分钟的电影,每秒25帧,则一部电影的大小为2.637M * 90分钟 60秒 * 25帧 = 347.651G。看看一部视频就300G,我们硬盘才多少G,所以如果直接存储RGB是存储不下来的,所以需要压缩编码。
所以这时候YUV格式就出现了,(历史怎么来的,我们这里不探讨,有兴趣的同学可以自己去查)。
YUV,是一种颜色编码方法。常使用在各个视频处理组件中。 YUV在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽。YUV是编译true-color颜色空间(color space)的种类,Y’UV, YUV, YCbCr,YPbPr等专有名词都可以称为YUV,彼此有重叠。“
Y”表示明亮度(Luminance或Luma),也就是灰阶值,
“U”和“V”表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。《百度百科》
概念的东西还是要抄抄百度百科的是吧。
7.2 采样格式
像RGB是每个像素都要3个字节表示,所以不存在采样。但是在YUV格式中,需要对UV分量的数据进行压缩,在压缩过后,其实对整体的质量影响不大,这样YUV所占用的空间就比RGB的要小。
YUV是怎么压缩的呢?其实就是把颜色分量的采样调整了一下,所以我们平时表示YUV也用比率来表示不同的格式。比如YUV444 表示三者是比值此是 4:4:4,即一个点数据点,Y,U,V的空间都是一样大小。
目前主要有如下比例,注意所有格式中Y比值都是4,占一个字节,表示没有减少采样。不同格式中,减小只是UV的采样值。
-
4:4:4 表示色度值(UV)没有减少采样。即Y,U,V各占一个字节,加上Alpha通道一个字节,总共占4字节.这个格式其实就是24bpp的RGB格式了。
-
4:2:2 表示UV分量采样减半,比如第一个像素采样Y,U,第二个像素采样Y,V,依次类推,这样每个点占用2个字节.二个像素组成一个宏像素.
-
4:2:0 这种采样并不意味着只有Y,Cb而没有Cr分量,这里的0说的U,V分量隔行才采样一次。比如第一行采样 4:2:0 ,第二行采样 4:0:2 ,依次类推…在这种采样方式下,每一个像素占用16bits或10bits空间.
-
4:1:1 可以参考4:2:2分量,是进一步压缩,每隔四个点才采一次U和V分量。一般是第0点采Y,U,第1点采Y,第3点采YV,第四点采Y,依次类推。
除了4:4:4采样,其余采样后信号重新还原显示后,会丢失部分UV数据,只能用相临的数据补齐,但人眼对UV不敏感,因此总体感觉损失不大。
用三个图来直观地表示采集的方式吧,以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量。
先记住下面这段话,以后提取每个像素的YUV分量会用到。
YUV 4:4:4采样,每一个Y对应一组UV分量。
YUV 4:2:2采样,每两个Y共用一组UV分量。
YUV 4:2:0采样,每四个Y共用一组UV分量。
还是下面这个描述容易懂。
7.3 存储格式
YUV的存储和RGB最大的不同,就是RGB每个点的数据都是连续保存在一起的,而YUV数据为了节省空间,UV分量会减少,像上面所说,几个Y才共用一个UV分量,所以YUV的数据是不对等的。
YUV格式有两大类:planar和packed:
对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。
对于packed的YUV格式,每个像素点的Y,U,V是连续交叉存储的。
7.3.1 YU12/I420
该格式属于4:2:0类型,存储方式上面已经说过,就是先存储把全部的Y分量存完,再存U分量,最后存V分量,从网上找了一张很形象的图:
可以看到,第一行的Y1Y2和第二行的Y7Y8共同使用一组UV分量U1V1。
7.3.2 YV12
该格式与YU12基本一样,唯一的区别是先存储V分量再存储U分量,对应到上图把第五行和第六行位置互换一下就是了。
以上两种格式我们可以看到都是4:2:0的,因为都是planar方式存储,简称420p。
除了上面两种,还有两种4:2:0,NV12和NV21,这两种是比较特殊的存储格式,是planar和packed混合存储的,分别看下:
7.3.3 NV12
该格式是先存储全部的Y分量,然后UV分量交叉存储,用图像表示下:
很直观,不多说了。
7.3.4 NV21
该格式与NV21的区别和上面YU12/YV12一样,唯一的区别只是UV分量交叉的顺序不同,NV12是U排前面,NV21是V排前面,用图像表示如下:
上面两种虽然也是4:2:0类型,但是并不是完全的planar格式,所以又称为420sp,与420p进行区分。
上面说的都是4:2:0类型的,下面说几个4:2:2类型较常见的
7.3.5 YUV422P
名字中带P表示是planar格式存储,该格式存储方式与I420是一样的,唯一的区别是UV分量的数量不同,I420中四个Y共用一组UV,而该格式中两个Y共用一组UV,也就是说UV分量相对于I420在数量上多了一倍,从网上找了一张图,如下:
如上图,在渲染时Y00与Y01会共用U00和V00.
7.3.6 YUYV/YUY2
该格式属于4:2:2类型,且是用packed形式存储的,上面也简单的说过,存储方式如下图:
可以看到,每两个Y分量共用一组UV分量,存储顺序是YUYV。
7.3.7 YVYU
该格式与YUYV相似,只是存储时UV分量顺序不同而已,为YVYU。
7.3.8 UYVY
该格式也是4:2:2类型,与上面两种方式并无大的不同,从网上找了一张图如下:
可以看到存储时YUV分量的顺序如名字所示:UYVY。
7.4 计算
上面讲过,90分钟的1280720的电影,每秒25帧存储的RGB格式大小:2.637M * 90分钟 60秒 * 25帧 = 347.651G。现在我们用YUV420p的格式计算一下,YUV420p描述一个像素的大小为:1(Y)+0.5(2/4 UV) = 1.5,一张1280720的图片大小:1280720*1.5byte = 1.318MB,所以电影大小的话为:1.318M * 90分钟 * 60秒 * 25帧 = 173.76GB。
是不是感觉还是很大,虽然比RGB小了3倍,但是还是很大,所以压缩到YUV格式还是不够的,这时候还需要继续压缩,压缩成h264格式,也就是上篇文章写的h264格式解析,可以好好了解,就会发现之间的联系了。
7.5 YUV和RGB转换
Y = 0.299R + 0.587G+ 0.114B
U = -0.147R -0.289G + 0.436B
V = 0.615R - 0.515G- 0.100B
R = Y + 1.14V
G = Y - 0.39U -0.58V
B = Y + 2.03U
网上找到的,我也还没试过。
YUV420p的 YUV分量大小:
即YUV数据的0--720×480字节是Y分量值,
720×480--720×480×5/4字节是U分量
720×480×5/4 --720×480×3/2字节是V分量。
7.6 举例
下一节实现编码的话,就是读取这个YUV的文件,一帧一帧进行编码,编码之后,就可以利用我们之前的推流,进行推,就是实现这个编码的时候,一直编码不成功,所以才来复习一下YUV格式,把原来的编码文章往后推了一章。
既然学习了YUV格式,我们就知道存储一帧720480的420P的视频总大小=720480*1.5=518400,然后我们在上面的视频中截取第一帧,也就是518400字节大小,可以用dd命令截取,这个比较好用,
dd if=720x480_25fps_420p.yuv of=1.yuv count=1 bs=518400
截取出来的图片,使用yuv的软件打开
论证了上面的说法,我们就可以把这一帧的数据进行编码了,下一节解析FFMPEF编码。
7.7 其他
libyuv,Google开源的实现各种YUV与RGB之间相互转换,旋转,缩放的库,比FFmpeg效率高,有兴趣可以去看看。