图像和流媒体 -- 详解YUV数据格式

如需转载请注明出处:https://blog.csdn.net/qq_29350001/article/details/78283369

我们在讲 FFmpeg 系列的时候,有提到 YUV 的。其中包括YUV播放器、简单的YUV格式介绍。

参看:FFmpeg再学习 -- 视音频基础知识

接下来详细研究一下:

参看:YUV -- 维基百科

参看:图文详解YUV420数据格式

一、YUV简介

YUV,是一种颜色编码方法。常使用在各个影像处理元件中。 YUV在对照片或影片编码时,考虑到人类的感知能力,允许降低色度的带宽。
YUV是编译true-color颜色空间(color space)的种类,Y'UV, YUV, YCbCr,YPbPr等专有名词都可以称为YUV,彼此有重叠。“Y”表示明亮度(Luminance、Luma),“U”和“V”则是色度、浓度(Chrominance、Chroma),Y′UV, YUV, YCbCr, YPbPr所指涉的范围,常有混淆或重叠的情况。从历史的演变来说,其中YUV和Y'UV通常用来编码电视的模拟信号,而YCbCr则是用来描述数位的影像信号,适合影片与图片压缩以及传输,例如MPEG、JPEG。 但在现今,YUV通常已经在电脑系统上广泛使用。
Y'代表明亮度(luma; brightness)而U与V储存色度(色讯; chrominance; color)部分; 亮度(luminance)记作Y,而Y'的prime符号记作伽玛校正。
YUV Formats分成两个格式:
紧缩格式(packed formats):将Y、U、V值储存成Macro Pixels阵列,和RGB的存放方式类似。
平面格式(planar formats):将Y、U、V的三个分量分别存放在不同的矩阵中。

紧缩格式(packed format)中的YUV是混合在一起的,对于YUV4:4:4格式而言,用紧缩格式很合适的,因此就有了UYVY、YUYV等。

平面格式(planar formats)是指每Y分量,U分量和V分量都是以独立的平面组织的,也就是说所有的U分量必须在Y分量后面,而V分量在所有的U分量后面,此一格式适用于采样(subsample)。平面格式(planar format)有I420(4:2:0)、YV12、IYUV等。

二、历史

Y'UV的发明是由于彩色电视与黑白电视的过渡时期[1]。黑白视讯只有Y(Luma,Luminance)视讯,也就是灰阶值。到了彩色电视规格的制定,是以YUV/YIQ的格式来处理彩色电视图像,把UV视作表示彩度的C(Chrominance或Chroma),如果忽略C信号,那么剩下的Y(Luma)信号就跟之前的黑白电视信号相同,这样一来便解决彩色电视机与黑白电视机的相容问题。Y'UV最大的优点在于只需占用极少的带宽。
因为UV分别代表不同颜色信号,所以直接使用R与B信号表示色度的UV。 也就是说UV信号告诉了电视要偏移某象素的的颜色,而不改变其亮度。 或者UV信号告诉了显示器使得某个颜色亮度依某个基准偏移。 UV的值越高,代表该像素会有更饱和的颜色。
彩色图像记录的格式,常见的有RGB、YUV、CMYK等。彩色电视最早的构想是使用RGB三原色来同时传输。这种设计方式是原来黑白带宽的3倍,在当时并不是很好的设计。RGB诉求于人眼对色彩的感应,YUV则着重于视觉对于亮度的敏感程度,Y代表的是亮度,UV代表的是彩度(因此黑白电影可省略UV,相近于RGB),分别用Cr和Cb来表示,因此YUV的记录通常以Y:UV的格式呈现。

三、常用的YUV格式

为节省带宽起见,大多数YUV格式平均使用的每像素位数都少于24位元。主要的抽样(subsample)格式有YCbCr 4:2:0、YCbCr 4:2:2、YCbCr 4:1:1和YCbCr 4:4:4。YUV的表示法称为A:B:C表示法:
4:4:4表示完全取样。
4:2:2表示2:1的水平取样,垂直完全采样。
4:2:0表示2:1的水平取样,垂直2:1采样。
4:1:1表示4:1的水平取样,垂直完全采样。
最常用Y:UV记录的比重通常1:1或2:1,DVD-Video是以YUV 4:2:0的方式记录,也就是我们俗称的I420,YUV4:2:0并不是说只有U(即Cb), V(即Cr)一定为0,而是指U:V互相援引,时见时隐,也就是说对于每一个行,只有一个U或者V分量,如果一行是4:2:0的话,下一行就是4:0:2,再下一行是4:2:0...以此类推。至于其他常见的YUV格式有YUY2、YUYV、YVYU、UYVY、AYUV、Y41P、Y411、Y211、IF09、IYUV、YV12、YVU9、YUV411、YUV420等。

 

