前言
想要搭建万能的适配平台,万里长征的第二步是YUV格式里颜色空间等的转化,只有了解熟悉了这一格式和修改方法才能自己根据需要做出调整不是
定义
YUV模型是根据一个亮度(Y分量)和两个色度(UV分量)来定义颜色空间,细分常见的格式有很多,在存储方式部分展开细细说明。比如我们在海思平台下使用相机默认图像格式是NV21或者叫YUV420SP格式
YUV 的原理是把亮度(Luma)与色度(Chroma)分离。
“Y”表示亮度,也就是灰度值。
“U”表示蓝色通道与亮度的差值。
“V”表示红色通道与亮度的差值。
其中 Y 信号分量除了表示亮度信号外,还含有较多的绿色通道量,单纯的 Y 分量可以显示出完整的黑白图像。
U、V 分量分别表示蓝 (blue)、红 (red) 分量信号,只含有色度信息,所以 YUV 也称为 YCbCr,其中,Cb、Cr的含义等同于U、V,C 可以理解为 component 或者 color。
并且,YUV不像RGB那样要求三个独立的视频信号同时传输,所以用YUV方式传送占用极少的频宽。
存储格式
主要有两大类格式,packed和planer(后面会讲衍生的夹在中间的形式)
packed就是说比如有对应每个像素Y0U0V0 Y1U1V1 Y2U2V2 Y3U3V3,先连续存储所有像素点的 Y,紧接着存储所有像素点的 U,随后是所有像素点的 V。相当于将 YUV 拆分成三个平面 (plane) 存储。
planer就是Y0Y1Y2Y3 U0U1U2U3 V0V1V2V3,每个像素点的 Y,U,V 是连续交替存储的
看起来好像和RGB换汤不换药啊。别急别急,听后面慢慢分析~
RGB相互转化
谈到rgb顺便补充下和RGB的转换公式吧,
体现在代码上也可以是
Y = (0.257 * R) + (0.504 * G) + (0.098 * B) + 16 Cr = V = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128 Cb = U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128 B = 1.164(Y - 16) + 2.018(U - 128) G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) R = 1.164(Y - 16) + 1.596(V - 128)
很明显,单单一张图片都有不小的浮点运算量的,还好海思平台有现成的硬件加速可以实现,不需要我们一个个像素硬转。
在RGB色彩空间中,三个颜色的重要程度相同,所以需要使用相同的分辨率进行存储,最多使用RGB565这样的形式减少量化的精度,但数据量还是很大的。
因为研究发现人眼对亮度的敏感超过色度。将图像的亮度信息和颜色信息分离,并使用不同的分辨率进行存储,这样在对主观感觉影响很小的前提下,可以更加有效地存储图像数据
YUV的取值范围
与RGB每个像素点的每个分量取值范围为0-255不同(每个分量占8bit),YUV取值范围有两种:
1. 以Rec.601为代表(还包括BT.709 / BT.2020)的广播电视标准中,Y的取值范围是16-235,U、V的取值范围是16-240。FFmpeg中称之为“mpeg”范围。
2. 以JPEG为代表的标准中,Y、U、V的取值范围都是0-255。FFmpeg中称之为“jpeg” 范围。
实际中最常见的是第1种取值范围的YUV(可以自己观察一下YUV的数据,会发现其中亮度分量没有取值为0、255这样的数值)。很多人在这个地方会有疑惑,为什么会去掉“两边”的取值呢?
原因:
• 在广播电视系统中不传输很低和很高的数值,实际上是为了防止信号变动造成过载,因而把这“两边”的数值作为“保护带”。
YUV的采样格式
主要的采样格式有YUV4:4:4、YUV4:2:2、YUV4:2:0 ,其中YUV4:2:0是最常用的采样格式。
采样就是根据一定的间隔取值。其中的比例是指 Y、U、V 表示的像素,三者分别占的比值。
下图是YUV4:4:4,YUV4:2:2,YUV4:2:0的采样示意图,以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量。
• YUV 4:4:4采样,每一个Y对应一组UV分量。
• YUV 4:2:2采样,每两个Y共用一组UV分量。
• YUV 4:2:0采样,每四个Y共用一组UV分量。
YUV存储方式
看到这里,就会发现到yuv的一个精髓了,开始从存储方式上与RGB渐行渐远
下面用图的形式给出常见的YUV数据的存储方式,并在后面附有取样每个像素点的YUV数据的方法。开始根据YUV的数量进行排列组合吧!
(1) YUYV 格式 (属于YUV422)
相邻的两个Y共用其相邻的两个Cb、Cr,对于像素点Y’00、Y’01 而言,其Cb、Cr的值均为 Cb00、Cr00,其他的像素点的YUV取值依次类推。
(2) UYVY 格式 (属于YUV422)
与YUYV不同的是UV的排列顺序不一样,还原其每个像素点的YUV值的方法与上面一样。
(3) YUV422P(属于YUV422)
YUV422P是一种平面模式,其每一个像素点的YUV值提取方法也是遵循YUV422格式的最基本提取方法,即两个Y共用一个UV。比如,对于像素点Y’00、Y’01 而言,其Cb、Cr的值均为 Cb00、Cr00。
(4) YUV420P格式(YVU420P)(属于YUV420)
YUV420P,Y,U,V三个分量都是平面格式,分为YU12(安卓平台下也通常叫I420)和YV12。I420格式和YV12格式的不同处在U平面和V平面的位置不同。注意,上图中,Y’00、Y’01、Y’10、Y’11共用Cr00、Cb00,其他依次类推。在I420格式中,U平面紧跟在Y平面之后,然后才是V平面(即:YUV);但YV12则是相反(即:YVU)。其实,只要注意从适当的位置提取U和V值,YU12和YV12都可以使用相同的算法进行处理。
I420: YYYYYYYY UU VV
YV12: YYYYYYYY VV UU
很明显,YV12和I420并没有很大区别,换了UV顺序罢了I420也叫YU12,,IYUV
(5) YUV420sp 分为NV12、NV21(YVU420SP),也属于YUV420。
这两种格式的不同在于UV交错排列的顺序不同,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)为交错存储,而不是分为三个plane。其提取方式与上一种类似,即Y’00、Y’01、Y’10、Y’11共用Cr00、Cb00,其他依次类推。
假设一个分辨率为8X4的YUV图像,它们的格式如下图:
YUV420sp格式如下图
NV12: YYYYYYYY UVUV
NV21: YYYYYYYY VUVU
同样的,NV12和NV21也是交换了uv的顺序而已
大小计算
以w*h大小图像的YUV420数据为例,
其存储格式是: 共大小为(w * h * 3/2)字节,
Y分量:(w * h)个字节
U(Cb)分量:(w * h/4)个字节
V(Cr)分量:(w * h/4)个字节
特殊情况
考虑到海思为了硬件加速采用的对齐机制,实际的数据可能是这样的,如果我们想进行更改切记考虑到padding的问题(最开始的分辨率选的刚刚好踩坑,下篇会提到)