文章目录
1 位、比特、字节的概念
1.1 位(bit)
我们常说的比特,其实就是位。这是计算机中的最小数据单位,一位二进制的数,说白了就是一个0或者1。
1.2 字节(Byte)
字节是存储空间的基本计量单位,1个字节=8个比特,下面我们用英文单位来表示这个等式:1B=8b。这里我们就一定要对大写的B(Byte)和小写的b(bit)进行严格的区分了。
可见,我们定义的东西最小都是8个比特,即256种8位二进制数的排列方式表示的。
因此,我们经常定义的类型,现在可以大致了解他们的大小:
char:1字节,8bit。最常用的ASCII码定义了128个字符,小于256,故可以表示;
int:4字节,32bit,数据范围为-2147483648~2147483647;
long、double等都是同样的道理,这里不再赘述。
1.3 其他
这些就是我们老生常谈的东西了,前面我们知道了1B=8b,后面我们从B开始看一些更大的东西:
KB:1 KB = 1024 B
MB:1 MB = 1024 KB
GB:1 GB = 1024 MB
TB:1 TB = 1024 GB
1.4 和视频相关
在VVC中,编码方式通常设比特深度为10,即用10位来表示一个像素值,故范围就是0~1023了。
2 分辨率、像素的概念
2.1 分辨率
一张图片分辨率是1920x1080,就是说这张图片在屏幕上按1:1放大时,水平方向有1920个像素点(色块),垂直方向有1080个像素点(色块)。
2.2 像素
每张图片都是由色块组成的,每个色块称为一个像素,如1920x1080的图片就有2073600个像素
2.3 像素值
每个像素里面的颜色就是像素值。若要表示颜色,最原始的情况是采用三原色(红、绿、蓝)使用不同的深浅得到各种想要的颜色,后来为了视频压缩的方便,通过计算得到YCbCr。其中Y是亮度,对应黑白电视,这个分量更为重要,而Cb为相应Y的蓝色分量,Cb的b就是blue;Cr为相应Y的蓝色分量,Cr的r就是red。
前面1.4我们提过像素值的范围是0-1023了哦。这里Y,Cb,Cr的值都是0-1023的值。
3 YCbCr通道的4:2:0格式
人眼对Y分量更敏感,对Cb、Cr分量相对没有过高的要求,故进行视频压缩时,可以采用4:2:0取样方法。
可见,每4个Y分量共用一组Cb和Cr分量,这样对于Cb和Cr分量,需要表示的面积仅仅是Y分量的1/4,且对人眼影响很小,几乎不会看出瑕疵。
4 yuv文件
4.1 存储方式
yuv文件先将Y分量的像素值存起来,占据整个文件2/3的大小,然后将Cb分量的像素值存起来,占据整个文件1/6的大小,最后将Cb分量的像素值存起来,占据整个文件最后1/6的大小。这样,一个视频的一帧就可以被完整表达出来。
4.2 播放方式
视频主要的功能是用来看的。我们已知了yuv文件的储存方法,那么如何将它播放成视频呢?这里需要SDL来把.yuv“扔”在电脑屏幕上。这里就是一个大的话题了,有兴趣的可以参考雷神博客(向雷神致敬),如果后面还会开这种杂谈的话,会具体谈一谈这个的~
5 计算实例
这是编码得到basketballdrill的一帧(分辨率832x480,比特深度为10bit):
下面我们来计算得出这个大小:
比特深度为10,而字节是存储空间的基本计量单位,10位要占用2字节。故一个通道的一个像素都要占用2字节来存储像素值。
分辨率是832x480,则Y分量共有832x480个像素值,Cb和Cr分别有416x240个像素值。
综上,这里的1198080的来源就是:
Y: 832x480x2 = 798720;
Cb:416x240x2 = 199680;
Cr:416x240x2 = 199680;
以上三者相加,得出结果:1198080。
思考:
以下问题请读者自行考虑:
1、如果比特深度为8,为什么大小减小了一半?
2、比特深度为9或10,视频的大小都是一样的,那为何不使用比特深度为16进行视频的编码?
6 比特深度从10转8
带着上面的问题1,我们继续深入内存中,我们探究一下存储像素值的方式:
以比特深度10为例,像素值256将会以下面的方式存储:00000001 00000000
,特别要注意的是,如果将其按char类型8位数组tmp的方式存储,则tmp[1]并非按上面的顺序得到的00000000
,而是前面的00000001
,tmp[0]才是00000000
。这里可能需要读者暂停思考下,因为C语言的逻辑顺序和底层内存并不相同哦。
下面我们要做的是把10比特转换成8比特,那么256这个值应当变成256/4即64。我想,到了这里大家应该都会明白计算的方式了,前面我们知道10比特深度高位的8位只有最后2位有值,将其左移8位得到的数值加上低位的8位即可得到真正的10比特深度像素值,那么接下来除以4就可以将其“下采样”成8比特深度的yuv图片。
举个例子:还是上面的256,高位上tmp[1] = 1,左移8位变为256,加上低位的0依然等于256,除以4等于64,这就是8比特深度下的256的像素值。
YUVBitConvert(int width, int height, short *yuv)//10bit深度,像素值应使用short
{
unsigned char* yuv10 = new unsigned char[width * height * 3];
memcpy(yuv10, yuv, width * height * 3 * sizeof(unsigned char));
unsigned char* yuv8 = m_yuv;
for (int i = 0; i < height * width * 3 / 2; ++i) //由10变成8,每个像素值都少了一位
{
yuv8[i] = (yuv10[2 * i] + yuv10[2 * i + 1] << 8) >> 2;
}
delete yuv10;
yuv10= NULL;
}