一、RGB与YUV
首先了解视频编码为什么使用YUV420而不是RGB。RGB 色彩空间更适合图像采集和显示, YUV 空间用于编码和存储则比较好。在存储和编码之前,RGB 图像要转换为 YUV 图像,而 YUV 图像在显示之前通常有必要转换回 RGB。这里显示的时候 YUV 转成 RGB 通常是硬件或者软件内部做了,我们写代码开发的时候YUV转 RGB显示到屏幕这个过程通常是透明的。
看图说话。
![](https://i-blog.csdnimg.cn/blog_migrate/17c4b27a52a416d406f808203f71aa20.png)
下面看一下两者之间内存暂用对比,这里采用RGB24对比:RGB24使用24位来表示一个像素,RGB分量都用8位表示,取值范围为0-255。在一个2*2的像素区域,RGB暂用的字节数为2*2*3=12字节。那么用yuv表示,占用的字节数为4(Y)+1(u)+1(v)=6字节,其中Y占用4个字节,U和V各占用1字节,比例为4:1:1
所以在一个宽高为w*h的设备上,使用rgb表示编码占用的字节数为w*h*3,使用yuv表示暂用的内存为w*h*+w*h/4+w*h/4 = w*h*3/2. 显然可以计算得到此种格式下YUV所占用的数据量是RGB24的二分之一
二、YUV为什么可以
由于我们眼睛的视网膜杆细胞多于视网膜的锥细胞,而视网膜的杆细胞是识别亮度的,锥细胞是识别色度的,所以我们的眼睛对于明暗的分辨要比对颜色的分辨要精细,也就是我们眼睛对于亮度的敏感程度要大于色度的敏感程度。那么,我们在存储图像信息时,为了节约空间,就没有必要将所有的色度信息全部存储下来了。
因此在上色项目中,我们只是在较小尺寸上进行上色任务,然后结合上色小图片的颜色信息(UV)与超分后图片的纹理信息(Y)综合得到最终结果。
视觉心理学研究表明,人的视觉系统对光的感知程度可以用两个属性来描述:亮度(luminance)跟 色度(chrominance)。色度感知 包含两个维度:色调(Hue)和 色饱和度(saturation)。色调是由光波的峰值定义的,描述的是光的颜色。色饱和度是由光波的谱宽定义的,描述的是光的纯度。
因此 HVS 对色彩的感知主要有 3个属性:亮度(luminance),色调(Hue)和 色饱和度(saturation)。也就是 YUV 色彩空间,Y 代表 亮度,U代表色调,V代表色饱和度
三、YUV的不同格式
YUV 相比于 RGB 格式最大的好处是可以做到在保持图像质量降低不明显的前提下,减小文件大小。TUV 格式之所以能够做到,是因为进行了采样操作。
YUV 码流的存储格式与其采样方式密切相关,主流的采样方式有三种:YUV 4:4:4(YUV444),YUV 4:2:2(YUV422),YUV 4:2:0(YUV420)。
YUV 4:4:4 采样,每一个 Y 对应一组 UV 分量。
YUV 4:2:2 采样,每两个 Y 共用一组 UV 分量。
YUV 4:2:0 采样,每四个 Y 共用一组 UV 分量。
对于最常用的YUV420格式与422格式,仍然有不同的区分。
![](https://i-blog.csdnimg.cn/blog_migrate/9260cb6c965b585f354b9453a95dbc04.png)
![](https://i-blog.csdnimg.cn/blog_migrate/11d35d0f75cecf5dfb3b4616186d6831.png)
尤其要注意的是nv21虽然是yuv420的一种,但nv21是针对android设备的视频编码。因此一般的从安卓手机中拿到的raw数据都是nv21格式。
nv21编码格式:比如一张3000*4032的图片,经过nv21编码后,会变成前面3000*4032字节全是Y,从3000*4032字节长度开始,U和V会交替排列,uv的字节长度合起来为3000/2*4032。
I420也是YUV420编码格式的一种,由于android手机厂商的原因,摄像头采集到的数据永远都是经过NV21编码的数据,但是对于这种数据不能够显示在苹果或windows平台,那么需要对这个编码格式的数据需要重新编码,其中I420这种编码格式,所有的厂商都是适配的。
四、raw转nv21转RGB代码
def yuv420sp_to_rgb888(width, height, yuv):
# function requires both width and height to be multiples of 4
if (width % 4) or (height % 4):
raise Exception("width and height must be multiples of 4")
rgb_bytes = bytearray(width*height*3)
#yuv = np.array(yuv, dtype=np.uint8).reshape(height*3, width)
array = np.frombuffer(yuv, dtype=np.uint8).reshape(4500, 4032)
print(array.shape)
RGBImage = cv2.cvtColor(array, cv2.COLOR_YUV2RGB_NV21)
return RGBImage
def raw2jpg(source, dest):
print("opening file")
f = open(source, "rb")
yuv = f.read()
f.close()
print("read file")
img = yuv420sp_to_rgb888(4032, 3000, yuv)
print("finished conversion. Creating image object")
cv2.imwrite(dest, img)
print("Save completed")
注意下opencv内部对于nv12等不同格式YUV与RGB转换都是非常到位的,合理使用opencv工具可以起到提升工作效率的效果