分享

RGB、YUV420、NV21、I420编码; raw转nv21转RGB代码

 金刚光 2023-05-10 发布于辽宁


一、RGB与YUV

首先了解视频编码为什么使用YUV420而不是RGB。RGB 色彩空间更适合图像采集和显示, YUV 空间用于编码和存储则比较好。在存储和编码之前,RGB 图像要转换为 YUV 图像,而 YUV 图像在显示之前通常有必要转换回 RGB。这里显示的时候 YUV 转成 RGB 通常是硬件或者软件内部做了,我们写代码开发的时候YUV转 RGB显示到屏幕这个过程通常是透明的。

看图说话。

所以从内存的角度来说:yuv单位像素使用的内存更低,但是两者表示的效果是一致的;也可以认为rgb过度绘制了,把人眼无法分辨的区域也进行了绘制。

下面看一下两者之间内存暂用对比,这里采用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格式,仍然有不同的区分。

尤其要注意的是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 RGBImagedef 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")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

注意下opencv内部对于nv12等不同格式YUV与RGB转换都是非常到位的,合理使用opencv工具可以起到提升工作效率的效果

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多