用三个图来直观地表示采集的方式吧,以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量。

先记住下面这段话,以后提取每个像素的YUV分量会用到。
YUV 4:4:4采样,每一个Y对应一组UV分量。
YUV 4:2:2采样,每两个Y共用一组UV分量。 
YUV 4:2:0采样,每四个Y共用一组UV分量。 

四、存储方式

下面我用图的形式给出常见的YUV码流的存储方式,并在存储方式后面附有取样每个像素点的YUV数据的方法,其中,Cb、Cr的含义等同于U、V。

(1) YUVY 格式 (属于YUV422)

YUYV为YUV422采样的存储格式中的一种,相邻的两个Y共用其相邻的两个Cb、Cr,分析,对于像素点Y'00、Y'01 而言,其Cb、Cr的值均为 Cb00、Cr00,其他的像素点的YUV取值依次类推。 

(2) UYVY 格式 (属于YUV422)

UYVY格式也是YUV422采样的存储格式中的一种,只不过与YUYV不同的是UV的排列顺序不一样而已,还原其每个像素点的YUV值的方法与上面一样。

(3) YUV422P(属于YUV422)

YUV422P也属于YUV422的一种,它是一种Plane模式,即平面模式,并不是将YUV数据交错存储,而是先存放所有的Y分量,然后存储所有的U(Cb)分量,最后存储所有的V(Cr)分量,如上图所示。其每一个像素点的YUV值提取方法也是遵循YUV422格式的最基本提取方法,即两个Y共用一个UV。比如,对于像素点Y'00、Y'01 而言,其Cb、Cr的值均为 Cb00、Cr00。

(4)YV12,YU12格式(属于YUV420)

YU12和YV12属于YUV420格式,也是一种Plane模式,将Y、U、V分量分别打包,依次存储。其每一个像素点的YUV数据提取遵循YUV420格式的提取方式,即4个Y分量共用一组UV。注意,上图中,Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00,其他依次类推。

(5)NV12、NV21(属于YUV420)

NV12和NV21属于YUV420格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)为交错存储,而不是分为三个plane。其提取方式与上一种类似,即Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00

(6)了解更多

想了解更多类型的存储方式,参看:YUV Formats

中文的有人翻译了,参看:V4L2文档翻译(十)

 

五、YUV与RGB的转换公式

参看:YUV -- 维基百科

这部分我没看懂...   

六、推荐一个工具

参看:Image Analyzer

参看:GSG:Debugging projects

参看:软件操作演示

文章最后,是有视频都哦!!可以下载下来看看。

 

 

七、RGB 介绍

既然都提到RGB了,就简单讲一下吧。

参看:三原色光模式 -- 维基百科

三原色光模式(RGB color model),又称RGB颜色模型或红绿蓝颜色模型,是一种加色模型,将红(Red)、绿(Green)、蓝(Blue)三原色的色光以不同的比例相加,以产生多种多样的色光。(且三原色的红绿蓝不可能用其他单色光合成)

三原色光的相加:红光加绿光为黄光,黄光加蓝光为白光

再有一个是  RGB颜色查询对照表  

 

八、像素

最后,我要讲一下像素,上面提到很多次了。

参看:分辨率和像素是什么关系? -- 知乎

像素即px,是画面中最小的点(单位色块)。像素的大小是没有固定长度值的,不同设备上1个单位像素色块的大小是不一样的。
分辨率=画面水平方向的像素值 * 画面垂直方向的像素值。分辨率可以分为两方面:屏幕分辨率和图像分辨率。

1. 屏幕分辨率:     

例如,屏幕分辨率是1024×768,也就是说设备屏幕的水平方向上有1024个像素点,垂直方向上有768个像素点。像素的大小是没有固定长度的,不同设备上一个单位像素色块的大小是不一样的。    例如,尺寸面积大小相同的两块屏幕,分辨率大小可以是不一样的,分辨率高的屏幕上面像素点(色块)就多,所以屏幕内可以展示的画面就更细致,单个色块面积更小。而分辨率低的屏幕上像素点(色块)更少,单个像素面积更大,可以显示的画面就没那么细致。

2. 图像分辨率:    

例如,一张图片分辨率是500x200,也就是说这张图片在屏幕上按1:1放大时,水平方向有500个像素点(色块),垂直方向有200个像素点(色块)。    在同一台设备上,图片分辨率越高,这张图片1:1放大时,图片面积越大;图片分辨率越低,这张图片1:1缩放时,图片面积越小。(可以理解为图片的像素点和屏幕的像素点是一个一个对应的)。    但是,在屏幕上把图片超过100%放大时,为什么图片上像素色块也变的越大,其实是设备通过算法对图像进行了像素补足,我们把图片放的很大后看到的一块一块的方格子,虽然理解为一个图像像素,但是其实是已经补充了很多个屏幕像素;同理,把图片小于100%缩小时,也是通过算法将图片像素进行减少。     

最后,虽然不同设备上像素块大小会不一样,但是同一台硬件设备上的屏幕分辨率、像素块大小是不会变的。PC电脑上之所以可以调整屏幕分辨率,其实也是通过算法转换了。

 

举个例子吧,我用的华为P8手机(是该换了...),看看其参数。

这里就有摄像头参数介绍了,后置摄像头 1300万像素。或者说 vivo X9前置2000万柔光双摄,这个广告熟不熟悉。

1300万像素啥意思呢?

打开手机相机设置,查看分辨率,你可以看到有如下几个选项。

其中图片大小最大的 13M 4160x3120(4:3) 即摄像头最大像素

4160x3120 = 12979200 = 1300 万像素

如需转载请注明出处:https://blog.csdn.net/qq_29350001/article/details/78283369

 

  • 26
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要获取一张yuv420图片的数据,可以使用libjpeg-turbo提供的API函数进行操作。以下是获取yuv420图片数据的基本步骤: 1. 打开yuv420图片文件并读取数据,可以使用标准的文件操作函数(例如fopen、fread等)。 2. 创建libjpeg-turbo解码器对象,可以使用tjInitDecompress函数。此函数返回一个指向解码器对象的指针。 3. 将yuv420图片数据解码为RGB格式,可以使用tjDecodeYUV函数。此函数将yuv420数据转换为RGB数据,并将转换后的数据存储在一个指定的缓冲区中。 4. 关闭解码器对象,可以使用tjDestroy函数。此函数释放解码器对象占用的内存。 以下是一个获取yuv420图片数据的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <turbojpeg.h> int main(int argc, char **argv) { int width, height, subsample, colorspace; unsigned char *rgb_buffer = NULL; FILE *f = NULL; tjhandle handle = NULL; // 打开yuv420图片文件并读取数据 f = fopen("test.yuv", "rb"); if (f == NULL) { printf("Error: failed to open file\n"); return -1; } fseek(f, 0, SEEK_END); long file_size = ftell(f); fseek(f, 0, SEEK_SET); unsigned char *yuv_buffer = (unsigned char *)malloc(file_size); fread(yuv_buffer, file_size, 1, f); fclose(f); // 创建解码器对象 handle = tjInitDecompress(); if (handle == NULL) { printf("Error: failed to create decompressor\n"); goto fail; } // 获取图片信息 if (tjDecompressHeader3(handle, yuv_buffer, file_size, &width, &height, &subsample, &colorspace) != 0) { printf("Error: failed to get image info\n"); goto fail; } // 分配RGB缓冲区 rgb_buffer = (unsigned char *)malloc(width * height * TJPF_RGB); if (rgb_buffer == NULL) { printf("Error: failed to allocate RGB buffer\n"); goto fail; } // 将yuv420数据解码为RGB数据 if (tjDecodeYUV(handle, yuv_buffer, file_size, subsample, rgb_buffer, width, 0, height, TJPF_RGB, TJFLAG_FASTDCT) != 0) { printf("Error: failed to decode image\n"); goto fail; } // 关闭解码器对象 tjDestroy(handle); // 在这里处理RGB数据 // 释放内存 free(yuv_buffer); free(rgb_buffer); return 0; fail: if (handle != NULL) { tjDestroy(handle); } if (yuv_buffer != NULL) { free(yuv_buffer); } if (rgb_buffer != NULL) { free(rgb_buffer); } return -1; } ``` 在上面的示例代码中,我们首先打开yuv420图片文件并读取数据,然后创建libjpeg-turbo解码器对象。接下来,我们使用tjDecompressHeader3函数获取图片信息,并分配RGB缓冲区。最后,我们使用tjDecodeYUV函数将yuv420数据解码为RGB数据,并将其存储在RGB缓冲区中。在解码完成后,我们关闭解码器对象,并释放占用的内存。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

聚优致成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值