一 什么是YUV文件
Y'UV的发明是由于彩色电视与黑白电视的过渡时期。黑白视讯只有Y(Luma,Luminance)视讯,也就是灰阶值。到了彩色电视规格的制定,是以YUV/YIQ的格式来处理彩色电视图像,把UV视作表示彩度的C(Chrominance或Chroma),如果忽略C信号,那么剩下的Y(Luma)信号就跟之前的黑白电视信号相同,这样一来便解决彩色电视机与黑白电视机的相容问。YYV文件实际上是将RGB文件拆解成了亮度和色度信息保存,Y'UV最大的优点在于只需占用极少的带宽。
RGB文件最大的问题是RGB的视频文件难以操作,如果我希望降低视频文件的亮度,RGB文件必须同时处理RGB三色,并且在数据上会存在失真,为了解决这个问题,YCbCr形式的文件就诞生了,Y是亮度频道(luminance channel),Cb是蓝色色度频道(Bluechannel),Cr是红色色度频道(Red channel)。
1.1 YUV文件的采样方式
YUV文件的色度频道的采样频率是可以比亮度频道的采样频率低的,但不会明显的降低视觉质量,于是乎就有一种用于描述YUV文件的采样频率比的方法A:B:C
l 444代表没有亮度和色度都没有下采样
l 422表示2:1的水平下采样,但是没有垂直下采样。每行扫描都含有4个Y采样点,2个U采样点和两个V采样点。即,在水平方向的隔行采样,垂直方向全采样。
l 420表示2:1的水平下采样和2:1的垂直下采样。每行扫描都好友4个Y采样点,1个U采样点和一个V采样点。即,在水平方向隔行采样,垂直方向也隔行采样。
1.2YUV文件的存储方式
对于8bit深度的数据而言,一个字节就是一个采样点。10bit或者12bit的数据深度,则需要两个字节来存储一个采样点,此时低位优先,最低有效位数据对齐。即:Y[0]Y[1]表示一个Y采样数据,那么其值为Y[1]*256+Y[0]。
YUV文件的存储方式指的是YUV文件中YUV数据在其Byte System中的排布或者说位置。YUV文件的数据存储方式有两种Packed format和 Planarformat。
在Packed format中,YUV文件的Y/U/V数据打包在了一起,依次存放,如下图所示。
在Planar format中,YUV的文件存放是分开的,先存放一帧图片的Y数据,然后存放UV数据。
IMC4,i420格式的文件存放方式:Y + U + V
IMC2、YV12的存放方式:Y + V + U
1.3 YUV和RGB之间的转换
从RGB到YUV:
从YUV到RGB:
以矩阵表示法(matrix representation),可得到公式:
二 HEVC标准测试视频文件分析
HEVC标准测试文件有很多,此处对BasketballDrill_832x480_50.yuv分析其文件格式。
此文件的分辨率为832x480,文件数据深度为8bit,文件格式为420,总帧数为501帧。
在这里我用了两个软件:Elecard和Binary Viewer
首先用Binary Viewer打开该文件:
其文件大小为300119040字节
计算YUV数据量:(832*480 + 832*480/4 + 832*480/4)*501 = 300119040
由此可以看出,YUV文件全部是数据,此处就有一个疑惑,它的文件格式,文件名等等信息怎么存储的呢?有二进制查看器打开其他的文件,比如一个图片什么的,都会找到文件格式、文件名之类的东西,但是YUV文件却什么都没有,只有数据,真心不理解。
再用Elecard,以YV12显示的方式打开该文件,并查看其144*15那个点的数据。
由图中可以看到,红色框中的UV值都是一样的,因为是420采样,横向和纵向都下采样了,在从420转换成444时,直接填充。所以才会出现一个2*2的块中的UV值都是一样的。且下标都是从0开始,所以:
Y的位置为:832*15+145 = 12625
U的位置为:832*480 + 832/2*(15-1)/2 + 144/2 +1 =402345
V的位置为:832*480 + 832*480/4 + 832/2*(15-1)/2 + 144/2 +1 = 502185
从Binary Viewer定位到这些位置,便可以查看到相应的值。需要注意两点,Binary Viewer中的下标是从0开始的,所以在定位时,需要减1;并且,上面是YV12打开的,所以UV的位置反了。
三 Matlab读取一个YUV文件并查看
此处我读取了一帧的数据,然后将YUV420转为YUV44最后转为RGB显示出来:
fid=fopen('BasketballDrill_832x480_50.yuv','rb');
width = 832
height = 480
%% Read a yuv file ,thefile format is i420
Y = fread(fid,[width,height],'uint8');
U = fread(fid,[width/2,height/2],'uint8');
V = fread(fid,[width/2,height/2],'uint8');
%% inital memory for yuv444
YY=zeros(width,height,'uint8');
UU=zeros(width,height,'uint8');
VV=zeros(width,height,'uint8');
%% fill the empty of yuv444in U and V
UU(1:2:width-1,1:2:height-1)=U(:,:);
UU(1:2:width-1,2:2:height)=U(:,:);
UU(2:2:width,1:2:height-1)=U(:,:);
UU(2:2:width,2:2:height)=U(:,:);
VV(1:2:width-1,1:2:height-1)=V(:,:);
VV(1:2:width-1,2:2:height)=V(:,:);
VV(2:2:width,1:2:height-1)=V(:,:);
VV(2:2:width,2:2:height)=V(:,:);
%% transform the yuv444 toRGB,and show the picture
YUV444=cat(3,Y',UU',VV');
RGB=ycbcr2rgb(YUV444);
imshow(RGB)
最后显示的图像如图所示:
在U/V都为0时的图像:
只有U为0时的图像:
只有V为0的图像: